mirror of https://github.com/swig/swig
Add support for Python variable annotations as a feature.
Both function annotations and variable annotations are turned on using the "python:annotations" feature. Example: %feature("python:annotations", "c"); struct V { float val; }; The generated code contains a variable annotation containing the C float type: class V(object): val: "float" = property(_example.V_val_get, _example.V_val_set) ... Python 3.5 and earlier do not support variable annotations, so variable annotations can be turned off with a "python:annotations:novar" feature flag. Example turning on function annotations but not variable annotations globally: %feature("python:annotations", "c"); %feature("python:annotations:novar"); or via the command line: -features python:annotations=c,python:annotations:novar Closes #1951
This commit is contained in:
parent
0ba26d8f73
commit
3159de3e9f
|
@ -7,6 +7,37 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
|
||||||
Version 4.1.0 (in progress)
|
Version 4.1.0 (in progress)
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
2022-03-02: geographika, wsfulton
|
||||||
|
[Python] #1951 Add Python variable annotations support.
|
||||||
|
|
||||||
|
Both function annotations and variable annotations are turned on using the
|
||||||
|
"python:annotations" feature. Example:
|
||||||
|
|
||||||
|
%feature("python:annotations", "c");
|
||||||
|
|
||||||
|
struct V {
|
||||||
|
float val;
|
||||||
|
};
|
||||||
|
|
||||||
|
The generated code contains a variable annotation containing the C float type:
|
||||||
|
|
||||||
|
class V(object):
|
||||||
|
val: "float" = property(_example.V_val_get, _example.V_val_set)
|
||||||
|
...
|
||||||
|
|
||||||
|
Python 3.5 and earlier do not support variable annotations, so variable
|
||||||
|
annotations can be turned off with a "python:annotations:novar" feature flag.
|
||||||
|
Example turning on function annotations but not variable annotations globally:
|
||||||
|
|
||||||
|
%feature("python:annotations", "c");
|
||||||
|
%feature("python:annotations:novar");
|
||||||
|
|
||||||
|
or via the command line:
|
||||||
|
|
||||||
|
-features python:annotations=c,python:annotations:novar
|
||||||
|
|
||||||
|
*** POTENTIAL INCOMPATIBILITY ***
|
||||||
|
|
||||||
2022-03-02: olly
|
2022-03-02: olly
|
||||||
#891 Give error for typemap argument without a value. Previously
|
#891 Give error for typemap argument without a value. Previously
|
||||||
SWIG segfaulted.
|
SWIG segfaulted.
|
||||||
|
@ -23,6 +54,8 @@ Version 4.1.0 (in progress)
|
||||||
|
|
||||||
-features python:annotations=c
|
-features python:annotations=c
|
||||||
|
|
||||||
|
Also see entry dated 2022-03-02, regarding variable annotations.
|
||||||
|
|
||||||
*** POTENTIAL INCOMPATIBILITY ***
|
*** POTENTIAL INCOMPATIBILITY ***
|
||||||
|
|
||||||
2022-02-26: wsfulton
|
2022-02-26: wsfulton
|
||||||
|
|
|
@ -1478,7 +1478,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
<li><a href="Python.html#Python_python3support">Python 3 Support</a>
|
<li><a href="Python.html#Python_python3support">Python 3 Support</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="Python.html#Python_annotations">Function annotations</a>
|
<li><a href="Python.html#Python_annotations">Python function annotations and variable annotations</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="Python.html#Python_annotations_c">C/C++ annotation types</a>
|
<li><a href="Python.html#Python_annotations_c">C/C++ annotation types</a>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -133,7 +133,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
<li><a href="#Python_python3support">Python 3 Support</a>
|
<li><a href="#Python_python3support">Python 3 Support</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#Python_annotations">Function annotations</a>
|
<li><a href="#Python_annotations">Python function annotations and variable annotations</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#Python_annotations_c">C/C++ annotation types</a>
|
<li><a href="#Python_annotations_c">C/C++ annotation types</a>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -2536,7 +2536,7 @@ assert(issubclass(B.Derived, A.Base))
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
<li><p><a href="#Python_annotations">Python function annotations</a> are not supported.
|
<li><p><a href="#Python_annotations">Python annotations</a> are not supported.
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -3998,7 +3998,7 @@ Also included in the table for comparison is using the <tt>-builtin</tt> option
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Although the <tt>-fastproxy</tt> option results in faster code over the default, the generated proxy code is not as user-friendly
|
Although the <tt>-fastproxy</tt> option results in faster code over the default, the generated proxy code is not as user-friendly
|
||||||
as docstring/doxygen comments, <a href="#Python_annotations">Python function annotations</a> and functions with default values are not visible in the generated Python proxy class.
|
as docstring/doxygen comments, <a href="#Python_annotations">Python annotations</a> and functions with default values are not visible in the generated Python proxy class.
|
||||||
The <tt>-olddefs</tt> option can rectify this.
|
The <tt>-olddefs</tt> option can rectify this.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -6762,13 +6762,15 @@ The following are Python 3 new features that are currently supported by
|
||||||
SWIG.
|
SWIG.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<H3><a name="Python_annotations">33.12.1 Function annotations</a></H3>
|
<H3><a name="Python_annotations">33.12.1 Python function annotations and variable annotations</a></H3>
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Python 3 supports function annotations as defined in
|
Python 3 supports function annotations as defined in
|
||||||
<a href="https://www.python.org/dev/peps/pep-3107/">PEP 3107</a>.
|
<a href="https://www.python.org/dev/peps/pep-3107/">PEP 3107</a>.
|
||||||
Note that there is currently no annotations support for the <tt>-builtin</tt> nor
|
Python 3.6 and later additionally support variable annotations as defined in
|
||||||
|
<a href="https://www.python.org/dev/peps/pep-526/">PEP 526</a>.
|
||||||
|
Note that currently there is no annotations support in SWIG for the <tt>-builtin</tt> nor
|
||||||
the <tt>-fastproxy</tt> option.
|
the <tt>-fastproxy</tt> option.
|
||||||
Annotations are added via the <tt>python:annotations</tt>
|
Annotations are added via the <tt>python:annotations</tt>
|
||||||
<a href="Customization.html#Customization_features">%feature directives</a>.
|
<a href="Customization.html#Customization_features">%feature directives</a>.
|
||||||
|
@ -6779,7 +6781,7 @@ SWIG currently supports one type of function annotation.
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The <tt>%feature("python:annotations", "c")</tt> directive generates function annotations
|
The <tt>%feature("python:annotations", "c")</tt> directive generates annotations
|
||||||
containing C/C++ types. For example:
|
containing C/C++ types. For example:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -6789,16 +6791,16 @@ int *global_ints(int &ri);
|
||||||
</pre></div>
|
</pre></div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The generated code then contains function annotations containing the C types:
|
The generated code then contains function annotations containing the C++ types:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="targetlang"><pre>
|
<div class="targetlang"><pre>
|
||||||
def global_ints(ri: "int &") -> "int *":
|
def global_ints(ri: "int &") -> "int *":
|
||||||
return _python_annotations_c.global_ints(ri)
|
return _example.global_ints(ri)
|
||||||
</pre></div>
|
</pre></div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
There are some limitations with annotations support, for example, overloaded functions use
|
There are some limitations with function annotations support, for example, overloaded functions use
|
||||||
<tt>*args</tt> or <tt>**kwargs</tt> when keyword arguments are enabled.
|
<tt>*args</tt> or <tt>**kwargs</tt> when keyword arguments are enabled.
|
||||||
The parameter names and types are then not shown. For example, with input:
|
The parameter names and types are then not shown. For example, with input:
|
||||||
</p>
|
</p>
|
||||||
|
@ -6815,13 +6817,65 @@ Only the return type is annotated.
|
||||||
|
|
||||||
<div class="targetlang"><pre>
|
<div class="targetlang"><pre>
|
||||||
def global_overloaded(*args) -> "int *":
|
def global_overloaded(*args) -> "int *":
|
||||||
return _python_annotations_c.global_overloaded(*args)
|
return _example.global_overloaded(*args)
|
||||||
</pre></div>
|
</pre></div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Below is an example demonstrating variable annotations.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="code"><pre>
|
||||||
|
%feature("python:annotations", "c");
|
||||||
|
|
||||||
|
struct V {
|
||||||
|
float val;
|
||||||
|
};
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The generated code contains a variable annotation containing the C <tt>float</tt> type:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="targetlang"><pre>
|
||||||
|
class V(object):
|
||||||
|
val: "float" = property(_example.V_val_get, _example.V_val_set)
|
||||||
|
...
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Variable annotations are only supported from Python 3.6. If you need to support earlier versions of Python, you'll need to turn variable annotations off via the <tt>python:annotations:novar</tt> feature flag.
|
||||||
|
It is quite easy to support function annotations but turn off variable annotations. The next example shows how to do this for all variables.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="code"><pre>
|
||||||
|
%feature("python:annotations", "c"); // Turn on function annotations and variable annotations globally
|
||||||
|
%feature("python:annotations:novar"); // Turn off variable annotations globally
|
||||||
|
|
||||||
|
struct V {
|
||||||
|
float val;
|
||||||
|
void vv(float *v) const;
|
||||||
|
};
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The resulting code will work with versions older than Python 3.6 as the variable annotations are turned off:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="targetlang"><pre>
|
||||||
|
class V(object):
|
||||||
|
val = property(_example.V_val_get, _example.V_val_set)
|
||||||
|
|
||||||
|
def vv(self, v: "float *") -> "void":
|
||||||
|
return _example.V_vv(self, v)
|
||||||
|
...
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<b>Compatibility Note:</b> SWIG-4.1.0 changed the way that function annotations are generated.
|
<b>Compatibility Note:</b> SWIG-4.1.0 changed the way that function annotations are generated.
|
||||||
Prior versions required the <tt>-py3</tt> option which enabled function annotation support
|
Prior versions required the <tt>-py3</tt> option to generate function annotation support
|
||||||
containing C/C++ types instead of supporting <tt>%feature("python:annotations", "c")</tt>.
|
containing C/C++ types instead of supporting <tt>%feature("python:annotations", "c")</tt>.
|
||||||
|
Variable annotations were also added in SWIG-4.1.0.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<H3><a name="Python_nn75">33.12.2 Buffer interface</a></H3>
|
<H3><a name="Python_nn75">33.12.2 Buffer interface</a></H3>
|
||||||
|
|
|
@ -49,6 +49,7 @@ CPP_TEST_CASES += \
|
||||||
primitive_types \
|
primitive_types \
|
||||||
python_abstractbase \
|
python_abstractbase \
|
||||||
python_annotations_c \
|
python_annotations_c \
|
||||||
|
python_annotations_variable_c \
|
||||||
python_append \
|
python_append \
|
||||||
python_builtin \
|
python_builtin \
|
||||||
python_destructor_exception \
|
python_destructor_exception \
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Variable annotations for properties is only supported in python-3.6 and later (PEP 526)
|
||||||
|
if sys.version_info[0:2] >= (3, 6):
|
||||||
|
from python_annotations_variable_c import *
|
||||||
|
|
||||||
|
# No SWIG __annotations__ support with -builtin or -fastproxy
|
||||||
|
annotations_supported = not(is_python_builtin() or is_python_fastproxy())
|
||||||
|
|
||||||
|
if annotations_supported:
|
||||||
|
ts = TemplateShort()
|
||||||
|
anno = ts.__annotations__
|
||||||
|
if anno != {'member_variable': 'int'}:
|
||||||
|
raise RuntimeError("annotations mismatch: {}".format(anno))
|
||||||
|
|
||||||
|
ts = StructWithVar()
|
||||||
|
anno = ts.__annotations__
|
||||||
|
if anno != {'member_variable': 'int'}:
|
||||||
|
raise RuntimeError("annotations mismatch: {}".format(anno))
|
||||||
|
|
||||||
|
ts = StructWithVarNotAnnotated()
|
||||||
|
if getattr(ts, "__annotations__", None) != None:
|
||||||
|
anno = ts.__annotations__
|
||||||
|
raise RuntimeError("annotations mismatch: {}".format(anno))
|
|
@ -0,0 +1,44 @@
|
||||||
|
%module python_annotations_variable_c
|
||||||
|
|
||||||
|
// Tests Python variable annotations, containing C/C++ types, in C++ member variables wrappers.
|
||||||
|
// Member variables are wrapped using Python properties.
|
||||||
|
// This is in a separate test to python_annotations_c.i (which tests function annotations) so that runtime testing
|
||||||
|
// of variable annotations can be done which requires Python 3.6 and later. A syntax error occurs in earlier
|
||||||
|
// versions of Python when importing code containing variable annotations.
|
||||||
|
|
||||||
|
%feature("python:annotations", "c");
|
||||||
|
%feature("python:annotations:novar") member_variable_not_annotated;
|
||||||
|
|
||||||
|
|
||||||
|
%inline %{
|
||||||
|
namespace Space {
|
||||||
|
template<class T>
|
||||||
|
struct Template {
|
||||||
|
int member_variable;
|
||||||
|
int member_variable_not_annotated;
|
||||||
|
};
|
||||||
|
struct StructWithVar{
|
||||||
|
int member_variable;
|
||||||
|
};
|
||||||
|
struct StructWithVarNotAnnotated {
|
||||||
|
int member_variable_not_annotated;
|
||||||
|
};
|
||||||
|
short global_variable;
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
%template(TemplateShort) Space::Template<short>;
|
||||||
|
|
||||||
|
%inline %{
|
||||||
|
#ifdef SWIGPYTHON_BUILTIN
|
||||||
|
int is_python_builtin() { return 1; }
|
||||||
|
#else
|
||||||
|
int is_python_builtin() { return 0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined SWIGPYTHON_FASTPROXY
|
||||||
|
int is_python_fastproxy() { return 1; }
|
||||||
|
#else
|
||||||
|
int is_python_fastproxy() { return 0; }
|
||||||
|
#endif
|
||||||
|
%}
|
||||||
|
|
|
@ -2395,20 +2395,20 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------
|
/* ------------------------------------------------------------
|
||||||
* returnPropertyAnnotation()
|
* variableAnnotation()
|
||||||
*
|
*
|
||||||
* Helper function for constructing a property annotation
|
* Helper function for constructing a variable annotation
|
||||||
* return a empty string for Python 2.x
|
|
||||||
* ------------------------------------------------------------ */
|
* ------------------------------------------------------------ */
|
||||||
|
|
||||||
String *returnPropertyAnnotation(Node *n) {
|
String *variableAnnotation(Node *n) {
|
||||||
String *ret = 0;
|
String *type = Getattr(n, "type");
|
||||||
ret = Getattr(n, "type");
|
if (type)
|
||||||
if (ret) {
|
type = SwigType_str(type, 0);
|
||||||
ret = SwigType_str(ret, 0);
|
bool anno = Equal(Getattr(n, "feature:python:annotations"), "c") ? true : false;
|
||||||
}
|
anno = GetFlag(n, "feature:python:annotations:novar") ? false : anno;
|
||||||
return (ret && py3) ? NewStringf(": \"%s\"", ret)
|
String *annotation = (type && anno) ? NewStringf(": \"%s\"", type) : NewString("");
|
||||||
: NewString("");
|
Delete(type);
|
||||||
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------
|
/* ------------------------------------------------------------
|
||||||
|
@ -5059,12 +5059,14 @@ public:
|
||||||
String *setname = Swig_name_set(NSPACE_TODO, mname);
|
String *setname = Swig_name_set(NSPACE_TODO, mname);
|
||||||
String *getname = Swig_name_get(NSPACE_TODO, mname);
|
String *getname = Swig_name_get(NSPACE_TODO, mname);
|
||||||
int assignable = is_assignable(n);
|
int assignable = is_assignable(n);
|
||||||
Printv(f_shadow, tab4, symname, returnPropertyAnnotation(n), " = property(", module, ".", getname, NIL);
|
String *variable_annotation = variableAnnotation(n);
|
||||||
|
Printv(f_shadow, tab4, symname, variable_annotation, " = property(", module, ".", getname, NIL);
|
||||||
if (assignable)
|
if (assignable)
|
||||||
Printv(f_shadow, ", ", module, ".", setname, NIL);
|
Printv(f_shadow, ", ", module, ".", setname, NIL);
|
||||||
if (have_docstring(n))
|
if (have_docstring(n))
|
||||||
Printv(f_shadow, ", doc=", docstring(n, AUTODOC_VAR, tab4), NIL);
|
Printv(f_shadow, ", doc=", docstring(n, AUTODOC_VAR, tab4), NIL);
|
||||||
Printv(f_shadow, ")\n", NIL);
|
Printv(f_shadow, ")\n", NIL);
|
||||||
|
Delete(variable_annotation);
|
||||||
Delete(mname);
|
Delete(mname);
|
||||||
Delete(setname);
|
Delete(setname);
|
||||||
Delete(getname);
|
Delete(getname);
|
||||||
|
|
Loading…
Reference in New Issue