def testRemovesMmapOnDispose(self): mapper = PythonMapper(DLL_PATH) try: sys.modules['csv'] = object() mapper.Dispose() self.assertFalse('mmap' in sys.modules) self.assertFalse('_csv' in sys.modules) self.assertFalse('csv' in sys.modules) finally: mapper.Dispose()
def testRemovesMmapOnDispose(self): mapper = PythonMapper(os.path.join("build", "ironclad", "python26.dll")) try: sys.modules['csv'] = object() mapper.Dispose() self.assertFalse('mmap' in sys.modules) self.assertFalse('_csv' in sys.modules) self.assertFalse('csv' in sys.modules) finally: mapper.Dispose()
def testPyList_DeallocDecRefsItemsAndCallsCorrectFreeFunction(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) deallocTypes = CreateTypes(mapper) calls = [] def CustomFree(ptr): calls.append(ptr) mapper.PyObject_Free(listPtr) self.freeDgt = dgt_void_ptr(CustomFree) CPyMarshal.WriteFunctionPtrField(mapper.PyList_Type, PyTypeObject, "tp_free", self.freeDgt) listPtr = mapper.Store([1, 2, 3]) itemPtrs = [] dataStore = CPyMarshal.ReadPtrField(listPtr, PyListObject, "ob_item") for _ in range(3): itemPtrs.append(CPyMarshal.ReadPtr(dataStore)) dataStore = OffsetPtr(dataStore, CPyMarshal.PtrSize) mapper.IC_PyList_Dealloc(listPtr) for itemPtr in itemPtrs: self.assertEquals(itemPtr in frees, True, "did not decref item") self.assertEquals(calls, [listPtr], "did not call type's free function") mapper.Dispose() deallocTypes()
def testDeleteList(self): deallocs = [] mapper = PythonMapper(GetAllocatingTestAllocator([], deallocs)) deallocTypes = CreateTypes(mapper) item1 = object() item2 = object() itemPtr1 = mapper.Store(item1) itemPtr2 = mapper.Store(item2) listPtr = mapper.PyList_New(0) mapper.PyList_Append(listPtr, itemPtr1) mapper.PyList_Append(listPtr, itemPtr2) mapper.DecRef(itemPtr1) mapper.DecRef(itemPtr2) self.assertEquals(len(deallocs), 1, "should have deallocated original data block only at this point") dataStore = CPyMarshal.ReadPtrField(listPtr, PyListObject, "ob_item") mapper.DecRef(listPtr) listDeallocs = deallocs[1:] self.assertEquals(len(listDeallocs), 4, "should dealloc list object; data store; both items") expectedDeallocs = [listPtr, dataStore, itemPtr1, itemPtr2] self.assertEquals(set(listDeallocs), set(expectedDeallocs), "deallocated wrong stuff") mapper.Dispose() deallocTypes()
def testPyList_New_NonZeroLength(self): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) deallocTypes = CreateTypes(mapper) del allocs[:] SIZE = 27 listPtr = mapper.PyList_New(SIZE) listStruct = Marshal.PtrToStructure(listPtr, PyListObject) self.assertEquals(listStruct.ob_refcnt, 1, "bad refcount") self.assertEquals(listStruct.ob_type, mapper.PyList_Type, "bad type") self.assertEquals(listStruct.ob_size, SIZE, "bad ob_size") self.assertEquals(listStruct.allocated, SIZE, "bad allocated") dataPtr = listStruct.ob_item self.assertNotEquals(dataPtr, IntPtr.Zero, "failed to allocate space for data") expectedAllocs = [(dataPtr, (SIZE * CPyMarshal.PtrSize)), (listPtr, Marshal.SizeOf(PyListObject))] self.assertEquals(set(allocs), set(expectedAllocs), "allocated wrong") for _ in range(SIZE): self.assertEquals(CPyMarshal.ReadPtr(dataPtr), IntPtr.Zero, "failed to zero memory") dataPtr = OffsetPtr(dataPtr, CPyMarshal.PtrSize) mapper.Dispose() deallocTypes()
def testLoadsStubWhenPassedPathAndUnloadsOnDispose(self): mapper = PythonMapper(DLL_PATH) try: self.assertNotEquals(Unmanaged.GetModuleHandle(PYTHON_DLL), IntPtr.Zero, "library not mapped by construction") self.assertNotEquals(PythonMapper._Py_NoneStruct, IntPtr.Zero, "mapping not set up") # weak side-effect test to hopefully prove that ReadyBuiltinTypes has been called self.assertEquals(CPyMarshal.ReadPtrField(mapper.PyLong_Type, PyTypeObject, "tp_base"), mapper.PyBaseObject_Type) mapper.Dispose() self.assertEquals(Unmanaged.GetModuleHandle(PYTHON_DLL), IntPtr.Zero, "library not unmapped by Dispose") finally: mapper.Dispose()
def testShrink(self): allocs = [] frees = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, frees)) deallocTypes = CreateTypes(mapper) del allocs[:] oldLength = 365 newLength = 20 ptrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(IntPtr)) try: strPtr = mapper.PyString_FromStringAndSize(IntPtr.Zero, oldLength) Marshal.WriteIntPtr(ptrPtr, strPtr) baseSize = Marshal.SizeOf(PyStringObject) self.assertEquals(allocs, [(strPtr, oldLength + baseSize)], "allocated wrong") self.assertEquals(mapper._PyString_Resize(ptrPtr, newLength), 0, "bad return on success") self.assertHasStringType(strPtr, mapper) self.assertStringObjectHasLength(strPtr, newLength) self.assertEquals(allocs, [(strPtr, oldLength + baseSize)], "unexpected extra alloc") self.assertEquals(frees, [], "unexpected frees") finally: mapper.Dispose() Marshal.FreeHGlobal(ptrPtr) deallocTypes()
def testPyMem_Free_Null(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) mapper.PyMem_Free(IntPtr.Zero) self.assertEquals(frees, [], "freed inappropriately") mapper.Dispose()
def testStoreTupleCreatesTupleType(self): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) typeBlock = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject())) mapper.RegisterData("PyTuple_Type", typeBlock) theTuple = (0, 1, 2) tuplePtr = mapper.Store(theTuple) self.assertEquals( CPyMarshal.ReadPtrField(tuplePtr, PyTupleObject, "ob_type"), typeBlock, "wrong type") dataPtr = OffsetPtr(tuplePtr, Marshal.OffsetOf(PyTupleObject, "ob_item")) for i in range(3): item = mapper.Retrieve(CPyMarshal.ReadPtr(dataPtr)) self.assertEquals(item, i, "did not store data") dataPtr = OffsetPtr(dataPtr, CPyMarshal.PtrSize) tuplePtr2 = mapper.Store(theTuple) self.assertEquals(tuplePtr2, tuplePtr, "didn't realise already had this tuple") self.assertEquals(mapper.RefCount(tuplePtr), 2, "didn't incref") mapper.Dispose() Marshal.FreeHGlobal(typeBlock)
def testLoadsModuleAndUnloadsOnDispose(self): mapper = PythonMapper(DLL_PATH) try: origcwd = os.getcwd() mapper.LoadModule(os.path.join("tests", "data", "setvalue.pyd"), "some.module") self.assertEquals(os.getcwd(), origcwd, "failed to restore working directory") self.assertNotEquals(Unmanaged.GetModuleHandle("setvalue.pyd"), IntPtr.Zero, "library not mapped by construction") mapper.Dispose() self.assertEquals(Unmanaged.GetModuleHandle("setvalue.pyd"), IntPtr.Zero, "library not unmapped by Dispose") self.assertEquals(Unmanaged.GetModuleHandle(PYTHON_DLL), IntPtr.Zero, "library not unmapped by Dispose") finally: mapper.Dispose()
def testRefCountIncRefDecRef(self): frees = [] allocator = GetAllocatingTestAllocator([], frees) mapper = PythonMapper(allocator) deallocTypes = CreateTypes(mapper) obj1 = object() ptr = mapper.Store(obj1) self.assertEquals(mapper.HasPtr(ptr), True) mapper.IncRef(ptr) self.assertEquals(mapper.RefCount(ptr), 2, "unexpected refcount") self.assertEquals(mapper.HasPtr(ptr), True) del frees[:] mapper.DecRef(ptr) self.assertEquals(mapper.RefCount(ptr), 1, "unexpected refcount") self.assertEquals(mapper.HasPtr(ptr), True) self.assertEquals(frees, [], "unexpected deallocations") mapper.DecRef(ptr) self.assertEquals(mapper.HasPtr(ptr), False) self.assertEquals(frees, [ptr], "unexpected deallocations") self.assertRaises(KeyError, lambda: mapper.PyObject_Free(ptr)) mapper.Dispose() deallocTypes()
def testZero(self): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) resultPtr = getattr(mapper, MALLOC_NAME)(0) self.assertEquals(allocs, [(resultPtr, 1)], "bad alloc") mapper.Dispose()
def testSomeItems(self): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) deallocTypes = CreateTypes(mapper) typeSpec = { "tp_basicsize": 32, "tp_itemsize": 64, } typePtr, deallocType = MakeTypePtr(mapper, typeSpec) del allocs[:] result = mapper.PyType_GenericAlloc(typePtr, 3) self.assertEquals(allocs, [(result, 224)], "allocated wrong") refcount = CPyMarshal.ReadIntField(result, PyObject, "ob_refcnt") self.assertEquals(refcount, 1, "bad initialisation") instanceType = CPyMarshal.ReadPtrField(result, PyObject, "ob_type") self.assertEquals(instanceType, typePtr, "bad type ptr") size = CPyMarshal.ReadIntField(result, PyVarObject, "ob_size") self.assertEquals(size, 3, "bad ob_size") headerSize = Marshal.SizeOf(PyVarObject) zerosPtr = OffsetPtr(result, headerSize) for i in range(224 - headerSize): self.assertEquals(CPyMarshal.ReadByte(zerosPtr), 0, "not zeroed") zerosPtr = OffsetPtr(zerosPtr, 1) mapper.Dispose() deallocTypes() deallocType()
def testBasicStoreRetrieveFree(self): frees = [] allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, frees)) deallocTypes = CreateTypes(mapper) del allocs[:] obj1 = object() ptr = mapper.Store(obj1) self.assertEquals(len(allocs), 1, "unexpected number of allocations") self.assertEquals(allocs[0], (ptr, Marshal.SizeOf(PyObject)), "unexpected result") self.assertNotEquals(ptr, IntPtr.Zero, "did not store reference") self.assertEquals(mapper.RefCount(ptr), 1, "unexpected refcount") self.assertEquals(CPyMarshal.ReadPtrField(ptr, PyObject, "ob_type"), mapper.PyBaseObject_Type, "nearly-opaque pointer had wrong type") obj2 = mapper.Retrieve(ptr) self.assertTrue(obj1 is obj2, "retrieved wrong object") self.assertEquals(frees, [], "unexpected deallocations") mapper.PyObject_Free(ptr) self.assertEquals(frees, [ptr], "unexpected deallocations") self.assertRaises(KeyError, lambda: mapper.PyObject_Free(ptr)) mapper.Dispose() deallocTypes()
def testDestroysObjectsOfUnmanagedTypesFirst(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) deallocTypes = CreateTypes(mapper) modulePtr = MakeAndAddEmptyModule(mapper) module = mapper.Retrieve(modulePtr) calls = [] def Del(instancePtr): calls.append(("del", instancePtr)) mapper.PyObject_Free(instancePtr) typeSpec = {'tp_name': 'klass', 'tp_dealloc': Del} typePtr, deallocType = MakeTypePtr(mapper, typeSpec) mapper.PyModule_AddObject(modulePtr, 'klass', typePtr) easyptr = mapper.Store(123) instance1 = module.klass() hardptr = mapper.Store(instance1) instance2 = module.klass() brokenptr = mapper.Store(instance2) CPyMarshal.WritePtrField(brokenptr, PyObject, 'ob_type', IntPtr.Zero) mapper.Dispose() self.assertEquals( frees.index(hardptr) < frees.index(easyptr), True, "failed to dealloc in correct order") self.assertEquals(calls, [('del', hardptr)], "failed to clean up klass instance") deallocType() deallocTypes()
def testStoreSameObjectIncRefsOriginal(self): frees = [] allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, frees)) deallocTypes = CreateTypes(mapper) del allocs[:] obj1 = object() result1 = mapper.Store(obj1) result2 = mapper.Store(obj1) self.assertEquals(allocs, [(result1, Marshal.SizeOf(PyObject))], "unexpected result") self.assertEquals(result1, result2, "did not return same ptr") self.assertEquals(mapper.RefCount(result1), 2, "did not incref") mapper.DecRef(result1) del frees[:] mapper.DecRef(result1) self.assertEquals(frees, [result1], "did not free memory") result3 = mapper.Store(obj1) self.assertEquals( allocs, [(result1, Marshal.SizeOf(PyObject)), (result3, Marshal.SizeOf(PyObject))], "unexpected result -- failed to clear reverse mapping?") mapper.Dispose() deallocTypes()
def testExtensionTypesAutoActualisable(self): discoveryModes = { "IncRef": lambda f, o: self.assertMaps(mapper, f, o, 5), "Retrieve": lambda f, o: self.assertMaps(mapper, f, o, 4), "DecRef": lambda f, o: self.assertMaps(mapper, f, o, 3), "RefCount": lambda f, o: self.assertMaps(mapper, f, o, 4), } allocator = HGlobalAllocator() mapper = PythonMapper(allocator) deallocTypes = CreateTypes(mapper) # delay deallocs to avoid types with the same addresses causing confusion userTypeDeallocs = [] try: for (mode, TestFunc) in discoveryModes.items(): typePtr, deallocType = MakeTypePtr(mapper, {"tp_name": mode + "Class"}) userTypeDeallocs.append(deallocType) objPtr = allocator.Alloc(Marshal.SizeOf(PyObject)) CPyMarshal.WriteIntField(objPtr, PyObject, "ob_refcnt", 2) CPyMarshal.WritePtrField(objPtr, PyObject, "ob_type", typePtr) discoveryFunc = getattr(mapper, mode) TestFunc(discoveryFunc, objPtr) finally: mapper.Dispose() for deallocFunc in userTypeDeallocs: deallocFunc() deallocTypes()
def testPySlice_DeallocDecRefsItemsAndCallsCorrectFreeFunction(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) deallocTypes = CreateTypes(mapper) calls = [] def CustomFree(ptr): calls.append(ptr) freeDgt = dgt_void_ptr(CustomFree) CPyMarshal.WriteFunctionPtrField(mapper.PySlice_Type, PyTypeObject, "tp_free", freeDgt) slicePtr = mapper.Store(slice(1, 2, 3)) del frees[:] mapper.IC_PySlice_Dealloc(slicePtr) self.assertEquals(len(frees), 3, "did not dealloc each item") self.assertEquals(calls, [slicePtr], "did not call type's free function") mapper.PyObject_Free(slicePtr) mapper.Dispose() deallocTypes()
def testStoreStringCreatesStringType(self): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) deallocTypes = CreateTypes(mapper) del allocs[:] testString = "fnord" + self.getStringWithValues(1, 256) testBytes = self.byteArrayFromString(testString) testData = self.ptrFromByteArray(testBytes) testLength = len(testString) try: strPtr = mapper.Store(testString) baseSize = Marshal.SizeOf(PyStringObject) self.assertEquals(allocs, [(strPtr, testLength + baseSize)], "allocated wrong") self.assertHasStringType(strPtr, mapper) self.assertStringObjectHasLength(strPtr, testLength) self.assertStringObjectHasDataBytes(strPtr, testBytes) self.assertEquals(mapper.Retrieve(strPtr), testString, "failed to read string data") strPtr2 = mapper.Store(testString) self.assertEquals(strPtr2, strPtr, "did not remember already had this string") self.assertEquals(mapper.RefCount(strPtr), 2, "did not incref on store") finally: mapper.Dispose() deallocTypes()
def testPyTuple_DeallocDecRefsItemsAndCallsCorrectFreeFunction(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) deallocTypes = CreateTypes(mapper) calls = [] def CustomFree(ptr): calls.append(ptr) freeDgt = dgt_void_ptr(CustomFree) CPyMarshal.WriteFunctionPtrField(mapper.PyTuple_Type, PyTypeObject, "tp_free", freeDgt) tuplePtr, itemPtrs = MakeTuple(mapper, (1, 2, 3)) mapper.IC_PyTuple_Dealloc(tuplePtr) for itemPtr in itemPtrs: self.assertEquals(itemPtr in frees, True, "did not decref item") self.assertEquals(calls, [tuplePtr], "did not call type's free function") mapper.PyObject_Free(tuplePtr) mapper.Dispose() deallocTypes()
def testCreateStringWithData(self): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) deallocTypes = CreateTypes(mapper) del allocs[:] try: testString = "we also run the shovel racket" + self.getStringWithValues( 0, 256) testBytes = self.byteArrayFromString(testString) testData = self.ptrFromByteArray(testBytes) testLength = len(testString) strPtr = mapper.PyString_FromStringAndSize(testData, testLength) baseSize = Marshal.SizeOf(PyStringObject) self.assertEquals(allocs, [(strPtr, testLength + baseSize)], "allocated wrong") self.assertHasStringType(strPtr, mapper) self.assertStringObjectHasLength(strPtr, testLength) self.assertStringObjectHasDataBytes(strPtr, testBytes) self.assertEquals(mapper.Retrieve(strPtr), testString, "failed to read string data") finally: mapper.Dispose() deallocTypes()
def testCreateEmptyString(self): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) deallocTypes = CreateTypes(mapper) del allocs[:] try: testString = "we run the grease racket in this town" + self.getStringWithValues( 0, 256) testLength = len(testString) strPtr = mapper.PyString_FromStringAndSize(IntPtr.Zero, testLength) baseSize = Marshal.SizeOf(PyStringObject) self.assertEquals(allocs, [(strPtr, testLength + baseSize)], "allocated wrong") self.assertStringObjectHasLength(strPtr, testLength) self.assertHasStringType(strPtr, mapper) testBytes = self.byteArrayFromString(testString) self.fillStringDataWithBytes(strPtr, testBytes) resultStr = mapper.Retrieve(strPtr) self.assertEquals(resultStr, testString, "failed to read string data") strPtr2 = mapper.Store(resultStr) self.assertEquals(strPtr2, strPtr, "did not remember already had this string") self.assertEquals(mapper.RefCount(strPtr), 2, "did not incref on store") finally: mapper.Dispose() deallocTypes()
def assertPyTuple_New_Works(self, length): allocs = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, [])) typeBlock = Marshal.AllocHGlobal(Marshal.SizeOf(PyTypeObject)) mapper.RegisterData("PyTuple_Type", typeBlock) tuplePtr = mapper.PyTuple_New(length) expectedSize = Marshal.SizeOf(PyTupleObject) + (CPyMarshal.PtrSize * (length - 1)) self.assertEquals(allocs, [(tuplePtr, expectedSize)], "bad alloc") tupleStruct = Marshal.PtrToStructure(tuplePtr, PyTupleObject) self.assertEquals(tupleStruct.ob_refcnt, 1, "bad refcount") self.assertEquals(tupleStruct.ob_type, mapper.PyTuple_Type, "bad type") self.assertEquals(tupleStruct.ob_size, length, "bad size") self.assertEquals(mapper.PyTuple_Size(tuplePtr), length, "should still work with uninitialised tuple imo") dataPtr = OffsetPtr(tuplePtr, Marshal.OffsetOf(PyTupleObject, "ob_item")) itemPtrs = [] for i in range(length): self.assertEquals(CPyMarshal.ReadPtr(dataPtr), IntPtr.Zero, "item memory not zeroed") itemPtr = mapper.Store(i + 100) CPyMarshal.WritePtr(dataPtr, itemPtr) itemPtrs.append(itemPtr) dataPtr = OffsetPtr(dataPtr, CPyMarshal.PtrSize) immutableTuple = mapper.Retrieve(tuplePtr) self.assertEquals(immutableTuple, tuple(i + 100 for i in range(length)), "broken") tuplePtr2 = mapper.Store(immutableTuple) self.assertEquals(tuplePtr2, tuplePtr, "didn't realise already had this object stored") self.assertEquals(mapper.RefCount(tuplePtr), 2, "didn't incref") mapper.Dispose() Marshal.FreeHGlobal(typeBlock)
def testIgnoresBridgeObjectsNotAllocatedByAllocator(self): obj = object() ptr = Marshal.AllocHGlobal(Marshal.SizeOf(PyObject)) CPyMarshal.WriteIntField(ptr, PyObject, 'ob_refcnt', 2) mapper = PythonMapper() mapper.StoreBridge(ptr, obj) mapper.Dispose()
def testPyMem_Free_NonNull(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) ptr = mapper.PyMem_Malloc(123) mapper.PyMem_Free(ptr) self.assertEquals(frees, [ptr], "did not free") mapper.Dispose()
def assertRuns(self, test_code=''): mapper = PythonMapper(DLL_PATH) try: exec '\n'.join([import_code, test_code]) in globals(), locals_ finally: mapper.Dispose() if module in sys.modules: del sys.modules[module]
def testNullPtr(self): allocs = [] frees = [] mapper = PythonMapper(GetAllocatingTestAllocator(allocs, frees)) mem = getattr(mapper, REALLOC_NAME)(IntPtr.Zero, 4) self.assertEquals(frees, []) self.assertEquals(allocs, [(mem, 4)]) mapper.Dispose()
def testNullPointers(self): allocator = GetDoNothingTestAllocator([]) mapper = PythonMapper(allocator) self.assertEquals(mapper.HasPtr(IntPtr.Zero), False) self.assertRaisesClr(CannotInterpretException, lambda: mapper.IncRef(IntPtr.Zero)) self.assertRaisesClr(CannotInterpretException, lambda: mapper.DecRef(IntPtr.Zero)) self.assertRaisesClr(CannotInterpretException, lambda: mapper.Retrieve(IntPtr.Zero)) self.assertRaisesClr(CannotInterpretException, lambda: mapper.RefCount(IntPtr.Zero)) mapper.Dispose()
def testIC_PyBaseObject_Init(self, mapper, _): "this function shouldn't do anything..." mapper = PythonMapper() deallocTypes = CreateTypes(mapper) self.assertEquals(mapper.IC_PyBaseObject_Init(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero), 0) mapper.Dispose() deallocTypes()
def testCanFreeWithRefCount0(self): frees = [] mapper = PythonMapper(GetAllocatingTestAllocator([], frees)) objPtr = mapper.Store(object()) CPyMarshal.WriteIntField(objPtr, PyObject, "ob_refcnt", 0) mapper.PyObject_Free(objPtr) self.assertEquals(frees, [objPtr], "didn't actually release memory") mapper.Dispose()