SIP 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 include a full C/C++ parser.

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
};

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 ::= [
        %API |
        %CompositeModule |
        %ConsolidatedModule |
        %Copying |
        %DefaultEncoding |
        %DefaultMetatype |
        %DefaultSupertype |
        %ExportedHeaderCode |
        %ExportedTypeHintCode |
        %Extract |
        %Feature |
        %Import |
        %Include |
        %InitialisationCode |
        %License |
        %MappedType |
        %Module |
        %ModuleCode |
        %ModuleHeaderCode |
        %OptionalInclude |
        %Platforms |
        %PreInitialisationCode |
        %PostInitialisationCode |
        %Timeline |
        %TypeHintCode |
        %UnitCode |
        mapped-type-template]

statement :: [class-statement | function | variable]

class-statement :: [
        %If |
        class |
        class-template |
        enum |
        namespace |
        opaque-class |
        operator |
        struct |
        typedef |
        exception]

class ::= class name [: super-classes] [class-annotations]
        { {class-line} };

super-classes ::= [public | protected | private] name
        [, super-classes]

class-line ::= [
        class-statement |
        %BIGetBufferCode |
        %BIGetReadBufferCode |
        %BIGetWriteBufferCode |
        %BIGetSegCountCode |
        %BIGetCharBufferCode |
        %BIReleaseBufferCode |
        %ConvertToSubClassCode |
        %ConvertToTypeCode |
        %Docstring |
        %GCClearCode |
        %GCTraverseCode |
        %InstanceCode |
        %PickleCode |
        %TypeCode |
        %TypeHeaderCode |
        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] )
        [exceptions] [function-annotations]
        [c++-constructor-signature] ; [%Docstring]
        [%MethodCode]

c++-constructor-signature ::= [( [argument-list] )]

destructor ::= [virtual] ~ name () [exceptions] [= 0]
        [function-annotations] ; [%MethodCode]
        [%VirtualCatcherCode]

method ::= [Q_SIGNAL] [Q_SLOT] type name (
        [argument-list] ) [const] [final] [exceptions]
        [= 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]
        [exceptions] [= 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__ | __cmp__ | __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__ | __long__ | __lshift__ | __lt__ |
        __matmul | __mod__ | __mul__ | __ne__ |
        __neg__ | __next__ | __nonzero__ | __or__ |
        __pos__ | __repr__ | __rshift__ | __setattr__ |
        __setitem__ | __str__ | __sub__ | __truediv__ |
        __xor__]

operator ::= operator-type
        ( [argument-list] ) [const] [final]
        [exceptions] [function-annotations] ;
        [%MethodCode]

virtual-operator ::= virtual operator-type
        ( [argument-list] ) [const] [final]
        [exceptions] [= 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] ) [exceptions]
        [function-annotations] ; [%Docstring]
        [%MethodCode]

namespace ::= namespace name [{ {namespace-line} }] ;

namespace-line ::= [%TypeHeaderCode | statement]

opaque-class ::= class scoped-name ;

struct ::= struct 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]

exceptions ::= throw ( [exception-list] )

exception-list ::= scoped-name [, exception-list]

argument-list ::= argument [, argument-list] [, ...]

argument ::= [
        type [name] [argument-annotations] [default-value] |
        SIP_ANYSLOT [default-value] |
        SIP_QOBJECT |
        SIP_RXOBJ_CON |
        SIP_RXOBJ_DIS |
        SIP_SIGNAL [default-value] |
        SIP_SLOT [default-value] |
        SIP_SLOT_CON |
        SIP_SLOT_DIS |
        SIP_SSIZE_T]

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 |
        char | signed char | unsigned char | wchar_t |
        int | unsigned | unsigned int |
        short | unsigned short |
        long | unsigned long |
        long long | unsigned long long |
        float | double |
        bool |
        void |
        PyObject |
        SIP_PYBUFFER |
        SIP_PYCALLABLE |
        SIP_PYDICT |
        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_ANYSLOT

Deprecated since version 4.18.

This is both a const char * and a PyObject * that is used as the type of the member instead of const char * in functions that implement the connection or disconnection of an explicitly generated signal to a slot. Handwritten code must be provided to interpret the conversion correctly.

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_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.

SIP_QOBJECT

Deprecated since version 4.18.

This is a QObject * that is a C++ instance of a class derived from Qt’s QObject class.

SIP_RXOBJ_CON

Deprecated since version 4.18.

This is a QObject * that is a C++ instance of a class derived from Qt’s QObject class. It is used as the type of the receiver instead of const QObject * in functions that implement a connection to a slot.

SIP_RXOBJ_DIS

Deprecated since version 4.18.

This is a QObject * that is a C++ instance of a class derived from Qt’s QObject class. It is used as the type of the receiver instead of const QObject * in functions that implement a disconnection from a slot.

SIP_SIGNAL

Deprecated since version 4.18.

This is a const char * that is used as the type of the signal instead of const char * in functions that implement the connection or disconnection of an explicitly generated signal to a slot.

SIP_SLOT

Deprecated since version 4.18.

This is a const char * that is used as the type of the member instead of const char * in functions that implement the connection or disconnection of an explicitly generated signal to a slot.

SIP_SLOT_CON

Deprecated since version 4.18.

This is a const char * that is used as the type of the member instead of const char * in functions that implement the connection of an internally generated signal to a slot. The type includes a comma separated list of types that is the C++ signature of of the signal.

To take an example, QAccel::connectItem() connects an internally generated signal to a slot. The signal is emitted when the keyboard accelerator is activated and it has a single integer argument that is the ID of the accelerator. The C++ signature is:

bool connectItem(int id, const QObject *receiver, const char *member);

The corresponding SIP specification is:

bool connectItem(int, SIP_RXOBJ_CON, SIP_SLOT_CON(int));
SIP_SLOT_DIS

Deprecated since version 4.18.

This is a const char * that is used as the type of the member instead of const char * in functions that implement the disconnection of an internally generated signal to a slot. The type includes a comma separated list of types that is the C++ signature of of the signal.

SIP_SSIZE_T

This is a Py_ssize_t in Python v2.5 and later and int in earlier versions of Python.

Classic Division and True Division

SIP supports the __div__ and __truediv__ special methods (and the corresponding inplace versions) for both Python v2 and v3.

For Python v2 the __div__ method will be used for both classic and true division if a __truediv__ method is not defined.

For Python v3 the __div__ method will be used for true division if a __truediv__ method is not defined.

For all versions of Python, if both methods are defined then __div__ should be defined first.

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.