Skip to content

Commit

Permalink
Merge pull request #515 from Jongy/rpyc-python-3.11-support
Browse files Browse the repository at this point in the history
teleporation: Support Py3.11
  • Loading branch information
comrumino committed Nov 20, 2022
2 parents fbfbf0c + 9346db9 commit defdb60
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 17 deletions.
2 changes: 2 additions & 0 deletions rpyc/lib/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import time

is_py_3k = (sys.version_info[0] >= 3)
is_py_gte311 = is_py_3k and (sys.version_info[1] >= 11)
is_py_gte310 = is_py_3k and (sys.version_info[1] >= 10)
is_py_gte38 = is_py_3k and (sys.version_info[1] >= 8)
is_py_gte37 = is_py_3k and (sys.version_info[1] >= 7)

Expand Down
37 changes: 29 additions & 8 deletions rpyc/utils/teleportation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import opcode

from rpyc.lib.compat import is_py_gte38
from rpyc.lib.compat import is_py_gte38, is_py_gte310, is_py_gte311
from types import CodeType, FunctionType
from rpyc.core import brine, netref
from dis import _unpack_opargs
Expand Down Expand Up @@ -48,12 +48,19 @@ def _export_codeobj(cobj):
else:
raise TypeError(f"Cannot export a function with non-brinable constants: {const!r}")

if is_py_gte38:
if is_py_gte311:
# Constructor was changed in 3.11 by adding exceptionable and qualname
exported = (cobj.co_argcount, cobj.co_posonlyargcount, cobj.co_kwonlyargcount, cobj.co_nlocals,
cobj.co_stacksize, cobj.co_flags, cobj.co_code, tuple(consts2), cobj.co_names, cobj.co_varnames,
cobj.co_filename, cobj.co_name, cobj.co_qualname, cobj.co_firstlineno, cobj.co_linetable,
cobj.co_exceptiontable, cobj.co_freevars, cobj.co_cellvars)
elif is_py_gte38:
# Constructor was changed in 3.8 to support "advanced" programming styles
exported = (cobj.co_argcount, cobj.co_posonlyargcount, cobj.co_kwonlyargcount, cobj.co_nlocals,
cobj.co_stacksize, cobj.co_flags, cobj.co_code, tuple(consts2), cobj.co_names, cobj.co_varnames,
cobj.co_filename, cobj.co_name, cobj.co_firstlineno, cobj.co_lnotab, cobj.co_freevars,
cobj.co_cellvars)
cobj.co_filename, cobj.co_name, cobj.co_firstlineno,
cobj.co_linetable if is_py_gte310 else cobj.co_lnotab, # 3.10 switched from lnotab to linetable
cobj.co_freevars, cobj.co_cellvars)
else:
exported = (cobj.co_argcount, cobj.co_kwonlyargcount, cobj.co_nlocals, cobj.co_stacksize, cobj.co_flags,
cobj.co_code, tuple(consts2), cobj.co_names, cobj.co_varnames, cobj.co_filename,
Expand Down Expand Up @@ -81,14 +88,25 @@ def export_function(func):


def _import_codetup(codetup):
# Handle tuples sent from 3.8 as well as 3 < version < 3.8.
if len(codetup) == 16:
posonlyargcount = 0
exceptiontable = b""
qualname = ""
# Handle tuples sent from >=3.11, >=3.8 and <=3.8
if len(codetup) == 18:
(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames,
filename, name, qualname, firstlineno, lnotab, exceptiontable, freevars, cellvars) = codetup
if not is_py_gte310:
# Since version, codetup length, and lnotab length, lnotab is set as though there is no linenumber
lnotab = b'' # lnotab_notes.txt describes lnotab as imperfect anyway
elif len(codetup) == 16:
(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames,
filename, name, firstlineno, lnotab, freevars, cellvars) = codetup
if not is_py_gte310 and len(lnotab) > 2:
# Since version, codetup length, and lnotab length, lnotab is set as though there is no linenumber
lnotab = b'' # lnotab_notes.txt describes lnotab as imperfect anyway
else:
(argcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames,
filename, name, firstlineno, lnotab, freevars, cellvars) = codetup
posonlyargcount = 0

consts2 = []
for const in consts:
Expand All @@ -97,7 +115,10 @@ def _import_codetup(codetup):
else:
consts2.append(const)
consts = tuple(consts2)
if is_py_gte38:
if is_py_gte311:
codetup = (argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames,
filename, name, qualname, firstlineno, lnotab, exceptiontable, freevars, cellvars)
elif is_py_gte38:
codetup = (argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, code, consts, names, varnames,
filename, name, firstlineno, lnotab, freevars, cellvars)
else:
Expand Down
35 changes: 26 additions & 9 deletions tests/test_teleportation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
tracemalloc.start()

from rpyc.utils.teleportation import export_function, import_function
from rpyc.lib.compat import is_py_3k, is_py_gte38
from rpyc.lib.compat import is_py_3k, is_py_gte38, is_py_gte311
from rpyc.utils.classic import teleport_function


Expand Down Expand Up @@ -112,7 +112,14 @@ def get38_schema(cobj):
cobj.co_varnames, cobj.co_filename, cobj.co_name, cobj.co_firstlineno, cobj.co_lnotab,
cobj.co_freevars, cobj.co_cellvars)

if is_py_3k:
def get311_schema(cobj):
return (cobj.co_argcount, 2, cobj.co_kwonlyargcount, cobj.co_nlocals,
cobj.co_stacksize, cobj.co_flags, cobj.co_code, cobj.co_consts, cobj.co_names,
cobj.co_varnames, cobj.co_filename, cobj.co_name, cobj.co_qualname,
cobj.co_firstlineno, cobj.co_lnotab, cobj.co_exceptiontable,
cobj.co_freevars, cobj.co_cellvars)

if is_py_gte38:
pow37 = lambda x, y : x ** y # noqa
pow38 = lambda x, y : x ** y # noqa
export37 = get37_schema(pow37.__code__)
Expand All @@ -124,13 +131,23 @@ def get38_schema(cobj):
self.assertEqual(pow37_netref(2, 3), pow37(2, 3))
self.assertEqual(pow38_netref(2, 3), pow38(2, 3))
self.assertEqual(pow37_netref(x=2, y=3), pow37(x=2, y=3))
if not is_py_gte38:
return # skip remained of tests for 3.7
pow38.__code__ = types.CodeType(*export38) # pow38 = lambda x, y, /: x ** y
with self.assertRaises(TypeError): # show local behavior
pow38(x=2, y=3)
with self.assertRaises(TypeError):
pow38_netref(x=2, y=3)
if is_py_gte38 and not is_py_gte311:
pow38.__code__ = types.CodeType(*export38) # pow38 = lambda x, y, /: x ** y
with self.assertRaises(TypeError): # show local behavior
pow38(x=2, y=3)
with self.assertRaises(TypeError):
pow38_netref(x=2, y=3)
if is_py_gte311:
pow311 = lambda x, y : x ** y # noqa
export311 = get311_schema(pow311.__code__)
schema311 = (pow311.__name__, pow311.__module__, pow311.__defaults__, pow311.__kwdefaults__, export311)
pow311_netref = self.conn.modules["rpyc.utils.teleportation"].import_function(schema311)
self.assertTrue(is_py_gte311)
pow311.__code__ = types.CodeType(*export311) # pow311 = lambda x, y, /: x ** y
with self.assertRaises(TypeError): # show local behavior
pow311(x=2, y=3)
with self.assertRaises(TypeError):
pow311_netref(x=2, y=3)


if __name__ == "__main__":
Expand Down

0 comments on commit defdb60

Please sign in to comment.