diff options
Diffstat (limited to 'sources/shiboken6/shibokenmodule')
20 files changed, 538 insertions, 1391 deletions
diff --git a/sources/shiboken6/shibokenmodule/CMakeLists.txt b/sources/shiboken6/shibokenmodule/CMakeLists.txt index cd0dc176d..702750450 100644 --- a/sources/shiboken6/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken6/shibokenmodule/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + project(shibokenmodule) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/shibokenmodule.txt.in" @@ -7,25 +10,30 @@ set(sample_SRC ${CMAKE_CURRENT_BINARY_DIR}/Shiboken/shiboken_module_wrapper.cpp) set(shibokenmodule_TYPESYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_shiboken.xml) +shiboken_get_tool_shell_wrapper(shiboken tool_wrapper) + add_custom_command( -OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" -BYPRODUCTS ${sample_SRC} -# Note: shiboken6 is an executable target. By not specifying its explicit -# path, CMAKE figures it out, itself! -# This fixes an issue with Visual Studio, see https://github.com/PySide/shiboken6/pull/11 -COMMAND Shiboken6::shiboken6 - --project-file=${CMAKE_CURRENT_BINARY_DIR}/shibokenmodule.txt ${GENERATOR_EXTRA_FLAGS} -DEPENDS ${shibokenmodule_TYPESYSTEM} -WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -COMMENT "Running generator for 'Shiboken'..." + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" + BYPRODUCTS ${sample_SRC} + COMMAND + ${tool_wrapper} + $<TARGET_FILE:Shiboken6::shiboken6> + --project-file=${CMAKE_CURRENT_BINARY_DIR}/shibokenmodule.txt + ${GENERATOR_EXTRA_FLAGS} + DEPENDS ${shibokenmodule_TYPESYSTEM} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Running generator for 'Shiboken'..." ) add_library(shibokenmodule MODULE ${sample_SRC}) target_include_directories(shibokenmodule PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}) -set_property(TARGET shibokenmodule PROPERTY PREFIX "") -# PYSIDE-1497: This `..` is the crucial trick to unify the path location of `Shiboken`. -set_property(TARGET shibokenmodule PROPERTY OUTPUT_NAME "../Shiboken${PYTHON_EXTENSION_SUFFIX}") + +set_target_properties(shibokenmodule PROPERTIES + PREFIX "" + OUTPUT_NAME "Shiboken${PYTHON_EXTENSION_SUFFIX}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/..") if(WIN32) set_property(TARGET shibokenmodule PROPERTY SUFFIX ".pyd") diff --git a/sources/shiboken6/shibokenmodule/Shiboken.pyi b/sources/shiboken6/shibokenmodule/Shiboken.pyi index a6feeae15..6a1a63217 100644 --- a/sources/shiboken6/shibokenmodule/Shiboken.pyi +++ b/sources/shiboken6/shibokenmodule/Shiboken.pyi @@ -18,19 +18,20 @@ class Object(object): def __init__(self) -> None: ... -class VoidPtr(object): ... +class VoidPtr(object): + def __init__(self, value: int) -> None: ... def _unpickle_enum(arg__1: object, arg__2: object) -> object: ... -def createdByPython(arg__1: object) -> bool: ... -def delete(arg__1: object) -> None: ... -def dump(arg__1: object) -> object: ... -def getAllValidWrappers() -> object: ... -def getCppPointer(arg__1: object) -> object: ... -def invalidate(arg__1: object) -> None: ... +def createdByPython(arg__1: Shiboken.Object) -> bool: ... +def delete(arg__1: Shiboken.Object) -> None: ... +def dump(arg__1: object) -> str: ... +def getAllValidWrappers() -> list[Shiboken.Object]: ... +def getCppPointer(arg__1: Shiboken.Object) -> tuple[int, ...]: ... +def invalidate(arg__1: Shiboken.Object) -> None: ... def isValid(arg__1: object) -> bool: ... -def ownedByPython(arg__1: object) -> bool: ... -def wrapInstance(arg__1: int, arg__2: type) -> object: ... +def ownedByPython(arg__1: Shiboken.Object) -> bool: ... +def wrapInstance(arg__1: int, arg__2: type) -> Shiboken.Object: ... # eof diff --git a/sources/shiboken6/shibokenmodule/__init__.py.in b/sources/shiboken6/shibokenmodule/__init__.py.in index 5508403a9..c859160bc 100644 --- a/sources/shiboken6/shibokenmodule/__init__.py.in +++ b/sources/shiboken6/shibokenmodule/__init__.py.in @@ -25,6 +25,3 @@ import functools import typing from shiboken6.Shiboken import * - -# Trigger signature initialization via __builtins__. -_init_pyside_extension() diff --git a/sources/shiboken6/shibokenmodule/_config.py.in b/sources/shiboken6/shibokenmodule/_config.py.in index 92b3cd23c..600c431c9 100644 --- a/sources/shiboken6/shibokenmodule/_config.py.in +++ b/sources/shiboken6/shibokenmodule/_config.py.in @@ -9,3 +9,4 @@ version_info = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MI @PACKAGE_BUILD_COMMIT_HASH_DESCRIBED@ @PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ @PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT@ +@QT_MACOS_DEPLOYMENT_TARGET@ diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/enum_310.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/enum_310.py deleted file mode 100644 index b6430c89a..000000000 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/enum_310.py +++ /dev/null @@ -1,1102 +0,0 @@ -# Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -PSF_LICENSE = """ -PSF LICENSE AGREEMENT FOR PYTHON 3.10.4 - -1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and - the Individual or Organization ("Licensee") accessing and otherwise using Python - 3.10.4 software in source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby - grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, - analyze, test, perform and/or display publicly, prepare derivative works, - distribute, and otherwise use Python 3.10.4 alone or in any derivative - version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright © 2001-2022 Python Software Foundation; All Rights - Reserved" are retained in Python 3.10.4 alone or in any derivative version - prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on or - incorporates Python 3.10.4 or any part thereof, and wants to make the - derivative work available to others as provided herein, then Licensee hereby - agrees to include in any such work a brief summary of the changes made to Python - 3.10.4. - -4. PSF is making Python 3.10.4 available to Licensee on an "AS IS" basis. - PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF - EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR - WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE - USE OF PYTHON 3.10.4 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.10.4 - FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF - MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.10.4, OR ANY DERIVATIVE - THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material breach of - its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any relationship - of agency, partnership, or joint venture between PSF and Licensee. This License - Agreement does not grant permission to use PSF trademarks or trade name in a - trademark sense to endorse or promote products or services of Licensee, or any - third party. - -8. By copying, installing or otherwise using Python 3.10.4, Licensee agrees - to be bound by the terms and conditions of this License Agreement. -""" - -import sys -from types import MappingProxyType, DynamicClassAttribute - - -__all__ = [ - 'EnumMeta', - 'Enum', 'IntEnum', 'Flag', 'IntFlag', - 'auto', 'unique', - ] - - -def _is_descriptor(obj): - """ - Returns True if obj is a descriptor, False otherwise. - """ - return ( - hasattr(obj, '__get__') or - hasattr(obj, '__set__') or - hasattr(obj, '__delete__') - ) - -def _is_dunder(name): - """ - Returns True if a __dunder__ name, False otherwise. - """ - return ( - len(name) > 4 and - name[:2] == name[-2:] == '__' and - name[2] != '_' and - name[-3] != '_' - ) - -def _is_sunder(name): - """ - Returns True if a _sunder_ name, False otherwise. - """ - return ( - len(name) > 2 and - name[0] == name[-1] == '_' and - name[1:2] != '_' and - name[-2:-1] != '_' - ) - -def _is_private(cls_name, name): - # do not use `re` as `re` imports `enum` - pattern = '_%s__' % (cls_name, ) - pat_len = len(pattern) - if ( - len(name) > pat_len - and name.startswith(pattern) - and name[pat_len:pat_len+1] != ['_'] - and (name[-1] != '_' or name[-2] != '_') - ): - return True - else: - return False - -def _make_class_unpicklable(cls): - """ - Make the given class un-picklable. - """ - def _break_on_call_reduce(self, proto): - raise TypeError('%r cannot be pickled' % self) - cls.__reduce_ex__ = _break_on_call_reduce - cls.__module__ = '<unknown>' - -_auto_null = object() -class auto: - """ - Instances are replaced with an appropriate value in Enum class suites. - """ - value = _auto_null - - -class _EnumDict(dict): - """ - Track enum member order and ensure member names are not reused. - - EnumMeta will use the names found in self._member_names as the - enumeration member names. - """ - def __init__(self): - super().__init__() - self._member_names = [] - self._last_values = [] - self._ignore = [] - self._auto_called = False - - def __setitem__(self, key, value): - """ - Changes anything not dundered or not a descriptor. - - If an enum member name is used twice, an error is raised; duplicate - values are not checked for. - - Single underscore (sunder) names are reserved. - """ - if _is_private(self._cls_name, key): - import warnings - warnings.warn( - "private variables, such as %r, will be normal attributes in 3.11" - % (key, ), - DeprecationWarning, - stacklevel=2, - ) - if _is_sunder(key): - if key not in ( - '_order_', '_create_pseudo_member_', - '_generate_next_value_', '_missing_', '_ignore_', - ): - raise ValueError('_names_ are reserved for future Enum use') - if key == '_generate_next_value_': - # check if members already defined as auto() - if self._auto_called: - raise TypeError("_generate_next_value_ must be defined before members") - setattr(self, '_generate_next_value', value) - elif key == '_ignore_': - if isinstance(value, str): - value = value.replace(',',' ').split() - else: - value = list(value) - self._ignore = value - already = set(value) & set(self._member_names) - if already: - raise ValueError( - '_ignore_ cannot specify already set names: %r' - % (already, ) - ) - elif _is_dunder(key): - if key == '__order__': - key = '_order_' - elif key in self._member_names: - # descriptor overwriting an enum? - raise TypeError('Attempted to reuse key: %r' % key) - elif key in self._ignore: - pass - elif not _is_descriptor(value): - if key in self: - # enum overwriting a descriptor? - raise TypeError('%r already defined as: %r' % (key, self[key])) - if isinstance(value, auto): - if value.value == _auto_null: - value.value = self._generate_next_value( - key, - 1, - len(self._member_names), - self._last_values[:], - ) - self._auto_called = True - value = value.value - self._member_names.append(key) - self._last_values.append(value) - super().__setitem__(key, value) - - -# Dummy value for Enum as EnumMeta explicitly checks for it, but of course -# until EnumMeta finishes running the first time the Enum class doesn't exist. -# This is also why there are checks in EnumMeta like `if Enum is not None` -Enum = None - -class EnumMeta(type): - """ - Metaclass for Enum - """ - @classmethod - def __prepare__(metacls, cls, bases, **kwds): - # check that previous enum members do not exist - metacls._check_for_existing_members(cls, bases) - # create the namespace dict - enum_dict = _EnumDict() - enum_dict._cls_name = cls - # inherit previous flags and _generate_next_value_ function - member_type, first_enum = metacls._get_mixins_(cls, bases) - if first_enum is not None: - enum_dict['_generate_next_value_'] = getattr( - first_enum, '_generate_next_value_', None, - ) - return enum_dict - - def __new__(metacls, cls, bases, classdict, **kwds): - # an Enum class is final once enumeration items have been defined; it - # cannot be mixed with other types (int, float, etc.) if it has an - # inherited __new__ unless a new __new__ is defined (or the resulting - # class will fail). - # - # remove any keys listed in _ignore_ - classdict.setdefault('_ignore_', []).append('_ignore_') - ignore = classdict['_ignore_'] - for key in ignore: - classdict.pop(key, None) - member_type, first_enum = metacls._get_mixins_(cls, bases) - __new__, save_new, use_args = metacls._find_new_( - classdict, member_type, first_enum, - ) - - # save enum items into separate mapping so they don't get baked into - # the new class - enum_members = {k: classdict[k] for k in classdict._member_names} - for name in classdict._member_names: - del classdict[name] - - # adjust the sunders - _order_ = classdict.pop('_order_', None) - - # check for illegal enum names (any others?) - invalid_names = set(enum_members) & {'mro', ''} - if invalid_names: - raise ValueError('Invalid enum member name: {0}'.format( - ','.join(invalid_names))) - - # create a default docstring if one has not been provided - if '__doc__' not in classdict: - classdict['__doc__'] = 'An enumeration.' - - enum_class = super().__new__(metacls, cls, bases, classdict, **kwds) - enum_class._member_names_ = [] # names in definition order - enum_class._member_map_ = {} # name->value map - enum_class._member_type_ = member_type - - # save DynamicClassAttribute attributes from super classes so we know - # if we can take the shortcut of storing members in the class dict - dynamic_attributes = { - k for c in enum_class.mro() - for k, v in c.__dict__.items() - if isinstance(v, DynamicClassAttribute) - } - - # Reverse value->name map for hashable values. - enum_class._value2member_map_ = {} - - # If a custom type is mixed into the Enum, and it does not know how - # to pickle itself, pickle.dumps will succeed but pickle.loads will - # fail. Rather than have the error show up later and possibly far - # from the source, sabotage the pickle protocol for this class so - # that pickle.dumps also fails. - # - # However, if the new class implements its own __reduce_ex__, do not - # sabotage -- it's on them to make sure it works correctly. We use - # __reduce_ex__ instead of any of the others as it is preferred by - # pickle over __reduce__, and it handles all pickle protocols. - if '__reduce_ex__' not in classdict: - if member_type is not object: - methods = ('__getnewargs_ex__', '__getnewargs__', - '__reduce_ex__', '__reduce__') - if not any(m in member_type.__dict__ for m in methods): - if '__new__' in classdict: - # too late, sabotage - _make_class_unpicklable(enum_class) - else: - # final attempt to verify that pickling would work: - # travel mro until __new__ is found, checking for - # __reduce__ and friends along the way -- if any of them - # are found before/when __new__ is found, pickling should - # work - sabotage = None - for chain in bases: - for base in chain.__mro__: - if base is object: - continue - elif any(m in base.__dict__ for m in methods): - # found one, we're good - sabotage = False - break - elif '__new__' in base.__dict__: - # not good - sabotage = True - break - if sabotage is not None: - break - if sabotage: - _make_class_unpicklable(enum_class) - # instantiate them, checking for duplicates as we go - # we instantiate first instead of checking for duplicates first in case - # a custom __new__ is doing something funky with the values -- such as - # auto-numbering ;) - for member_name in classdict._member_names: - value = enum_members[member_name] - if not isinstance(value, tuple): - args = (value, ) - else: - args = value - if member_type is tuple: # special case for tuple enums - args = (args, ) # wrap it one more time - if not use_args: - enum_member = __new__(enum_class) - if not hasattr(enum_member, '_value_'): - enum_member._value_ = value - else: - enum_member = __new__(enum_class, *args) - if not hasattr(enum_member, '_value_'): - if member_type is object: - enum_member._value_ = value - else: - enum_member._value_ = member_type(*args) - value = enum_member._value_ - enum_member._name_ = member_name - enum_member.__objclass__ = enum_class - enum_member.__init__(*args) - # If another member with the same value was already defined, the - # new member becomes an alias to the existing one. - for name, canonical_member in enum_class._member_map_.items(): - if canonical_member._value_ == enum_member._value_: - enum_member = canonical_member - break - else: - # Aliases don't appear in member names (only in __members__). - enum_class._member_names_.append(member_name) - # performance boost for any member that would not shadow - # a DynamicClassAttribute - if member_name not in dynamic_attributes: - setattr(enum_class, member_name, enum_member) - # now add to _member_map_ - enum_class._member_map_[member_name] = enum_member - try: - # This may fail if value is not hashable. We can't add the value - # to the map, and by-value lookups for this value will be - # linear. - enum_class._value2member_map_[value] = enum_member - except TypeError: - pass - - # double check that repr and friends are not the mixin's or various - # things break (such as pickle) - # however, if the method is defined in the Enum itself, don't replace - # it - for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): - if name in classdict: - continue - class_method = getattr(enum_class, name) - obj_method = getattr(member_type, name, None) - enum_method = getattr(first_enum, name, None) - if obj_method is not None and obj_method is class_method: - setattr(enum_class, name, enum_method) - - # replace any other __new__ with our own (as long as Enum is not None, - # anyway) -- again, this is to support pickle - if Enum is not None: - # if the user defined their own __new__, save it before it gets - # clobbered in case they subclass later - if save_new: - enum_class.__new_member__ = __new__ - enum_class.__new__ = Enum.__new__ - - # py3 support for definition order (helps keep py2/py3 code in sync) - if _order_ is not None: - if isinstance(_order_, str): - _order_ = _order_.replace(',', ' ').split() - if _order_ != enum_class._member_names_: - raise TypeError('member order does not match _order_') - - return enum_class - - def __bool__(self): - """ - classes/types should always be True. - """ - return True - - def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1): - """ - Either returns an existing member, or creates a new enum class. - - This method is used both when an enum class is given a value to match - to an enumeration member (i.e. Color(3)) and for the functional API - (i.e. Color = Enum('Color', names='RED GREEN BLUE')). - - When used for the functional API: - - `value` will be the name of the new class. - - `names` should be either a string of white-space/comma delimited names - (values will start at `start`), or an iterator/mapping of name, value pairs. - - `module` should be set to the module this class is being created in; - if it is not set, an attempt to find that module will be made, but if - it fails the class will not be picklable. - - `qualname` should be set to the actual location this class can be found - at in its module; by default it is set to the global scope. If this is - not correct, unpickling will fail in some circumstances. - - `type`, if set, will be mixed in as the first base class. - """ - if names is None: # simple value lookup - return cls.__new__(cls, value) - # otherwise, functional API: we're creating a new Enum type - return cls._create_( - value, - names, - module=module, - qualname=qualname, - type=type, - start=start, - ) - - def __contains__(cls, obj): - if not isinstance(obj, Enum): - import warnings - warnings.warn( - "in 3.12 __contains__ will no longer raise TypeError, but will return True if\n" - "obj is a member or a member's value", - DeprecationWarning, - stacklevel=2, - ) - raise TypeError( - "unsupported operand type(s) for 'in': '%s' and '%s'" % ( - type(obj).__qualname__, cls.__class__.__qualname__)) - return isinstance(obj, cls) and obj._name_ in cls._member_map_ - - def __delattr__(cls, attr): - # nicer error message when someone tries to delete an attribute - # (see issue19025). - if attr in cls._member_map_: - raise AttributeError("%s: cannot delete Enum member." % cls.__name__) - super().__delattr__(attr) - - def __dir__(self): - return ( - ['__class__', '__doc__', '__members__', '__module__'] - + self._member_names_ - ) - - def __getattr__(cls, name): - """ - Return the enum member matching `name` - - We use __getattr__ instead of descriptors or inserting into the enum - class' __dict__ in order to support `name` and `value` being both - properties for enum members (which live in the class' __dict__) and - enum members themselves. - """ - if _is_dunder(name): - raise AttributeError(name) - try: - return cls._member_map_[name] - except KeyError: - raise AttributeError(name) from None - - def __getitem__(cls, name): - return cls._member_map_[name] - - def __iter__(cls): - """ - Returns members in definition order. - """ - return (cls._member_map_[name] for name in cls._member_names_) - - def __len__(cls): - return len(cls._member_names_) - - @property - def __members__(cls): - """ - Returns a mapping of member name->value. - - This mapping lists all enum members, including aliases. Note that this - is a read-only view of the internal mapping. - """ - return MappingProxyType(cls._member_map_) - - def __repr__(cls): - return "<enum %r>" % cls.__name__ - - def __reversed__(cls): - """ - Returns members in reverse definition order. - """ - return (cls._member_map_[name] for name in reversed(cls._member_names_)) - - def __setattr__(cls, name, value): - """ - Block attempts to reassign Enum members. - - A simple assignment to the class namespace only changes one of the - several possible ways to get an Enum member from the Enum class, - resulting in an inconsistent Enumeration. - """ - member_map = cls.__dict__.get('_member_map_', {}) - if name in member_map: - raise AttributeError('Cannot reassign members.') - super().__setattr__(name, value) - - def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1): - """ - Convenience method to create a new Enum class. - - `names` can be: - - * A string containing member names, separated either with spaces or - commas. Values are incremented by 1 from `start`. - * An iterable of member names. Values are incremented by 1 from `start`. - * An iterable of (member name, value) pairs. - * A mapping of member name -> value pairs. - """ - metacls = cls.__class__ - bases = (cls, ) if type is None else (type, cls) - _, first_enum = cls._get_mixins_(cls, bases) - classdict = metacls.__prepare__(class_name, bases) - - # special processing needed for names? - if isinstance(names, str): - names = names.replace(',', ' ').split() - if isinstance(names, (tuple, list)) and names and isinstance(names[0], str): - original_names, names = names, [] - last_values = [] - for count, name in enumerate(original_names): - value = first_enum._generate_next_value_(name, start, count, last_values[:]) - last_values.append(value) - names.append((name, value)) - - # Here, names is either an iterable of (name, value) or a mapping. - for item in names: - if isinstance(item, str): - member_name, member_value = item, names[item] - else: - member_name, member_value = item - classdict[member_name] = member_value - enum_class = metacls.__new__(metacls, class_name, bases, classdict) - - # TODO: replace the frame hack if a blessed way to know the calling - # module is ever developed - if module is None: - try: - module = sys._getframe(2).f_globals['__name__'] - except (AttributeError, ValueError, KeyError): - pass - if module is None: - _make_class_unpicklable(enum_class) - else: - enum_class.__module__ = module - if qualname is not None: - enum_class.__qualname__ = qualname - - return enum_class - - def _convert_(cls, name, module, filter, source=None): - """ - Create a new Enum subclass that replaces a collection of global constants - """ - # convert all constants from source (or module) that pass filter() to - # a new Enum called name, and export the enum and its members back to - # module; - # also, replace the __reduce_ex__ method so unpickling works in - # previous Python versions - module_globals = vars(sys.modules[module]) - if source: - source = vars(source) - else: - source = module_globals - # _value2member_map_ is populated in the same order every time - # for a consistent reverse mapping of number to name when there - # are multiple names for the same number. - members = [ - (name, value) - for name, value in source.items() - if filter(name)] - try: - # sort by value - members.sort(key=lambda t: (t[1], t[0])) - except TypeError: - # unless some values aren't comparable, in which case sort by name - members.sort(key=lambda t: t[0]) - cls = cls(name, members, module=module) - cls.__reduce_ex__ = _reduce_ex_by_name - module_globals.update(cls.__members__) - module_globals[name] = cls - return cls - - @staticmethod - def _check_for_existing_members(class_name, bases): - for chain in bases: - for base in chain.__mro__: - if issubclass(base, Enum) and base._member_names_: - raise TypeError( - "%s: cannot extend enumeration %r" - % (class_name, base.__name__) - ) - - @staticmethod - def _get_mixins_(class_name, bases): - """ - Returns the type for creating enum members, and the first inherited - enum class. - - bases: the tuple of bases that was given to __new__ - """ - if not bases: - return object, Enum - - def _find_data_type(bases): - data_types = set() - for chain in bases: - candidate = None - for base in chain.__mro__: - if base is object: - continue - elif issubclass(base, Enum): - if base._member_type_ is not object: - data_types.add(base._member_type_) - break - elif '__new__' in base.__dict__: - if issubclass(base, Enum): - continue - data_types.add(candidate or base) - break - else: - candidate = candidate or base - if len(data_types) > 1: - raise TypeError('%r: too many data types: %r' % (class_name, data_types)) - elif data_types: - return data_types.pop() - else: - return None - - # ensure final parent class is an Enum derivative, find any concrete - # data type, and check that Enum has no members - first_enum = bases[-1] - if not issubclass(first_enum, Enum): - raise TypeError("new enumerations should be created as " - "`EnumName([mixin_type, ...] [data_type,] enum_type)`") - member_type = _find_data_type(bases) or object - if first_enum._member_names_: - raise TypeError("Cannot extend enumerations") - return member_type, first_enum - - @staticmethod - def _find_new_(classdict, member_type, first_enum): - """ - Returns the __new__ to be used for creating the enum members. - - classdict: the class dictionary given to __new__ - member_type: the data type whose __new__ will be used by default - first_enum: enumeration to check for an overriding __new__ - """ - # now find the correct __new__, checking to see of one was defined - # by the user; also check earlier enum classes in case a __new__ was - # saved as __new_member__ - __new__ = classdict.get('__new__', None) - - # should __new__ be saved as __new_member__ later? - save_new = __new__ is not None - - if __new__ is None: - # check all possibles for __new_member__ before falling back to - # __new__ - for method in ('__new_member__', '__new__'): - for possible in (member_type, first_enum): - target = getattr(possible, method, None) - if target not in { - None, - None.__new__, - object.__new__, - Enum.__new__, - }: - __new__ = target - break - if __new__ is not None: - break - else: - __new__ = object.__new__ - - # if a non-object.__new__ is used then whatever value/tuple was - # assigned to the enum member name will be passed to __new__ and to the - # new enum member's __init__ - if __new__ is object.__new__: - use_args = False - else: - use_args = True - return __new__, save_new, use_args - - -class Enum(metaclass=EnumMeta): - """ - Generic enumeration. - - Derive from this class to define new enumerations. - """ - def __new__(cls, value): - # all enum instances are actually created during class construction - # without calling this method; this method is called by the metaclass' - # __call__ (i.e. Color(3) ), and by pickle - if type(value) is cls: - # For lookups like Color(Color.RED) - return value - # by-value search for a matching enum member - # see if it's in the reverse mapping (for hashable values) - try: - return cls._value2member_map_[value] - except KeyError: - # Not found, no need to do long O(n) search - pass - except TypeError: - # not there, now do long search -- O(n) behavior - for member in cls._member_map_.values(): - if member._value_ == value: - return member - # still not found -- try _missing_ hook - try: - exc = None - result = cls._missing_(value) - except Exception as e: - exc = e - result = None - try: - if isinstance(result, cls): - return result - else: - ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__)) - if result is None and exc is None: - raise ve_exc - elif exc is None: - exc = TypeError( - 'error in %s._missing_: returned %r instead of None or a valid member' - % (cls.__name__, result) - ) - if not isinstance(exc, ValueError): - exc.__context__ = ve_exc - raise exc - finally: - # ensure all variables that could hold an exception are destroyed - exc = None - ve_exc = None - - def _generate_next_value_(name, start, count, last_values): - """ - Generate the next value when not given. - - name: the name of the member - start: the initial start value or None - count: the number of existing members - last_value: the last value assigned or None - """ - for last_value in reversed(last_values): - try: - return last_value + 1 - except TypeError: - pass - else: - return start - - @classmethod - def _missing_(cls, value): - return None - - def __repr__(self): - return "<%s.%s: %r>" % ( - self.__class__.__name__, self._name_, self._value_) - - def __str__(self): - return "%s.%s" % (self.__class__.__name__, self._name_) - - def __dir__(self): - """ - Returns all members and all public methods - """ - added_behavior = [ - m - for cls in self.__class__.mro() - for m in cls.__dict__ - if m[0] != '_' and m not in self._member_map_ - ] + [m for m in self.__dict__ if m[0] != '_'] - return (['__class__', '__doc__', '__module__'] + added_behavior) - - def __format__(self, format_spec): - """ - Returns format using actual value type unless __str__ has been overridden. - """ - # mixed-in Enums should use the mixed-in type's __format__, otherwise - # we can get strange results with the Enum name showing up instead of - # the value - - # pure Enum branch, or branch with __str__ explicitly overridden - str_overridden = type(self).__str__ not in (Enum.__str__, Flag.__str__) - if self._member_type_ is object or str_overridden: - cls = str - val = str(self) - # mix-in branch - else: - cls = self._member_type_ - val = self._value_ - return cls.__format__(val, format_spec) - - def __hash__(self): - return hash(self._name_) - - def __reduce_ex__(self, proto): - return self.__class__, (self._value_, ) - - # DynamicClassAttribute is used to provide access to the `name` and - # `value` properties of enum members while keeping some measure of - # protection from modification, while still allowing for an enumeration - # to have members named `name` and `value`. This works because enumeration - # members are not set directly on the enum class -- __getattr__ is - # used to look them up. - - @DynamicClassAttribute - def name(self): - """The name of the Enum member.""" - return self._name_ - - @DynamicClassAttribute - def value(self): - """The value of the Enum member.""" - return self._value_ - - -class IntEnum(int, Enum): - """Enum where members are also (and must be) ints""" - - -def _reduce_ex_by_name(self, proto): - return self.name - -class Flag(Enum): - """ - Support for flags - """ - - def _generate_next_value_(name, start, count, last_values): - """ - Generate the next value when not given. - - name: the name of the member - start: the initial start value or None - count: the number of existing members - last_value: the last value assigned or None - """ - if not count: - return start if start is not None else 1 - for last_value in reversed(last_values): - try: - high_bit = _high_bit(last_value) - break - except Exception: - raise TypeError('Invalid Flag value: %r' % last_value) from None - return 2 ** (high_bit+1) - - @classmethod - def _missing_(cls, value): - """ - Returns member (possibly creating it) if one can be found for value. - """ - original_value = value - if value < 0: - value = ~value - possible_member = cls._create_pseudo_member_(value) - if original_value < 0: - possible_member = ~possible_member - return possible_member - - @classmethod - def _create_pseudo_member_(cls, value): - """ - Create a composite member iff value contains only members. - """ - pseudo_member = cls._value2member_map_.get(value, None) - if pseudo_member is None: - # verify all bits are accounted for - _, extra_flags = _decompose(cls, value) - if extra_flags: - raise ValueError("%r is not a valid %s" % (value, cls.__qualname__)) - # construct a singleton enum pseudo-member - pseudo_member = object.__new__(cls) - pseudo_member._name_ = None - pseudo_member._value_ = value - # use setdefault in case another thread already created a composite - # with this value - pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) - return pseudo_member - - def __contains__(self, other): - """ - Returns True if self has at least the same flags set as other. - """ - if not isinstance(other, self.__class__): - raise TypeError( - "unsupported operand type(s) for 'in': '%s' and '%s'" % ( - type(other).__qualname__, self.__class__.__qualname__)) - return other._value_ & self._value_ == other._value_ - - def __repr__(self): - cls = self.__class__ - if self._name_ is not None: - return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) - members, uncovered = _decompose(cls, self._value_) - return '<%s.%s: %r>' % ( - cls.__name__, - '|'.join([str(m._name_ or m._value_) for m in members]), - self._value_, - ) - - def __str__(self): - cls = self.__class__ - if self._name_ is not None: - return '%s.%s' % (cls.__name__, self._name_) - members, uncovered = _decompose(cls, self._value_) - if len(members) == 1 and members[0]._name_ is None: - return '%s.%r' % (cls.__name__, members[0]._value_) - else: - return '%s.%s' % ( - cls.__name__, - '|'.join([str(m._name_ or m._value_) for m in members]), - ) - - def __bool__(self): - return bool(self._value_) - - def __or__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.__class__(self._value_ | other._value_) - - def __and__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.__class__(self._value_ & other._value_) - - def __xor__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.__class__(self._value_ ^ other._value_) - - def __invert__(self): - members, uncovered = _decompose(self.__class__, self._value_) - inverted = self.__class__(0) - for m in self.__class__: - if m not in members and not (m._value_ & self._value_): - inverted = inverted | m - return self.__class__(inverted) - - -class IntFlag(int, Flag): - """ - Support for integer-based Flags - """ - - @classmethod - def _missing_(cls, value): - """ - Returns member (possibly creating it) if one can be found for value. - """ - if not isinstance(value, int): - raise ValueError("%r is not a valid %s" % (value, cls.__qualname__)) - new_member = cls._create_pseudo_member_(value) - return new_member - - @classmethod - def _create_pseudo_member_(cls, value): - """ - Create a composite member iff value contains only members. - """ - pseudo_member = cls._value2member_map_.get(value, None) - if pseudo_member is None: - need_to_create = [value] - # get unaccounted for bits - _, extra_flags = _decompose(cls, value) - # timer = 10 - while extra_flags: - # timer -= 1 - bit = _high_bit(extra_flags) - flag_value = 2 ** bit - if (flag_value not in cls._value2member_map_ and - flag_value not in need_to_create - ): - need_to_create.append(flag_value) - if extra_flags == -flag_value: - extra_flags = 0 - else: - extra_flags ^= flag_value - for value in reversed(need_to_create): - # construct singleton pseudo-members - pseudo_member = int.__new__(cls, value) - pseudo_member._name_ = None - pseudo_member._value_ = value - # use setdefault in case another thread already created a composite - # with this value - pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) - return pseudo_member - - def __or__(self, other): - if not isinstance(other, (self.__class__, int)): - return NotImplemented - result = self.__class__(self._value_ | self.__class__(other)._value_) - return result - - def __and__(self, other): - if not isinstance(other, (self.__class__, int)): - return NotImplemented - return self.__class__(self._value_ & self.__class__(other)._value_) - - def __xor__(self, other): - if not isinstance(other, (self.__class__, int)): - return NotImplemented - return self.__class__(self._value_ ^ self.__class__(other)._value_) - - __ror__ = __or__ - __rand__ = __and__ - __rxor__ = __xor__ - - def __invert__(self): - result = self.__class__(~self._value_) - return result - - -def _high_bit(value): - """ - returns index of highest bit, or -1 if value is zero or negative - """ - return value.bit_length() - 1 - -def unique(enumeration): - """ - Class decorator for enumerations ensuring unique member values. - """ - duplicates = [] - for name, member in enumeration.__members__.items(): - if name != member.name: - duplicates.append((name, member.name)) - if duplicates: - alias_details = ', '.join( - ["%s -> %s" % (alias, name) for (alias, name) in duplicates]) - raise ValueError('duplicate values found in %r: %s' % - (enumeration, alias_details)) - return enumeration - -def _decompose(flag, value): - """ - Extract all members from the value. - """ - # _decompose is only called if the value is not named - not_covered = value - negative = value < 0 - members = [] - for member in flag: - member_value = member.value - if member_value and member_value & value == member_value: - members.append(member) - not_covered &= ~member_value - if not negative: - tmp = not_covered - while tmp: - flag_value = 2 ** _high_bit(tmp) - if flag_value in flag._value2member_map_: - members.append(flag._value2member_map_[flag_value]) - not_covered &= ~flag_value - tmp &= ~flag_value - if not members and value in flag._value2member_map_: - members.append(flag._value2member_map_[value]) - members.sort(key=lambda m: m._value_, reverse=True) - if len(members) > 1 and members[0].value == value: - # we have the breakdown, don't need the value member itself - members.pop(0) - return members, not_covered diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/feature.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/feature.py index 92bc30d2b..7a0871ee7 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/feature.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/feature.py @@ -1,6 +1,9 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +# flake8: noqa F:821 +# flake8: noqa F:401 + """ __feature__.py (renamed to feature.py) @@ -15,9 +18,10 @@ The normal usage is like Alternatively, there is the `set_selection` function which uses select_id's and takes an optional `mod_name` parameter. -The select id `-1` has the spectial meaning "ignore this module". +The select id `-1` has the special meaning "ignore this module". """ +import inspect import sys from contextlib import contextmanager @@ -79,11 +83,13 @@ None to indicate that a normal import should be performed, and All these variables are transparently kept in module `builtins`. """ + def feature_import(name, *args, **kwargs): # PYSIDE-1368: The `__name__` attribute does not need to exist in all modules. # PYSIDE-1398: sys._getframe(1) may not exist when embedding. # PYSIDE-1338: The "1" below is the redirection in loader.py . # PYSIDE-1548: Ensure that features are not affected by other imports. + # PYSIDE-2029: Need to always switch. The cache was wrong interpreted. calling_frame = _cf = sys._getframe(1).f_back importing_module = _cf.f_globals.get("__name__", "__main__") if _cf else "__main__" existing = pyside_feature_dict.get(importing_module, 0) @@ -105,27 +111,68 @@ def feature_import(name, *args, **kwargs): # Initialize feature (multiple times allowed) and clear cache. sys.modules["PySide6.QtCore"].__init_feature__() return sys.modules["__feature__"] - - if importing_module not in pyside_feature_dict: - # Ignore new modules if not from PySide. - default = 0 if name.split(".")[0] == "PySide6" else -1 - pyside_feature_dict[importing_module] = default # Redirect to the original import return None + _is_initialized = False + def __init__(): global _is_initialized if not _is_initialized: # use _one_ recursive import... import PySide6.QtCore # Initialize all prior imported modules - for name in sys.modules: - pyside_feature_dict.setdefault(name, -1) + for name, module in sys.modules.items(): + if name not in pyside_feature_dict: + pyside_feature_dict[name] = 0 if _mod_uses_pyside(module) else -1 _is_initialized = True +def feature_imported(module): + # PYSIDE-2029: Need to inspect imported modules for PySide usage. + """ + Set the module feature default after import. + + A module that uses PySide has a switching default of 0 = "no feature". + Otherwise the default is -1 = "ignore this module". + """ + + # PYSIDE-1368: The `__name__` attribute does not need to exist in all modules. + if hasattr(module, "__name__"): + name = module.__name__ + if name not in pyside_feature_dict: + pyside_feature_dict[name] = 0 if _mod_uses_pyside(module) else -1 + + +def _mod_uses_pyside(module): + """ + Find out if this module uses PySide. + + Simple approach: Search the source code for the string "PySide6". + Maybe we later support source-less modules by inspecting all code objects. + """ + try: + source = inspect.getsource(module) + except TypeError: + # this is a builtin module like sys + return False + except OSError: + # this is a module withot source file + return False + except SyntaxError: + # PYSIDE-2189: A UnicodeError happens in tokenize.py in find_cookie + # which is then creating a SyntaxError in inspect. + # This is undocumented and a Python error, seen in Python 3.10.2 on Windows, + # importing `pythoncom` of the win32 package. + return False + except Exception: + # PYSIDE-2393: pytest behaves weird when allowing any other error. + return False + return "PySide6" in source + + def set_selection(select_id, mod_name=None): """ Internal use: Set the feature directly by Id. @@ -141,6 +188,7 @@ def set_selection(select_id, mod_name=None): sys.modules["PySide6.QtCore"].__init_feature__() return _current_selection(flag) + # The set_section(0) case seems to be unsafe. We will migrate to # use the opaque feature.reset() call in all test cases. def reset(): diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/fix-complaints.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/fix-complaints.py index 3e1d49328..f7190b12f 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/fix-complaints.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/fix-complaints.py @@ -9,7 +9,6 @@ Run it once after copying a new version. It is idem-potent, unless you are changing messages (what I did, of course :-) . """ -import os import glob from pathlib import Path @@ -24,6 +23,7 @@ offending_words = { utf8_line = "# This Python file uses the following encoding: utf-8\n" marker_line = f"# It has been edited by {Path(__file__).name} .\n" + def patch_file(fname): with fname.open() as f: lines = f.readlines() @@ -41,6 +41,7 @@ def patch_file(fname): with open(fname, "w") as f: f.write("".join(lines)) + def doit(): dirname = Path(__file__).parent patched_files = [] @@ -51,6 +52,7 @@ def doit(): print("Working on", fname) patch_file(fname) + if __name__ == "__main__": doit() diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/shibokensupport.pyproject b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/shibokensupport.pyproject index a08143968..7147a4148 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/shibokensupport.pyproject +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/shibokensupport.pyproject @@ -1,6 +1,5 @@ { "files": ["__init__.py", - "enum_310.py", "feature.py", "fix-complaints.py", "signature/__init__.py", diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py index 6b428e613..c2a19efef 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/errorhandler.py @@ -1,6 +1,8 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +# flake8: noqa E:721 + """ errorhandler.py @@ -19,12 +21,10 @@ This matter will be improved in a later version. """ import collections.abc -import inspect -import sys import typing from shibokensupport.signature import get_signature -from shibokensupport.signature.mapping import update_mapping, namespace +from shibokensupport.signature.mapping import namespace from textwrap import dedent @@ -77,8 +77,8 @@ def seterror_argument(args, func_name, info): if info == "<": msg = f"{func_name}(): not enough arguments" elif info == "0": - msg = (f"{func_name}(): not enough arguments. " - "Note: keyword arguments are only supported for optional parameters.") + msg = (f"{func_name}(): not enough arguments. " + "Note: keyword arguments are only supported for optional parameters.") elif info == ">": msg = f"{func_name}(): too many arguments" elif info.isalnum(): @@ -87,6 +87,12 @@ def seterror_argument(args, func_name, info): msg = f"{func_name}(): {info}" err = AttributeError return err, msg + if isinstance(info, Exception): + # PYSIDE-2230: Python 3.12 seems to always do normalization. + err = type(info) + info = info.args[0] + msg = f"{func_name}(): {info}" + return err, msg if info and type(info) is dict: msg = f"{func_name}(): unsupported keyword '{tuple(info)[0]}'" return AttributeError, msg @@ -126,6 +132,8 @@ def check_string_type(s): def make_helptext(func): existing_doc = func.__doc__ + if existing_doc is None and hasattr(func, "__dict__"): + existing_doc = func.__dict__.get("__doc__") sigs = get_signature(func) if not sigs: return existing_doc diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/importhandler.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/importhandler.py index dbde18f18..bae264294 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/importhandler.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/importhandler.py @@ -34,9 +34,10 @@ def finish_import(module): except Exception as e: name = e.__class__.__qualname__ print(72 * "*") - print(f"Error in deprecated.py, ignored:") + print("Error in deprecated.py, ignored:") print(f" {name}: {e}") + """ A note for people who might think this could be written in pure Python: @@ -62,4 +63,3 @@ module, it is *perhaps* possible to solve that. I tried for a day and then gave up, since the solution is anyway not too nice when __import__ must be overridden. """ -#eof diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/layout.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/layout.py index d7dfa3451..0e781cbcb 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/layout.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/layout.py @@ -79,6 +79,7 @@ class SignatureLayout(SimpleNamespace): The only allowed values are '{allowed_values}'. """)) + # The following names are used literally in this module. # This way, we avoid the dict hashing problem. signature = SignatureLayout() @@ -115,7 +116,7 @@ def define_nameless_parameter(): P = inspect.Parameter newname = "NamelessParameter" bases = P.__bases__ - body = dict(P.__dict__) # get rid of mappingproxy + body = dict(P.__dict__) # get rid of mappingproxy if "__slots__" in body: # __slots__ would create duplicates for name in body["__slots__"]: @@ -167,12 +168,13 @@ def make_signature_nameless(signature): signature.parameters[key].__class__ = NamelessParameter -_POSITIONAL_ONLY = inspect._POSITIONAL_ONLY -_POSITIONAL_OR_KEYWORD = inspect._POSITIONAL_OR_KEYWORD -_VAR_POSITIONAL = inspect._VAR_POSITIONAL -_KEYWORD_ONLY = inspect._KEYWORD_ONLY -_VAR_KEYWORD = inspect._VAR_KEYWORD -_empty = inspect._empty +_POSITIONAL_ONLY = inspect._POSITIONAL_ONLY # noqa E:201 +_POSITIONAL_OR_KEYWORD = inspect._POSITIONAL_OR_KEYWORD # noqa E:201 +_VAR_POSITIONAL = inspect._VAR_POSITIONAL # noqa E:201 +_KEYWORD_ONLY = inspect._KEYWORD_ONLY # noqa E:201 +_VAR_KEYWORD = inspect._VAR_KEYWORD # noqa E:201 +_empty = inspect._empty # noqa E:201 + def create_signature(props, key): if not props: @@ -183,9 +185,9 @@ def create_signature(props, key): return list(create_signature(elem, key) for elem in props["multi"]) if type(key) is tuple: - sig_kind, modifier = key + _, modifier = key else: - sig_kind, modifier = key, "signature" + _, modifier = key, "signature" layout = globals()[modifier] # lookup of the modifier in this module if not isinstance(layout, SignatureLayout): @@ -200,7 +202,7 @@ def create_signature(props, key): # parser. pass else: - if varnames[0] in ("self", "cls"): + if varnames and varnames[0] in ("self", "cls"): varnames = varnames[1:] # calculate the modifications @@ -234,8 +236,8 @@ def create_signature(props, key): if kind == _VAR_POSITIONAL: kind = _KEYWORD_ONLY sig = inspect.Signature(params, - return_annotation=annotations.get('return', _empty), - __validate_parameters__=False) + return_annotation=annotations.get('return', _empty), + __validate_parameters__=False) # the special case of nameless parameters if not layout.parameter_names: diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py index 779ac2b4a..5650e2bc1 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py @@ -14,9 +14,7 @@ by producing a lot of clarity. import inspect import sys import types -import typing from shibokensupport.signature import get_signature as get_sig -from shibokensupport.signature.layout import create_signature """ @@ -38,6 +36,10 @@ if hasattr(sys, "pypy_version_info"): _normal_functions += (type(get_sig),) +def signal_check(thing): + return thing and type(thing) in (Signal, SignalInstance) + + class ExactEnumerator(object): """ ExactEnumerator enumerates all signatures in a module as they are. @@ -48,13 +50,13 @@ class ExactEnumerator(object): """ def __init__(self, formatter, result_type=dict): - global EnumMeta + global EnumMeta, Signal, SignalInstance try: # Lazy import - from PySide6.QtCore import Qt + from PySide6.QtCore import Qt, Signal, SignalInstance EnumMeta = type(Qt.Key) except ImportError: - EnumMeta = None + EnumMeta = Signal = SignalInstance = None self.fmt = formatter self.result_type = result_type @@ -102,7 +104,7 @@ class ExactEnumerator(object): return ret if "<" in class_name: # This is happening in QtQuick for some reason: - ## class QSharedPointer<QQuickItemGrabResult >: + # class std::shared_ptr<QQuickItemGrabResult >: # We simply skip over this class. return ret bases_list = [] @@ -120,9 +122,19 @@ class ExactEnumerator(object): functions = [] enums = [] properties = [] + signals = [] + attributes = {} for thing_name, thing in class_members: - if inspect.isclass(thing): + if signal_check(thing): + signals.append((thing_name, thing)) + elif inspect.isclass(thing): + # If this is the only member of the class, it causes the stub + # to be printed empty without ..., as self.fmt.have_body will + # then be True. (Example: QtCore.QCborTag). Skip it to avoid + # this problem. + if thing_name == "_member_type_": + continue subclass_name = ".".join((class_name, thing_name)) subclasses.append((subclass_name, thing)) elif inspect.isroutine(thing): @@ -130,22 +142,32 @@ class ExactEnumerator(object): functions.append((func_name, thing)) elif type(type(thing)) is EnumMeta: # take the real enum name, not what is in the dict - enums.append((thing_name, type(thing).__qualname__, thing)) + if not thing_name.startswith("_"): + enums.append((thing_name, type(thing).__qualname__, thing)) elif isinstance(thing, property): properties.append((thing_name, thing)) + # Support attributes that have PySide types as values, + # but we skip the 'staticMetaObject' that needs + # to be defined at a QObject level. + elif "PySide" in str(type(thing)) and "QMetaObject" not in str(type(thing)): + if class_name not in attributes: + attributes[class_name] = {} + attributes[class_name][thing_name] = thing if thing_name in self.collision_candidates: self.collision_track.add(thing_name) init_signature = getattr(klass, "__signature__", None) - enums.sort(key=lambda tup: tup[1 : 3]) # sort by class then enum value + # sort by class then enum value + enums.sort(key=lambda tup: (tup[1], tup[2].value)) # We want to handle functions and properties together. func_prop = sorted(functions + properties, key=lambda tup: tup[0]) # find out how many functions create a signature sigs = list(_ for _ in functions if get_sig(_[1])) - self.fmt.have_body = bool(subclasses or sigs or properties or enums or init_signature) + self.fmt.have_body = bool(subclasses or sigs or properties or enums or # noqa W:504 + init_signature or signals or attributes) with self.fmt.klass(class_name, class_str): self.fmt.level += 1 @@ -155,8 +177,25 @@ class ExactEnumerator(object): if len(enums): self.section() for enum_name, enum_class_name, value in enums: - with self.fmt.enum(enum_class_name, enum_name, int(value)): + with self.fmt.enum(enum_class_name, enum_name, value.value): + pass + if hasattr(self.fmt, "signal"): + # this is an optional feature + if len(signals): + self.section() + for signal_name, signal in signals: + sig_class = type(signal) + sig_class_name = f"{sig_class.__qualname__}" + sig_str = str(signal) + with self.fmt.signal(sig_class_name, signal_name, sig_str): pass + if hasattr(self.fmt, "attribute"): + if len(attributes): + self.section() + for class_name, attrs in attributes.items(): + for attr_name, attr_value in attrs.items(): + with self.fmt.attribute(attr_name, attr_value): + pass if len(subclasses): self.section() for subclass_name, subclass in subclasses: diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py index 292b83fae..ce12dd6c8 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py @@ -3,6 +3,8 @@ LICENSE_TEXT = """ # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only """ +# flake8: noqa E:402 + """ pyi_generator.py @@ -21,7 +23,6 @@ from pathlib import Path from contextlib import contextmanager from textwrap import dedent -from shiboken6 import Shiboken from shibokensupport.signature.lib.enum_sig import HintingEnumerator from shibokensupport.signature.lib.tool import build_brace_pattern @@ -59,6 +60,7 @@ class Formatter(Writer): unrelated tasks of enumeration and formatting apart. """ def __init__(self, outfile, options, *args): + # XXX Find out which of these patches is still necessary! self.options = options Writer.__init__(self, outfile, *args) # patching __repr__ to disable the __repr__ of typing.TypeVar: @@ -74,8 +76,12 @@ class Formatter(Writer): """ def _typevar__repr__(self): return f"typing.{self.__name__}" - typing.TypeVar.__repr__ = _typevar__repr__ - + # This is no longer necessary for modern typing versions. + # Ignore therefore if the repr is read-only and cannot be changed. + try: + typing.TypeVar.__repr__ = _typevar__repr__ + except TypeError: + pass # Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]" # I tried hard to replace typing.Optional by a simple override, but # this became _way_ too much. @@ -84,6 +90,7 @@ class Formatter(Writer): pattern = fr"\b Union \s* \[ \s* {brace_pat} \s*, \s* NoneType \s* \]" replace = r"Optional[\1]" optional_searcher = re.compile(pattern, flags=re.VERBOSE) + def optional_replacer(source): return optional_searcher.sub(replace, str(source)) self.optional_replacer = optional_replacer @@ -151,6 +158,18 @@ class Formatter(Writer): self.print(f"{spaces}{enum_name:25}: {class_name} = ... # {hexval}") yield + @contextmanager + def attribute(self, attr_name, attr_value): + spaces = indent * self.level + self.print(f"{spaces}{attr_name:25} = ... # type: {type(attr_value).__qualname__}") + yield + + @contextmanager + def signal(self, class_name, sig_name, sig_str): + spaces = indent * self.level + self.print(f"{spaces}{sig_name:25}: ClassVar[{class_name}] = ... # {sig_str}") + yield + def find_imports(text): return [imp for imp in PySide6.__all__ if f"PySide6.{imp}." in text] @@ -159,12 +178,14 @@ def find_imports(text): FROM_IMPORTS = [ (None, ["builtins"]), (None, ["os"]), - (None, ["enum"] if sys.pyside63_option_python_enum else []), - ("typing", typing.__all__), - ("PySide6.QtCore", ["PyClassProperty"]), + (None, ["enum"]), + ("collections.abc", ["Iterable"]), + ("typing", sorted(typing.__all__)), + ("PySide6.QtCore", ["PyClassProperty", "Signal", "SignalInstance"]), ("shiboken6", ["Shiboken"]), ] + def filter_from_imports(from_struct, text): """ Build a reduced new `from` structure (nfs) with found entries, only @@ -174,8 +195,15 @@ def filter_from_imports(from_struct, text): lis = [] nfs.append((mod, lis)) for each in imports: - if re.search(rf"(\b|@){each}\b([^\s\(:]|\n)", text): - lis.append(each) + # PYSIDE-1603: We search text that is a usage of the class `each`, + # but only if the class is not also defined here. + if (f"class {each}(") not in text: + if re.search(rf"(\b|@){each}\b([^\s\(:]|\n)", text): + lis.append(each) + # Search if a type is present in the return statement + # of function declarations: '... -> here:' + if re.search(rf"->.*{each}.*:", text): + lis.append(each) if not lis: nfs.pop() return nfs @@ -214,12 +242,10 @@ def generate_pyi(import_name, outpath, options): obj = getattr(top, plainname) if import_name != plainname else top if not getattr(obj, "__file__", None) or Path(obj.__file__).is_dir(): raise ModuleNotFoundError(f"We do not accept a namespace as module `{plainname}`") - module = sys.modules[import_name] outfile = io.StringIO() fmt = Formatter(outfile, options) fmt.print(LICENSE_TEXT.strip()) - need_imports = options._pyside_call and not USE_PEP563 if USE_PEP563: fmt.print("from __future__ import annotations") fmt.print() @@ -261,13 +287,12 @@ def generate_pyi(import_name, outpath, options): wr.print(f"from {mod} import {import_args}") wr.print() wr.print() + wr.print("NoneType: TypeAlias = type[None]") + wr.print() else: wr.print(line) if not options.quiet: options.logger.info(f"Generated: {outfilepath}") - # PYSIDE-1735: .pyi files are no longer compatible with Python, because - # enum classes contain ellipsis which a Python enum forbids. - # We will implement tests with Mypy, instead. def main(): @@ -282,11 +307,10 @@ def main(): pyi_generator will try to generate an interface "<module>.pyi". """)) parser.add_argument("module", - help="The full path name of an importable module binary (.pyd, .so)") + help="The full path name of an importable module binary (.pyd, .so)") # noqa E:128 parser.add_argument("--quiet", action="store_true", help="Run quietly") - parser.add_argument("--check", action="store_true", help="Test the output") parser.add_argument("--outpath", - help="the output directory (default = location of module binary)") + help="the output directory (default = location of module binary)") # noqa E:128 options = parser.parse_args() module = options.module outpath = options.outpath @@ -304,6 +328,7 @@ def main(): options.logger = logger generate_pyi(module, outpath, options=options) + if __name__ == "__main__": main() # eof diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py index a7900e6b2..979dcf4ce 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py @@ -44,8 +44,8 @@ def build_brace_pattern(level, separators): ro, rc = round_ = "()" so, sc = square = "[]" - co, cc = curly = "CD" # we insert "{}", later... - ao, ac = angle = "<>" + co, cc = curly = "CD" # noqa E:201 we insert "{}", later... + ao, ac = angle = "<>" # noqa E:201 q2, bs, q1 = '"', "\\", "'" allpat = round_ + square + curly + angle __ = " " @@ -79,8 +79,8 @@ def build_brace_pattern(level, separators): {indent} )* """) for idx in range(level): - pattern = pattern.format(replacer = repeated if idx < level-1 else no_braces_q, - indent = idx * " ", **locals()) + pattern = pattern.format(replacer=repeated if idx < level - 1 else no_braces_q, + indent=idx * " ", **locals()) return pattern.replace("C", "{").replace("D", "}") diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/loader.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/loader.py index aa2fa3adb..fb4c9eeca 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/loader.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/loader.py @@ -1,6 +1,9 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +# flake8: noqa E:402 +# flake8: noqa F:401 + """ loader.py @@ -30,22 +33,27 @@ import types def pyside_type_init(type_key, sig_strings): return parser.pyside_type_init(type_key, sig_strings) + # name used in signature.cpp def create_signature(props, key): return layout.create_signature(props, key) + # name used in signature.cpp def seterror_argument(args, func_name, info): return errorhandler.seterror_argument(args, func_name, info) + # name used in signature.cpp def make_helptext(func): return errorhandler.make_helptext(func) + # name used in signature.cpp def finish_import(module): return importhandler.finish_import(module) + # name used in signature.cpp def feature_import(*args, **kwds): # don't spend a stack level here for speed and compatibility @@ -54,6 +62,14 @@ def feature_import(*args, **kwds): return feature_import(*args, **kwds) +# name used in signature.cpp +def feature_imported(*args, **kwds): + # don't spend a stack level here for speed and compatibility + global feature_imported + feature_imported = feature.feature_imported + return feature_imported(*args, **kwds) + + import builtins import signature_bootstrap from shibokensupport import signature, feature @@ -100,6 +116,7 @@ def move_into_pyside_package(): put_into_package(PySide6.support.signature.lib, pyi_generator) put_into_package(PySide6.support.signature.lib, tool) + from shibokensupport.signature import mapping from shibokensupport.signature import errorhandler from shibokensupport.signature import layout @@ -109,13 +126,10 @@ from shibokensupport.signature import importhandler from shibokensupport.signature.lib import enum_sig from shibokensupport.signature.lib import pyi_generator from shibokensupport.signature.lib import tool -if sys.version_info[:2] < (3, 10): - # PYSIDE-1735: Use the faster and more complete enum implementation. - from shibokensupport import enum_310 as enum - sys.modules["enum"] = enum - # compatibility - if sys.version_info[:2] < (3, 8): - enum.Enum._convert = classmethod(enum.EnumMeta._convert_) + +import enum + +post_init = lambda: None # noqa E:731 default if "PySide6" in sys.modules: # We publish everything under "PySide6.support", again. @@ -123,17 +137,22 @@ if "PySide6" in sys.modules: # PYSIDE-1502: Make sure that support can be imported. try: import PySide6.support - except ModuleNotFoundError as e: + except ModuleNotFoundError: print("PySide6.support could not be imported. " "This is a serious configuration error.", file=sys.stderr) raise - # PYSIDE-1019: Modify `__import__` to be `__feature__` aware. - # __feature__ is already in sys.modules as feature, so this is actually no import - if not is_pypy: - # PYSIDE-535: Cannot enable __feature__ for various reasons. - import PySide6.support.feature - sys.modules["__feature__"] = PySide6.support.feature - builtins.__orig_import__ = builtins.__import__ - builtins.__import__ = builtins.__feature_import__ + + def post_init(): + """ + This function needs to be called explicitly when all function pointers are set. + Doing this during import has bad side-effects when preloading the loader. + """ + # PYSIDE-1019: Modify `__import__` to be `__feature__` aware. + if not is_pypy: + # PYSIDE-535: Cannot enable __feature__ for various reasons. + import PySide6.support.feature + sys.modules["__feature__"] = PySide6.support.feature + builtins.__orig_import__ = builtins.__import__ + builtins.__import__ = builtins.__feature_import__ # end of file diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index 2118d7e39..944a928e6 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -1,6 +1,8 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +# flake8: noqa E:203 + """ mapping.py @@ -20,12 +22,14 @@ from pathlib import Path from typing import TypeVar, Generic from _imp import is_builtin + class ellipsis(object): def __repr__(self): return "..." + ellipsis = ellipsis() -Point = typing.Tuple[float, float] +Point = typing.Tuple[int, int] Variant = typing.Any QImageCleanupFunction = typing.Callable @@ -38,7 +42,7 @@ _S = TypeVar("_S") MultiMap = typing.DefaultDict[str, typing.List[str]] # ulong_max is only 32 bit on windows. -ulong_max = 2*sys.maxsize+1 if len(struct.pack("L", 1)) != 4 else 0xffffffff +ulong_max = 2 * sys.maxsize + 1 if len(struct.pack("L", 1)) != 4 else 0xffffffff ushort_max = 0xffff GL_COLOR_BUFFER_BIT = 0x00004000 @@ -74,6 +78,7 @@ class _NotCalled(str): text = self if self.endswith(")") else self + "()" return eval(text, namespace) + USE_PEP563 = False # Note: we cannot know if this feature has been imported. # Otherwise it would be "sys.version_info[:2] >= (3, 7)". @@ -86,6 +91,7 @@ USE_PEP563 = False class Virtual(_NotCalled): pass + # Other types I simply could not find. class Missing(_NotCalled): # The string must be quoted, because the object does not exist. @@ -98,6 +104,7 @@ class Missing(_NotCalled): class Invalid(_NotCalled): pass + # Helper types class Default(_NotCalled): pass @@ -106,6 +113,7 @@ class Default(_NotCalled): class Instance(_NotCalled): pass + # Parameterized primitive variables class _Parameterized(object): def __init__(self, type): @@ -115,15 +123,18 @@ class _Parameterized(object): def __repr__(self): return f"{type(self).__name__}({self.type.__name__})" + # Mark the primitive variables to be moved into the result. class ResultVariable(_Parameterized): pass + # Mark the primitive variables to become Sequence, Iterable or List # (decided in the parser). class ArrayLikeVariable(_Parameterized): pass + StringList = ArrayLikeVariable(str) @@ -142,7 +153,7 @@ class Reloader(object): if getattr(mod, "__file__", None) and not Path(mod.__file__).is_dir(): ending = Path(mod.__file__).suffix return ending not in (".py", ".pyc", ".pyo", ".pyi") - return bool(is_builtin(mod.__name__)) + return bool(hasattr(mod, "__name__") and is_builtin(mod.__name__)) def update(self): """ @@ -180,12 +191,14 @@ def check_module(mod): mod_name = mod.__name__ raise ImportError(f"Module '{mod_name}' is not a binary module!") + update_mapping = Reloader().update type_map = {} namespace = globals() # our module's __dict__ type_map.update({ "...": ellipsis, + "Any": typing.Any, "bool": bool, "char": int, "double": float, @@ -200,7 +213,7 @@ type_map.update({ "PyObject": object, "PyObject*": object, "PyArrayObject": ArrayLikeVariable, # numpy - "PyPathLike": typing.Union[str, bytes, os.PathLike], + "PyPathLike": typing.Union[str, bytes, os.PathLike[str]], "PySequence": typing.Iterable, # important for numpy "PyTypeObject": type, "QChar": str, @@ -217,6 +230,7 @@ type_map.update({ "uintptr_t": int, "qintptr": int, "qsizetype": int, + "QFunctionPointer": int, "QList": ArrayLikeVariable, "qlonglong": int, "QMap": typing.Dict, @@ -227,6 +241,7 @@ type_map.update({ "qreal": float, "QSet": typing.Set, "QString": str, + "QLatin1String": str, "QStringView": str, "QStringList": StringList, "quint16": int, @@ -238,6 +253,7 @@ type_map.update({ "uint32_t": int, "uint64_t": int, "uint8_t": int, + "Union": typing.Union, "quintptr": int, "qulonglong": int, "QVariant": Variant, @@ -263,16 +279,16 @@ type_map.update({ "ulong": int, "ULONG_MAX": ulong_max, "UINT64_MAX": 0xffffffff, - "unsigned char": int, # 5.9 + "unsigned char": int, # 5.9 "unsigned char*": str, "unsigned int": int, - "unsigned long int": int, # 5.6, RHEL 6.6 + "unsigned long int": int, # 5.6, RHEL 6.6 "unsigned long long": int, "unsigned long": int, - "unsigned short int": int, # 5.6, RHEL 6.6 + "unsigned short int": int, # 5.6, RHEL 6.6 "unsigned short": int, "ushort": int, - "void": int, # be more specific? + "void": int, # be more specific? "WId": WId, "zero(bytes)": b"", "zero(Char)": 0, @@ -282,7 +298,11 @@ type_map.update({ "zero(str)": "", "zero(typing.Any)": None, "zero(Any)": None, - }) + # This can be refined by importing numpy.typing optionally, but better than nothing. + "numpy.ndarray": typing.List[typing.Any], + "std.array[int, 4]": typing.List[int], + "std.array[float, 4]": typing.List[float] +}) type_map.update({ # Handling variables declared as array: @@ -294,8 +314,8 @@ type_map.update({ "array long long*" : ArrayLikeVariable(int), "array long*" : ArrayLikeVariable(int), "array short*" : ArrayLikeVariable(int), - "array signed char*" : bytes, - "array unsigned char*" : bytes, + "array signed char*" : typing.Union[bytes, bytearray, memoryview], + "array unsigned char*" : typing.Union[bytes, bytearray, memoryview], "array unsigned int*" : ArrayLikeVariable(int), "array unsigned short*" : ArrayLikeVariable(int), # PYSIDE-1646: New macOS primitive types @@ -306,17 +326,17 @@ type_map.update({ "array int32_t*" : ArrayLikeVariable(int), "array uint32_t*" : ArrayLikeVariable(int), "array intptr_t*" : ArrayLikeVariable(int), - }) +}) type_map.update({ # Special cases: - "char*" : bytes, - "QChar*" : bytes, + "char*" : typing.Union[bytes, bytearray, memoryview], + "QChar*" : typing.Union[bytes, bytearray, memoryview], "quint32*" : int, # only for QRandomGenerator "quint8*" : bytearray, # only for QCborStreamReader and QCborValue - "uchar*" : bytes, - "unsigned char*": bytes, - }) + "uchar*" : typing.Union[bytes, bytearray, memoryview], + "unsigned char*": typing.Union[bytes, bytearray, memoryview], +}) type_map.update({ # Handling variables that are returned, eventually as Tuples: @@ -338,7 +358,7 @@ type_map.update({ "uint*" : ResultVariable(int), "unsigned int*" : ResultVariable(int), "QStringList*" : ResultVariable(StringList), - }) +}) type_map.update({ @@ -346,20 +366,21 @@ type_map.update({ "[typing.Any]" : [typing.Any], "[typing.Any,typing.Any]" : [typing.Any, typing.Any], "None" : None, - }) +}) # PYSIDE-1328: We need to handle "self" explicitly. type_map.update({ "self" : "self", "cls" : "cls", - }) +}) # PYSIDE-1538: We need to treat "std::optional" accordingly. type_map.update({ "std.optional": typing.Optional, }) + # The Shiboken Part def init_Shiboken(): type_map.update({ @@ -369,6 +390,7 @@ def init_Shiboken(): }) return locals() + def init_minimal(): type_map.update({ "MinBool": bool, @@ -384,7 +406,7 @@ def init_sample(): "const char*": str, "Complex": complex, "double": float, - "ByteArray&": bytes, + "ByteArray&": typing.Union[bytes, bytearray, memoryview], "Foo.HANDLE": int, "HANDLE": int, "Null": None, @@ -392,7 +414,7 @@ def init_sample(): "OddBool": bool, "PStr": str, "PyDate": datetime.date, - "PyBuffer": bytes, + "PyBuffer": typing.Union[bytes, bytearray, memoryview], "sample.bool": bool, "sample.char": int, "sample.double": float, @@ -428,6 +450,7 @@ def init_smart(): # This missing type should be defined in module smart. We cannot set it to Missing() # because it is a container type. Therefore, we supply a surrogate: global SharedPtr + class SharedPtr(Generic[_S]): __module__ = "smart" smart.SharedPtr = SharedPtr @@ -440,8 +463,8 @@ def init_smart(): # The PySide Part def init_PySide6_QtCore(): from PySide6.QtCore import Qt, QUrl, QDir, QKeyCombination - from PySide6.QtCore import QRect, QSize, QPoint, QLocale, QByteArray - from PySide6.QtCore import QMarginsF # 5.9 + from PySide6.QtCore import QRect, QRectF, QSize, QPoint, QLocale, QByteArray + from PySide6.QtCore import QMarginsF # 5.9 from PySide6.QtCore import SignalInstance try: # seems to be not generated by 5.9 ATM. @@ -452,40 +475,41 @@ def init_PySide6_QtCore(): "' '": " ", "'%'": "%", "'g'": "g", - "4294967295UL": 4294967295, # 5.6, RHEL 6.6 + "4294967295UL": 4294967295, # 5.6, RHEL 6.6 "CheckIndexOption.NoOption": Instance( - "PySide6.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11 + "PySide6.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11 "DescriptorType(-1)": int, # Native handle of QSocketDescriptor "false": False, "list of QAbstractAnimation": typing.List[PySide6.QtCore.QAbstractAnimation], "long long": int, "size_t": int, - "NULL": None, # 5.6, MSVC - "nullptr": None, # 5.9 - "PyBuffer": bytes, + "NULL": None, # 5.6, MSVC + "nullptr": None, # 5.9 + "PyBuffer": typing.Union[bytes, bytearray, memoryview], "PyByteArray": bytearray, - "PyBytes": bytes, + "PyBytes": typing.Union[bytes, bytearray, memoryview], "PyTuple": typing.Tuple, "QDeadlineTimer(QDeadlineTimer.Forever)": Instance("PySide6.QtCore.QDeadlineTimer"), "PySide6.QtCore.QUrl.ComponentFormattingOptions": - PySide6.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why??? + PySide6.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why??? "PyUnicode": typing.Text, "QByteArrayView": QByteArray, "Q_NULLPTR": None, "QCalendar.Unspecified": PySide6.QtCore.QCalendar.Unspecified, + "QCborTag(-1)": ulong_max, "QDir.Filters(AllEntries | NoDotAndDotDot)": Instance( "QDir.Filters(QDir.AllEntries | QDir.NoDotAndDotDot)"), "QDir.SortFlags(Name | IgnoreCase)": Instance( "QDir.SortFlags(QDir.Name | QDir.IgnoreCase)"), - "QEvent.Type.None" : None, - "QGenericArgument((0))": ellipsis, # 5.6, RHEL 6.6. Is that ok? + "QEvent.Type.None": None, + "QGenericArgument((0))": ellipsis, # 5.6, RHEL 6.6. Is that ok? "QGenericArgument()": ellipsis, "QGenericArgument(0)": ellipsis, - "QGenericArgument(NULL)": ellipsis, # 5.6, MSVC - "QGenericArgument(nullptr)": ellipsis, # 5.10 + "QGenericArgument(NULL)": ellipsis, # 5.6, MSVC + "QGenericArgument(nullptr)": ellipsis, # 5.10 "QGenericArgument(Q_NULLPTR)": ellipsis, "QJsonObject": typing.Dict[str, PySide6.QtCore.QJsonValue], - "QModelIndex()": Invalid("PySide6.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?! + "QModelIndex()": Invalid("PySide6.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?! "QModelIndexList": typing.List[PySide6.QtCore.QModelIndex], "PySideSignalInstance": SignalInstance, "QString()": "", @@ -493,17 +517,17 @@ def init_PySide6_QtCore(): "QStringList()": [], "QStringRef": str, "QStringRef": str, - "Qt.HANDLE": int, # be more explicit with some constants? + "Qt.HANDLE": int, # be more explicit with some constants? "QUrl.FormattingOptions(PrettyDecoded)": Instance( "QUrl.FormattingOptions(QUrl.PrettyDecoded)"), "QVariant()": Invalid(Variant), - "QVariant.Type": type, # not so sure here... - "QVariantMap": typing.Dict[str, Variant], + "QVariant.Type": type, # not so sure here... "QVariantMap": typing.Dict[str, Variant], + "std.chrono.seconds{5}" : ellipsis, }) try: type_map.update({ - "PySide6.QtCore.QMetaObject.Connection": PySide6.QtCore.Connection, # wrong! + "PySide6.QtCore.QMetaObject.Connection": PySide6.QtCore.Connection, # wrong! }) except AttributeError: # this does not exist on 5.9 ATM. @@ -528,7 +552,7 @@ def init_PySide6_QtConcurrent(): def init_PySide6_QtGui(): - from PySide6.QtGui import QPageLayout, QPageSize # 5.12 macOS + from PySide6.QtGui import QPageLayout, QPageSize # 5.12 macOS type_map.update({ "0.0f": 0.0, "1.0f": 1.0, @@ -537,10 +561,11 @@ def init_PySide6_QtGui(): "int32_t": int, "HBITMAP": int, "HICON": int, + "HMONITOR": int, "HRGN": int, - "QPixmap()": Default("PySide6.QtGui.QPixmap"), # can't create without qApp - "QPlatformSurface*": int, # a handle - "QVector< QTextLayout.FormatRange >()": [], # do we need more structure? + "QPixmap()": Default("PySide6.QtGui.QPixmap"), # can't create without qApp + "QPlatformSurface*": int, # a handle + "QVector< QTextLayout.FormatRange >()": [], # do we need more structure? "uint32_t": int, "uint8_t": int, "USHRT_MAX": ushort_max, @@ -554,8 +579,9 @@ def init_PySide6_QtGui(): def init_PySide6_QtWidgets(): - from PySide6.QtWidgets import QWidget, QMessageBox, QStyleOption, QStyleHintReturn, QStyleOptionComplex - from PySide6.QtWidgets import QGraphicsItem, QStyleOptionGraphicsItem # 5.9 + from PySide6.QtWidgets import (QWidget, QMessageBox, QStyleOption, + QStyleHintReturn, QStyleOptionComplex, + QGraphicsItem, QStyleOptionGraphicsItem) type_map.update({ "QMessageBox.StandardButtons(Yes | No)": Instance( "QMessageBox.StandardButtons(QMessageBox.Yes | QMessageBox.No)"), @@ -576,7 +602,7 @@ def init_PySide6_QtSql(): from PySide6.QtSql import QSqlDatabase type_map.update({ "QLatin1StringView(QSqlDatabase.defaultConnection)": QSqlDatabase.defaultConnection, - "QVariant.Invalid": Invalid("Variant"), # not sure what I should create, here... + "QVariant.Invalid": Invalid("Variant"), # not sure what I should create, here... }) return locals() @@ -600,7 +626,7 @@ def init_PySide6_QtOpenGL(): type_map.update({ "GLbitfield": int, "GLenum": int, - "GLfloat": float, # 5.6, MSVC 15 + "GLfloat": float, # 5.6, MSVC 15 "GLint": int, "GLuint": int, }) @@ -657,9 +683,36 @@ def init_PySide6_QtBluetooth(): return locals() +def init_PySide6_QtGraphs(): + from PySide6.QtGraphs import (QBarDataItem, QSurfaceDataItem) + QBarDataRow = typing.List[QBarDataItem] + QBarDataArray = typing.List[QBarDataRow] + QSurfaceDataRow = typing.List[QSurfaceDataItem] + QSurfaceDataArray = typing.List[QSurfaceDataRow] + type_map.update({ + "100.0f": 100.0, + "QBarDataArray": QBarDataArray, + "QBarDataArray*": QBarDataArray, + "QSurfaceDataArray": QSurfaceDataArray, + "QSurfaceDataArray*": QSurfaceDataArray, + }) + return locals() + + +def init_PySide6_QtHttpServer(): + type_map.update({ + "qMakePair(1u, 1u)": (1, 1), + }) + return locals() + + def init_testbinding(): type_map.update({ "testbinding.PySideCPP2.TestObjectWithoutNamespace": testbinding.TestObjectWithoutNamespace, + "testbinding.FlagsNamespace.Options": testbinding.Option, + "FlagsNamespace.Option.NoOptions": 0, + "StdIntList": typing.List[int], + 'Str("")': str(""), }) return locals() diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py index 4c8ade025..9b48ab442 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/parser.py @@ -1,21 +1,19 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +import ast import enum -import functools import keyword import os import re import sys -import types import typing import warnings from types import SimpleNamespace from shibokensupport.signature.mapping import (type_map, update_mapping, - namespace, _NotCalled, ResultVariable, ArrayLikeVariable) + namespace, _NotCalled, ResultVariable, ArrayLikeVariable) # noqa E:128 from shibokensupport.signature.lib.tool import build_brace_pattern -from shibokensupport import feature _DEBUG = False LIST_KEYWORDS = False @@ -40,8 +38,9 @@ guesses, we provide an entry in 'type_map' that resolves it. In effect, 'type_map' maps text to real Python objects. """ + def _get_flag_enum_option(): - from shiboken6 import (__version_info__ as ver, + from shiboken6 import (__version_info__ as ver, # noqa F:401 __minimum_python_version__ as pyminver, __maximum_python_version__ as pymaxver) @@ -49,7 +48,7 @@ def _get_flag_enum_option(): # This decides between delivered vs. dev versions. # When 6.4 is out, the switching mode will be gone. flag = ver[:2] >= (6, 4) - envname = "PYSIDE63_OPTION_PYTHON_ENUM" + envname = "PYSIDE6_OPTION_PYTHON_ENUM" sysname = envname.lower() opt = os.environ.get(envname) if opt: @@ -58,26 +57,35 @@ def _get_flag_enum_option(): flag = True elif opt in ("no", "off", "false"): flag = False - elif opt.isnumeric(): - flag = bool(int(opt)) + else: + # instead of a simple int() conversion, let's allow for "0xf" or "0b1111" + try: + flag = ast.literal_eval(opt) + except Exception: + flag = False # turn a forbidden option into an error elif hasattr(sys, sysname): - flag = bool(getattr(sys, sysname)) - # PYSIDE-1797: Emit a warning when we may remove pep384_issue33738.cpp - if pyminver and pyminver >= (3, 8): - warnings.warn(f"\n *** Python is at version {'.'.join(map(str, pyminver))} now. " - f"The file pep384_issue33738.cpp should be removed ASAP! ***") - # PYSIDE-1735: Emit a warning when we may update enum_310.py - if pymaxver and pymaxver > (3, 10): - warnings.warn(f"\n *** Python is at version {'.'.join(map(str, pymaxver))} now. " - f"Please check if enum_310.py should be updated! ***") - # PYSIDE-1735: Emit a warning when we may update enum_310.py + opt2 = flag = getattr(sys, sysname) + if not isinstance(flag, int): + flag = False # turn a forbidden option into an error + p = f"\n *** Python is at version {'.'.join(map(str, pyminver or (0,)))} now." + q = f"\n *** PySide is at version {'.'.join(map(str, ver[:2]))} now." + # _PepUnicode_AsString: Fix a broken promise + if pyminver and pyminver >= (3, 10): + warnings.warn(f"{p} _PepUnicode_AsString can now be replaced by PyUnicode_AsUTF8! ***") + # PYSIDE-1960: Emit a warning when we may remove bufferprocs_py37.(cpp|h) + if pyminver and pyminver >= (3, 11): + warnings.warn(f"{p} The files bufferprocs_py37.(cpp|h) should be removed ASAP! ***") + # PYSIDE-1735: Emit a warning when we should maybe evict forgiveness mode if ver[:2] >= (7, 0): - warnings.warn(f"\n *** PySide is at version {'.'.join(map(str, ver[:2]))} now. " - f"Please drop the forgiving Enum behavior in `mangled_type_getattro` ***") - # modify the sys attribute to bool + warnings.warn(f"{q} Please drop Enum forgiveness mode in `mangled_type_getattro` ***") + # PYSIDE-2404: Emit a warning when we should drop uppercase offset constants + if ver[:2] >= (7, 0): + warnings.warn(f"{q} Please drop uppercase type offsets in `copyOffsetEnumStream` ***") + # normalize the sys attribute setattr(sys, sysname, flag) - # modify the env attribute to "0" or "1" - os.environ[envname] = str(int(flag)) + os.environ[envname] = str(flag) + if int(flag) == 0: + raise RuntimeError(f"Old Enums are no longer supported. int({opt or opt2}) evaluates to 0)") return flag @@ -99,6 +107,7 @@ def dprint(*args, **kw): _cache = {} + def _parse_arglist(argstr): # The following is a split re. The string is broken into pieces which are # between the recognized strings. Because the re has groups, both the @@ -180,7 +189,7 @@ def _handle_instance_fixup(thing): if not match: return thing start, stop = match.start(), match.end() - 1 - pre, func, args = thing[:start], thing[start : stop], thing[stop:] + pre, func, args = thing[:start], thing[start:stop], thing[stop:] if func[0].isupper() or func.startswith("gl") and func[2:3].isupper(): return thing # Now convert this string to snake case. @@ -189,7 +198,7 @@ def _handle_instance_fixup(thing): if char.isupper(): if idx and func[idx - 1].isupper(): # two upper chars are forbidden - return things + return thing snake_func += f"_{char.lower()}" else: snake_func += char @@ -232,12 +241,14 @@ def try_to_guess(thing, valtype): return ret return None + def get_name(thing): if isinstance(thing, type): return getattr(thing, "__qualname__", thing.__name__) else: return thing.__name__ + def _resolve_value(thing, valtype, line): if thing in ("0", "None") and valtype: if valtype.startswith("PySide6.") or valtype.startswith("typing."): @@ -290,7 +301,7 @@ def to_string(thing): dot = "." in str(thing) or m not in (thing.__qualname__, "builtins") name = get_name(thing) ret = m + "." + name if dot else name - assert(eval(ret, globals(), namespace)) + assert (eval(ret, globals(), namespace)) return ret # Note: This captures things from the typing module: return str(thing) @@ -298,8 +309,9 @@ def to_string(thing): matrix_pattern = "PySide6.QtGui.QGenericMatrix" + def handle_matrix(arg): - n, m, typstr = tuple(map(lambda x:x.strip(), arg.split(","))) + n, m, typstr = tuple(map(lambda x: x.strip(), arg.split(","))) assert typstr == "float" result = f"PySide6.QtGui.QMatrix{n}x{m}" return eval(result, globals(), namespace) @@ -327,13 +339,13 @@ def _resolve_type(thing, line, level, var_handler, func_name=None): # Special case: Handle the generic matrices. if contr == matrix_pattern: return handle_matrix(thing) - contr = var_handler(_resolve_type(contr, line, level+1, var_handler)) + contr = var_handler(_resolve_type(contr, line, level + 1, var_handler)) if isinstance(contr, _NotCalled): raise SystemError("Container types must exist:", repr(contr)) contr = to_string(contr) pieces = [] for part in _parse_arglist(thing): - part = var_handler(_resolve_type(part, line, level+1, var_handler)) + part = var_handler(_resolve_type(part, line, level + 1, var_handler)) if isinstance(part, _NotCalled): # fix the tag (i.e. "Missing") by repr part = repr(part) @@ -343,7 +355,7 @@ def _resolve_type(thing, line, level, var_handler, func_name=None): # PYSIDE-1538: Make sure that the eval does not crash. try: return eval(result, globals(), namespace) - except Exception as e: + except Exception: warnings.warn(f"""pyside_type_init:_resolve_type UNRECOGNIZED: {result!r} @@ -393,18 +405,6 @@ def handle_retvar(obj): def calculate_props(line): - # PYSIDE-1735: QFlag is now divided into fields for future Python Enums, like - # "PySide.QtCore.^^Qt.ItemFlags^^Qt.ItemFlag^^" - # Resolve that until Enum is finally settled. - while "^^" in line: - parts = line.split("^^", 3) - selected = EnumSelect.SELECTION - line = parts[0] + parts[selected.value] + parts[3] - if selected is EnumSelect.NEW: - _old, _new = EnumSelect.OLD.value, EnumSelect.NEW.value - line = re.sub(rf"\b{parts[_old]}\b", parts[_new], line) - type_map[parts[_old]] = parts[_new] - parsed = SimpleNamespace(**_parse_line(line.strip())) arglist = parsed.arglist annotations = {} @@ -434,9 +434,9 @@ def calculate_props(line): props.defaults = defaults props.kwdefaults = {} props.annotations = annotations - props.varnames = varnames = tuple(tup[0] for tup in arglist) + props.varnames = tuple(tup[0] for tup in arglist) funcname = parsed.funcname - shortname = funcname[funcname.rindex(".")+1:] + shortname = funcname[funcname.rindex(".") + 1:] props.name = shortname props.multi = parsed.multi fix_variables(props, line) @@ -482,7 +482,6 @@ def fix_variables(props, line): else: diff -= 1 if retvars: - rvs = [] retvars = list(handle_retvar(rv) if isinstance(rv, ArrayLikeVariable) else rv for rv in retvars) if len(retvars) == 1: diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/qt_attribution.json b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/qt_attribution.json index e261bf271..0f05aea8b 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/qt_attribution.json +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/qt_attribution.json @@ -3,7 +3,7 @@ "Name": "Python", "QDocModule": "QtForPython", "QtUsage": "Used for Qt for Python in the signature extension.", - "Description": "Qt for Python is an add-on for Python. The signature packages of PySide uses certain copied and adapted source files (enum_310.py). See the folder sources/shiboken6/files.dir/shibokensupport .", + "Description": "Qt for Python is an add-on for Python. The signature packages of PySide uses certain copied and adapted source files. See the folder sources/shiboken6/files.dir/shibokensupport .", "Homepage": "http://www.python.org/", "Version": "3.7.0", "LicenseId": "Python-2.0", diff --git a/sources/shiboken6/shibokenmodule/shibokenmodule.cpp b/sources/shiboken6/shibokenmodule/shibokenmodule.cpp new file mode 100644 index 000000000..b3adfe78b --- /dev/null +++ b/sources/shiboken6/shibokenmodule/shibokenmodule.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// @snippet isvalid +bool isValid = Shiboken::Object::isValid(%1, false); +%PYARG_0 = %CONVERTTOPYTHON[bool](isValid); +// @snippet isvalid + +// @snippet wrapinstance +auto *pyType = reinterpret_cast<PyTypeObject *>(%2); +if (Shiboken::ObjectType::checkType(pyType)) { + auto *ptr = reinterpret_cast<void *>(%1); + if (auto *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(ptr)) { + Py_INCREF(wrapper); + %PYARG_0 = reinterpret_cast<PyObject *>(wrapper); + } else { + %PYARG_0 = Shiboken::Object::newObject(pyType, ptr, false, true); + } +} else { + PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); +} +// @snippet wrapinstance + +// @snippet getcpppointer +if (Shiboken::Object::checkType(%1)) { + std::vector<void*> ptrs = Shiboken::Object::cppPointers(reinterpret_cast<SbkObject *>(%1)); + %PYARG_0 = PyTuple_New(ptrs.size()); + for (std::size_t i = 0; i < ptrs.size(); ++i) + PyTuple_SET_ITEM(%PYARG_0, i, PyLong_FromVoidPtr(ptrs[i])); +} else { + PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); +} +// @snippet getcpppointer + +// @snippet delete +if (Shiboken::Object::checkType(%1)) { + Shiboken::Object::callCppDestructors(reinterpret_cast<SbkObject *>(%1)); +} else { + PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); +} +// @snippet delete + +// @snippet ownedbypython +if (Shiboken::Object::checkType(%1)) { + bool hasOwnership = Shiboken::Object::hasOwnership(reinterpret_cast<SbkObject *>(%1)); + %PYARG_0 = %CONVERTTOPYTHON[bool](hasOwnership); +} else { + PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); +} +// @snippet ownedbypython + +// @snippet createdbypython +if (Shiboken::Object::checkType(%1)) { + bool wasCreatedByPython = Shiboken::Object::wasCreatedByPython(reinterpret_cast<SbkObject *>(%1)); + %PYARG_0 = %CONVERTTOPYTHON[bool](wasCreatedByPython); +} else { + PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); +} +// @snippet createdbypython + +// @snippet disassembleframe +Shiboken::AutoDecRef label(PyObject_Str(%1)); +const char *marker = Shiboken::String::toCString(label); +disassembleFrame(marker); +Py_INCREF(Py_None); +%PYARG_0 = Py_None; +// @snippet disassembleframe + +// @snippet dump +if (!Shiboken::Object::checkType(%1)) { + %PYARG_0 = Shiboken::String::fromCString("Ordinary Python type."); +} else { + std::string str = Shiboken::Object::info(reinterpret_cast<SbkObject *>(%1)); + %PYARG_0 = Shiboken::String::fromCString(str.c_str()); +} +// @snippet dump + +// @snippet getallvalidwrappers +const auto setAll = Shiboken::BindingManager::instance().getAllPyObjects(); +PyObject* listAll = PyList_New(0); +if (listAll == nullptr) + return nullptr; +for (auto *o : setAll) { + if (o != nullptr) { + if (PyList_Append(listAll, o) != 0) { + Py_DECREF(listAll); + return nullptr; + } + } +} +return listAll; +// @snippet getallvalidwrappers + +// @snippet dumptypegraph +const bool ok = Shiboken::BindingManager::instance().dumpTypeGraph(%1); +%PYARG_0 = %CONVERTTOPYTHON[bool](ok); +// @snippet dumptypegraph + +// @snippet dumpwrappermap +Shiboken::BindingManager::instance().dumpWrapperMap(); +// @snippet dumpwrappermap + +// @snippet init +// Add __version__ and __version_info__ attributes to the module +PyObject* version = PyTuple_New(5); +PyTuple_SET_ITEM(version, 0, PyLong_FromLong(SHIBOKEN_MAJOR_VERSION)); +PyTuple_SET_ITEM(version, 1, PyLong_FromLong(SHIBOKEN_MINOR_VERSION)); +PyTuple_SET_ITEM(version, 2, PyLong_FromLong(SHIBOKEN_MICRO_VERSION)); +PyTuple_SET_ITEM(version, 3, Shiboken::String::fromCString(SHIBOKEN_RELEASE_LEVEL)); +PyTuple_SET_ITEM(version, 4, PyLong_FromLong(SHIBOKEN_SERIAL)); +PyModule_AddObject(module, "__version_info__", version); +PyModule_AddStringConstant(module, "__version__", SHIBOKEN_VERSION); +VoidPtr::addVoidPtrToModule(module); +Shiboken::initShibokenSupport(module); +// @snippet init diff --git a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml index 1bee3f543..aa08a8bbf 100644 --- a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml +++ b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml @@ -1,13 +1,14 @@ <?xml version="1.0" ?> +<!-- +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +--> <typesystem package="Shiboken"> <primitive-type name="bool" /> <primitive-type name="unsigned long" /> <primitive-type name="size_t" /> <add-function signature="isValid(PyObject*)" return-type="bool"> - <inject-code> - bool isValid = Shiboken::Object::isValid(%1, false); - %PYARG_0 = %CONVERTTOPYTHON[bool](isValid); - </inject-code> + <inject-code file="shibokenmodule.cpp" snippet="isvalid"/> </add-function> <add-function signature="invalidate(PyObject*)"> @@ -17,116 +18,48 @@ </add-function> <add-function signature="wrapInstance(size_t, PyTypeObject)" return-type="PyObject*"> - <inject-code> - auto *pyType = reinterpret_cast<PyTypeObject *>(%2); - if (Shiboken::ObjectType::checkType(pyType)) { - %PYARG_0 = Shiboken::Object::newObject(pyType, - reinterpret_cast<void *>(%1), - false, true); - } else { - PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); - } - </inject-code> + <inject-code file="shibokenmodule.cpp" snippet="wrapinstance"/> </add-function> - <add-function signature="getCppPointer(PyObject*)" return-type="PyObject*"> - <inject-code> - if (Shiboken::Object::checkType(%1)) { - std::vector<void*> ptrs = Shiboken::Object::cppPointers(reinterpret_cast<SbkObject *>(%1)); - %PYARG_0 = PyTuple_New(ptrs.size()); - for (std::size_t i = 0; i < ptrs.size(); ++i) - PyTuple_SET_ITEM(%PYARG_0, i, PyLong_FromVoidPtr(ptrs[i])); - } else { - PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); - } - </inject-code> + <add-function signature="getCppPointer(PyObject*)" return-type="PySequence*"> + <inject-code file="shibokenmodule.cpp" snippet="getcpppointer"/> </add-function> <add-function signature="delete(PyObject*)"> - <inject-code> - if (Shiboken::Object::checkType(%1)) { - Shiboken::Object::callCppDestructors(reinterpret_cast<SbkObject *>(%1)); - } else { - PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); - } - </inject-code> + <inject-code file="shibokenmodule.cpp" snippet="delete"/> </add-function> <add-function signature="ownedByPython(PyObject*)" return-type="bool"> - <inject-code> - if (Shiboken::Object::checkType(%1)) { - bool hasOwnership = Shiboken::Object::hasOwnership(reinterpret_cast<SbkObject *>(%1)); - %PYARG_0 = %CONVERTTOPYTHON[bool](hasOwnership); - } else { - PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); - } - </inject-code> + <inject-code file="shibokenmodule.cpp" snippet="ownedbypython"/> </add-function> - <add-function signature="createdByPython(PyObject*)" return-type="bool"> - <inject-code> - if (Shiboken::Object::checkType(%1)) { - bool wasCreatedByPython = Shiboken::Object::wasCreatedByPython(reinterpret_cast<SbkObject *>(%1)); - %PYARG_0 = %CONVERTTOPYTHON[bool](wasCreatedByPython); - } else { - PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type."); - } - </inject-code> - </add-function> + <add-function signature="createdByPython(PyObject*)" return-type="bool"> + <inject-code file="shibokenmodule.cpp" snippet="createdbypython"/> + </add-function> - <add-function signature="dump(PyObject*)" return-type="PyObject*"> - <inject-code> - if (!Shiboken::Object::checkType(%1)) { - %PYARG_0 = Shiboken::String::fromCString("Ordinary Python type."); - } else { - std::string str = Shiboken::Object::info(reinterpret_cast<SbkObject *>(%1)); - %PYARG_0 = Shiboken::String::fromCString(str.c_str()); - } - </inject-code> + <add-function signature="disassembleFrame(PyObject*)" return-type="PyObject"> + <inject-code file="shibokenmodule.cpp" snippet="disassembleframe"/> </add-function> - <add-function signature="getAllValidWrappers(void)" return-type="PyObject*"> - <inject-code> - std::set<PyObject*> setAll = Shiboken::BindingManager::instance().getAllPyObjects(); - PyObject* listAll = PyList_New(0); - if (listAll == NULL) - return NULL; + <add-function signature="dump(PyObject*)" return-type="const char *"> + <inject-code file="shibokenmodule.cpp" snippet="dump"/> + </add-function> - const std::set<PyObject*>::iterator end = setAll.end(); - for (std::set<PyObject*>::iterator iter = setAll.begin(); iter != end; ++iter) { - if (*iter != NULL) { - if (PyList_Append(listAll, *iter) != 0) { - Py_DECREF(listAll); - return NULL; - } - } - } - return listAll; - </inject-code> + <add-function signature="getAllValidWrappers(void)" return-type="PySequence*"> + <inject-code file="shibokenmodule.cpp" snippet="getallvalidwrappers"/> </add-function> - <add-function signature="_unpickle_enum(PyObject*, PyObject*)" return-type="PyObject*"> - <inject-code> - %PYARG_0 = Shiboken::Enum::unpickleEnum(%1, %2); - </inject-code> + <add-function signature="dumpTypeGraph(const char *@fileName@)" return-type="bool"> + <inject-code file="shibokenmodule.cpp" snippet="dumptypegraph"/> + </add-function> + + <add-function signature="dumpWrapperMap()"> + <inject-code file="shibokenmodule.cpp" snippet="dumpwrappermap"/> </add-function> <extra-includes> <include file-name="sbkversion.h" location="local"/> <include file-name="voidptr.h" location="local"/> </extra-includes> - <inject-code position="end"> - // Add __version__ and __version_info__ attributes to the module - PyObject* version = PyTuple_New(5); - PyTuple_SET_ITEM(version, 0, PyLong_FromLong(SHIBOKEN_MAJOR_VERSION)); - PyTuple_SET_ITEM(version, 1, PyLong_FromLong(SHIBOKEN_MINOR_VERSION)); - PyTuple_SET_ITEM(version, 2, PyLong_FromLong(SHIBOKEN_MICRO_VERSION)); - PyTuple_SET_ITEM(version, 3, Shiboken::String::fromCString(SHIBOKEN_RELEASE_LEVEL)); - PyTuple_SET_ITEM(version, 4, PyLong_FromLong(SHIBOKEN_SERIAL)); - PyModule_AddObject(module, "__version_info__", version); - PyModule_AddStringConstant(module, "__version__", SHIBOKEN_VERSION); - - Shiboken::initSignature(module); - VoidPtr::addVoidPtrToModule(module); - </inject-code> + <inject-code position="end" file="shibokenmodule.cpp" snippet="init"/> </typesystem> |