From ecee4a76a7d81bb5bce547bd3e822a8cc9fffcab Mon Sep 17 00:00:00 2001 From: Yonatan Goldschmidt Date: Sat, 19 Dec 2020 18:34:53 +0200 Subject: [PATCH 1/3] Clean up teleportation.py Remove unused imports & remove Py2 notation from names --- rpyc/utils/teleportation.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/rpyc/utils/teleportation.py b/rpyc/utils/teleportation.py index 5fa5f2b0..922390f5 100644 --- a/rpyc/utils/teleportation.py +++ b/rpyc/utils/teleportation.py @@ -1,9 +1,5 @@ import opcode -import sys -try: - import __builtin__ -except ImportError: - import builtins as __builtin__ # noqa: F401 + from rpyc.lib.compat import is_py_gte38 from types import CodeType, FunctionType from rpyc.core import brine, netref @@ -67,16 +63,16 @@ def _export_codeobj(cobj): def export_function(func): - func_closure = func.__closure__ - func_code = func.__code__ - func_defaults = func.__defaults__ + closure = func.__closure__ + code = func.__code__ + defaults = func.__defaults__ - if func_closure: + if closure: raise TypeError("Cannot export a function closure") - if not brine.dumpable(func_defaults): - raise TypeError("Cannot export a function with non-brinable defaults (func_defaults)") + if not brine.dumpable(defaults): + raise TypeError("Cannot export a function with non-brinable defaults (__defaults__)") - return func.__name__, func.__module__, func_defaults, _export_codeobj(func_code)[1] + return func.__name__, func.__module__, defaults, _export_codeobj(code)[1] def _import_codetup(codetup): From c73b99b72492262104ab8876fe2983200a791b90 Mon Sep 17 00:00:00 2001 From: Yonatan Goldschmidt Date: Sat, 19 Dec 2020 18:35:00 +0200 Subject: [PATCH 2/3] Include __kwdefaults__ of teleported function --- rpyc/utils/teleportation.py | 11 +++++++++-- tests/test_teleportation.py | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/rpyc/utils/teleportation.py b/rpyc/utils/teleportation.py index 922390f5..6ed8844a 100644 --- a/rpyc/utils/teleportation.py +++ b/rpyc/utils/teleportation.py @@ -66,13 +66,18 @@ def export_function(func): closure = func.__closure__ code = func.__code__ defaults = func.__defaults__ + kwdefaults = func.__kwdefaults__ + if kwdefaults is not None: + kwdefaults = tuple(kwdefaults.items()) if closure: raise TypeError("Cannot export a function closure") if not brine.dumpable(defaults): raise TypeError("Cannot export a function with non-brinable defaults (__defaults__)") + if not brine.dumpable(kwdefaults): + raise TypeError("Cannot export a function with non-brinable defaults (__kwdefaults__)") - return func.__name__, func.__module__, defaults, _export_codeobj(code)[1] + return func.__name__, func.__module__, defaults, kwdefaults, _export_codeobj(code)[1] def _import_codetup(codetup): @@ -102,7 +107,7 @@ def _import_codetup(codetup): def import_function(functup, globals=None, def_=True): - name, modname, defaults, codetup = functup + name, modname, defaults, kwdefaults, codetup = functup if globals is None: try: mod = __import__(modname, None, None, "*") @@ -116,6 +121,8 @@ def import_function(functup, globals=None, def_=True): globals.setdefault('__builtins__', __builtins__) codeobj = _import_codetup(codetup) funcobj = FunctionType(codeobj, globals, name, defaults) + if kwdefaults is not None: + funcobj.__kwdefaults__ = {t[0]: t[1] for t in kwdefaults} if def_: globals[name] = funcobj return funcobj diff --git a/tests/test_teleportation.py b/tests/test_teleportation.py index c49aead0..2177aaf6 100644 --- a/tests/test_teleportation.py +++ b/tests/test_teleportation.py @@ -101,8 +101,8 @@ def get38_schema(cobj): pow38 = lambda x, y : x ** y # noqa export37 = get37_schema(pow37.__code__) export38 = get38_schema(pow38.__code__) - schema37 = (pow37.__name__, pow37.__module__, pow37.__defaults__, export37) - schema38 = (pow38.__name__, pow38.__module__, pow38.__defaults__, export38) + schema37 = (pow37.__name__, pow37.__module__, pow37.__defaults__, pow37.__kwdefaults__, export37) + schema38 = (pow38.__name__, pow38.__module__, pow38.__defaults__, pow38.__kwdefaults__, export38) pow37_netref = self.conn.modules["rpyc.utils.teleportation"].import_function(schema37) pow38_netref = self.conn.modules["rpyc.utils.teleportation"].import_function(schema38) self.assertEqual(pow37_netref(2, 3), pow37(2, 3)) From dd88a4720ba3b61bd9aa31e46a90409bbe34e7a9 Mon Sep 17 00:00:00 2001 From: Yonatan Goldschmidt Date: Sat, 19 Dec 2020 18:35:03 +0200 Subject: [PATCH 3/3] Add tests for teleported __defaults__ and __kwdefaults__ --- tests/test_teleportation.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_teleportation.py b/tests/test_teleportation.py index 2177aaf6..4a15b52b 100644 --- a/tests/test_teleportation.py +++ b/tests/test_teleportation.py @@ -26,6 +26,14 @@ def g(b): return g +def defaults(a=5, b="hi", c=(5.5, )): + return a, b, c + + +def kwdefaults(pos=5, *, a=42, b="bye", c=(12.4, )): + return pos, a, b, c + + def h(a): import os return a * os.getpid() @@ -82,6 +90,14 @@ def test_def(self): self.assertEqual(foo_(), 43) self.assertEqual(bar_(), 42) + def test_defaults(self): + defaults_ = teleport_function(self.conn, defaults) + self.assertEqual(defaults_(), defaults()) + + def test_kwdefaults(self): + kwdefaults_ = teleport_function(self.conn, kwdefaults) + self.assertEqual(kwdefaults_(), kwdefaults()) + def test_compat(self): # assumes func has only brineable types def get37_schema(cobj):