diff options
author | Carl Friedrich Bolz-Tereick <cfbolz@gmx.de> | 2021-01-30 16:30:07 +0100 |
---|---|---|
committer | Carl Friedrich Bolz-Tereick <cfbolz@gmx.de> | 2021-01-30 16:30:07 +0100 |
commit | 86b6e669b00a1475ce55d1599b7c0a0998654525 (patch) | |
tree | f10125fefceeb3eb839a3562953718c32e161ba4 /pypy | |
parent | pretty subtle bug: when iterating over a map dict, some items would go missing! (diff) | |
parent | merge branch to improve PyModule* (diff) | |
download | pypy-86b6e669b00a1475ce55d1599b7c0a0998654525.tar.gz pypy-86b6e669b00a1475ce55d1599b7c0a0998654525.tar.bz2 pypy-86b6e669b00a1475ce55d1599b7c0a0998654525.zip |
merge default
Diffstat (limited to 'pypy')
-rw-r--r-- | pypy/doc/whatsnew-head.rst | 10 | ||||
-rw-r--r-- | pypy/module/_winreg/interp_winreg.py | 148 | ||||
-rw-r--r-- | pypy/module/_winreg/moduledef.py | 5 | ||||
-rw-r--r-- | pypy/module/_winreg/test/test_winreg.py | 27 | ||||
-rw-r--r-- | pypy/module/cpyext/eval.py | 2 | ||||
-rw-r--r-- | pypy/module/cpyext/modsupport.py | 13 | ||||
-rw-r--r-- | pypy/module/cpyext/stubs.py | 7 | ||||
-rw-r--r-- | pypy/module/cpyext/test/test_cpyext.py | 4 |
8 files changed, 182 insertions, 34 deletions
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst index d78f81d90b..f932c3b2d8 100644 --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -45,6 +45,16 @@ Backport msvc detection from python3, which probably breaks using Visual Studio 2008 (MSVC9, or the version that used to be used to build CPython2.7 on Windows) +.. branch: py2.7-winreg + +Backport fixes to winreg adding reflection and fix for passing None (bpo +21151). + +.. branch: pymodule_new-const-charp + +Change parameter type of ``PyModule_New`` to ``const char*``, add +``PyModule_Check`` and ``PyModule_CheckExact`` + .. branch: map-improvements diff --git a/pypy/module/_winreg/interp_winreg.py b/pypy/module/_winreg/interp_winreg.py index c7af44d6da..dc29cd844c 100644 --- a/pypy/module/_winreg/interp_winreg.py +++ b/pypy/module/_winreg/interp_winreg.py @@ -6,6 +6,61 @@ from pypy.interpreter.error import OperationError, oefmt, wrap_windowserror from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib import rwinreg, rwin32 from rpython.rlib.rarithmetic import r_uint, intmask +from rpython.translator.tool.cbuild import ExternalCompilationInfo + + +# wrappers needed to call the reflection functions loaded at runtime +# using WINAPI convention +eci = ExternalCompilationInfo( + includes=['windows.h'], + post_include_bits=[ + "RPY_EXTERN LONG\n" + "pypy_RegChangeReflectionKey(FARPROC address, HKEY key);\n" + "RPY_EXTERN LONG\n" + "pypy_RegQueryReflectionKey(FARPROC address, HKEY key, LPBOOL isDisabled);\n" + "RPY_EXTERN LONG\n" + "pypy_RegDeleteKeyExA(FARPROC address, HKEY key, LPCSTR subkey,\n" + " REGSAM sam, DWORD reserved);\n" + ], + separate_module_sources=[''' + LONG + pypy_RegChangeReflectionKey(FARPROC address, HKEY key) { + LONG (WINAPI *func)(HKEY); + *(FARPROC*)&func = address; + return func(key); + } + + LONG + pypy_RegQueryReflectionKey(FARPROC address, HKEY key, LPBOOL isDisabled) { + LONG (WINAPI *func)(HKEY, LPBOOL); + *(FARPROC*)&func = address; + return func(key, isDisabled); + } + + LONG + pypy_RegDeleteKeyExA(FARPROC address, HKEY key, LPCSTR subkey, + REGSAM sam, DWORD reserved) { + LONG (WINAPI *func)(HKEY, LPCSTR, REGSAM, DWORD); + *(FARPROC*)&func = address; + return func(key, subkey, sam, reserved); + } + '''], +) +pypy_RegChangeReflectionKey = rffi.llexternal( + 'pypy_RegChangeReflectionKey', + [rffi.VOIDP, rwinreg.HKEY], + rffi.LONG, compilation_info=eci) + +pypy_RegQueryReflectionKey = rffi.llexternal( + 'pypy_RegQueryReflectionKey', + [rffi.VOIDP, rwinreg.HKEY, rwin32.LPBOOL], + rffi.LONG, compilation_info=eci) + +pypy_RegDeleteKeyExA = rffi.llexternal( + 'pypy_RegDeleteKeyExA', + [rffi.VOIDP, rwinreg.HKEY, rffi.CCHARP, rwinreg.REGSAM, rwin32.DWORD], + rffi.LONG, compilation_info=eci) + def raiseWindowsError(space, errcode, context): message = rwin32.FormatError(errcode) @@ -258,6 +313,8 @@ But the underlying API call doesn't return the type, Lame Lame Lame, DONT USE TH if ret != 0: raiseWindowsError(space, ret, 'RegQueryValue') length = intmask(bufsize_p[0]) + if length == 0: + return space.w_None return space.newtext(rffi.charp2strn(buf, length - 1)) def convert_to_regdata(space, w_value, typ): @@ -328,8 +385,7 @@ def convert_to_regdata(space, w_value, typ): else: # REG_BINARY and ALL unknown data types. if space.is_w(w_value, space.w_None): buflen = 0 - buf = lltype.malloc(rffi.CCHARP.TO, 1, flavor='raw') - buf[0] = '\0' + buf = lltype.nullptr(rffi.CCHARP.TO) else: try: value = w_value.readbuf_w(space) @@ -385,7 +441,10 @@ def convert_from_regdata(space, buf, buflen, typ): return space.newlist(l) else: # REG_BINARY and all other types - return space.newbytes(rffi.charpsize2str(buf, buflen)) + if buflen == 0: + return space.w_None + else: + return space.newbytes(rffi.charpsize2str(buf, buflen)) @unwrap_spec(value_name="text", typ=int) def SetValueEx(space, w_hkey, value_name, w_reserved, typ, w_value): @@ -424,7 +483,8 @@ the configuration registry. This helps the registry perform efficiently.""" try: ret = rwinreg.RegSetValueExA(hkey, value_name, 0, typ, buf, buflen) finally: - lltype.free(buf, flavor='raw') + if buf != lltype.nullptr(rffi.CCHARP.TO): + lltype.free(buf, flavor='raw') if ret != 0: raiseWindowsError(space, ret, 'RegSetValueEx') @@ -707,30 +767,84 @@ def ExpandEnvironmentStrings(space, w_source): except WindowsError as e: raise wrap_windowserror(space, e) + +class ReflectionFunction(object): + def __init__(self, name, stdcall_wrapper): + self.name = name + self.handle = lltype.nullptr(rffi.VOIDP.TO) + self.wrapper = stdcall_wrapper + + def check(self): + if self.handle != lltype.nullptr(rffi.VOIDP.TO): + return True + from rpython.rlib.rdynload import GetModuleHandle, dlsym + lib = GetModuleHandle("advapi32.dll") + try: + handle = dlsym(lib, self.name) + except KeyError: + return False + self.handle = handle + return True + + def call(self, *args): + assert self.handle != lltype.nullptr(rffi.VOIDP.TO) + return self.wrapper(self.handle, *args) + + +_RegDisableReflectionKey = ReflectionFunction( + "RegDisableReflectionKey", pypy_RegChangeReflectionKey) +_RegEnableReflectionKey = ReflectionFunction( + "RegEnableReflectionKey", pypy_RegChangeReflectionKey) +_RegQueryReflectionKey = ReflectionFunction( + "RegQueryReflectionKey", pypy_RegQueryReflectionKey) +_RegDeleteKeyExA = ReflectionFunction("RegDeleteKeyExA", pypy_RegDeleteKeyExA) + + def DisableReflectionKey(space, w_key): """Disables registry reflection for 32-bit processes running on a 64-bit Operating System. Will generally raise NotImplemented if executed on a 32-bit Operating System. If the key is not on the reflection list, the function succeeds but has no effect. Disabling reflection for a key does not affect reflection of any subkeys.""" - raise oefmt(space.w_NotImplementedError, - "not implemented on this platform") + if not _RegDisableReflectionKey.check(): + raise oefmt(space.w_NotImplementedError, + "not implemented on this platform") + else: + hkey = hkey_w(w_key, space) + ret = _RegDisableReflectionKey.call(hkey) + if ret != 0: + raiseWindowsError(space, ret, 'RegDisableReflectionKey') def EnableReflectionKey(space, w_key): """Restores registry reflection for the specified disabled key. Will generally raise NotImplemented if executed on a 32-bit Operating System. Restoring reflection for a key does not affect reflection of any subkeys.""" - raise oefmt(space.w_NotImplementedError, - "not implemented on this platform") + if not _RegEnableReflectionKey.check(): + raise oefmt(space.w_NotImplementedError, + "not implemented on this platform") + else: + hkey = hkey_w(w_key, space) + ret = _RegEnableReflectionKey.call(hkey) + if ret != 0: + raiseWindowsError(space, ret, 'RegEnableReflectionKey') def QueryReflectionKey(space, w_key): """bool = QueryReflectionKey(hkey) - Determines the reflection state for the specified key. Will generally raise NotImplemented if executed on a 32-bit Operating System.""" - raise oefmt(space.w_NotImplementedError, - "not implemented on this platform") + if not _RegQueryReflectionKey.check(): + raise oefmt(space.w_NotImplementedError, + "not implemented on this platform") + else: + hkey = hkey_w(w_key, space) + with lltype.scoped_alloc(rwin32.LPBOOL.TO, 1) as isDisabled: + ret = _RegQueryReflectionKey.call(hkey, isDisabled) + if ret != 0: + raiseWindowsError(space, ret, 'RegQueryReflectionKey') + return space.newbool(intmask(isDisabled[0]) != 0) -@unwrap_spec(subkey="text") -def DeleteKeyEx(space, w_key, subkey): + +@unwrap_spec(sub_key="text", access=r_uint, reserved=int) +def DeleteKeyEx(space, w_key, sub_key, access=rwinreg.KEY_WOW64_64KEY, reserved=0): """DeleteKeyEx(key, sub_key, sam, res) - Deletes the specified key. key is an already open key, or any one of the predefined HKEY_* constants. @@ -744,5 +858,11 @@ def DeleteKeyEx(space, w_key, subkey): If the method succeeds, the entire key, including all of its values, is removed. If the method fails, a WindowsError exception is raised. On unsupported Windows versions, NotImplementedError is raised.""" - raise oefmt(space.w_NotImplementedError, - "not implemented on this platform") + if not _RegDeleteKeyExA.check(): + raise oefmt(space.w_NotImplementedError, + "not implemented on this platform") + else: + hkey = hkey_w(w_key, space) + ret = _RegDeleteKeyExA.call(hkey, sub_key, access, reserved) + if ret != 0: + raiseWindowsError(space, ret, 'RegDeleteKeyEx') diff --git a/pypy/module/_winreg/moduledef.py b/pypy/module/_winreg/moduledef.py index d865ca691e..b20bd15d62 100644 --- a/pypy/module/_winreg/moduledef.py +++ b/pypy/module/_winreg/moduledef.py @@ -72,3 +72,8 @@ to see what constants are used, and where.""" for name, value in constants.iteritems(): interpleveldefs[name] = "space.wrap(%s)" % (value,) + + import pypy.module.sys.version + if pypy.module.sys.version.CPYTHON_VERSION < (3, 6): + del interpleveldefs["REG_QWORD"] + del interpleveldefs["REG_QWORD_LITTLE_ENDIAN"] diff --git a/pypy/module/_winreg/test/test_winreg.py b/pypy/module/_winreg/test/test_winreg.py index ae2d8ee227..1d25cbb900 100644 --- a/pypy/module/_winreg/test/test_winreg.py +++ b/pypy/module/_winreg/test/test_winreg.py @@ -31,6 +31,7 @@ class AppTestFfi: def setup_class(cls): import _winreg + from platform import machine space = cls.space cls.root_key = _winreg.HKEY_CURRENT_USER cls.test_key_name = "SOFTWARE\\Pypy Registry Test Key - Delete Me" @@ -38,6 +39,7 @@ class AppTestFfi: cls.w_test_key_name = space.wrap(cls.test_key_name) cls.w_canSaveKey = space.wrap(canSaveKey) cls.w_tmpfilename = space.wrap(str(udir.join('winreg-temp'))) + cls.w_win64_machine = space.wrap(machine() == "AMD64") test_data = [ ("Int Value", 0xFEDCBA98, _winreg.REG_DWORD), @@ -45,6 +47,7 @@ class AppTestFfi: ("Unicode Value", u"A unicode Value", _winreg.REG_SZ), ("Str Expand", "The path is %path%", _winreg.REG_EXPAND_SZ), ("Multi Str", ["Several", "string", u"values"], _winreg.REG_MULTI_SZ), + ("Raw None", None, _winreg.REG_BINARY), ("Raw data", "binary"+chr(0)+"data", _winreg.REG_BINARY), ] cls.w_test_data = space.wrap(test_data) @@ -175,14 +178,19 @@ class AppTestFfi: def test_delete(self): # must be run after test_SetValueEx - from _winreg import OpenKey, KEY_ALL_ACCESS, DeleteValue, DeleteKey + from _winreg import OpenKey, KEY_ALL_ACCESS, DeleteValue, DeleteKey, DeleteKeyEx key = OpenKey(self.root_key, self.test_key_name, 0, KEY_ALL_ACCESS) sub_key = OpenKey(key, "sub_key", 0, KEY_ALL_ACCESS) for name, value, type in self.test_data: DeleteValue(sub_key, name) - DeleteKey(key, "sub_key") + if self.win64_machine: + DeleteKeyEx(key, "sub_key", KEY_ALL_ACCESS, 0) + else: + DeleteKey(key, "sub_key") + + raises(OSError, OpenKey, key, "sub_key") def test_connect(self): from _winreg import ConnectRegistry, HKEY_LOCAL_MACHINE @@ -255,3 +263,18 @@ class AppTestFfi: raises(NotImplementedError, DeleteKeyEx, self.root_key, self.test_key_name) + def test_reflection(self): + import sys + from _winreg import DisableReflectionKey, EnableReflectionKey, \ + QueryReflectionKey, OpenKey, HKEY_LOCAL_MACHINE + # Adapted from lib-python test + if not self.win64_machine: + skip("Requires 64-bit host") + # Test that we can call the query, enable, and disable functions + # on a key which isn't on the reflection list with no consequences. + with OpenKey(HKEY_LOCAL_MACHINE, "Software") as key: + # HKLM\Software is redirected but not reflected in all OSes + assert QueryReflectionKey(key) + assert EnableReflectionKey(key) is None + assert DisableReflectionKey(key) is None + assert QueryReflectionKey(key) diff --git a/pypy/module/cpyext/eval.py b/pypy/module/cpyext/eval.py index a9bb200094..5e16133b94 100644 --- a/pypy/module/cpyext/eval.py +++ b/pypy/module/cpyext/eval.py @@ -64,7 +64,7 @@ def PyEval_GetFrame(space): caller = space.getexecutioncontext().gettopframe_nohidden() return caller # borrowed ref, may be null -@cpython_api([PyCodeObject, PyObject, PyObject], PyObject) +@cpython_api([PyObject, PyObject, PyObject], PyObject) def PyEval_EvalCode(space, w_code, w_globals, w_locals): """This is a simplified interface to PyEval_EvalCodeEx(), with just the code object, and the dictionaries of global and local variables. diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py index ea27269b5b..b5d212b81b 100644 --- a/pypy/module/cpyext/modsupport.py +++ b/pypy/module/cpyext/modsupport.py @@ -1,7 +1,7 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import cpython_api, cpython_struct, \ METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING, \ - METH_NOARGS, METH_O, METH_VARARGS + METH_NOARGS, METH_O, METH_VARARGS, build_type_checkers from pypy.module.cpyext.pyobject import PyObject, as_pyobj from pypy.interpreter.module import Module from pypy.module.cpyext.methodobject import ( @@ -11,7 +11,9 @@ from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall from pypy.module.cpyext.state import State from pypy.interpreter.error import oefmt -@cpython_api([rffi.CCHARP], PyObject) +PyModule_Check, PyModule_CheckExact = build_type_checkers("Module", Module) + +@cpython_api([CONST_STRING], PyObject) def PyModule_New(space, name): """ Return a new module object with the __name__ attribute set to name. @@ -116,13 +118,6 @@ def convert_method_defs(space, dict_w, methods, w_type, w_self=None, name=None): dict_w[methodname] = w_obj -@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL) -def PyModule_Check(space, w_obj): - w_type = space.gettypeobject(Module.typedef) - w_obj_type = space.type(w_obj) - return int(space.is_w(w_type, w_obj_type) or - space.issubtype_w(w_obj_type, w_type)) - @cpython_api([PyObject], PyObject, result_borrowed=True) def PyModule_GetDict(space, w_mod): if PyModule_Check(space, w_mod): diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py index 3dba4ffa8a..23e818ee94 100644 --- a/pypy/module/cpyext/stubs.py +++ b/pypy/module/cpyext/stubs.py @@ -1203,13 +1203,6 @@ def PyMethod_ClearFreeList(space): """ raise NotImplementedError -@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL) -def PyModule_CheckExact(space, p): - """Return true if p is a module object, but not a subtype of - PyModule_Type. - """ - raise NotImplementedError - @cpython_api([PyObject], rffi.CCHARP) def PyModule_GetFilename(space, module): """Return the name of the file from which module was loaded using module's diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py index 997357716a..3b5d41bf38 100644 --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -34,7 +34,9 @@ class TestApi: def test_signature(self): common_functions = api.FUNCTIONS_BY_HEADER[api.pypy_decl] assert 'PyModule_Check' in common_functions - assert common_functions['PyModule_Check'].argtypes == [api.PyObject] + assert common_functions['PyModule_Check'].argtypes == [cts.gettype("void *")] + assert 'PyModule_GetDict' in common_functions + assert common_functions['PyModule_GetDict'].argtypes == [api.PyObject] class SpaceCompiler(SystemCompilationInfo): |