mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2025-07-21 12:41:10 +02:00
Merge pull request #2668 from lojack5/type-stubs
Some checks failed
ci-build / build-source-dist (push) Has been cancelled
ci-build / Build wxPython documentation (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.10) (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.11) (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.12) (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.13) (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.9) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.10) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.11) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.12) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.13) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.9) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.10) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.11) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.12) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.13) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.9) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.10) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.11) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.12) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.13) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.9) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.10) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.11) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.12) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.13) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.9) (push) Has been cancelled
ci-build / Publish Python distribution to PyPI (push) Has been cancelled
ci-build / Create GitHub Release and upload source (push) Has been cancelled
ci-build / Upload wheels to snapshot-builds on wxpython.org (push) Has been cancelled
Some checks failed
ci-build / build-source-dist (push) Has been cancelled
ci-build / Build wxPython documentation (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.10) (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.11) (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.12) (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.13) (push) Has been cancelled
ci-build / build-wheels (arm64, macos-14, 3.9) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.10) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.11) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.12) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.13) (push) Has been cancelled
ci-build / build-wheels (x64, macos-13, 3.9) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.10) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.11) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.12) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.13) (push) Has been cancelled
ci-build / build-wheels (x64, ubuntu-22.04, 3.9) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.10) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.11) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.12) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.13) (push) Has been cancelled
ci-build / build-wheels (x64, windows-2022, 3.9) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.10) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.11) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.12) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.13) (push) Has been cancelled
ci-build / build-wheels (x86, windows-2022, 3.9) (push) Has been cancelled
ci-build / Publish Python distribution to PyPI (push) Has been cancelled
ci-build / Create GitHub Release and upload source (push) Has been cancelled
ci-build / Upload wheels to snapshot-builds on wxpython.org (push) Has been cancelled
Type stub improvements
This commit is contained in:
@@ -43,7 +43,9 @@ def run():
|
||||
|
||||
# Allow on-the-fly creation of a wx.BitmapBundle from a wx.Bitmap, wx.Icon
|
||||
# or a wx.Image
|
||||
c.convertFromPyObject = """\
|
||||
c.convertFromPyObject = tools.AutoConversionInfo(
|
||||
('wx.Bitmap', 'wx.Icon', ),
|
||||
"""\
|
||||
// Check for type compatibility
|
||||
if (!sipIsErr) {
|
||||
if (sipCanConvertToType(sipPy, sipType_wxBitmap, SIP_NO_CONVERTORS))
|
||||
@@ -86,7 +88,7 @@ def run():
|
||||
*sipCppPtr = reinterpret_cast<wxBitmapBundle*>(
|
||||
sipConvertToType(sipPy, sipType_wxBitmapBundle, sipTransferObj, SIP_NO_CONVERTORS, 0, sipIsErr));
|
||||
return 0; // not a new instance
|
||||
"""
|
||||
""")
|
||||
|
||||
|
||||
c = module.find('wxBitmapBundleImpl')
|
||||
|
@@ -192,7 +192,9 @@ def run():
|
||||
# String with color name or #RRGGBB or #RRGGBBAA format
|
||||
# None (converts to wxNullColour)
|
||||
c.allowNone = True
|
||||
c.convertFromPyObject = """\
|
||||
c.convertFromPyObject = tools.AutoConversionInfo(
|
||||
('wx.Colour', '_ThreeInts', '_FourInts', 'str', 'None'),
|
||||
"""\
|
||||
// is it just a typecheck?
|
||||
if (!sipIsErr) {
|
||||
if (sipPy == Py_None)
|
||||
@@ -273,7 +275,7 @@ def run():
|
||||
*sipCppPtr = reinterpret_cast<wxColour*>(sipConvertToType(
|
||||
sipPy, sipType_wxColour, sipTransferObj, SIP_NO_CONVERTORS, 0, sipIsErr));
|
||||
return 0; // not a new instance
|
||||
"""
|
||||
""")
|
||||
|
||||
module.addPyCode('NamedColour = wx.deprecated(Colour, "Use Colour instead.")')
|
||||
|
||||
|
@@ -70,7 +70,9 @@ def run():
|
||||
|
||||
c.find('GetPtr').overloads[0].ignore()
|
||||
|
||||
c.convertFromPyObject = """\
|
||||
c.convertFromPyObject = tools.AutoConversionInfo(
|
||||
('str', 'None', ),
|
||||
"""\
|
||||
// Code to test a PyObject for compatibility with wxPGPropArgCls
|
||||
if (!sipIsErr) {
|
||||
if (sipCanConvertToType(sipPy, sipType_wxPGPropArgCls, SIP_NO_CONVERTORS))
|
||||
@@ -109,7 +111,7 @@ def run():
|
||||
SIP_NO_CONVERTORS, 0, sipIsErr));
|
||||
return 0; // not a new instance
|
||||
}
|
||||
"""
|
||||
""")
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
|
@@ -76,7 +76,9 @@ def run():
|
||||
c.includeCppCode('src/stream_input.cpp')
|
||||
|
||||
# Use that class for the convert code
|
||||
c.convertFromPyObject = """\
|
||||
c.convertFromPyObject = tools.AutoConversionInfo(
|
||||
(), # TODO: Track down what python types actually can be wrapped
|
||||
"""\
|
||||
// is it just a typecheck?
|
||||
if (!sipIsErr) {
|
||||
if (wxPyInputStream::Check(sipPy))
|
||||
@@ -86,7 +88,7 @@ def run():
|
||||
// otherwise do the conversion
|
||||
*sipCppPtr = new wxPyInputStream(sipPy);
|
||||
return 0; //sipGetState(sipTransferObj);
|
||||
"""
|
||||
""")
|
||||
|
||||
# Add Python file-like methods so a wx.InputStream can be used as if it
|
||||
# was any other Python file object.
|
||||
@@ -236,7 +238,9 @@ def run():
|
||||
c.includeCppCode('src/stream_output.cpp')
|
||||
|
||||
# Use that class for the convert code
|
||||
c.convertFromPyObject = """\
|
||||
c.convertFromPyObject = tools.AutoConversionInfo(
|
||||
(), # TODO: Track down what python types can actually be converted
|
||||
"""\
|
||||
// is it just a typecheck?
|
||||
if (!sipIsErr) {
|
||||
if (wxPyOutputStream::Check(sipPy))
|
||||
@@ -246,7 +250,7 @@ def run():
|
||||
// otherwise do the conversion
|
||||
*sipCppPtr = new wxPyOutputStream(sipPy);
|
||||
return sipGetState(sipTransferObj);
|
||||
"""
|
||||
""")
|
||||
|
||||
|
||||
# Add Python file-like methods so a wx.OutputStream can be used as if it
|
||||
|
@@ -311,7 +311,9 @@ def run():
|
||||
|
||||
# Add some code (like MappedTypes) to automatically convert from a Python
|
||||
# datetime.date or a datetime.datetime object
|
||||
c.convertFromPyObject = """\
|
||||
c.convertFromPyObject = tools.AutoConversionInfo(
|
||||
('datetime', 'date', ),
|
||||
"""\
|
||||
// Code to test a PyObject for compatibility with wxDateTime
|
||||
if (!sipIsErr) {
|
||||
if (sipCanConvertToType(sipPy, sipType_wxDateTime, SIP_NO_CONVERTORS))
|
||||
@@ -335,7 +337,7 @@ def run():
|
||||
sipPy, sipType_wxDateTime, sipTransferObj, SIP_NO_CONVERTORS, 0, sipIsErr));
|
||||
|
||||
return 0; // Not a new instance
|
||||
"""
|
||||
""")
|
||||
|
||||
|
||||
#---------------------------------------------
|
||||
|
@@ -15,12 +15,13 @@ wxWidgets API info which we need from them.
|
||||
import sys
|
||||
import os
|
||||
import pprint
|
||||
from typing import Optional
|
||||
import xml.etree.ElementTree as ET
|
||||
import copy
|
||||
|
||||
from .tweaker_tools import FixWxPrefix, magicMethods, \
|
||||
from .tweaker_tools import AutoConversionInfo, FixWxPrefix, MethodType, magicMethods, \
|
||||
guessTypeInt, guessTypeFloat, guessTypeStr, \
|
||||
textfile_open
|
||||
textfile_open, Signature, removeWxPrefix
|
||||
from sphinxtools.utilities import findDescendants
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
@@ -280,11 +281,14 @@ class FunctionDef(BaseDef, FixWxPrefix):
|
||||
"""
|
||||
Information about a standalone function.
|
||||
"""
|
||||
_default_method_type = MethodType.FUNCTION
|
||||
|
||||
def __init__(self, element=None, **kw):
|
||||
super(FunctionDef, self).__init__()
|
||||
self.type = None
|
||||
self.definition = ''
|
||||
self.argsString = ''
|
||||
self.signature: Optional[Signature] = None
|
||||
self.pyArgsString = ''
|
||||
self.isOverloaded = False
|
||||
self.overloads = []
|
||||
@@ -406,7 +410,10 @@ class FunctionDef(BaseDef, FixWxPrefix):
|
||||
else:
|
||||
parent = self.klass
|
||||
item = self.findOverload(matchText)
|
||||
assert item is not None
|
||||
item.pyName = newName
|
||||
if item.signature:
|
||||
item.signature.method_name = newName
|
||||
item.__dict__.update(kw)
|
||||
|
||||
if item is self and not self.hasOverloads():
|
||||
@@ -470,8 +477,8 @@ class FunctionDef(BaseDef, FixWxPrefix):
|
||||
Create a pythonized version of the argsString in function and method
|
||||
items that can be used as part of the docstring.
|
||||
"""
|
||||
params = list()
|
||||
returns = list()
|
||||
params: list[Signature.Parameter] = []
|
||||
returns: list[str] = []
|
||||
if self.type and self.type != 'void':
|
||||
returns.append(self.cleanType(self.type))
|
||||
|
||||
@@ -483,6 +490,7 @@ class FunctionDef(BaseDef, FixWxPrefix):
|
||||
'wxArrayInt()' : '[]',
|
||||
'wxEmptyString': "''", # Makes signatures much shorter
|
||||
}
|
||||
P = Signature.Parameter
|
||||
if isinstance(self, CppMethodDef):
|
||||
# rip apart the argsString instead of using the (empty) list of parameters
|
||||
lastP = self.argsString.rfind(')')
|
||||
@@ -495,22 +503,15 @@ class FunctionDef(BaseDef, FixWxPrefix):
|
||||
if '=' in arg:
|
||||
default = arg.split('=')[1].strip()
|
||||
arg = arg.split('=')[0].strip()
|
||||
if default in defValueMap:
|
||||
default = defValueMap.get(default)
|
||||
else:
|
||||
default = self.fixWxPrefix(default, True)
|
||||
default = defValueMap.get(default, default)
|
||||
default = self.fixWxPrefix(default, True)
|
||||
# now grab just the last word, it should be the variable name
|
||||
# The rest will be the type information
|
||||
arg_type, arg = arg.rsplit(None, 1)
|
||||
arg, arg_type = self.parseNameAndType(arg, arg_type)
|
||||
if arg_type:
|
||||
if default == 'None':
|
||||
arg = f'{arg}: Optional[{arg_type}]'
|
||||
else:
|
||||
arg = f'{arg}: {arg_type}'
|
||||
if default:
|
||||
arg += '=' + default
|
||||
params.append(arg)
|
||||
arg, arg_type = self.parseNameAndType(arg, arg_type, True)
|
||||
params.append(P(arg, arg_type, default))
|
||||
if default == 'None':
|
||||
params[-1].make_optional()
|
||||
else:
|
||||
for param in self.items:
|
||||
assert isinstance(param, ParamDef)
|
||||
@@ -518,36 +519,40 @@ class FunctionDef(BaseDef, FixWxPrefix):
|
||||
continue
|
||||
if param.arraySize:
|
||||
continue
|
||||
s, param_type = self.parseNameAndType(param.pyName or param.name, param.type)
|
||||
s, param_type = self.parseNameAndType(param.pyName or param.name, param.type, not param.out)
|
||||
if param.out:
|
||||
if param_type:
|
||||
returns.append(param_type)
|
||||
else:
|
||||
default = ''
|
||||
if param.inOut:
|
||||
if param_type:
|
||||
returns.append(param_type)
|
||||
if param.default:
|
||||
default = param.default
|
||||
if default in defValueMap:
|
||||
default = defValueMap.get(default)
|
||||
if param_type:
|
||||
if default == 'None':
|
||||
s = f'{s}: Optional[{param_type}]'
|
||||
else:
|
||||
s = f'{s}: {param_type}'
|
||||
default = defValueMap.get(default, default)
|
||||
default = '|'.join([self.cleanName(x, True) for x in default.split('|')])
|
||||
s = f'{s}={default}'
|
||||
elif param_type:
|
||||
s = f'{s} : {param_type}'
|
||||
params.append(s)
|
||||
|
||||
self.pyArgsString = f"({', '.join(params)})"
|
||||
if not returns:
|
||||
self.pyArgsString = f'{self.pyArgsString} -> None'
|
||||
params.append(P(s, param_type, default))
|
||||
if default == 'None':
|
||||
params[-1].make_optional()
|
||||
if getattr(self, 'isCtor', False):
|
||||
name = '__init__'
|
||||
else:
|
||||
name = self.pyName or self.name
|
||||
name = self.fixWxPrefix(name)
|
||||
# __bool__ and __nonzero__ need to be defined as returning int for SIP, but for Python
|
||||
# __bool__ is required to return a bool:
|
||||
if name in ('__bool__', '__nonzero__'):
|
||||
return_type = 'bool'
|
||||
elif not returns:
|
||||
return_type = 'None'
|
||||
elif len(returns) == 1:
|
||||
self.pyArgsString = f'{self.pyArgsString} -> {returns[0]}'
|
||||
elif len(returns) > 1:
|
||||
self.pyArgsString = f"{self.pyArgsString} -> Tuple[{', '.join(returns)}]"
|
||||
return_type = returns[0]
|
||||
else:
|
||||
return_type = f"Tuple[{', '.join(returns)}]"
|
||||
kind = MethodType.STATIC_METHOD if getattr(self, 'isStatic', False) else type(self)._default_method_type
|
||||
self.signature = Signature(name, *params, return_type=return_type, method_type=kind)
|
||||
self.pyArgsString = self.signature.args_string(False)
|
||||
|
||||
|
||||
def collectPySignatures(self):
|
||||
@@ -584,6 +589,8 @@ class MethodDef(FunctionDef):
|
||||
"""
|
||||
Represents a class method, ctor or dtor declaration.
|
||||
"""
|
||||
_default_method_type = MethodType.METHOD
|
||||
|
||||
def __init__(self, element=None, className=None, **kw):
|
||||
super(MethodDef, self).__init__()
|
||||
self.className = className
|
||||
@@ -693,7 +700,7 @@ class ClassDef(BaseDef):
|
||||
self.headerCode = []
|
||||
self.cppCode = []
|
||||
self.convertToPyObject = None
|
||||
self.convertFromPyObject = None
|
||||
self._convertFromPyObject = None
|
||||
self.allowNone = False # Allow the convertFrom code to handle None too.
|
||||
self.instanceCode = None # Code to be used to create new instances of this class
|
||||
self.innerclasses = []
|
||||
@@ -713,6 +720,26 @@ class ClassDef(BaseDef):
|
||||
if element is not None:
|
||||
self.extract(element)
|
||||
|
||||
@property
|
||||
def convertFromPyObject(self) -> Optional[str]:
|
||||
return self._convertFromPyObject
|
||||
|
||||
@convertFromPyObject.setter
|
||||
def convertFromPyObject(self, value: AutoConversionInfo) -> None:
|
||||
self._convertFromPyObject = value.code
|
||||
name = self.pyName or self.name
|
||||
name = removeWxPrefix(name)
|
||||
FixWxPrefix.register_autoconversion(name, value.convertables)
|
||||
|
||||
def is_top_level(self) -> bool:
|
||||
"""Check if this class is a subclass of wx.TopLevelWindow"""
|
||||
if not self.nodeBases:
|
||||
return False
|
||||
all_classes, specials = self.nodeBases
|
||||
if 'wxTopLevelWindow' in specials:
|
||||
return True
|
||||
return 'wxTopLevelWindow' in all_classes
|
||||
|
||||
|
||||
def renameClass(self, newName):
|
||||
self.pyName = newName
|
||||
@@ -1270,7 +1297,7 @@ class CppMethodDef(MethodDef):
|
||||
NOTE: This one is not automatically extracted, but can be added to
|
||||
classes in the tweaker stage
|
||||
"""
|
||||
def __init__(self, type, name, argsString, body, doc=None, isConst=False,
|
||||
def __init__(self, type, name, argsString: str, body, doc=None, isConst=False,
|
||||
cppSignature=None, virtualCatcherCode=None, **kw):
|
||||
super(CppMethodDef, self).__init__()
|
||||
self.type = type
|
||||
|
@@ -22,7 +22,7 @@ want to add some type info to that version of the file eventually...
|
||||
"""
|
||||
|
||||
import sys, os, re
|
||||
from typing import Union
|
||||
from typing import Optional, Union
|
||||
import etgtools.extractors as extractors
|
||||
import etgtools.generators as generators
|
||||
from etgtools.generators import nci, Utf8EncodingStream, textfile_open
|
||||
@@ -80,6 +80,7 @@ header_pyi = """\
|
||||
|
||||
typing_imports = """\
|
||||
from __future__ import annotations
|
||||
from datetime import datetime, date
|
||||
from enum import IntEnum, IntFlag, auto
|
||||
from typing import (Any, overload, TypeAlias, Generic,
|
||||
Union, Optional, List, Tuple, Callable
|
||||
@@ -89,6 +90,12 @@ try:
|
||||
except ImportError:
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
_TwoInts: TypeAlias = Tuple[int, int]
|
||||
_ThreeInts: TypeAlias = Tuple[int, int, int]
|
||||
_FourInts: TypeAlias = Tuple[int, int, int, int]
|
||||
_TwoFloats: TypeAlias = Tuple[float, float]
|
||||
_FourFloats: TypeAlias = Tuple[float, float, float, float]
|
||||
|
||||
"""
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -295,6 +302,8 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
|
||||
name = define.pyName or define.name
|
||||
if '"' in define.value:
|
||||
stream.write(f'{name}: str\n')
|
||||
elif define.value in ('true', 'false'):
|
||||
stream.write(f'{name}: bool\n')
|
||||
else:
|
||||
stream.write(f'{name}: int\n')
|
||||
|
||||
@@ -320,7 +329,8 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
|
||||
t = typedef.type.replace('>', '')
|
||||
t = t.replace(' ', '')
|
||||
bases = t.split('<')
|
||||
bases = [self.fixWxPrefix(b, True) for b in bases]
|
||||
bases = (self.fixWxPrefix(b, True) for b in bases)
|
||||
bases = [b.replace('*', '') for b in bases] # fix for RichTextLine*
|
||||
name = self.fixWxPrefix(typedef.name)
|
||||
|
||||
# Now write the Python equivalent class for the typedef
|
||||
@@ -388,7 +398,7 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
|
||||
# these are the only kinds of items allowed to be items in a PyClass
|
||||
dispatch = {
|
||||
extractors.PyFunctionDef : self.generatePyFunction,
|
||||
extractors.PyPropertyDef : self.generatePyProperty,
|
||||
extractors.PyPropertyDef : lambda a,b,c: self.generatePyProperty(pc, a, b, c),
|
||||
extractors.PyCodeDef : self.generatePyCode,
|
||||
extractors.PyClassDef : self.generatePyClass,
|
||||
}
|
||||
@@ -410,16 +420,11 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
|
||||
stream.write('\n@overload')
|
||||
elif is_overload:
|
||||
stream.write('\n@overload')
|
||||
stream.write('\ndef %s' % function.pyName)
|
||||
argsString = function.pyArgsString
|
||||
if not argsString:
|
||||
argsString = '()'
|
||||
if '(' != argsString[0]:
|
||||
pos = argsString.find('(')
|
||||
argsString = argsString[pos:]
|
||||
argsString = argsString.replace('::', '.')
|
||||
stream.write(argsString)
|
||||
stream.write(':\n')
|
||||
if not function.signature:
|
||||
function.makePyArgsString()
|
||||
assert function.signature is not None
|
||||
for line in function.signature.definition_lines():
|
||||
stream.write(f'\n{line}')
|
||||
if is_overload:
|
||||
stream.write(' ...\n')
|
||||
else:
|
||||
@@ -498,8 +503,8 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
|
||||
dispatch = {
|
||||
extractors.MemberVarDef : self.generateMemberVar,
|
||||
extractors.TypedefDef : lambda a,b,c: None,
|
||||
extractors.PropertyDef : self.generateProperty,
|
||||
extractors.PyPropertyDef : self.generatePyProperty,
|
||||
extractors.PropertyDef : lambda a,b,c: self.generateProperty(klass, a, b, c),
|
||||
extractors.PyPropertyDef : lambda a,b,c: self.generatePyProperty(klass, a, b, c),
|
||||
extractors.MethodDef : self.generateMethod,
|
||||
extractors.EnumDef : self.generateEnum,
|
||||
extractors.CppMethodDef : self.generateCppMethod,
|
||||
@@ -517,7 +522,8 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
|
||||
if item.isCtor:
|
||||
item.klass = klass
|
||||
self.generateMethod(item, stream, indent2,
|
||||
name='__init__', docstring=klass.pyDocstring)
|
||||
name='__init__', docstring=klass.pyDocstring,
|
||||
is_top_level_init=klass.is_top_level())
|
||||
|
||||
for item in public:
|
||||
item.klass = klass
|
||||
@@ -532,6 +538,15 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
|
||||
stream.write('%s# end of class %s\n\n' % (indent, klassName))
|
||||
|
||||
|
||||
def find_method(self, klass: extractors.ClassDef, method_name: str) -> Optional[extractors.MethodDef]:
|
||||
methods = (i for i in klass if isinstance(i, extractors.MethodDef) and not i.isCtor and not i.isDtor)
|
||||
for method in methods:
|
||||
name = method.name or method.pyName
|
||||
if name == method_name:
|
||||
return method
|
||||
return None
|
||||
|
||||
|
||||
def generateMemberVar(self, memberVar, stream, indent):
|
||||
assert isinstance(memberVar, extractors.MemberVarDef)
|
||||
if memberVar.ignored or piIgnored(memberVar):
|
||||
@@ -544,27 +559,58 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
|
||||
stream.write(f'{indent}{memberVar.name}: {member_type}\n')
|
||||
|
||||
|
||||
def generateProperty(self, prop, stream, indent):
|
||||
def generateProperty(self, klass, prop, stream, indent):
|
||||
assert isinstance(prop, extractors.PropertyDef)
|
||||
self._generateProperty(prop, stream, indent)
|
||||
self._generateProperty(klass, prop, stream, indent)
|
||||
|
||||
|
||||
def generatePyProperty(self, prop, stream, indent):
|
||||
def generatePyProperty(self, klass, prop, stream, indent):
|
||||
assert isinstance(prop, extractors.PyPropertyDef)
|
||||
self._generateProperty(prop, stream, indent)
|
||||
self._generateProperty(klass, prop, stream, indent)
|
||||
|
||||
def _generateProperty(self, prop: Union[extractors.PyPropertyDef, extractors.PropertyDef], stream, indent: str):
|
||||
def _generateProperty(self, klass: extractors.ClassDef, prop: Union[extractors.PyPropertyDef, extractors.PropertyDef], stream, indent: str):
|
||||
if prop.ignored or piIgnored(prop):
|
||||
return
|
||||
value_type = ''
|
||||
if prop.getter:
|
||||
getter = self.find_method(klass, prop.getter)
|
||||
if getter and getter.signature:
|
||||
value_type = getter.signature.return_type
|
||||
if prop.setter:
|
||||
setter = self.find_method(klass, prop.setter)
|
||||
if setter and setter.signature:
|
||||
value_type = setter.signature[0].type_hint
|
||||
if prop.setter and prop.getter:
|
||||
stream.write(f'{indent}{prop.name} = property({prop.getter}, {prop.setter})\n')
|
||||
if value_type:
|
||||
stream.write(f'{indent}@property\n')
|
||||
stream.write(f'{indent}def {prop.name}(self) -> {value_type}: ...\n')
|
||||
stream.write(f'{indent}@{prop.name}.setter\n')
|
||||
stream.write(f'{indent}def {prop.name}(self, value: {value_type}, /) -> None: ...\n')
|
||||
else:
|
||||
stream.write(f'{indent}{prop.name} = property({prop.getter}, {prop.setter})\n')
|
||||
elif prop.getter:
|
||||
stream.write(f'{indent}{prop.name} = property({prop.getter})\n')
|
||||
if value_type:
|
||||
stream.write(f'{indent}@property\n')
|
||||
stream.write(f'{indent}def {prop.name}(self) -> {value_type}: ...\n')
|
||||
else:
|
||||
stream.write(f'{indent}{prop.name} = property({prop.getter})\n')
|
||||
elif prop.setter:
|
||||
# Can't use the decorator syntax in this situation
|
||||
stream.write(f'{indent}{prop.name} = property(fset={prop.setter})\n')
|
||||
|
||||
|
||||
def generateMethod(self, method, stream, indent, name=None, docstring=None, is_overload=False):
|
||||
def generateMethod(self, method, stream, indent, name=None, docstring=None, is_overload=False, is_top_level_init=False):
|
||||
"""Write the python declaration for a method (type-stub or otherwise):
|
||||
method: MethodDef holding information about the method
|
||||
stream: output stream to write to
|
||||
indent: indentation level to use when writing
|
||||
name: name of the method, if wanting to override what is identified in `method`
|
||||
docstring: docstring to use, if wanting to override what is in method.pyDocString
|
||||
is_overload: If this declaration should be marked with `@typing.overload`
|
||||
is_top_level_init: If this class is a subclass of wx.TopLevelWindow and is an __init__ method, to apply the
|
||||
transformation `parent: <WindowType>` -> `parent: Optional[<WindowType>]`, because TopLevelWindow
|
||||
allows for a `None` parent.
|
||||
"""
|
||||
assert isinstance(method, extractors.MethodDef)
|
||||
for m in method.all(): # use the first not ignored if there are overloads
|
||||
if not m.ignored or piIgnored(m):
|
||||
@@ -575,34 +621,23 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
|
||||
if method.isDtor:
|
||||
return
|
||||
|
||||
name = name or method.pyName or method.name
|
||||
if name in magicMethods:
|
||||
name = magicMethods[name]
|
||||
|
||||
# write the method declaration
|
||||
if not is_overload and method.hasOverloads():
|
||||
for m in method.overloads:
|
||||
self.generateMethod(m, stream, indent, name, None, True)
|
||||
self.generateMethod(m, stream, indent, name, None, True, is_top_level_init)
|
||||
stream.write(f'\n{indent}@overload')
|
||||
elif is_overload:
|
||||
stream.write(f'\n{indent}@overload')
|
||||
if method.isStatic:
|
||||
stream.write('\n%s@staticmethod' % indent)
|
||||
stream.write('\n%sdef %s' % (indent, name))
|
||||
argsString = method.pyArgsString
|
||||
if not argsString:
|
||||
argsString = '()'
|
||||
if '(' != argsString[0]:
|
||||
pos = argsString.find('(')
|
||||
argsString = argsString[pos:]
|
||||
if not method.isStatic:
|
||||
if argsString == '()':
|
||||
argsString = '(self)'
|
||||
else:
|
||||
argsString = '(self, ' + argsString[1:]
|
||||
argsString = argsString.replace('::', '.')
|
||||
stream.write(argsString)
|
||||
stream.write(':\n')
|
||||
if not method.signature:
|
||||
method.makePyArgsString()
|
||||
assert method.signature is not None
|
||||
if name is not None:
|
||||
method.signature.method_name = name
|
||||
if is_top_level_init and 'parent' in method.signature:
|
||||
method.signature['parent'].make_optional()
|
||||
for line in method.signature.definition_lines():
|
||||
stream.write(f'\n{indent}{line}')
|
||||
stream.write('\n')
|
||||
indent2 = indent + ' '*4
|
||||
|
||||
# docstring
|
||||
|
@@ -14,6 +14,7 @@ the various XML elements passed by the Phoenix extractors into ReST format.
|
||||
"""
|
||||
|
||||
# Standard library stuff
|
||||
import keyword
|
||||
import os
|
||||
import operator
|
||||
import sys
|
||||
@@ -28,7 +29,7 @@ import xml.etree.ElementTree as ET
|
||||
import etgtools.extractors as extractors
|
||||
import etgtools.generators as generators
|
||||
from etgtools.item_module_map import ItemModuleMap
|
||||
from etgtools.tweaker_tools import removeWxPrefix
|
||||
from etgtools.tweaker_tools import removeWxPrefix, ParameterType
|
||||
|
||||
# Sphinx-Phoenix specific stuff
|
||||
from sphinxtools.inheritance import InheritanceDiagram
|
||||
@@ -580,31 +581,14 @@ class ParameterList(Node):
|
||||
if xml_item.hasOverloads() and not is_overload:
|
||||
return
|
||||
|
||||
arguments = xml_item.pyArgsString
|
||||
if xml_item.signature is None:
|
||||
xml_item.makePyArgsString()
|
||||
assert xml_item.signature is not None
|
||||
signature = xml_item.signature.signature()
|
||||
arguments = list(xml_item.signature)
|
||||
if not arguments:
|
||||
return
|
||||
|
||||
if hasattr(xml_item, 'isStatic') and not xml_item.isStatic:
|
||||
if arguments[:2] == '()':
|
||||
return
|
||||
|
||||
arguments = arguments[1:]
|
||||
|
||||
if '->' in arguments:
|
||||
arguments, dummy = arguments.split("->")
|
||||
|
||||
arguments = arguments.strip()
|
||||
if arguments.endswith(','):
|
||||
arguments = arguments[0:-1]
|
||||
|
||||
if arguments.startswith('('):
|
||||
arguments = arguments[1:]
|
||||
if arguments.endswith(')'):
|
||||
arguments = arguments[0:-1]
|
||||
|
||||
signature = name + '(%s)'%arguments
|
||||
arguments = arguments.split(',')
|
||||
|
||||
py_parameters = []
|
||||
for key, parameter in self.py_parameters.items():
|
||||
pdef = parameter.pdef
|
||||
@@ -619,37 +603,23 @@ class ParameterList(Node):
|
||||
' ==> Parameter list from wxWidgets XML items: %s\n\n' \
|
||||
'This may be a documentation bug in wxWidgets or a side-effect of removing the `wx` prefix from signatures.\n\n'
|
||||
|
||||
theargs = []
|
||||
|
||||
for arg in arguments:
|
||||
|
||||
myarg = arg.split('=')[0].strip()
|
||||
if myarg:
|
||||
theargs.append(myarg)
|
||||
|
||||
if '*' in arg or ')' in arg:
|
||||
arg_name = arg.name
|
||||
if arg.position_type in (ParameterType.VAR_ARGS, ParameterType.KWARGS):
|
||||
continue
|
||||
if arg_name.startswith('_') and keyword.iskeyword(arg_name[1:]): # Reserved Python keywords we've had to rename
|
||||
arg_name = arg_name[1:]
|
||||
|
||||
#if '*' in arg_name:
|
||||
# continue
|
||||
|
||||
arg = arg.split('=')[0].strip()
|
||||
|
||||
if arg and arg not in py_parameters:
|
||||
|
||||
if arg_name not in py_parameters:
|
||||
class_name = ''
|
||||
if hasattr(xml_item, 'className') and xml_item.className is not None:
|
||||
class_name = wx2Sphinx(xml_item.className)[1] + '.'
|
||||
|
||||
print((message % (class_name + name, arg, signature, py_parameters)))
|
||||
|
||||
## for param in py_parameters:
|
||||
## if param not in theargs:
|
||||
## class_name = ''
|
||||
## if hasattr(xml_item, 'className') and xml_item.className is not None:
|
||||
## class_name = wx2Sphinx(xml_item.className)[1] + '.'
|
||||
##
|
||||
## print '\n ||| %s;%s;%s |||\n'%(class_name[0:-1], signature, param)
|
||||
## with open('mismatched.txt', 'a') as fid:
|
||||
## fid.write('%s;%s;%s\n'%(class_name[0:-1], signature, param))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
@@ -12,6 +12,7 @@ Some helpers and utility functions that can assist with the tweaker
|
||||
stage of the ETG scripts.
|
||||
"""
|
||||
|
||||
import enum
|
||||
import etgtools as extractors
|
||||
from .generators import textfile_open
|
||||
import keyword
|
||||
@@ -19,7 +20,7 @@ import re
|
||||
import sys, os
|
||||
import copy
|
||||
import textwrap
|
||||
from typing import Optional, Tuple
|
||||
from typing import NamedTuple, Optional, Tuple, Union
|
||||
|
||||
|
||||
isWindows = sys.platform.startswith('win')
|
||||
@@ -40,6 +41,174 @@ magicMethods = {
|
||||
}
|
||||
|
||||
|
||||
class AutoConversionInfo(NamedTuple):
|
||||
convertables: Tuple[str, ...] # String type-hints for each of the types that can be automatically converted to this class
|
||||
code: str # Code that will be added to SIP for this conversion
|
||||
|
||||
|
||||
class ParameterType(enum.Enum):
|
||||
VAR_ARGS = enum.auto()
|
||||
KWARGS = enum.auto()
|
||||
POSITIONAL_ONLY = enum.auto()
|
||||
DEFAULT = enum.auto()
|
||||
|
||||
|
||||
class MethodType(enum.Enum):
|
||||
STATIC_METHOD = enum.auto() # Class @staticmethod method
|
||||
CLASS_METHOD = enum.auto() # Class @classmethod method
|
||||
METHOD = enum.auto() # Class regular method
|
||||
FUNCTION = enum.auto() # non-class function
|
||||
|
||||
|
||||
class Signature:
|
||||
"""Like inspect.Signature, but a bit simpler because we need it only for a few purposes:
|
||||
- Creation from a C++ args string
|
||||
- We *don't* want stringized (ie: all of them) type-hints to be evaluated, since we're
|
||||
processing them in a context where most of them will be unresolvable.
|
||||
"""
|
||||
|
||||
class Parameter:
|
||||
__slots__ = ('name', 'type_hint', 'default', 'position_type', )
|
||||
name: str
|
||||
type_hint: Optional[str]
|
||||
default: Optional[str]
|
||||
position_type: ParameterType
|
||||
|
||||
def __init__(self, name: str, type_hint: Optional[str] = None, default: Optional[str] = None, position_type: ParameterType = ParameterType.DEFAULT) -> None:
|
||||
if name.startswith('**'):
|
||||
name = name[2:]
|
||||
position_type = ParameterType.KWARGS
|
||||
elif name.startswith('*'):
|
||||
name = name[1:]
|
||||
position_type = ParameterType.VAR_ARGS
|
||||
type_hint = type_hint.replace('::', '.') if type_hint else None
|
||||
default = default.replace('::', '.') if default else None
|
||||
self.name = name
|
||||
self.type_hint = type_hint
|
||||
self.default = default
|
||||
self.position_type = position_type
|
||||
|
||||
@property
|
||||
def _position_marking(self) -> str:
|
||||
if self.position_type is ParameterType.KWARGS:
|
||||
return '**'
|
||||
elif self.position_type is ParameterType.VAR_ARGS:
|
||||
return '*'
|
||||
return ''
|
||||
|
||||
def untyped(self) -> str:
|
||||
if self.default is None:
|
||||
return f'{self._position_marking}{self.name}'
|
||||
else:
|
||||
return f'{self._position_marking}{self.name}={self.default}'
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.type_hint is None and self.default is None:
|
||||
return f'{self._position_marking}{self.name}'
|
||||
elif self.type_hint is None:
|
||||
return f'{self._position_marking}{self.name}={self.default}'
|
||||
elif self.default is None:
|
||||
return f'{self._position_marking}{self.name}: {self.type_hint}'
|
||||
else:
|
||||
return f'{self._position_marking}{self.name}: {self.type_hint}={self.default}'
|
||||
|
||||
def make_optional(self) -> None:
|
||||
if self.type_hint is not None and not self.type_hint.startswith('Optional['):
|
||||
self.type_hint = f'Optional[{self.type_hint}]'
|
||||
|
||||
__slots__ = ('_method_name', 'return_type', '_parameters', '_method_type', )
|
||||
_method_name: str
|
||||
return_type: Optional[str]
|
||||
_parameters: dict[str, Parameter]
|
||||
_method_type: MethodType
|
||||
|
||||
def __init__(self, method_name: str, *parameters: Parameter, return_type: Optional[str] = None, method_type: MethodType = MethodType.METHOD) -> None:
|
||||
self._parameters = {
|
||||
p.name: p
|
||||
for p in parameters
|
||||
}
|
||||
self.return_type = return_type.replace('::', '.') if return_type else None
|
||||
self._method_type = method_type
|
||||
self.method_name = method_name
|
||||
|
||||
@property
|
||||
def method_name(self) -> str:
|
||||
return self._method_name
|
||||
|
||||
@method_name.setter
|
||||
def method_name(self, value: str, /) -> None:
|
||||
self._method_name = magicMethods.get(value, value)
|
||||
|
||||
def __getitem__(self, key: Union[str, int]) -> Parameter:
|
||||
"""Get parameter by name or by index. Indexing is into the paramters skips 'cls' and 'self' for
|
||||
classmethods and methods.
|
||||
"""
|
||||
if isinstance(key, int):
|
||||
key = list(self._parameters)[key]
|
||||
if isinstance(key, str):
|
||||
return self._parameters[key]
|
||||
else:
|
||||
raise TypeError(f'Indexing must be via parameter name or index, got {key}')
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._parameters.values())
|
||||
|
||||
def __contains__(self, parameter_name: str) -> bool:
|
||||
return parameter_name in self._parameters
|
||||
|
||||
def args_string(self, typed: bool = True, include_selfcls: bool = False) -> str:
|
||||
"""Get a string of just the parameters needed for the method signature,
|
||||
optionally with 'self' or 'cls' where applicable, and type-hints
|
||||
"""
|
||||
if include_selfcls and self.is_classmethod:
|
||||
parameters = (type(self).Parameter('cls'), *self._parameters.values())
|
||||
elif include_selfcls and self.is_method:
|
||||
parameters = (type(self).Parameter('self'), *self._parameters.values())
|
||||
else:
|
||||
parameters = self._parameters.values()
|
||||
stringizer = str if typed else type(self).Parameter.untyped
|
||||
return_type = f' -> {self.return_type}' if self.return_type else ''
|
||||
return f"({', '.join(map(stringizer, parameters))}){return_type}"
|
||||
|
||||
def signature(self, typed: bool = True) -> str:
|
||||
"""Get the full signature for the function/method, including method and
|
||||
all required python syntax, optionally including all type-hints.
|
||||
"""
|
||||
return f'def {self.method_name}{self.args_string(typed, True)}:'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.signature()
|
||||
|
||||
def definition_lines(self, typed: bool = True) -> list[str]:
|
||||
"""return the lines required to write the full method definition,
|
||||
including decorators
|
||||
"""
|
||||
if self.is_staticmethod:
|
||||
lines = ['@staticmethod']
|
||||
elif self.is_classmethod:
|
||||
lines = ['@classmethod']
|
||||
else:
|
||||
lines = []
|
||||
lines.append(self.signature(typed))
|
||||
return lines
|
||||
|
||||
@property
|
||||
def is_staticmethod(self) -> bool:
|
||||
return self._method_type is MethodType.STATIC_METHOD
|
||||
|
||||
@property
|
||||
def is_classmethod(self) -> bool:
|
||||
return self._method_type is MethodType.CLASS_METHOD
|
||||
|
||||
@property
|
||||
def is_method(self) -> bool:
|
||||
return self._method_type is MethodType.METHOD
|
||||
|
||||
@property
|
||||
def is_function(self) -> bool:
|
||||
return self._method_type is MethodType.FUNCTION
|
||||
|
||||
|
||||
def removeWxPrefixes(node):
|
||||
"""
|
||||
Rename items with a 'wx' prefix to not have the prefix. If the back-end
|
||||
@@ -85,6 +254,11 @@ class FixWxPrefix(object):
|
||||
"""
|
||||
|
||||
_coreTopLevelNames = None
|
||||
_auto_conversions: dict[str, Tuple[str, ...]] = {}
|
||||
|
||||
@classmethod
|
||||
def register_autoconversion(cls, class_name: str, convertables: Tuple[str, ...]) -> None:
|
||||
cls._auto_conversions[class_name] = convertables
|
||||
|
||||
def fixWxPrefix(self, name, checkIsCore=False):
|
||||
# By default remove the wx prefix like normal
|
||||
@@ -126,7 +300,9 @@ class FixWxPrefix(object):
|
||||
names.append(item.name)
|
||||
elif isinstance(item, ast.AnnAssign):
|
||||
if isinstance(item.target, ast.Name):
|
||||
names.append(item.target.id)
|
||||
# Exclude typing TypeAlias's from detection
|
||||
if not (item.annotation == 'TypeAlias' and item.target.id.startswith('_')):
|
||||
names.append(item.target.id)
|
||||
|
||||
names = list()
|
||||
filename = 'wx/core.pyi'
|
||||
@@ -150,7 +326,11 @@ class FixWxPrefix(object):
|
||||
|
||||
Finally, the 'wx.' prefix is added if needed.
|
||||
"""
|
||||
for txt in ['const', '*', '&', ' ']:
|
||||
name = re.sub(r'(const(?![\w\d]))', '', name) # remove 'const', but not 'const'raints
|
||||
replacements = [' ', '*']
|
||||
if not is_expression:
|
||||
replacements.extend(['&'])
|
||||
for txt in replacements:
|
||||
name = name.replace(txt, '')
|
||||
name = name.replace('::', '.')
|
||||
if not is_expression:
|
||||
@@ -161,9 +341,9 @@ class FixWxPrefix(object):
|
||||
if fix_wx:
|
||||
return self.fixWxPrefix(name, True)
|
||||
else:
|
||||
return removeWxPrefix(name)
|
||||
return name
|
||||
|
||||
def cleanType(self, type_name: str) -> str:
|
||||
def cleanType(self, type_name: str, is_input: bool = False) -> str:
|
||||
"""Process a C++ type name for use as a type annotation in Python code.
|
||||
Handles translation of common C++ types to Python types, as well as a
|
||||
few specific wx types to Python types.
|
||||
@@ -216,9 +396,13 @@ class FixWxPrefix(object):
|
||||
return f'List[{type_name}]'
|
||||
else:
|
||||
return 'list'
|
||||
allowed_types = self._auto_conversions.get(type_name, ())
|
||||
if allowed_types and is_input:
|
||||
allowed_types = (type_name, *(self.cleanType(t) for t in allowed_types))
|
||||
type_name = f"Union[{', '.join(allowed_types)}]"
|
||||
return type_map.get(type_name, type_name)
|
||||
|
||||
def parseNameAndType(self, name_string: str, type_string: Optional[str]) -> Tuple[str, Optional[str]]:
|
||||
def parseNameAndType(self, name_string: str, type_string: Optional[str], is_input: bool = False) -> Tuple[str, Optional[str]]:
|
||||
"""Given an identifier name and an optional type annotation, process
|
||||
these per cleanName and cleanType. Further performs transforms on the
|
||||
identifier name that may be required due to the type annotation.
|
||||
@@ -227,7 +411,7 @@ class FixWxPrefix(object):
|
||||
"""
|
||||
name_string = self.cleanName(name_string, fix_wx=False)
|
||||
if type_string:
|
||||
type_string = self.cleanType(type_string)
|
||||
type_string = self.cleanType(type_string, is_input)
|
||||
if type_string == '...':
|
||||
name_string = '*args'
|
||||
type_string = None
|
||||
@@ -856,7 +1040,9 @@ def addGetIMMethodTemplate(module, klass, fields):
|
||||
|
||||
def convertTwoIntegersTemplate(CLASS):
|
||||
# Note: The GIL is already acquired where this code is used.
|
||||
return """\
|
||||
return AutoConversionInfo(
|
||||
('_TwoInts', ),
|
||||
"""\
|
||||
// is it just a typecheck?
|
||||
if (!sipIsErr) {{
|
||||
// is it already an instance of {CLASS}?
|
||||
@@ -884,12 +1070,14 @@ def convertTwoIntegersTemplate(CLASS):
|
||||
Py_DECREF(o1);
|
||||
Py_DECREF(o2);
|
||||
return SIP_TEMPORARY;
|
||||
""".format(**locals())
|
||||
""".format(**locals()))
|
||||
|
||||
|
||||
def convertFourIntegersTemplate(CLASS):
|
||||
# Note: The GIL is already acquired where this code is used.
|
||||
return """\
|
||||
return AutoConversionInfo(
|
||||
('_FourInts', ),
|
||||
"""\
|
||||
// is it just a typecheck?
|
||||
if (!sipIsErr) {{
|
||||
// is it already an instance of {CLASS}?
|
||||
@@ -921,13 +1109,15 @@ def convertFourIntegersTemplate(CLASS):
|
||||
Py_DECREF(o3);
|
||||
Py_DECREF(o4);
|
||||
return SIP_TEMPORARY;
|
||||
""".format(**locals())
|
||||
""".format(**locals()))
|
||||
|
||||
|
||||
|
||||
def convertTwoDoublesTemplate(CLASS):
|
||||
# Note: The GIL is already acquired where this code is used.
|
||||
return """\
|
||||
return AutoConversionInfo(
|
||||
('_TwoFloats', ),
|
||||
"""\
|
||||
// is it just a typecheck?
|
||||
if (!sipIsErr) {{
|
||||
// is it already an instance of {CLASS}?
|
||||
@@ -955,12 +1145,14 @@ def convertTwoDoublesTemplate(CLASS):
|
||||
Py_DECREF(o1);
|
||||
Py_DECREF(o2);
|
||||
return SIP_TEMPORARY;
|
||||
""".format(**locals())
|
||||
""".format(**locals()))
|
||||
|
||||
|
||||
def convertFourDoublesTemplate(CLASS):
|
||||
# Note: The GIL is already acquired where this code is used.
|
||||
return """\
|
||||
return AutoConversionInfo(
|
||||
('_FourFloats', ),
|
||||
"""\
|
||||
// is it just a typecheck?
|
||||
if (!sipIsErr) {{
|
||||
// is it already an instance of {CLASS}?
|
||||
@@ -993,7 +1185,7 @@ def convertFourDoublesTemplate(CLASS):
|
||||
Py_DECREF(o3);
|
||||
Py_DECREF(o4);
|
||||
return SIP_TEMPORARY;
|
||||
""".format(**locals())
|
||||
""".format(**locals()))
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user