Specification Files
A SIP specification consists of some C/C++ type and function declarations and some directives. The declarations may contain annotations which provide SIP with additional information that cannot be expressed in C/C++. SIP does not implement a full C/C++ parser.
Syntax Definition
The following is a semi-formal description of the syntax of a specification file.
specification ::= {module-statement} module-statement ::= [module-directive | statement] module-directive ::= [%CompositeModule
|%Copying
|%DefaultDocstringFormat
|%DefaultDocstringSignature
|%DefaultEncoding
|%DefaultMetatype
|%DefaultSupertype
|%ExportedHeaderCode
|%ExportedTypeHintCode
|%Extract
|%Feature
|%HideNamespace
|%Import
|%Include
|%InitialisationCode
|%License
|%MappedType
|%Module
|%ModuleCode
|%ModuleHeaderCode
|%Platforms
|%PreInitialisationCode
|%PostInitialisationCode
|%Timeline
|%TypeHintCode
|%UnitCode
|%UnitPostIncludeCode
|%VirtualErrorHandler
| mapped-type-template] statement :: [class-statement | function | variable] class-statement :: [%If
| class | class-template | enum | namespace | opaque-class | operator | struct | union | typedef | exception] class ::= class name [: super-classes] [class-annotations] { {class-line} }; super-classes ::= [public | protected | private] name [, super-classes] class-line ::= [ class-statement |%BIGetBufferCode
|%BIReleaseBufferCode
|%ConvertToSubClassCode
|%ConvertToTypeCode
|%Docstring
|%FinalisationCode
|%GCClearCode
|%GCTraverseCode
|%InstanceCode
|%PickleCode
|%TypeCode
|%TypeHeaderCode
|%TypeHintCode
| constructor | destructor | method | static-method | virtual-method | special-method | operator | virtual-operator | class-variable | public: | public Q_SLOTS: | public slots: | protected: | protected Q_SLOTS: | protected slots: | private: | private Q_SLOTS: | private slots: | Q_SIGNALS: | signals:] constructor ::= [explicit] name ( [argument-list] ) [noexcept] [function-annotations] [c++-constructor-signature] ; [%Docstring
] [%MethodCode
] c++-constructor-signature ::= [( [argument-list] )] destructor ::= [virtual] ~ name () [noexcept] [= 0] [function-annotations] ; [%MethodCode
] [%VirtualCatcherCode
] method ::= [Q_SIGNAL] [Q_SLOT] type name ( [argument-list] ) [const] [final] [noexcept] [= 0] [function-annotations] [c++-signature] ; [%Docstring
] [%MethodCode
] c++-signature ::= [ type ( [argument-list] )] static-method ::= static function virtual-method ::= [Q_SIGNAL] [Q_SLOT] virtual type name ( [argument-list] ) [const] [final] [noexcept] [= 0] [function-annotations] [c++-signature] ; [%MethodCode
] [%VirtualCatcherCode
] [%VirtualCallCode
] special-method ::= type special-method-name ( [argument-list] ) [function-annotations] ; [%MethodCode
] special-method-name ::= [__abs__ | __add__ | __and__ | __aiter__ | __anext__ | __await__ | __bool__ | __call__ | __contains__ | __delattr__ | __delitem__ | __div__ | __eq__ | __float__ | __floordiv__ | __ge__ | __getattr__ | __getattribute__ | __getitem__ | __gt__ | __hash__ | __iadd__ | __iand__ | __idiv__ | __ifloordiv__ | __ilshift__ | __imatmul__ | __imod__ | __imul__ | __index__ | __int__ | __invert__ | __ior__ | __irshift__ | __isub__ | __iter__ | __itruediv__ | __ixor__ | __le__ | __len__ | __lshift__ | __lt__ | __matmul | __mod__ | __mul__ | __ne__ | __neg__ | __next__ | __or__ | __pos__ | __repr__ | __rshift__ | __setattr__ | __setitem__ | __str__ | __sub__ | __truediv__ | __xor__] operator ::= operator-type ( [argument-list] ) [const] [final] [noexcept] [function-annotations] ; [%MethodCode
] virtual-operator ::= virtual operator-type ( [argument-list] ) [const] [final] [noexcept] [= 0] [function-annotations] ; [%MethodCode
] [%VirtualCatcherCode
] [%VirtualCallCode
] operatator-type ::= [ operator-function | operator-cast ] operator-function ::= type operator operator-name operator-cast ::= operator type operator-name ::= [+ | - | * | / | % | & | | | ^ | << | >> | += | -= | *= | /= | %= | &= | |= | ^= | <<= | >>= | ~ | () | [] | < | <= | == | != | > | >>= | =] class-variable ::= [static] variable class-template :: = template < type-list > class mapped-type-template :: = template < type-list >%MappedType
enum ::= enum [enum-key] [name] [enum-annotations] { {enum-line} }; enum-key ::= [class | struct] enum-line ::= [%If
| name [enum-annotations] , function ::= type name ( [argument-list] ) [noexcept] [function-annotations] ; [%Docstring
] [%MethodCode
] namespace ::= namespace name [{ {namespace-line} }] ; namespace-line ::= [%TypeHeaderCode
| statement] opaque-class ::= class scoped-name ; struct ::= struct name { {class-line} }; union ::= union name { {class-line} }; typedef ::= typedef [typed-name | function-pointer] typedef-annotations ; variable::= typed-name [variable-annotations] ; [%AccessCode
] [%GetCode
] [%SetCode
] exception ::=%Exception
exception-name [exception-base] { [%TypeHeaderCode
]%RaiseCode
}; exception-name ::= scoped-name exception-base ::= ( [exception-name | python-exception] ) python-exception ::= [SIP_ArithmeticError | SIP_AssertionError | SIP_AttributeError | SIP_BaseException | SIP_BlockingIOError | SIP_BrokenPipeError | SIP_BufferError | SIP_ChildProcessError | SIP_ConnectionAbortedError | SIP_ConnectionError | SIP_ConnectionRefusedError | SIP_ConnectionResetError | SIP_EnvironmentError | SIP_EOFError | SIP_Exception | SIP_FileExistsError | SIP_FileNotFoundError | SIP_FloatingPointError | SIP_GeneratorExit | SIP_ImportError | SIP_IndentationError | SIP_IndexError | SIP_InterruptedError | SIP_IOError | SIP_IsADirectoryError | SIP_KeyboardInterrupt | SIP_KeyError | SIP_LookupError | SIP_MemoryError | SIP_NameError | SIP_NotADirectoryError | SIP_NotImplementedError | SIP_OSError | SIP_OverflowError | SIP_PermissionError | SIP_ProcessLookupError | SIP_ReferenceError | SIP_RuntimeError | SIP_StandardError | SIP_StopIteration | SIP_SyntaxError | SIP_SystemError | SIP_SystemExit | SIP_TabError | SIP_TimeoutError | SIP_TypeError | SIP_UnboundLocalError | SIP_UnicodeDecodeError | SIP_UnicodeEncodeError | SIP_UnicodeError | SIP_UnicodeTranslateError | SIP_ValueError | SIP_VMSError | SIP_WindowsError | SIP_ZeroDivisionError | SIP_Warning | SIP_BytesWarning | SIP_DeprecationWarning | SIP_FutureWarning | SIP_ImportWarning | SIP_PendingDeprecationWarning | SIP_ResourceWarning | SIP_RuntimeWarning | SIP_SyntaxWarning | SIP_UnicodeWarning | SIP_UserWarning] argument-list ::= argument [, argument-list] [, ...] argument ::= type [name] [argument-annotations] [default-value] default-value ::= = expression expression ::= [value | value binary-operator expression] value ::= [unary-operator] simple-value simple-value ::= [scoped-name | function-call | real-value | integer-value | boolean-value | string-value | character-value] typed-name::= type name function-pointer::= type (* name )( [type-list] ) type-list ::= type [, type-list] function-call ::= scoped-name ( [value-list] ) value-list ::= value [, value-list] real-value ::= a floating point number integer-value ::= a number boolean-value ::= [true | false] string-value ::= " {character} " character-value ::= ' character ' unary-operator ::= [! | ~ | - | + | * | &] binary-operator ::= [- | + | * | / | & | |] argument-annotations ::= see Argument Annotations class-annotations ::= see Class Annotations enum-annotations ::= see Enum Annotations function-annotations ::= see Function Annotations typedef-annotations ::= see Typedef Annotations variable-annotations ::= see Variable Annotations type ::= [const] base-type {*} [&] type-list ::= type [, type-list] base-type ::= [scoped-name | template | struct scoped-name | union scoped-name | char | signed char | unsigned char | wchar_t | int | unsigned | unsigned int | size_t | short | unsigned short | long | unsigned long | long long | unsigned long long | float | double | bool | void | Py_hash_t | Py_ssize_t | PyObject |SIP_PYBUFFER
|SIP_PYCALLABLE
|SIP_PYDICT
|SIP_PYENUM
|SIP_PYLIST
|SIP_PYOBJECT
|SIP_PYSLICE
|SIP_PYTUPLE
|SIP_PYTYPE
] scoped-name ::= name [:: scoped-name] template ::= scoped-name < type-list > dotted-name ::= name [. dotted-name] name ::= _A-Za-z {_A-Za-z0-9}
Here is a short list of differences between C++ and the subset supported by SIP that might trip you up.
SIP does not support the use of
[]
in types. Use pointers instead.A global
operator
can only be defined if its first argument is a class or a named enum that has been wrapped in the same module.Variables declared outside of a class are effectively read-only.
Variable Numbers of Arguments
SIP supports the use of ...
as the last part of a function signature. Any
remaining arguments are collected as a Python tuple.
Additional SIP Types
SIP supports a number of additional data types that can be used in Python signatures.
- SIP_PYBUFFER
This is a PyObject *
that implements the Python buffer protocol.
- SIP_PYCALLABLE
This is a PyObject *
that is a Python callable object.
- SIP_PYDICT
This is a PyObject *
that is a Python dictionary object.
- SIP_PYENUM
This is a PyObject *
that is a Python enum object.
- SIP_PYLIST
This is a PyObject *
that is a Python list object.
- SIP_PYOBJECT
This is a PyObject *
of any Python type. The type PyObject *
can also
be used.
- SIP_PYSLICE
This is a PyObject *
that is a Python slice object.
- SIP_PYTUPLE
This is a PyObject *
that is a Python tuple object.
- SIP_PYTYPE
This is a PyObject *
that is a Python type object.
Python API vs. C/C++ API
It is important to understand that a SIP specification describes the Python
API, i.e. the API available to the Python programmer when they import
the
generated module. It does not have to accurately represent the underlying
C/C++ library. There is nothing wrong with omitting functions that make
little sense in a Python context, or adding functions implemented with
handwritten code that have no C/C++ equivalent. It is even possible (and
sometimes necessary) to specify a different super-class hierarchy for a C++
class. All that matters is that the generated code compiles properly.
In most cases the Python API matches the C/C++ API. In some cases handwritten
code (see %MethodCode
) is used to map from one to the other
without SIP having to know the details itself. However, there are a few cases
where SIP generates a thin wrapper around a C++ method or constructor (see
Generated Derived Classes) and needs to know the exact C++ signature. To deal
with these cases SIP allows two signatures to be specified. For example:
class Klass
{
public:
// The Python signature is a tuple, but the underlying C++ signature
// is a 2 element array.
Klass(SIP_PYTUPLE) [(int *)];
%MethodCode
int iarr[2];
if (PyArg_ParseTuple(a0, "ii", &iarr[0], &iarr[1]))
{
// Note that we use the SIP generated derived class
// constructor.
Py_BEGIN_ALLOW_THREADS
sipCpp = new sipKlass(iarr);
Py_END_ALLOW_THREADS
}
%End
};
Namespaces
SIP implements C++ namespaces as a Python class which cannot be instantiated. The contents of the namespace, including nested namespaces, are implemented as attributes of the class.
The namespace class is created in the module that SIP is parsing when it first sees the namespace defined. If a function (for example) is defined in a namespace that is first defined in another module then the function is added to the namespace class in that other module.
Say that we have a file a.sip
that defines a module a_module
as
follows:
%Module a_module
namespace N
{
void hello();
};
We also have a file b.sip
that defines a module b_module
as follows:
%Module b_module
%Import a.sip
namespace N
{
void bye();
};
When SIP parses b.sip
it first sees the N
namespace defined in module
a_module
. Therefore it places the bye()
function in the N
Python
class in the a_module
. It does not create an N
Python class in the
b_module
. Consequently the following code will call the bye()
function:
import a_module
import b_module
a_module.N.bye()
While this reflects the C++ usage it may not be obvious to the Python
programmer who might expect to call the bye()
function using:
import b_module
b_module.N.bye()
In order to achieve this behavior make sure that the N
namespace is first
defined in the b_module
. The following version of b.sip
does this:
%Module b_module
namespace N;
%Import a.sip
namespace N
{
void bye();
};
Alternatively you could just move the %Import
directive so that it
is at the end of the file.