mirror of https://github.com/KLayout/klayout.git
Experimental: implicitly calling constructors from arguments passed tuples or lists for objects - this allows using a (x,y) tuple for Vector or Point arguments
This commit is contained in:
parent
5961eab84b
commit
b1ddb702b8
|
|
@ -200,7 +200,8 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
|
|||
|
||||
tl_assert (cls_decl != 0);
|
||||
|
||||
int argc = args == NULL ? 0 : int (PyTuple_Size (args));
|
||||
bool is_tuple = PyTuple_Check (args);
|
||||
int argc = args == NULL ? 0 : (is_tuple ? int (PyTuple_Size (args)) : int (PyList_Size (args)));
|
||||
|
||||
// get number of candidates by argument count
|
||||
const gsi::MethodBase *meth = 0;
|
||||
|
|
@ -277,9 +278,10 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
|
|||
int sc = 0;
|
||||
int i = 0;
|
||||
for (gsi::MethodBase::argument_iterator a = (*m)->begin_arguments (); is_valid && i < argc && a != (*m)->end_arguments (); ++a, ++i) {
|
||||
if (test_arg (*a, PyTuple_GetItem (args, i), false /*strict*/)) {
|
||||
PyObject *arg = is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i);
|
||||
if (test_arg (*a, arg, false /*strict*/)) {
|
||||
++sc;
|
||||
} else if (test_arg (*a, PyTuple_GetItem (args, i), true /*loose*/)) {
|
||||
} else if (test_arg (*a, arg, true /*loose*/)) {
|
||||
// non-scoring match
|
||||
} else {
|
||||
is_valid = false;
|
||||
|
|
@ -326,7 +328,8 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
|
|||
// one candidate, but needs checking whether compatibility is given - this avoid having to route NotImplemented over TypeError exceptions later
|
||||
int i = 0;
|
||||
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) {
|
||||
if (! test_arg (*a, PyTuple_GetItem (args, i), true /*loose*/)) {
|
||||
PyObject *arg = is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i);
|
||||
if (! test_arg (*a, arg, true /*loose*/)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -607,16 +610,17 @@ special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, P
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap)
|
||||
{
|
||||
bool is_tuple = PyTuple_Check (args);
|
||||
int i = 0;
|
||||
int argc = args == NULL ? 0 : int (PyTuple_Size (args));
|
||||
int argc = args == NULL ? 0 : (is_tuple ? int (PyTuple_Size (args)) : int (PyList_Size (args)));
|
||||
|
||||
try {
|
||||
|
||||
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) {
|
||||
push_arg (*a, arglist, PyTuple_GetItem (args, i), heap);
|
||||
push_arg (*a, arglist, is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i), heap);
|
||||
}
|
||||
|
||||
} catch (tl::Exception &ex) {
|
||||
|
|
@ -726,7 +730,7 @@ property_getter_adaptor (int mid, PyObject *self, PyObject *args)
|
|||
|
||||
PYA_TRY
|
||||
|
||||
int argc = args == NULL ? 0 : int (PyTuple_Size (args));
|
||||
int argc = args == NULL ? 0 : (PyTuple_Check (args) ? int (PyTuple_Size (args)) : int (PyList_Size (args)));
|
||||
if (argc != 0) {
|
||||
throw tl::Exception (tl::to_string (tr ("Property getters must not have an argument")));
|
||||
}
|
||||
|
|
@ -747,12 +751,12 @@ property_setter_adaptor (int mid, PyObject *self, PyObject *args)
|
|||
|
||||
PYA_TRY
|
||||
|
||||
int argc = args == NULL ? 0 : int (PyTuple_Size (args));
|
||||
int argc = args == NULL ? 0 : (PyTuple_Check (args) ? int (PyTuple_Size (args)) : int (PyList_Size (args)));
|
||||
if (argc != 1) {
|
||||
throw tl::Exception (tl::to_string (tr ("Property setter needs exactly one argument")));
|
||||
}
|
||||
|
||||
PyObject *value = PyTuple_GetItem (args, 0);
|
||||
PyObject *value = PyTuple_Check (args) ? PyTuple_GetItem (args, 0) : PyList_GetItem (args, 0);
|
||||
if (value) {
|
||||
ret = property_setter_impl (mid, self, value);
|
||||
}
|
||||
|
|
@ -777,7 +781,8 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args)
|
|||
p->destroy ();
|
||||
}
|
||||
|
||||
const gsi::MethodBase *meth = match_method (mid, self, args, PyTuple_Size (args) > 0 || ! p->cls_decl ()->can_default_create ());
|
||||
int argc = PyTuple_Check (args) ? int (PyTuple_Size (args)) : int (PyList_Size (args));
|
||||
const gsi::MethodBase *meth = match_method (mid, self, args, argc > 0 || ! p->cls_decl ()->can_default_create ());
|
||||
|
||||
if (meth && meth->smt () == gsi::MethodBase::None) {
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
namespace pya
|
||||
{
|
||||
|
||||
void push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Serialization adaptors for strings, variants, vectors and maps
|
||||
|
||||
|
|
@ -454,6 +456,8 @@ struct writer<gsi::ObjectType>
|
|||
{
|
||||
void operator() (gsi::SerialArgs *aa, PyObject *arg, const gsi::ArgType &atype, tl::Heap *heap)
|
||||
{
|
||||
const gsi::ClassBase *acls = atype.cls ();
|
||||
|
||||
if (arg == Py_None || arg == NULL) {
|
||||
|
||||
if (! (atype.is_ptr () || atype.is_cptr ())) {
|
||||
|
|
@ -465,14 +469,50 @@ struct writer<gsi::ObjectType>
|
|||
|
||||
}
|
||||
|
||||
if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) {
|
||||
if (PyTuple_Check (arg) || PyList_Check (arg)) {
|
||||
|
||||
// we may implicitly convert a tuple into a constructor call of a target object -
|
||||
// for now we only check whether the number of arguments is compatible with the list given.
|
||||
|
||||
int n = PyTuple_Check (arg) ? int (PyTuple_Size (arg)) : int (PyList_Size (arg));
|
||||
const gsi::MethodBase *meth = 0;
|
||||
for (gsi::ClassBase::method_iterator c = acls->begin_constructors (); c != acls->end_constructors (); ++c) {
|
||||
if ((*c)->compatible_with_num_args (n)) {
|
||||
meth = *c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!meth) {
|
||||
throw tl::Exception (tl::to_string (tr ("No constructor of %s available that takes %d arguments (implicit call from tuple)")), acls->name (), n);
|
||||
}
|
||||
|
||||
// implicit call of constructor
|
||||
gsi::SerialArgs retlist (meth->retsize ());
|
||||
gsi::SerialArgs arglist (meth->argsize ());
|
||||
|
||||
push_args (arglist, meth, arg, *heap);
|
||||
|
||||
meth->call (0, arglist, retlist);
|
||||
|
||||
void *new_obj = retlist.read<void *> (*heap);
|
||||
if (new_obj && (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ())) {
|
||||
// For pointers or refs, ownership over these objects is not transferred.
|
||||
// Hence we have to keep them on the heap.
|
||||
// TODO: what if the called method takes ownership using keep()?
|
||||
heap->push (new gsi::ObjectHolder (acls, new_obj));
|
||||
}
|
||||
|
||||
aa->write<void *> (new_obj);
|
||||
|
||||
} else if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) {
|
||||
|
||||
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
|
||||
if (! cls_decl) {
|
||||
throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
|
||||
}
|
||||
|
||||
if (cls_decl->is_derived_from (atype.cls ())) {
|
||||
if (cls_decl->is_derived_from (acls)) {
|
||||
|
||||
PYAObjectBase *p = PYAObjectBase::from_pyobject (arg);
|
||||
|
||||
|
|
@ -483,14 +523,15 @@ struct writer<gsi::ObjectType>
|
|||
aa->write<void *> (p->obj ());
|
||||
}
|
||||
|
||||
} else if (cls_decl->can_convert_to (atype.cls ())) {
|
||||
} else if (cls_decl->can_convert_to (acls)) {
|
||||
|
||||
PYAObjectBase *p = PYAObjectBase::from_pyobject (arg);
|
||||
|
||||
// We can convert objects for cref and cptr, but ownership over these objects is not transferred.
|
||||
// Hence we have to keep them on the heap.
|
||||
void *new_obj = atype.cls ()->create_obj_from (p->cls_decl (), p->obj ());
|
||||
heap->push (new gsi::ObjectHolder (atype.cls (), new_obj));
|
||||
// TODO: what if the called method takes ownership using keep()?
|
||||
void *new_obj = acls->create_obj_from (p->cls_decl (), p->obj ());
|
||||
heap->push (new gsi::ObjectHolder (acls, new_obj));
|
||||
aa->write<void *> (new_obj);
|
||||
|
||||
} else {
|
||||
|
|
@ -504,7 +545,7 @@ struct writer<gsi::ObjectType>
|
|||
throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name));
|
||||
}
|
||||
|
||||
if (cls_decl->is_derived_from (atype.cls ())) {
|
||||
if (cls_decl->is_derived_from (acls)) {
|
||||
|
||||
PYAObjectBase *p = PYAObjectBase::from_pyobject (arg);
|
||||
|
||||
|
|
@ -515,7 +556,7 @@ struct writer<gsi::ObjectType>
|
|||
aa->write<void *> (atype.cls ()->clone (p->obj ()));
|
||||
}
|
||||
|
||||
} else if (cls_decl->can_convert_to (atype.cls ())) {
|
||||
} else if (cls_decl->can_convert_to (acls)) {
|
||||
|
||||
PYAObjectBase *p = PYAObjectBase::from_pyobject (arg);
|
||||
aa->write<void *> (atype.cls ()->create_obj_from (cls_decl, p->obj ()));
|
||||
|
|
@ -1141,19 +1182,38 @@ struct test_arg_func<gsi::ObjectType>
|
|||
{
|
||||
void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose)
|
||||
{
|
||||
const gsi::ClassBase *acls = atype.cls ();
|
||||
|
||||
// for const X * or X *, null is an allowed value
|
||||
if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) {
|
||||
*ret = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (loose && (PyTuple_Check (arg) || PyList_Check (arg))) {
|
||||
|
||||
// we may implicitly convert a tuple into a constructor call of a target object -
|
||||
// for now we only check whether the number of arguments is compatible with the list given.
|
||||
|
||||
int n = PyTuple_Check (arg) ? int (PyTuple_Size (arg)) : int (PyList_Size (arg));
|
||||
*ret = false;
|
||||
for (gsi::ClassBase::method_iterator c = acls->begin_constructors (); c != acls->end_constructors (); ++c) {
|
||||
if ((*c)->compatible_with_num_args (n)) {
|
||||
*ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg));
|
||||
if (! cls_decl) {
|
||||
*ret = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (! (cls_decl == atype.cls () || (loose && (cls_decl->is_derived_from (atype.cls ()) || cls_decl->can_convert_to(atype.cls ()))))) {
|
||||
if (! (cls_decl == acls || (loose && (cls_decl->is_derived_from (atype.cls ()) || cls_decl->can_convert_to (atype.cls ()))))) {
|
||||
*ret = false;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue