Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Teleport functions' keyword-only argument defaults #422

Merged
merged 3 commits into from
Dec 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 17 additions & 14 deletions rpyc/utils/teleportation.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -67,16 +63,21 @@ def _export_codeobj(cobj):


def export_function(func):
func_closure = func.__closure__
func_code = func.__code__
func_defaults = func.__defaults__

if func_closure:
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(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__)")
if not brine.dumpable(kwdefaults):
raise TypeError("Cannot export a function with non-brinable defaults (__kwdefaults__)")

return func.__name__, func.__module__, func_defaults, _export_codeobj(func_code)[1]
return func.__name__, func.__module__, defaults, kwdefaults, _export_codeobj(code)[1]


def _import_codetup(codetup):
Expand Down Expand Up @@ -106,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, "*")
Expand All @@ -120,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
20 changes: 18 additions & 2 deletions tests/test_teleportation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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):
Expand All @@ -101,8 +117,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))
Expand Down