diff --git a/Examples/test-suite/python/python_nondynamic_runme.py b/Examples/test-suite/python/python_nondynamic_runme.py index 6a430961e..9be47b3cc 100644 --- a/Examples/test-suite/python/python_nondynamic_runme.py +++ b/Examples/test-suite/python/python_nondynamic_runme.py @@ -1,21 +1,41 @@ import python_nondynamic +def is_python_modern(): + """Return True if SWIG is generating Python code using -modern. Works only if %python_nondynamic has been used.""" + return hasattr(python_nondynamic, "_swig_setattr_nondynamic_class_variable") + +def debug_print(s): + show_debug = False + if show_debug: + print(s) + aa = python_nondynamic.A() aa.a = 1 aa.b = 2 + +# Check values are really set +if python_nondynamic.retrieve_A_a(aa) != 1: raise RuntimeError("a not set correctly") +if python_nondynamic.retrieve_A_b(aa) != 2: raise RuntimeError("b not set correctly") + try: aa.c = 2 - err = 0 -except: - err = 1 - -if not err: - raise RuntimeError, "A is not static" + raise RuntimeError("A is not static") +except AttributeError as e: + debug_print(e) + pass +class PurePythonClass(object): + def __init__(self): + self.variables = dict() + def __set__(self, name, value): + self.variables[name] = value + pass class B(python_nondynamic.A): c = 4 + cc = PurePythonClass() + cc.nnn = "new attrib" def __init__(self): python_nondynamic.A.__init__(self) @@ -23,26 +43,66 @@ class B(python_nondynamic.A): pass bb = B() +bb.a = 4 +bb.b = 5 +# Check values are really set +if python_nondynamic.retrieve_A_a(bb) != 4: raise RuntimeError("a not set correctly") +if python_nondynamic.retrieve_A_b(bb) != 5: raise RuntimeError("b not set correctly") try: bb.c = 3 - err = 0 -except: - err = 1 - -if not err: - print "bb.c = %d" % bb.c - print "B.c = %d" % B.c - raise RuntimeError, "B.c class variable messes up nondynamic-ness of B" + print("bb.c = {}".format(bb.c)) + print("B.c = {}".format(B.c)) + raise RuntimeError("B.c class variable messes up nondynamic-ness of B") +except AttributeError as e: + debug_print(e) + pass try: bb.d = 2 - err = 0 -except: - err = 1 - -if not err: - raise RuntimeError, "B is not static" + raise RuntimeError("B is not static") +except AttributeError as e: + debug_print(e) + pass cc = python_nondynamic.C() cc.d = 3 + +# An inconsistency between builtin and (non-builtin/modern). +# Class variables cannot be set on builtin types, like other Python builtins, eg list.classvar=111 fails +if python_nondynamic.is_python_builtin(): + try: + python_nondynamic.C.classvar = 111 + raise RuntimeError("C should not allow static variables to be added when using builtin") + except AttributeError as e: + debug_print(e) + pass +else: + python_nondynamic.C.classvar = 111 + +if is_python_modern(): + # Not working with builtin or non-modern :( + try: + B.a = 10 + raise RuntimeError("B should not allow adding a class variable by setting it as an instance variable") + except AttributeError as e: + debug_print(e) + pass + + try: + python_nondynamic.A.a = 10 + raise RuntimeError("A should not allow adding a class variable by setting it as an instance variable") + except AttributeError as e: + debug_print(e) + pass + +if not python_nondynamic.is_python_builtin(): + try: + bb.cc = 3 + print("bb.cc = {}".format(bb.cc)) + print("B.cc = {}".format(B.cc)) + raise RuntimeError("B.cc class variable messes up nondynamic-ness of B") + except AttributeError as e: + debug_print(e) + pass + diff --git a/Examples/test-suite/python_nondynamic.i b/Examples/test-suite/python_nondynamic.i index 54a3f28ca..14c8d6b0e 100644 --- a/Examples/test-suite/python_nondynamic.i +++ b/Examples/test-suite/python_nondynamic.i @@ -54,5 +54,14 @@ int b; }; + int retrieve_A_a(const struct A* self) { return self->a; } + int retrieve_A_b(const struct A* self) { return self->b; } +%} +%inline %{ +#ifdef SWIGPYTHON_BUILTIN +int is_python_builtin() { return 1; } +#else +int is_python_builtin() { return 0; } +#endif %} diff --git a/Source/Modules/python.cxx b/Source/Modules/python.cxx index bdbb4e9b9..a713f4eaa 100755 --- a/Source/Modules/python.cxx +++ b/Source/Modules/python.cxx @@ -951,15 +951,32 @@ public: } } if (modern) { - Printv(f_shadow, "\n", "def _swig_setattr_nondynamic_method(set):\n", tab4, "def set_attr(self, name, value):\n", + Printv(f_shadow, "\n", "def _swig_setattr_nondynamic_instance_variable(set):\n", + tab4, "def set_instance_attr(self, name, value):\n", #ifdef USE_THISOWN - tab4, tab4, "if hasattr(self, name) or (name in (\"this\", \"thisown\")):\n", + tab4, tab4, "if name in (\"this\", \"thisown\"):\n", + tab4, tab4, tab4, "set(self, name, value)\n", #else - tab4, tab4, "if name == \"thisown\":\n", tab8, tab4, "return self.this.own(value)\n", tab4, tab4, "if hasattr(self, name) or (name == \"this\"):\n", + tab4, tab4, "if name == \"thisown\":\n", + tab4, tab4, tab4, "self.this.own(value)\n", + tab4, tab4, "elif name == \"this\":\n", + tab4, tab4, tab4, "set(self, name, value)\n", #endif + tab4, tab4, "elif hasattr(self, name) and isinstance(getattr(type(self), name), property):\n", tab4, tab4, tab4, "set(self, name, value)\n", tab4, tab4, "else:\n", - tab4, tab4, tab4, "raise AttributeError(\"You cannot add attributes to %s\" % self)\n", tab4, "return set_attr\n\n\n", NIL); + tab4, tab4, tab4, "raise AttributeError(\"You cannot add instance attributes to %s\" % self)\n", + tab4, "return set_instance_attr\n\n", NIL); + + Printv(f_shadow, "\n", "def _swig_setattr_nondynamic_class_variable(set):\n", + tab4, "def set_class_attr(cls, name, value):\n", + tab4, tab4, "if hasattr(cls, name) and not isinstance(getattr(cls, name), property):\n", + tab4, tab4, tab4, "set(cls, name, value)\n", + tab4, tab4, "else:\n", + tab4, tab4, tab4, "raise AttributeError(\"You cannot add class attributes to %s\" % cls)\n", + tab4, "return set_class_attr\n\n", NIL); + + Printv(f_shadow, "\n", NIL); } if (directorsEnabled()) { @@ -4535,8 +4552,8 @@ public: /* Add static attribute */ if (GetFlag(n, "feature:python:nondynamic")) { Printv(f_shadow_file, - tab4, "__setattr__ = _swig_setattr_nondynamic_method(object.__setattr__)\n", - tab4, "class __metaclass__(type):\n", tab4, tab4, "__setattr__ = _swig_setattr_nondynamic_method(type.__setattr__)\n", NIL); + tab4, "__setattr__ = _swig_setattr_nondynamic_instance_variable(object.__setattr__)\n", + tab4, "class __metaclass__(type):\n", tab4, tab4, "__setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)\n", NIL); } } }