Skip to content
Permalink
Browse files

Promote methods to functions.

  • Loading branch information...
Thrameos committed Jun 22, 2019
1 parent 86d4333 commit bd077b4bdc02f7aa72e0d73b447fa9d14d3239f6
Showing with 58 additions and 28 deletions.
  1. +3 −3 native/python/include/pyjp_method.h
  2. +35 −10 native/python/pyjp_method.cpp
  3. +5 −3 setupext/build_ext.py
  4. +15 −12 test/jpypetest/test_jmethod.py
@@ -20,8 +20,8 @@

struct PyJPMethod
{
PyObject_HEAD

PyFunctionObject func;
static PyTypeObject Type;

// Python-visible methods
@@ -60,7 +60,7 @@ struct PyJPMethod
PyObject* m_Instance;
PyObject* m_Doc;
PyObject* m_Annotations;
PyObject* m_Code;
PyObject* m_CodeRep;

} ;

@@ -27,14 +27,23 @@ static PyMethodDef methodMethods[] = {
struct PyGetSetDef methodGetSet[] = {
{"__self__", (getter) (&PyJPMethod::getSelf), NULL, NULL, NULL},
{"__name__", (getter) (&PyJPMethod::getName), NULL, NULL, NULL},
{"__qualname__", (getter) (&PyJPMethod::getQualName), NULL, NULL, NULL},
{"__doc__", (getter) (&PyJPMethod::getDoc), (setter) (&PyJPMethod::setDoc), NULL, NULL},
{"__annotations__", (getter) (&PyJPMethod::getAnnotations), (setter) (&PyJPMethod::setAnnotations), NULL, NULL},
#if PY_MAJOR_VERSION >= 3
{"__closure__", (getter) (&PyJPMethod::getClosure), NULL, NULL, NULL},
{"__code__", (getter) (&PyJPMethod::getCode), NULL, NULL, NULL},
{"__defaults__", (getter) (&PyJPMethod::getNone), NULL, NULL, NULL},
{"__kwdefaults__", (getter) (&PyJPMethod::getNone), NULL, NULL, NULL},
{"__code__", (getter) (&PyJPMethod::getCode), NULL, NULL, NULL},
{"__globals__", (getter) (&PyJPMethod::getGlobals), NULL, NULL, NULL},
{"__closure__", (getter) (&PyJPMethod::getClosure), NULL, NULL, NULL},
{"__qualname__", (getter) (&PyJPMethod::getQualName), NULL, NULL, NULL},
#else
{"func_closure", (getter) (&PyJPMethod::getClosure), NULL, NULL, NULL},
{"func_code", (getter) (&PyJPMethod::getCode), NULL, NULL, NULL},
{"func_defaults", (getter) (&PyJPMethod::getNone), NULL, NULL, NULL},
{"func_doc", (getter) (&PyJPMethod::getDoc), (setter) (&PyJPMethod::setDoc), NULL, NULL},
{"func_globals", (getter) (&PyJPMethod::getGlobals), NULL, NULL, NULL},
{"func_name", (getter) (&PyJPMethod::getName), NULL, NULL, NULL},
#endif
{NULL},
};

@@ -84,6 +93,10 @@ PyTypeObject PyJPMethod::Type = {

void PyJPMethod::initType(PyObject* module)
{
// We inherit from PyFunction_Type just so we are an instatnce
// for purposes of inspect and tab completion tools. But
// we will just ignore their memory layout as we have our own.
PyJPMethod::Type.tp_base = &PyFunction_Type;
PyType_Ready(&PyJPMethod::Type);
Py_INCREF(&PyJPMethod::Type);
PyModule_AddObject(module, "PyJPMethod", (PyObject*) (&PyJPMethod::Type));
@@ -109,7 +122,7 @@ PyObject* PyJPMethod::__new__(PyTypeObject* type, PyObject* args, PyObject* kwar
self->m_Instance = NULL;
self->m_Doc = NULL;
self->m_Annotations = NULL;
self->m_Code = NULL;
self->m_CodeRep = NULL;
return (PyObject*) self;
}

@@ -170,7 +183,7 @@ int PyJPMethod::traverse(PyJPMethod *self, visitproc visit, void *arg)
Py_VISIT(self->m_Instance);
Py_VISIT(self->m_Doc);
Py_VISIT(self->m_Annotations);
Py_VISIT(self->m_Code);
Py_VISIT(self->m_CodeRep);
return 0;
}

@@ -179,7 +192,7 @@ int PyJPMethod::clear(PyJPMethod *self)
Py_CLEAR(self->m_Instance);
Py_CLEAR(self->m_Doc);
Py_CLEAR(self->m_Annotations);
Py_CLEAR(self->m_Code);
Py_CLEAR(self->m_CodeRep);
return 0;
}

@@ -334,13 +347,13 @@ PyObject *PyJPMethod::getCodeAttr(PyJPMethod *self, void *context, const char* a
try
{
ASSERT_JVM_RUNNING("PyJPMethod::getCode");
if (self->m_Code == NULL)
if (self->m_CodeRep == NULL)
{
JPPyObject out(JPPythonEnv::getMethodCode(self));
self->m_Code = out.get();
Py_XINCREF(self->m_Code);
self->m_CodeRep = out.get();
Py_XINCREF(self->m_CodeRep);
}
return PyObject_GetAttrString(self->m_Code, attr);
return PyObject_GetAttrString(self->m_CodeRep, attr);
}
PY_STANDARD_CATCH;
return NULL;
@@ -349,17 +362,29 @@ PyObject *PyJPMethod::getCodeAttr(PyJPMethod *self, void *context, const char* a

PyObject *PyJPMethod::getCode(PyJPMethod *self, void *context)
{
#if PY_MAJOR_VERSION >= 3
return getCodeAttr(self, context, "__code__");
#else
return getCodeAttr(self, context, "func_code");
#endif
}

PyObject *PyJPMethod::getClosure(PyJPMethod *self, void *context)
{
#if PY_MAJOR_VERSION >= 3
return getCodeAttr(self, context, "__closure__");
#else
return getCodeAttr(self, context, "func_closure");
#endif
}

PyObject *PyJPMethod::getGlobals(PyJPMethod *self, void *context)
{
#if PY_MAJOR_VERSION >= 3
return getCodeAttr(self, context, "__globals__");
#else
return getCodeAttr(self, context, "func_globals");
#endif
}

PyObject* PyJPMethod::isBeanAccessor(PyJPMethod* self, PyObject* arg)
@@ -39,8 +39,6 @@ def initialize_options(self, *args):
"""omit -Wstrict-prototypes from CFLAGS since its only valid for C code."""
import distutils.sysconfig
cfg_vars = distutils.sysconfig.get_config_vars()
# if 'CFLAGS' in cfg_vars:
# cfg_vars['CFLAGS'] = cfg_vars['CFLAGS'].replace('-Wstrict-prototypes', '')
replacement = {
'-Wstrict-prototypes': '',
'-Wimplicit-function-declaration': '',
@@ -50,8 +48,12 @@ def initialize_options(self, *args):
replacement['-O3'] = '-O0'

for k, v in cfg_vars.items():
if not isinstance(v, str):
continue
if not k=="OPT" and not "FLAGS" in k:
continue
for r, t in replacement.items():
if isinstance(v, str) and k.find("FLAGS") != -1 and v.find(r) != -1:
if v.find(r) != -1:
v = v.replace(r, t)
cfg_vars[k] = v
build_ext.initialize_options(self)
@@ -22,15 +22,23 @@
import inspect

# Code from stackoverflow
# Reference http://stackoverflow.com/questions/13503079/how-to-create-a-copy-of-a-python-function
def copy_func(f):
"""Based on http://stackoverflow.com/a/6528148/190597 (Glenn Maynard)"""
g = types.FunctionType(f.__code__, f.__globals__, name=f.__name__,
argdefs=f.__defaults__,
closure=f.__closure__)
if sys.version_info[0] < 3:
g = types.FunctionType(f.func_code, f.func_globals, name=f.func_name,
argdefs=f.func_defaults,
closure=f.func_closure)
else:
g = types.FunctionType(f.__code__, f.__globals__, name=f.__name__,
argdefs=f.__defaults__,
closure=f.__closure__)
g.__kwdefaults__ = f.__kwdefaults__

g = functools.update_wrapper(g, f)
g.__kwdefaults__ = f.__kwdefaults__
return g


class JMethodTestCase(common.JPypeTestCase):
""" Test for methods of JMethod (_jpype.PyJPMethod)
@@ -66,6 +74,7 @@ def testMethodName(self):
self.assertEqual(self.cls.substring.__name__, "substring")
self.assertEqual(self.obj.substring.__name__, "substring")

@common.unittest.skipIf(sys.version_info[0] < 3, "skip on Python2")
def testMethodQualName(self):
self.assertEqual(self.cls.substring.__qualname__, "java.lang.String.substring")
self.assertEqual(self.obj.substring.__qualname__, "java.lang.String.substring")
@@ -91,19 +100,14 @@ def testMethodInspectSignature(self):
self.assertIsInstance(inspect.signature(self.obj.substring), inspect.Signature)
self.assertEqual(inspect.signature(self.obj.substring).return_annotation, self.cls)

@common.unittest.skipIf(sys.version_info[0] < 3, "skip on Python2")
def testMethodInspectFunction(self):
self.assertTrue(inspect._signature_isfunctionlike(self.cls.substring))
self.assertTrue(inspect._signature_isfunctionlike(self.obj.substring))
self.assertTrue(inspect.isfunction(self.cls.substring))
self.assertTrue(inspect.isfunction(self.obj.substring))

def testMethodInspectRoutine(self):
self.assertTrue(inspect.isroutine(self.cls.substring))
self.assertTrue(inspect.isroutine(self.obj.substring))

def testMethodInspectMethodDesc(self):
self.assertTrue(inspect.ismethoddescriptor(self.cls.substring))
self.assertTrue(inspect.ismethoddescriptor(self.obj.substring))

def testMethodClassCall(self):
self.assertEqual(self.cls.substring(self.obj, 1), "oo")

@@ -117,7 +121,6 @@ def testMethodClassCallFail(self):
def testMethodCall(self):
self.assertEqual(self.obj.substring(1), "oo")

@common.unittest.skipIf(sys.version_info[0] < 3, "skip on Python2")
def testMethodClone(self):
a = copy_func(self.cls.substring)
self.assertEqual(a(self.obj, 1), "oo")

0 comments on commit bd077b4

Please sign in to comment.
You can’t perform that action at this time.