File: C:/Users/fred/anaconda3/Lib/site-packages/sipbuild/generator/outputs/formatters/argument.py
# Copyright (c) 2023, Riverbank Computing Limited
# All rights reserved.
#
# This copy of SIP is licensed for use under the terms of the SIP License
# Agreement. See the file LICENSE for more details.
#
# This copy of SIP may also used under the terms of the GNU General Public
# License v2 or v3 as published by the Free Software Foundation which can be
# found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ('AS IS'
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from ...scoped_name import STRIP_NONE
from ...specification import ArgumentType, ArrayArgument, ClassKey, ValueType
from ..type_hints import format_voidptr, TypeHintManager
from .base_formatter import BaseFormatter
from .utils import format_scoped_py_name
class ArgumentFormatter(BaseFormatter):
""" This creates various string representations of an argument. """
def cpp_type(self, *, name=None, scope=None, strip=STRIP_NONE,
make_public=False, use_typename=True, as_xml=False):
""" Return the argument as a C++ type. """
arg = self.object
original_typedef = arg.original_typedef
nr_derefs = len(arg.derefs)
is_reference = arg.is_reference
s = ''
if use_typename and original_typedef is not None and not original_typedef.no_type_name and arg.array is not ArrayArgument.ARRAY_SIZE:
# Reverse the previous expansion of the typedef.
if arg.is_const and not original_typedef.type.is_const:
s += 'const '
nr_derefs -= len(original_typedef.type.derefs)
if original_typedef.type.is_reference:
is_reference = False
s += original_typedef.fq_cpp_name.cpp_stripped(strip)
else:
# A function type is handled differently because of the position of
# the name.
if arg.type is ArgumentType.FUNCTION:
s += ArgumentFormatter(self.spec,
arg.definition.result).cpp_type(scope=scope,
strip=strip, as_xml=as_xml)
s += ' (' + '*' * nr_derefs + name + ')('
s += SignatureFormatter(self.spec,
arg.definition).cpp_arguments(scope=scope,
as_xml=as_xml)
s += ')'
return s
if arg.is_const:
s += 'const '
if arg.type in (ArgumentType.SBYTE, ArgumentType.SSTRING):
s += 'signed char'
elif arg.type in (ArgumentType.UBYTE, ArgumentType.USTRING):
s += 'unsigned char'
elif arg.type is ArgumentType.WSTRING:
s += 'wchar_t'
elif arg.type in (ArgumentType.BYTE, ArgumentType.ASCII_STRING, ArgumentType.LATIN1_STRING, ArgumentType.UTF8_STRING, ArgumentType.STRING):
s += 'char'
elif arg.type is ArgumentType.USHORT:
s += 'unsigned short'
elif arg.type is ArgumentType.SHORT:
s += 'short'
elif arg.type is ArgumentType.UINT:
# Qt4 moc uses "uint" in signal signatures. We do all the time
# and hope it is always defined.
s += 'uint'
elif arg.type in (ArgumentType.INT, ArgumentType.CINT):
s += 'int'
elif arg.type is ArgumentType.HASH:
s += 'Py_hash_t'
elif arg.type is ArgumentType.SSIZE:
s += 'Py_ssize_t'
elif arg.type is ArgumentType.SIZE:
s += 'size_t'
elif arg.type is ArgumentType.ULONG:
s += 'unsigned long'
elif arg.type is ArgumentType.LONG:
s += 'long'
elif arg.type is ArgumentType.ULONGLONG:
s += 'unsigned long long'
elif arg.type is ArgumentType.LONGLONG:
s += 'long long'
elif arg.type is ArgumentType.STRUCT:
s += 'struct ' + arg.definition.as_cpp
elif arg.type is ArgumentType.UNION:
s += 'union ' + arg.definition.as_cpp
elif arg.type is ArgumentType.CAPSULE:
nr_derefs = 1
s += 'void'
elif arg.type in (ArgumentType.FAKE_VOID, ArgumentType.VOID):
s += 'void'
elif arg.type in (ArgumentType.BOOL, ArgumentType.CBOOL):
s += 'bool'
elif arg.type in (ArgumentType.FLOAT, ArgumentType.CFLOAT):
s += 'float'
elif arg.type in (ArgumentType.DOUBLE, ArgumentType.CDOUBLE):
s += 'double'
elif arg.type is ArgumentType.DEFINED:
# The only defined types still remaining are arguments to
# templates and default values.
if as_xml:
s += arg.definition.as_py
else:
if self.spec.c_bindings:
s += 'struct '
s += arg.definition.cpp_stripped(strip)
elif arg.type is ArgumentType.MAPPED:
s += ArgumentFormatter(self.spec,
arg.definition.type).cpp_type(scope=scope, strip=strip,
as_xml=as_xml)
elif arg.type is ArgumentType.CLASS:
from .klass import ClassFormatter
if self.spec.c_bindings:
s += 'union ' if arg.definition.class_key is ClassKey.UNION else 'struct '
s += ClassFormatter(self.spec, arg.definition).scoped_name(
scope=scope, strip=strip, make_public=make_public,
as_xml=as_xml)
elif arg.type is ArgumentType.TEMPLATE:
from .template import TemplateFormatter
s += TemplateFormatter(self.spec, arg.definition, scope).cpp_type(
strip=strip, as_xml=as_xml)
elif arg.type is ArgumentType.ENUM:
enum = arg.definition
if enum.fq_cpp_name is None or (enum.is_protected and not make_public):
s += 'int'
else:
s += enum.fq_cpp_name.cpp_stripped(strip)
elif arg.type in (ArgumentType.PYOBJECT, ArgumentType.PYTUPLE, ArgumentType.PYLIST, ArgumentType.PYDICT, ArgumentType.PYCALLABLE, ArgumentType.PYSLICE, ArgumentType.PYTYPE, ArgumentType.PYBUFFER, ArgumentType.PYENUM, ArgumentType.ELLIPSIS):
s += 'PyObject *'
space_before_name = True
for i in range(nr_derefs):
# Note that we don't put a space before the '*' so that Qt
# normalised signal signatures are correct.
s += '*'
space_before_name = False
if arg.derefs[i]:
s += ' const'
space_before_name = True
if is_reference:
s += '&'
if name:
if space_before_name:
s += ' '
s += name
return s
# The types that are implicitly pointers.
_IMPLICIT_POINTERS = (ArgumentType.PYOBJECT, ArgumentType.PYTUPLE,
ArgumentType.PYLIST, ArgumentType.PYDICT, ArgumentType.PYCALLABLE,
ArgumentType.PYSLICE, ArgumentType.PYTYPE, ArgumentType.CAPSULE,
ArgumentType.PYBUFFER, ArgumentType.PYENUM)
def py_default_value(self, type_name, embedded=False, as_xml=False):
""" Return the Python representation of the argument's default value.
"""
from .value_list import ValueListFormatter
arg = self.object
# Use any explicitly provided documentation.
if arg.type_hints is not None and arg.type_hints.default_value is not None:
return arg.type_hints.default_value
# Translate some special cases.
if len(arg.default_value) == 1 and arg.default_value[0].value_type is ValueType.NUMERIC:
value = arg.default_value[0].value
if value == 0 and ('voidptr' in type_name or len(arg.derefs) > 0 or arg.type in self._IMPLICIT_POINTERS):
return 'None'
if arg.type in (ArgumentType.BOOL, ArgumentType.CBOOL):
return 'True' if value else 'False'
return ValueListFormatter(self.spec, arg.default_value).py_expression(
embedded=embedded, as_xml=as_xml)
def as_py_type(self, pep484=False, default_value=False, as_xml=False):
""" Return the argument as a Python type. """
arg = self.object
scope, name = self._py_arg(pep484, as_xml)
s = format_scoped_py_name(scope, name)
if default_value and arg.default_value is not None:
if arg.name is not None:
s += ' ' + arg.name.name
s += '=' + self.py_default_value(name)
return s
def as_rest_ref(self, out, as_xml=False):
""" Return the argument as a reST reference. """
arg = self.object
s = ''
hint = self._get_hint(out)
if hint is None:
if arg.type is ArgumentType.CLASS:
from .klass import ClassFormatter
s += ClassFormatter(self.spec, arg.definition).as_rest_ref()
elif arg.type is ArgumentType.ENUM:
if arg.definition.py_name is not None:
from .enum import EnumFormatter
s += EnumFormatter(self.spec, arg.definition).as_rest_ref()
else:
s += 'int'
elif arg.type is ArgumentType.MAPPED:
# There would normally be a type hint.
s += "unknown-type"
else:
s += self.as_py_type(as_xml=as_xml)
else:
context = arg.definition if arg.type is ArgumentType.CLASS else None
s += TypeHintManager(self.spec).as_rest_ref(hint, out, context,
as_xml=as_xml)
return s
def as_type_hint(self, module, out, defined):
""" Return the argument as a type hint. """
arg = self.object
s = ''
hint = self._get_hint(out)
if hint is None:
if arg.type is ArgumentType.CLASS:
from .klass import ClassFormatter
s += ClassFormatter(self.spec, arg.definition).as_type_hint(
module, defined)
elif arg.type is ArgumentType.ENUM:
if arg.definition.py_name is not None:
from .enum import EnumFormatter
s += EnumFormatter(self.spec, arg.definition).as_type_hint(
module, defined)
else:
s += 'int'
elif arg.type is ArgumentType.MAPPED:
# There would normally be a type hint.
s += 'typing.Any'
else:
s += self.as_py_type(pep484=True)
else:
context = arg.definition if arg.type is ArgumentType.CLASS else None
s += TypeHintManager(self.spec).as_type_hint(hint, out, context,
defined)
return s
def _get_hint(self, out):
""" Return the raw type hint. """
arg = self.object
# Use any explicit type hint unless the argument is constrained.
if arg.type_hints is None:
hint = None
elif out:
hint = arg.type_hints.hint_out
elif arg.is_constrained:
hint = None
else:
hint = arg.type_hints.hint_in
return hint
def _py_arg(self, pep484, as_xml):
""" Return an argument as a 2-tuple of scope and name. """
type = self.object.type
definition = self.object.definition
sip_module_name = self.spec.sip_module + '.' if self.spec.sip_module else ''
scope = None
name = "unknown-type"
if type is ArgumentType.CLASS:
scope = definition.scope
name = definition.py_name.name
elif type is ArgumentType.MAPPED:
if definition.py_name is not None:
name = definition.py_name.name
elif type is ArgumentType.ENUM:
if definition.py_name is None:
name = 'int'
else:
scope = definition.scope
name = definition.py_name.name
elif type is ArgumentType.DEFINED:
name = definition.as_py
elif type is ArgumentType.CAPSULE:
name = definition.base_name
elif type in (ArgumentType.STRUCT, ArgumentType.UNION, ArgumentType.VOID):
name = format_voidptr(self.spec, as_xml)
elif type in (ArgumentType.STRING, ArgumentType.SSTRING, ArgumentType.USTRING):
name = 'bytes'
elif type in (ArgumentType.WSTRING, ArgumentType.ASCII_STRING, ArgumentType.LATIN1_STRING, ArgumentType.UTF8_STRING):
name = 'bytes' if self.object.array is ArrayArgument.ARRAY else 'str'
elif type in (ArgumentType.BYTE, ArgumentType.SBYTE, ArgumentType.UBYTE, ArgumentType.USHORT, ArgumentType.UINT, ArgumentType.LONG, ArgumentType.LONGLONG, ArgumentType.ULONG, ArgumentType.ULONGLONG, ArgumentType.SHORT, ArgumentType.INT, ArgumentType.CINT, ArgumentType.SSIZE, ArgumentType.SIZE, ArgumentType.HASH):
name = 'int'
elif type in (ArgumentType.FLOAT, ArgumentType.CFLOAT, ArgumentType.DOUBLE, ArgumentType.CDOUBLE):
name = 'float'
elif type in (ArgumentType.BOOL, ArgumentType.CBOOL):
name = 'bool'
elif type in (ArgumentType.PYOBJECT, ArgumentType.ELLIPSIS):
name = 'typing.Any' if pep484 else 'Any'
elif type is ArgumentType.PYTUPLE:
name = 'typing.Tuple' if pep484 else 'Tuple'
elif type is ArgumentType.PYLIST:
name = 'typing.List' if pep484 else 'List'
elif type is ArgumentType.PYDICT:
name = 'typing.Dict' if pep484 else 'Dict'
elif type is ArgumentType.PYCALLABLE:
name = 'typing.Callable[..., None]' if pep484 else 'Callable[..., None]'
elif type is ArgumentType.PYSLICE:
name = 'slice'
elif type is ArgumentType.PYTYPE:
name = 'type'
elif type is ArgumentType.PYBUFFER:
if pep484:
name = sip_module_name + 'Buffer'
else:
# This replicates sip.pyi.
name = f'Union[bytes, bytearray, memoryview, {sip_module_name}array, {sip_module_name}voidptr]'
elif type is ArgumentType.PYENUM:
name = 'enum.Enum'
return scope, name