mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-06 00:07:29 -04:00
Use Enum for visibility
This commit is contained in:
parent
52f186007f
commit
cc8bf97092
@ -1,17 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import yaml
|
||||
from enum import Enum, auto
|
||||
from typing import List, Dict, Any
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
class Visibility(Enum):
|
||||
Private = auto()
|
||||
Protected = auto()
|
||||
Public = auto()
|
||||
|
||||
|
||||
# Constants
|
||||
PRIVATE = 0
|
||||
PROTECTED = 1
|
||||
PUBLIC = 2
|
||||
STRICT = 10
|
||||
UNSTRICT = 11
|
||||
MULTILINE_NO = 20
|
||||
@ -27,7 +33,8 @@ PREPEND_CODE_MAKE_PRIVATE = 42
|
||||
LINE = ''
|
||||
|
||||
# Parse command-line arguments
|
||||
parser = argparse.ArgumentParser(description="Convert header file to SIP and Python")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Convert header file to SIP and Python")
|
||||
parser.add_argument("-debug", action="store_true", help="Enable debug mode")
|
||||
parser.add_argument("-qt6", action="store_true", help="Enable Qt6 mode")
|
||||
parser.add_argument("-sip_output", help="SIP output file")
|
||||
@ -44,7 +51,8 @@ try:
|
||||
with open(headerfile, "r") as f:
|
||||
input_lines = f.read().splitlines()
|
||||
except IOError as e:
|
||||
print(f"Couldn't open '{headerfile}' for reading because: {e}", file=sys.stderr)
|
||||
print(f"Couldn't open '{headerfile}' for reading because: {e}",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Read configuration
|
||||
@ -53,13 +61,14 @@ try:
|
||||
with open(cfg_file, 'r') as f:
|
||||
sip_config = yaml.safe_load(f)
|
||||
except IOError as e:
|
||||
print(f"Couldn't open configuration file '{cfg_file}' because: {e}", file=sys.stderr)
|
||||
print(f"Couldn't open configuration file '{cfg_file}' because: {e}",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Initialize contexts
|
||||
sip_run = False
|
||||
header_code = False
|
||||
access = [PUBLIC]
|
||||
access: List[Visibility] = [Visibility.Public]
|
||||
classname: List[str] = []
|
||||
class_and_struct: List[str] = []
|
||||
declared_classes: List[str] = []
|
||||
@ -525,7 +534,8 @@ def replace_macros(line):
|
||||
|
||||
if is_qt6:
|
||||
# sip for Qt6 chokes on QList/QVector<QVariantMap>, but is happy if you expand out the map explicitly
|
||||
line = re.sub(r'(QList<\s*|QVector<\s*)QVariantMap', r'\1QMap<QString, QVariant>', line)
|
||||
line = re.sub(r'(QList<\s*|QVector<\s*)QVariantMap',
|
||||
r'\1QMap<QString, QVariant>', line)
|
||||
|
||||
return line
|
||||
|
||||
@ -572,12 +582,14 @@ def dbg_info(info):
|
||||
|
||||
if debug == 1:
|
||||
output.append(f"{info}\n")
|
||||
print(f"{line_idx} {len(access)} {sip_run} {multiline_definition} {info}")
|
||||
print(
|
||||
f"{line_idx} {len(access)} {sip_run} {multiline_definition} {info}")
|
||||
|
||||
|
||||
def exit_with_error(message):
|
||||
global headerfile, line_idx
|
||||
sys.exit(f"! Sipify error in {headerfile} at line :: {line_idx}\n! {message}")
|
||||
sys.exit(
|
||||
f"! Sipify error in {headerfile} at line :: {line_idx}\n! {message}")
|
||||
|
||||
|
||||
def sip_header_footer():
|
||||
@ -588,13 +600,19 @@ def sip_header_footer():
|
||||
# and over there we have to use ./3d/X.h entries because SIP parser does not allow a number
|
||||
# as the first letter of a relative path
|
||||
headerfile_x = re.sub(r'src/core/3d', r'src/core/./3d', headerfile)
|
||||
header_footer.append("/************************************************************************\n")
|
||||
header_footer.append(" * This file has been generated automatically from *\n")
|
||||
header_footer.append(" * *\n")
|
||||
header_footer.append(
|
||||
"/************************************************************************\n")
|
||||
header_footer.append(
|
||||
" * This file has been generated automatically from *\n")
|
||||
header_footer.append(
|
||||
" * *\n")
|
||||
header_footer.append(f" * {headerfile_x:<68} *\n")
|
||||
header_footer.append(" * *\n")
|
||||
header_footer.append(" * Do not edit manually ! Edit header and run scripts/sipify.py again *\n")
|
||||
header_footer.append(" ************************************************************************/\n")
|
||||
header_footer.append(
|
||||
" * *\n")
|
||||
header_footer.append(
|
||||
" * Do not edit manually ! Edit header and run scripts/sipify.py again *\n")
|
||||
header_footer.append(
|
||||
" ************************************************************************/\n")
|
||||
return header_footer
|
||||
|
||||
|
||||
@ -614,22 +632,27 @@ def create_class_links(line):
|
||||
_match = re.search(r'\b(Qgs[A-Z]\w+|Qgis)\b(\.?$|\W{2})', line)
|
||||
if _match:
|
||||
if actual_class and _match.group(1) != actual_class:
|
||||
line = re.sub(r'\b(Qgs[A-Z]\w+)\b(\.?$|\W{2})', r':py:class:`\1`\2', line)
|
||||
line = re.sub(r'\b(Qgs[A-Z]\w+)\b(\.?$|\W{2})',
|
||||
r':py:class:`\1`\2', line)
|
||||
|
||||
# Replace Qgs class methods with :py:func: links
|
||||
line = re.sub(r'\b((Qgs[A-Z]\w+|Qgis)\.[a-z]\w+\(\))(?!\w)', r':py:func:`\1`', line)
|
||||
line = re.sub(r'\b((Qgs[A-Z]\w+|Qgis)\.[a-z]\w+\(\))(?!\w)',
|
||||
r':py:func:`\1`', line)
|
||||
|
||||
# Replace other methods with :py:func: links
|
||||
if actual_class:
|
||||
line = re.sub(r'(?<!\.)\b([a-z]\w+)\(\)(?!\w)', rf':py:func:`~{actual_class}.\1`', line)
|
||||
line = re.sub(r'(?<!\.)\b([a-z]\w+)\(\)(?!\w)',
|
||||
rf':py:func:`~{actual_class}.\1`', line)
|
||||
else:
|
||||
line = re.sub(r'(?<!\.)\b([a-z]\w+)\(\)(?!\w)', r':py:func:`~\1`', line)
|
||||
line = re.sub(r'(?<!\.)\b([a-z]\w+)\(\)(?!\w)', r':py:func:`~\1`',
|
||||
line)
|
||||
|
||||
# Replace Qgs classes (but not the current class) with :py:class: links
|
||||
_match = re.search(r'\b(?<![`~])(Qgs[A-Z]\w+|Qgis)\b(?!\()', line)
|
||||
if _match:
|
||||
if not actual_class or _match.group(1) != actual_class:
|
||||
line = re.sub(r'\b(?<![`~])(Qgs[A-Z]\w+|Qgis)\b(?!\()', r':py:class:`\1`', line)
|
||||
line = re.sub(r'\b(?<![`~])(Qgs[A-Z]\w+|Qgis)\b(?!\()',
|
||||
r':py:class:`\1`', line)
|
||||
|
||||
return line
|
||||
|
||||
@ -707,7 +730,9 @@ def processDoxygenLine(line):
|
||||
if re.match(r'^\s*[\-#]', line):
|
||||
line = f"{prev_indent}{line}"
|
||||
indent = f"{prev_indent} "
|
||||
elif not re.match(r'^\s*[\\:]+(param|note|since|return|deprecated|warning|throws)', line):
|
||||
elif not re.match(
|
||||
r'^\s*[\\:]+(param|note|since|return|deprecated|warning|throws)',
|
||||
line):
|
||||
line = f"{indent}{line}"
|
||||
else:
|
||||
prev_indent = indent
|
||||
@ -734,7 +759,8 @@ def processDoxygenLine(line):
|
||||
if re.match(r'^\s*[\\@]brief', line):
|
||||
line = re.sub(r'[\\@]brief\s*', '', line)
|
||||
if found_since:
|
||||
exit_with_error(f"{headerfile}::{line_idx} Since annotation must come after brief")
|
||||
exit_with_error(
|
||||
f"{headerfile}::{line_idx} Since annotation must come after brief")
|
||||
found_since = False
|
||||
if re.match(r'^\s*$', line):
|
||||
return ""
|
||||
@ -755,7 +781,8 @@ def processDoxygenLine(line):
|
||||
|
||||
# Handle deprecated
|
||||
deprecated_match = re.search(
|
||||
r'\\deprecated(?:\s+since\s+QGIS\s+(?P<DEPR_VERSION>[0-9.]+)(,\s*)?)?(?P<DEPR_MESSAGE>.*)?', line,
|
||||
r'\\deprecated(?:\s+since\s+QGIS\s+(?P<DEPR_VERSION>[0-9.]+)(,\s*)?)?(?P<DEPR_MESSAGE>.*)?',
|
||||
line,
|
||||
re.IGNORECASE)
|
||||
if deprecated_match:
|
||||
prev_indent = indent
|
||||
@ -841,7 +868,8 @@ def detect_and_remove_following_body_or_initializerlist():
|
||||
re.search(pattern2, LINE) or
|
||||
re.match(pattern3, LINE)):
|
||||
|
||||
dbg_info("remove constructor definition, function bodies, member initializing list (1)")
|
||||
dbg_info(
|
||||
"remove constructor definition, function bodies, member initializing list (1)")
|
||||
|
||||
# Extract the parts we want to keep
|
||||
_match = re.match(pattern1, LINE)
|
||||
@ -864,7 +892,8 @@ def remove_following_body_or_initializerlist():
|
||||
|
||||
signature = ''
|
||||
|
||||
dbg_info("remove constructor definition, function bodies, member initializing list (2)")
|
||||
dbg_info(
|
||||
"remove constructor definition, function bodies, member initializing list (2)")
|
||||
line = read_line()
|
||||
|
||||
# Python signature
|
||||
@ -967,7 +996,9 @@ def fix_annotations(line):
|
||||
|
||||
# Combine multiple annotations
|
||||
while True:
|
||||
new_line = re.sub(r'/([\w,]+(="?[\w, \[\]]+"?)?)/\s*/([\w,]+(="?[\w, \[\]]+"?)?]?)/', r'/\1,\3/', line)
|
||||
new_line = re.sub(
|
||||
r'/([\w,]+(="?[\w, \[\]]+"?)?)/\s*/([\w,]+(="?[\w, \[\]]+"?)?]?)/',
|
||||
r'/\1,\3/', line)
|
||||
if new_line == line:
|
||||
break
|
||||
line = new_line
|
||||
@ -979,7 +1010,9 @@ def fix_annotations(line):
|
||||
|
||||
# Note: this was the original perl regex, which isn't compatible with Python:
|
||||
# line = re.sub(r"""=\s+[^=]*?\s+SIP_PYARGDEFAULT\(\s*\'?([^()']+)(\(\s*(?:[^()]++|(?2))*\s*\))?\'?\s*\)""", r'= \1', line)
|
||||
line = re.sub(r"""=\s+[^=]*?\s+SIP_PYARGDEFAULT\(\s*\'?([^()\']+)(\((?:[^()]|\([^()]*\))*\))?\'?\s*\)""", r'= \1',
|
||||
line = re.sub(
|
||||
r"""=\s+[^=]*?\s+SIP_PYARGDEFAULT\(\s*\'?([^()\']+)(\((?:[^()]|\([^()]*\))*\))?\'?\s*\)""",
|
||||
r'= \1',
|
||||
line)
|
||||
|
||||
# Remove argument
|
||||
@ -1019,10 +1052,14 @@ def fix_annotations(line):
|
||||
|
||||
def fix_constants(line):
|
||||
line = re.sub(r'\bstd::numeric_limits<double>::max\(\)', 'DBL_MAX', line)
|
||||
line = re.sub(r'\bstd::numeric_limits<double>::lowest\(\)', '-DBL_MAX', line)
|
||||
line = re.sub(r'\bstd::numeric_limits<double>::epsilon\(\)', 'DBL_EPSILON', line)
|
||||
line = re.sub(r'\bstd::numeric_limits<qlonglong>::min\(\)', 'LLONG_MIN', line)
|
||||
line = re.sub(r'\bstd::numeric_limits<qlonglong>::max\(\)', 'LLONG_MAX', line)
|
||||
line = re.sub(r'\bstd::numeric_limits<double>::lowest\(\)', '-DBL_MAX',
|
||||
line)
|
||||
line = re.sub(r'\bstd::numeric_limits<double>::epsilon\(\)', 'DBL_EPSILON',
|
||||
line)
|
||||
line = re.sub(r'\bstd::numeric_limits<qlonglong>::min\(\)', 'LLONG_MIN',
|
||||
line)
|
||||
line = re.sub(r'\bstd::numeric_limits<qlonglong>::max\(\)', 'LLONG_MAX',
|
||||
line)
|
||||
line = re.sub(r'\bstd::numeric_limits<int>::max\(\)', 'INT_MAX', line)
|
||||
line = re.sub(r'\bstd::numeric_limits<int>::min\(\)', 'INT_MIN', line)
|
||||
return line
|
||||
@ -1043,12 +1080,14 @@ def detect_comment_block(strict_mode=True):
|
||||
|
||||
if re.match(r'^\s*/\*', LINE) or (not strict_mode and '/*' in LINE):
|
||||
dbg_info("found comment block")
|
||||
comment = processDoxygenLine(re.sub(r'^\s*/\*(\*)?(.*?)\n?$', r'\2', LINE))
|
||||
comment = processDoxygenLine(
|
||||
re.sub(r'^\s*/\*(\*)?(.*?)\n?$', r'\2', LINE))
|
||||
comment = re.sub(r'^\s*$', '', comment)
|
||||
|
||||
while not re.search(r'\*/\s*(//.*?)?$', LINE):
|
||||
LINE = read_line()
|
||||
comment += processDoxygenLine(re.sub(r'\s*\*?(.*?)(/)?\n?$', r'\1', LINE))
|
||||
comment += processDoxygenLine(
|
||||
re.sub(r'\s*\*?(.*?)(/)?\n?$', r'\1', LINE))
|
||||
|
||||
comment = re.sub(r'\n\s+\n', '\n\n', comment)
|
||||
comment = re.sub(r'\n{3,}', '\n\n', comment)
|
||||
@ -1115,7 +1154,8 @@ while line_idx < line_count:
|
||||
|
||||
if is_qt6:
|
||||
LINE = re.sub(r'int\s*__len__\s*\(\s*\)', 'Py_ssize_t __len__()', LINE)
|
||||
LINE = re.sub(r'long\s*__hash__\s*\(\s*\)', 'Py_hash_t __hash__()', LINE)
|
||||
LINE = re.sub(r'long\s*__hash__\s*\(\s*\)', 'Py_hash_t __hash__()',
|
||||
LINE)
|
||||
|
||||
if is_qt6 and re.match(r'^\s*#ifdef SIP_PYQT5_RUN', LINE):
|
||||
dbg_info("do not process PYQT5 code")
|
||||
@ -1174,7 +1214,8 @@ while line_idx < line_count:
|
||||
LINE = read_line()
|
||||
if re.match(r'^\s*#if(def)?\s+', LINE):
|
||||
nesting_index += 1
|
||||
elif nesting_index == 0 and re.match(r'^\s*#(endif|else)', LINE):
|
||||
elif nesting_index == 0 and re.match(r'^\s*#(endif|else)',
|
||||
LINE):
|
||||
comment = ''
|
||||
break
|
||||
elif nesting_index != 0 and re.match(r'^\s*#endif', LINE):
|
||||
@ -1183,7 +1224,7 @@ while line_idx < line_count:
|
||||
|
||||
if re.match(r'^\s*#ifdef SIP_RUN', LINE):
|
||||
sip_run = True
|
||||
if access[-1] == PRIVATE:
|
||||
if access[-1] == Visibility.Private:
|
||||
dbg_info("writing private content (1)")
|
||||
if private_section_line:
|
||||
write_output("PRV1", private_section_line + "\n")
|
||||
@ -1222,9 +1263,10 @@ while line_idx < line_count:
|
||||
LINE = read_line()
|
||||
if re.match(r'^\s*#if(def)?\s+', LINE):
|
||||
glob_ifdef_nesting_idx += 1
|
||||
elif re.match(r'^\s*#else', LINE) and glob_ifdef_nesting_idx == 0:
|
||||
elif re.match(r'^\s*#else',
|
||||
LINE) and glob_ifdef_nesting_idx == 0:
|
||||
# Code here will be printed out
|
||||
if access[-1] == PRIVATE:
|
||||
if access[-1] == Visibility.Private:
|
||||
dbg_info("writing private content (2)")
|
||||
if private_section_line != '':
|
||||
write_output("PRV2", private_section_line + "\n")
|
||||
@ -1248,7 +1290,8 @@ while line_idx < line_count:
|
||||
write_output("HCE", "%End\n")
|
||||
|
||||
# Skip forward declarations
|
||||
match = re.match(r'^\s*(template ?<class T> |enum\s+)?(class|struct) \w+(?P<external> *SIP_EXTERNAL)?;\s*(//.*)?$',
|
||||
match = re.match(
|
||||
r'^\s*(template ?<class T> |enum\s+)?(class|struct) \w+(?P<external> *SIP_EXTERNAL)?;\s*(//.*)?$',
|
||||
LINE)
|
||||
if match:
|
||||
if match.group('external'):
|
||||
@ -1267,7 +1310,8 @@ while line_idx < line_count:
|
||||
if not re.search(r'SIP_SKIP', LINE):
|
||||
dbg_info('Q_GADGET')
|
||||
write_output("HCE", " public:\n")
|
||||
write_output("HCE", " static const QMetaObject staticMetaObject;\n\n")
|
||||
write_output("HCE",
|
||||
" static const QMetaObject staticMetaObject;\n\n")
|
||||
continue
|
||||
|
||||
# Insert in Python output (python/module/__init__.py)
|
||||
@ -1302,7 +1346,8 @@ while line_idx < line_count:
|
||||
if multiline_definition != MULTILINE_NO:
|
||||
dbg_info('SIP_SKIP with MultiLine')
|
||||
opening_line = ''
|
||||
while not re.match(r'^[^()]*\(([^()]*\([^()]*\)[^()]*)*[^()]*$', opening_line):
|
||||
while not re.match(r'^[^()]*\(([^()]*\([^()]*\)[^()]*)*[^()]*$',
|
||||
opening_line):
|
||||
opening_line = output.pop()
|
||||
if len(output) < 1:
|
||||
exit_with_error('could not reach opening definition')
|
||||
@ -1313,7 +1358,8 @@ while line_idx < line_count:
|
||||
detect_and_remove_following_body_or_initializerlist()
|
||||
|
||||
# line skipped, go to next iteration
|
||||
match = re.search(r'SIP_PYTHON_SPECIAL_(\w+)\(\s*(".*"|\w+)\s*\)', LINE)
|
||||
match = re.search(r'SIP_PYTHON_SPECIAL_(\w+)\(\s*(".*"|\w+)\s*\)',
|
||||
LINE)
|
||||
if match:
|
||||
method_or_code = match.group(2)
|
||||
dbg_info(f"PYTHON SPECIAL method or code: {method_or_code}")
|
||||
@ -1333,13 +1379,14 @@ while line_idx < line_count:
|
||||
if detect_comment_block():
|
||||
continue
|
||||
|
||||
struct_match = re.match(r'^\s*struct(\s+\w+_EXPORT)?\s+(?P<structname>\w+)$', LINE)
|
||||
struct_match = re.match(
|
||||
r'^\s*struct(\s+\w+_EXPORT)?\s+(?P<structname>\w+)$', LINE)
|
||||
if struct_match:
|
||||
dbg_info(" going to struct => public")
|
||||
class_and_struct.append(struct_match.group('structname'))
|
||||
classname.append(classname[-1] if classname else struct_match.group(
|
||||
'structname')) # fake new class since struct has considered similarly
|
||||
access.append(PUBLIC)
|
||||
access.append(Visibility.Public)
|
||||
exported.append(exported[-1])
|
||||
glob_bracket_nesting_idx.append(0)
|
||||
|
||||
@ -1352,7 +1399,7 @@ while line_idx < line_count:
|
||||
|
||||
if class_pattern_match:
|
||||
dbg_info("class definition started")
|
||||
access.append(PUBLIC)
|
||||
access.append(Visibility.Public)
|
||||
exported.append(0)
|
||||
glob_bracket_nesting_idx.append(0)
|
||||
template_inheritance_template = []
|
||||
@ -1452,11 +1499,11 @@ while line_idx < line_count:
|
||||
else:
|
||||
LINE += f"\ntypedef {tpl}<{cls1},{cls2},{cls3}> {tpl}{cls1}{cls2}{cls3}Base;"
|
||||
|
||||
if any(x == PRIVATE for x in access) and len(access) != 1:
|
||||
if any(x == Visibility.Private for x in access) and len(access) != 1:
|
||||
dbg_info("skipping class in private context")
|
||||
continue
|
||||
|
||||
access[-1] = PRIVATE # private by default
|
||||
access[-1] = Visibility.Private # private by default
|
||||
write_output("CLS", f"{LINE}\n")
|
||||
|
||||
# Skip opening curly bracket, incrementing hereunder
|
||||
@ -1467,7 +1514,7 @@ while line_idx < line_count:
|
||||
|
||||
comment = ''
|
||||
header_code = True
|
||||
access[-1] = PRIVATE
|
||||
access[-1] = Visibility.Private
|
||||
continue
|
||||
|
||||
# Bracket balance in class/struct tree
|
||||
@ -1486,7 +1533,8 @@ while line_idx < line_count:
|
||||
glob_bracket_nesting_idx.pop()
|
||||
access.pop()
|
||||
|
||||
if exported[-1] == 0 and classname[-1] != sip_config.get('no_export_macro'):
|
||||
if exported[-1] == 0 and classname[-1] != sip_config.get(
|
||||
'no_export_macro'):
|
||||
exit_with_error(
|
||||
f"Class {classname[-1]} should be exported with appropriate [LIB]_EXPORT macro. "
|
||||
f"If this should not be available in python, wrap it in a `#ifndef SIP_RUN` block."
|
||||
@ -1499,7 +1547,8 @@ while line_idx < line_count:
|
||||
|
||||
if len(access) == 1:
|
||||
dbg_info("reached top level")
|
||||
access[-1] = PUBLIC # Top level should stay public
|
||||
access[
|
||||
-1] = Visibility.Public # Top level should stay public
|
||||
|
||||
comment = ''
|
||||
return_type = ''
|
||||
@ -1509,7 +1558,7 @@ while line_idx < line_count:
|
||||
|
||||
# Private members (exclude SIP_RUN)
|
||||
if re.match(r'^\s*private( slots)?:', LINE):
|
||||
access[-1] = PRIVATE
|
||||
access[-1] = Visibility.Private
|
||||
last_access_section_line = LINE
|
||||
private_section_line = LINE
|
||||
comment = ''
|
||||
@ -1519,27 +1568,28 @@ while line_idx < line_count:
|
||||
elif re.match(r'^\s*(public( slots)?|signals):.*$', LINE):
|
||||
dbg_info("going public")
|
||||
last_access_section_line = LINE
|
||||
access[-1] = PUBLIC
|
||||
access[-1] = Visibility.Public
|
||||
comment = ''
|
||||
|
||||
elif re.match(r'^\s*(protected)( slots)?:.*$', LINE):
|
||||
dbg_info("going protected")
|
||||
last_access_section_line = LINE
|
||||
access[-1] = PROTECTED
|
||||
access[-1] = Visibility.Protected
|
||||
comment = ''
|
||||
|
||||
elif access[-1] == PRIVATE and 'SIP_FORCE' in LINE:
|
||||
elif access[-1] == Visibility.Private and 'SIP_FORCE' in LINE:
|
||||
dbg_info("private with SIP_FORCE")
|
||||
if private_section_line:
|
||||
write_output("PRV3", private_section_line + "\n")
|
||||
private_section_line = ''
|
||||
|
||||
elif any(x == PRIVATE for x in access) and not sip_run:
|
||||
elif any(x == Visibility.Private for x in access) and not sip_run:
|
||||
comment = ''
|
||||
continue
|
||||
|
||||
# Skip operators
|
||||
if access[-1] != PRIVATE and re.search(r'operator(=|<<|>>|->)\s*\(', LINE):
|
||||
if access[-1] != Visibility.Private and re.search(
|
||||
r'operator(=|<<|>>|->)\s*\(', LINE):
|
||||
dbg_info("skip operator")
|
||||
detect_and_remove_following_body_or_initializerlist()
|
||||
continue
|
||||
@ -1560,10 +1610,12 @@ while line_idx < line_count:
|
||||
continue
|
||||
|
||||
# Handle Q_DECLARE_FLAGS in Qt6
|
||||
if is_qt6 and re.match(r'^\s*Q_DECLARE_FLAGS\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)', LINE):
|
||||
if is_qt6 and re.match(
|
||||
r'^\s*Q_DECLARE_FLAGS\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)', LINE):
|
||||
flags_name = re.search(r'\(\s*(\w+)\s*,\s*(\w+)\s*\)', LINE).group(1)
|
||||
flag_name = re.search(r'\(\s*(\w+)\s*,\s*(\w+)\s*\)', LINE).group(2)
|
||||
output_python.append(f"{actual_class}.{flags_name} = lambda flags=0: {actual_class}.{flag_name}(flags)\n")
|
||||
output_python.append(
|
||||
f"{actual_class}.{flags_name} = lambda flags=0: {actual_class}.{flag_name}(flags)\n")
|
||||
|
||||
# Enum declaration
|
||||
# For scoped and type-based enum, the type has to be removed
|
||||
@ -1572,13 +1624,19 @@ while line_idx < line_count:
|
||||
LINE):
|
||||
flags_name = re.search(r'\(\s*(\w+)\s*,\s*(\w+)\s*\)', LINE).group(1)
|
||||
flag_name = re.search(r'\(\s*(\w+)\s*,\s*(\w+)\s*\)', LINE).group(2)
|
||||
emkb = re.search(r'SIP_MONKEYPATCH_FLAGS_UNNEST\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)', LINE).group(1)
|
||||
emkf = re.search(r'SIP_MONKEYPATCH_FLAGS_UNNEST\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)', LINE).group(2)
|
||||
emkb = re.search(
|
||||
r'SIP_MONKEYPATCH_FLAGS_UNNEST\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)',
|
||||
LINE).group(1)
|
||||
emkf = re.search(
|
||||
r'SIP_MONKEYPATCH_FLAGS_UNNEST\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)',
|
||||
LINE).group(2)
|
||||
|
||||
if f"{emkb}.{emkf}" != f"{actual_class}.{flags_name}":
|
||||
output_python.append(f"{emkb}.{emkf} = {actual_class}.{flags_name}\n")
|
||||
output_python.append(
|
||||
f"{emkb}.{emkf} = {actual_class}.{flags_name}\n")
|
||||
|
||||
enum_monkey_patched_types.append([actual_class, flags_name, emkb, emkf])
|
||||
enum_monkey_patched_types.append(
|
||||
[actual_class, flags_name, emkb, emkf])
|
||||
|
||||
LINE = re.sub(r'\s*SIP_MONKEYPATCH_FLAGS_UNNEST\(.*?\)', '', LINE)
|
||||
|
||||
@ -1593,14 +1651,17 @@ while line_idx < line_count:
|
||||
enum_cpp_name = f"{actual_class}::{enum_qualname}" if actual_class else enum_qualname
|
||||
|
||||
if not isclass and enum_cpp_name not in ALLOWED_NON_CLASS_ENUMS:
|
||||
exit_with_error(f"Non class enum exposed to Python -- must be a enum class: {enum_cpp_name}")
|
||||
exit_with_error(
|
||||
f"Non class enum exposed to Python -- must be a enum class: {enum_cpp_name}")
|
||||
|
||||
oneliner = enum_match.group('oneliner')
|
||||
is_scope_based = bool(isclass)
|
||||
enum_decl = re.sub(r'\s*\bQ_DECL_DEPRECATED\b', '', enum_decl)
|
||||
|
||||
py_enum_type_match = re.search(r'SIP_ENUM_BASETYPE\(\s*(.*?)\s*\)', LINE)
|
||||
py_enum_type = py_enum_type_match.group(1) if py_enum_type_match else None
|
||||
py_enum_type_match = re.search(r'SIP_ENUM_BASETYPE\(\s*(.*?)\s*\)',
|
||||
LINE)
|
||||
py_enum_type = py_enum_type_match.group(
|
||||
1) if py_enum_type_match else None
|
||||
|
||||
if py_enum_type == "IntFlag":
|
||||
enum_intflag_types.append(enum_cpp_name)
|
||||
@ -1610,7 +1671,8 @@ while line_idx < line_count:
|
||||
if is_qt6:
|
||||
enum_decl += f" /BaseType={py_enum_type or 'IntEnum'}/"
|
||||
elif enum_type:
|
||||
exit_with_error(f"Unhandled enum type {enum_type} for {enum_cpp_name}")
|
||||
exit_with_error(
|
||||
f"Unhandled enum type {enum_type} for {enum_cpp_name}")
|
||||
elif isclass:
|
||||
enum_class_non_int_types.append(f"{actual_class}.{enum_qualname}")
|
||||
elif is_qt6:
|
||||
@ -1624,7 +1686,8 @@ while line_idx < line_count:
|
||||
_match = None
|
||||
if is_scope_based:
|
||||
_match = re.search(
|
||||
r'SIP_MONKEYPATCH_SCOPEENUM(_UNNEST)?(:?\(\s*(?P<emkb>\w+)\s*,\s*(?P<emkf>\w+)\s*\))?', LINE)
|
||||
r'SIP_MONKEYPATCH_SCOPEENUM(_UNNEST)?(:?\(\s*(?P<emkb>\w+)\s*,\s*(?P<emkf>\w+)\s*\))?',
|
||||
LINE)
|
||||
monkeypatch = is_scope_based and _match
|
||||
enum_mk_base = _match.group('emkb') if _match else ''
|
||||
|
||||
@ -1633,9 +1696,11 @@ while line_idx < line_count:
|
||||
enum_old_name = _match.group('emkf')
|
||||
if actual_class:
|
||||
if f"{enum_mk_base}.{enum_old_name}" != f"{actual_class}.{enum_qualname}":
|
||||
output_python.append(f"{enum_mk_base}.{enum_old_name} = {actual_class}.{enum_qualname}\n")
|
||||
output_python.append(
|
||||
f"{enum_mk_base}.{enum_old_name} = {actual_class}.{enum_qualname}\n")
|
||||
else:
|
||||
output_python.append(f"{enum_mk_base}.{enum_old_name} = {enum_qualname}\n")
|
||||
output_python.append(
|
||||
f"{enum_mk_base}.{enum_old_name} = {enum_qualname}\n")
|
||||
|
||||
if re.search(r'\{((\s*\w+)(\s*=\s*[\w\s<|]+.*?)?(,?))+\s*}', LINE):
|
||||
if '=' in LINE:
|
||||
@ -1645,7 +1710,8 @@ while line_idx < line_count:
|
||||
else:
|
||||
LINE = read_line()
|
||||
if not re.match(r'^\s*\{\s*$', LINE):
|
||||
exit_with_error('Unexpected content: enum should be followed by {')
|
||||
exit_with_error(
|
||||
'Unexpected content: enum should be followed by {')
|
||||
write_output("ENU2", f"{LINE}\n")
|
||||
|
||||
if is_scope_based:
|
||||
@ -1659,7 +1725,8 @@ while line_idx < line_count:
|
||||
continue
|
||||
if re.search(r'};', LINE):
|
||||
break
|
||||
if re.match(r'^\s*\w+\s*\|', LINE): # multi line declaration as sum of enums
|
||||
if re.match(r'^\s*\w+\s*\|',
|
||||
LINE): # multi line declaration as sum of enums
|
||||
continue
|
||||
|
||||
enum_match = re.match(
|
||||
@ -1667,20 +1734,32 @@ while line_idx < line_count:
|
||||
LINE)
|
||||
|
||||
enum_decl = f"{enum_match.group(1) or ''}{enum_match.group(3) or ''}{enum_match.group('optional_comma') or ''}" if enum_match else LINE
|
||||
enum_member = enum_match.group('em') or '' if enum_match else ''
|
||||
value_comment = enum_match.group('co') or '' if enum_match else ''
|
||||
compat_name = enum_match.group('compat') or enum_member if enum_match else ''
|
||||
enum_value = enum_match.group('enum_value') or '' if enum_match else ''
|
||||
enum_member = enum_match.group(
|
||||
'em') or '' if enum_match else ''
|
||||
value_comment = enum_match.group(
|
||||
'co') or '' if enum_match else ''
|
||||
compat_name = enum_match.group(
|
||||
'compat') or enum_member if enum_match else ''
|
||||
enum_value = enum_match.group(
|
||||
'enum_value') or '' if enum_match else ''
|
||||
|
||||
value_comment = value_comment.replace('::', '.').replace('"', '\\"')
|
||||
value_comment = re.sub(r'\\since .*?([\d.]+)', r'\\n.. versionadded:: \1\\n', value_comment, flags=re.I)
|
||||
value_comment = re.sub(r'\\deprecated (.*)', r'\\n.. deprecated:: \1\\n', value_comment, flags=re.I)
|
||||
value_comment = value_comment.replace('::', '.').replace('"',
|
||||
'\\"')
|
||||
value_comment = re.sub(r'\\since .*?([\d.]+)',
|
||||
r'\\n.. versionadded:: \1\\n',
|
||||
value_comment, flags=re.I)
|
||||
value_comment = re.sub(r'\\deprecated (.*)',
|
||||
r'\\n.. deprecated:: \1\\n',
|
||||
value_comment, flags=re.I)
|
||||
value_comment = re.sub(r'^\\n+', '', value_comment)
|
||||
value_comment = re.sub(r'\\n+$', '', value_comment)
|
||||
|
||||
dbg_info(f"is_scope_based:{is_scope_based} enum_mk_base:{enum_mk_base} monkeypatch:{monkeypatch}")
|
||||
dbg_info(
|
||||
f"is_scope_based:{is_scope_based} enum_mk_base:{enum_mk_base} monkeypatch:{monkeypatch}")
|
||||
|
||||
if enum_value and (re.search(r'.*<<.*', enum_value) or re.search(r'.*0x0.*', enum_value)):
|
||||
if enum_value and (
|
||||
re.search(r'.*<<.*', enum_value) or re.search(r'.*0x0.*',
|
||||
enum_value)):
|
||||
if f"{actual_class}::{enum_qualname}" not in enum_intflag_types:
|
||||
exit_with_error(
|
||||
f"{actual_class}::{enum_qualname} is a flags type, but was not declared with IntFlag type. Add 'SIP_ENUM_BASETYPE(IntFlag)' to the enum class declaration line")
|
||||
@ -1693,21 +1772,27 @@ while line_idx < line_count:
|
||||
if enum_old_name and compat_name != enum_member:
|
||||
output_python.append(
|
||||
f"{enum_mk_base}.{enum_old_name}.{compat_name} = {actual_class}.{enum_qualname}.{enum_member}\n")
|
||||
output_python.append(f"{enum_mk_base}.{compat_name}.is_monkey_patched = True\n")
|
||||
output_python.append(f"{enum_mk_base}.{compat_name}.__doc__ = \"{value_comment}\"\n")
|
||||
output_python.append(
|
||||
f"{enum_mk_base}.{compat_name}.is_monkey_patched = True\n")
|
||||
output_python.append(
|
||||
f"{enum_mk_base}.{compat_name}.__doc__ = \"{value_comment}\"\n")
|
||||
enum_members_doc.append(
|
||||
f"'* ``{compat_name}``: ' + {actual_class}.{enum_qualname}.{enum_member}.__doc__")
|
||||
else:
|
||||
output_python.append(f"{enum_mk_base}.{compat_name} = {enum_qualname}.{enum_member}\n")
|
||||
output_python.append(f"{enum_mk_base}.{compat_name}.is_monkey_patched = True\n")
|
||||
output_python.append(f"{enum_mk_base}.{compat_name}.__doc__ = \"{value_comment}\"\n")
|
||||
output_python.append(
|
||||
f"{enum_mk_base}.{compat_name} = {enum_qualname}.{enum_member}\n")
|
||||
output_python.append(
|
||||
f"{enum_mk_base}.{compat_name}.is_monkey_patched = True\n")
|
||||
output_python.append(
|
||||
f"{enum_mk_base}.{compat_name}.__doc__ = \"{value_comment}\"\n")
|
||||
enum_members_doc.append(
|
||||
f"'* ``{compat_name}``: ' + {enum_qualname}.{enum_member}.__doc__")
|
||||
else:
|
||||
if monkeypatch:
|
||||
output_python.append(
|
||||
f"{actual_class}.{compat_name} = {actual_class}.{enum_qualname}.{enum_member}\n")
|
||||
output_python.append(f"{actual_class}.{compat_name}.is_monkey_patched = True\n")
|
||||
output_python.append(
|
||||
f"{actual_class}.{compat_name}.is_monkey_patched = True\n")
|
||||
if actual_class:
|
||||
complete_class_path = '.'.join(classname)
|
||||
output_python.append(
|
||||
@ -1715,7 +1800,8 @@ while line_idx < line_count:
|
||||
enum_members_doc.append(
|
||||
f"'* ``{compat_name}``: ' + {actual_class}.{enum_qualname}.{enum_member}.__doc__")
|
||||
else:
|
||||
output_python.append(f"{enum_qualname}.{compat_name}.__doc__ = \"{value_comment}\"\n")
|
||||
output_python.append(
|
||||
f"{enum_qualname}.{compat_name}.__doc__ = \"{value_comment}\"\n")
|
||||
enum_members_doc.append(
|
||||
f"'* ``{compat_name}``: ' + {enum_qualname}.{enum_member}.__doc__")
|
||||
|
||||
@ -1737,10 +1823,12 @@ while line_idx < line_count:
|
||||
comment = comment.replace('\n', '\\n').replace('"', '\\"')
|
||||
|
||||
if actual_class:
|
||||
output_python.append(f'{actual_class}.{enum_qualname}.__doc__ = "{comment}\\n\\n" + ' +
|
||||
output_python.append(
|
||||
f'{actual_class}.{enum_qualname}.__doc__ = "{comment}\\n\\n" + ' +
|
||||
" + '\\n' + ".join(enum_members_doc) + '\n# --\n')
|
||||
else:
|
||||
output_python.append(f'{enum_qualname}.__doc__ = \'{comment}\\n\\n\' + ' +
|
||||
output_python.append(
|
||||
f'{enum_qualname}.__doc__ = \'{comment}\\n\\n\' + ' +
|
||||
" + '\\n' + ".join(enum_members_doc) + '\n# --\n')
|
||||
|
||||
# enums don't have Docstring apparently
|
||||
@ -1749,7 +1837,8 @@ while line_idx < line_count:
|
||||
|
||||
# Check for invalid use of doxygen command
|
||||
if re.search(r'.*//!<', LINE):
|
||||
exit_with_error('"\\!<" doxygen command must only be used for enum documentation')
|
||||
exit_with_error(
|
||||
'"\\!<" doxygen command must only be used for enum documentation')
|
||||
|
||||
# Handle override, final, and make private keywords
|
||||
if re.search(r'\boverride\b', LINE):
|
||||
@ -1763,8 +1852,11 @@ while line_idx < line_count:
|
||||
LINE = re.sub(r'^(\s*)Q_INVOKABLE ', r'\1', LINE)
|
||||
|
||||
# Keyword fixes
|
||||
LINE = re.sub(r'^(\s*template\s*<)(?:class|typename) (\w+>)(.*)$', r'\1\2\3', LINE)
|
||||
LINE = re.sub(r'^(\s*template\s*<)(?:class|typename) (\w+) *, *(?:class|typename) (\w+>)(.*)$', r'\1\2,\3\4',
|
||||
LINE = re.sub(r'^(\s*template\s*<)(?:class|typename) (\w+>)(.*)$',
|
||||
r'\1\2\3', LINE)
|
||||
LINE = re.sub(
|
||||
r'^(\s*template\s*<)(?:class|typename) (\w+) *, *(?:class|typename) (\w+>)(.*)$',
|
||||
r'\1\2,\3\4',
|
||||
LINE)
|
||||
LINE = re.sub(
|
||||
r'^(\s*template\s*<)(?:class|typename) (\w+) *, *(?:class|typename) (\w+) *, *(?:class|typename) (\w+>)(.*)$',
|
||||
@ -1787,14 +1879,16 @@ while line_idx < line_count:
|
||||
LINE = re.sub(r'\b\w+_EXPORT\s+', '', LINE)
|
||||
|
||||
# Skip non-method member declaration in non-public sections
|
||||
if not sip_run and access[-1] != PUBLIC and detect_non_method_member(LINE):
|
||||
if not sip_run and access[
|
||||
-1] != Visibility.Public and detect_non_method_member(LINE):
|
||||
dbg_info("skip non-method member declaration in non-public sections")
|
||||
continue
|
||||
|
||||
# Remove static const value assignment
|
||||
# https://regex101.com/r/DyWkgn/6
|
||||
if re.search(r'^\s*const static \w+', LINE):
|
||||
exit_with_error(f"const static should be written static const in {classname[-1]}")
|
||||
exit_with_error(
|
||||
f"const static should be written static const in {classname[-1]}")
|
||||
|
||||
# TODO needs fixing!!
|
||||
# original perl regex was:
|
||||
@ -1816,7 +1910,7 @@ while line_idx < line_count:
|
||||
|
||||
# Remove struct member assignment
|
||||
# https://regex101.com/r/OUwV75/1
|
||||
if not sip_run and access[-1] == PUBLIC:
|
||||
if not sip_run and access[-1] == Visibility.Public:
|
||||
# original perl regex: ^(\s*\w+[\w<> *&:,]* \*?\w+) = ([\-\w\:\.]+(< *\w+( \*)? *>)?)+(\([^()]*\))?\s*;
|
||||
# dbg_info(f"attempt struct member assignment '{LINE}'")
|
||||
|
||||
@ -1862,26 +1956,30 @@ while line_idx < line_count:
|
||||
(?:\/\/.*)? # Optional single-line comment
|
||||
$ # End of the line
|
||||
'''
|
||||
regex_verbose = re.compile(python_regex_verbose, re.VERBOSE | re.MULTILINE)
|
||||
regex_verbose = re.compile(python_regex_verbose,
|
||||
re.VERBOSE | re.MULTILINE)
|
||||
match = regex_verbose.match(LINE)
|
||||
if match:
|
||||
dbg_info(f"remove struct member assignment '={match.group(2)}'")
|
||||
LINE = f"{match.group(1)};"
|
||||
|
||||
# Catch Q_DECLARE_FLAGS
|
||||
match = re.search(r'^(\s*)Q_DECLARE_FLAGS\(\s*(.*?)\s*,\s*(.*?)\s*\)\s*$', LINE)
|
||||
match = re.search(r'^(\s*)Q_DECLARE_FLAGS\(\s*(.*?)\s*,\s*(.*?)\s*\)\s*$',
|
||||
LINE)
|
||||
if match:
|
||||
actual_class = f"{classname[-1]}::" if len(classname) >= 0 else ''
|
||||
dbg_info(f"Declare flags: {actual_class}")
|
||||
LINE = f"{match.group(1)}typedef QFlags<{actual_class}{match.group(3)}> {match.group(2)};\n"
|
||||
qflag_hash[f"{actual_class}{match.group(2)}"] = f"{actual_class}{match.group(3)}"
|
||||
qflag_hash[
|
||||
f"{actual_class}{match.group(2)}"] = f"{actual_class}{match.group(3)}"
|
||||
|
||||
if f"{actual_class}{match.group(3)}" not in enum_intflag_types:
|
||||
exit_with_error(
|
||||
f"{actual_class}{match.group(3)} is a flags type, but was not declared with IntFlag type. Add 'SIP_ENUM_BASETYPE(IntFlag)' to the enum class declaration line")
|
||||
|
||||
# Catch Q_DECLARE_OPERATORS_FOR_FLAGS
|
||||
match = re.search(r'^(\s*)Q_DECLARE_OPERATORS_FOR_FLAGS\(\s*(.*?)\s*\)\s*$', LINE)
|
||||
match = re.search(
|
||||
r'^(\s*)Q_DECLARE_OPERATORS_FOR_FLAGS\(\s*(.*?)\s*\)\s*$', LINE)
|
||||
if match:
|
||||
flags = match.group(2)
|
||||
flag = qflag_hash.get(flags)
|
||||
@ -1899,7 +1997,8 @@ while line_idx < line_count:
|
||||
output_python.append(
|
||||
"from enum import Enum\n\n\ndef _force_int(v): return int(v.value) if isinstance(v, Enum) else v\n\n\n")
|
||||
has_pushed_force_int = True
|
||||
output_python.append(f"{py_flag}.__bool__ = lambda flag: bool(_force_int(flag))\n")
|
||||
output_python.append(
|
||||
f"{py_flag}.__bool__ = lambda flag: bool(_force_int(flag))\n")
|
||||
output_python.append(
|
||||
f"{py_flag}.__eq__ = lambda flag1, flag2: _force_int(flag1) == _force_int(flag2)\n")
|
||||
output_python.append(
|
||||
@ -1924,28 +2023,36 @@ while line_idx < line_count:
|
||||
if multiline_definition != MULTILINE_NO:
|
||||
rolling_line = LINE
|
||||
rolling_line_idx = line_idx
|
||||
dbg_info("handle multiline definition to add virtual keyword or making private on opening line")
|
||||
while not re.match(r'^[^()]*\(([^()]*\([^()]*\)[^()]*)*[^()]*$', rolling_line):
|
||||
dbg_info(
|
||||
"handle multiline definition to add virtual keyword or making private on opening line")
|
||||
while not re.match(r'^[^()]*\(([^()]*\([^()]*\)[^()]*)*[^()]*$',
|
||||
rolling_line):
|
||||
rolling_line_idx -= 1
|
||||
rolling_line = input_lines[rolling_line_idx]
|
||||
if rolling_line_idx < 0:
|
||||
exit_with_error('could not reach opening definition')
|
||||
dbg_info(f'rolled back to {rolling_line_idx}: {rolling_line}')
|
||||
|
||||
if is_override_or_make_private == PREPEND_CODE_VIRTUAL and not re.match(r'^(\s*)virtual\b(.*)$',
|
||||
if is_override_or_make_private == PREPEND_CODE_VIRTUAL and not re.match(
|
||||
r'^(\s*)virtual\b(.*)$',
|
||||
rolling_line):
|
||||
idx = rolling_line_idx - line_idx + 1
|
||||
output[idx] = fix_annotations(re.sub(r'^(\s*?)\b(.*)$', r'\1 virtual \2\n', rolling_line))
|
||||
output[idx] = fix_annotations(
|
||||
re.sub(r'^(\s*?)\b(.*)$', r'\1 virtual \2\n',
|
||||
rolling_line))
|
||||
elif is_override_or_make_private == PREPEND_CODE_MAKE_PRIVATE:
|
||||
dbg_info("prepending private access")
|
||||
idx = rolling_line_idx - line_idx
|
||||
private_access = re.sub(r'(protected|public)', 'private', last_access_section_line)
|
||||
private_access = re.sub(r'(protected|public)', 'private',
|
||||
last_access_section_line)
|
||||
output.insert(idx + 1, private_access + "\n")
|
||||
output[idx + 1] = fix_annotations(rolling_line) + "\n"
|
||||
elif is_override_or_make_private == PREPEND_CODE_MAKE_PRIVATE:
|
||||
dbg_info("prepending private access")
|
||||
LINE = re.sub(r'(protected|public)', 'private', last_access_section_line) + "\n" + LINE + "\n"
|
||||
elif is_override_or_make_private == PREPEND_CODE_VIRTUAL and not re.match(r'^(\s*)virtual\b(.*)$', LINE):
|
||||
LINE = re.sub(r'(protected|public)', 'private',
|
||||
last_access_section_line) + "\n" + LINE + "\n"
|
||||
elif is_override_or_make_private == PREPEND_CODE_VIRTUAL and not re.match(
|
||||
r'^(\s*)virtual\b(.*)$', LINE):
|
||||
# SIP often requires the virtual keyword to be present, or it chokes on covariant return types
|
||||
# in overridden methods
|
||||
dbg_info('adding virtual keyword for overridden method')
|
||||
@ -1965,15 +2072,18 @@ while line_idx < line_count:
|
||||
match = re.match(pattern, LINE)
|
||||
if match:
|
||||
return_type_candidate = match.group(1)
|
||||
if not re.search(r'(void|SIP_PYOBJECT|operator|return|QFlag)', return_type_candidate):
|
||||
if not re.search(r'(void|SIP_PYOBJECT|operator|return|QFlag)',
|
||||
return_type_candidate):
|
||||
# replace :: with . (changes c++ style namespace/class directives to Python style)
|
||||
return_type = return_type_candidate.replace('::', '.')
|
||||
# replace with builtin Python types
|
||||
return_type = re.sub(r'\bdouble\b', 'float', return_type)
|
||||
return_type = re.sub(r'\bQString\b', 'str', return_type)
|
||||
return_type = re.sub(r'\bQStringList\b', 'list of str', return_type)
|
||||
return_type = re.sub(r'\bQStringList\b', 'list of str',
|
||||
return_type)
|
||||
|
||||
list_match = re.match(r'^(?:QList|QVector)<\s*(.*?)[\s*]*>$', return_type)
|
||||
list_match = re.match(r'^(?:QList|QVector)<\s*(.*?)[\s*]*>$',
|
||||
return_type)
|
||||
if list_match:
|
||||
return_type = f"list of {list_match.group(1)}"
|
||||
|
||||
@ -1992,7 +2102,8 @@ while line_idx < line_count:
|
||||
LINE = re.sub(r'^(\s*struct )\w+_EXPORT (.+)$', r'\1\2', LINE)
|
||||
|
||||
# Skip comments
|
||||
if re.match(r'^\s*typedef\s+\w+\s*<\s*\w+\s*>\s+\w+\s+.*SIP_DOC_TEMPLATE', LINE):
|
||||
if re.match(r'^\s*typedef\s+\w+\s*<\s*\w+\s*>\s+\w+\s+.*SIP_DOC_TEMPLATE',
|
||||
LINE):
|
||||
# support Docstring for template based classes in SIP 4.19.7+
|
||||
comment_template_docstring = True
|
||||
elif (multiline_definition == MULTILINE_NO and
|
||||
@ -2022,7 +2133,8 @@ while line_idx < line_count:
|
||||
|
||||
# MISSING
|
||||
# handle enum/flags QgsSettingsEntryEnumFlag
|
||||
match = re.match(r'^(\s*)const QgsSettingsEntryEnumFlag<(.*)> (.+);$', LINE)
|
||||
match = re.match(r'^(\s*)const QgsSettingsEntryEnumFlag<(.*)> (.+);$',
|
||||
LINE)
|
||||
if match:
|
||||
indent, enum_type, var_name = match.groups()
|
||||
|
||||
@ -2047,10 +2159,13 @@ while line_idx < line_count:
|
||||
|
||||
# append to class map file
|
||||
if args.class_map and actual_class:
|
||||
match = re.match(r'^ *(const |virtual |static )* *[\w:]+ +\*?(?P<method>\w+)\(.*$', LINE)
|
||||
match = re.match(
|
||||
r'^ *(const |virtual |static )* *[\w:]+ +\*?(?P<method>\w+)\(.*$',
|
||||
LINE)
|
||||
if match:
|
||||
with open(args.class_map, 'a') as f:
|
||||
f.write(f"{'.'.join(classname)}.{match.group('method')}: {headerfile}#L{line_idx}\n")
|
||||
f.write(
|
||||
f"{'.'.join(classname)}.{match.group('method')}: {headerfile}#L{line_idx}\n")
|
||||
|
||||
if python_signature:
|
||||
write_output("PSI", f"{python_signature}\n")
|
||||
@ -2061,10 +2176,13 @@ while line_idx < line_count:
|
||||
# https://regex101.com/r/DN01iM/4
|
||||
# TODO - original regex is incompatible with python -- it was:
|
||||
# ^([^()]+(\((?:[^()]++|(?1))*\)))*[^()]*\)([^()](throw\([^()]+\))?)*$:
|
||||
if re.match(r'^([^()]+(\((?:[^()]|\([^()]*\))*\)))*[^()]*\)([^()](throw\([^()]+\))?)*', LINE):
|
||||
if re.match(
|
||||
r'^([^()]+(\((?:[^()]|\([^()]*\))*\)))*[^()]*\)([^()](throw\([^()]+\))?)*',
|
||||
LINE):
|
||||
dbg_info("ending multiline")
|
||||
# remove potential following body
|
||||
if multiline_definition != MULTILINE_CONDITIONAL_STATEMENT and not re.search(r'(\{.*}|;)\s*(//.*)?$',
|
||||
if multiline_definition != MULTILINE_CONDITIONAL_STATEMENT and not re.search(
|
||||
r'(\{.*}|;)\s*(//.*)?$',
|
||||
LINE):
|
||||
dbg_info("remove following body of multiline def")
|
||||
last_line = LINE
|
||||
@ -2111,14 +2229,17 @@ while line_idx < line_count:
|
||||
waiting_for_return_to_end = False
|
||||
|
||||
for comment_line in comment_lines:
|
||||
if ('versionadded:' in comment_line or 'deprecated:' in comment_line) and out_params:
|
||||
if (
|
||||
'versionadded:' in comment_line or 'deprecated:' in comment_line) and out_params:
|
||||
dbg_info('out style parameters remain to flush!')
|
||||
# member has /Out/ parameters, but no return type, so flush out out_params docs now
|
||||
first_out_param = out_params.pop(0)
|
||||
write_output("CM7", f"{doc_prepend}:return: - {first_out_param}\n")
|
||||
write_output("CM7",
|
||||
f"{doc_prepend}:return: - {first_out_param}\n")
|
||||
|
||||
for out_param in out_params:
|
||||
write_output("CM7", f"{doc_prepend} - {out_param}\n")
|
||||
write_output("CM7",
|
||||
f"{doc_prepend} - {out_param}\n")
|
||||
write_output("CM7", f"{doc_prepend}\n")
|
||||
out_params = []
|
||||
|
||||
@ -2127,8 +2248,12 @@ while line_idx < line_count:
|
||||
param_name = param_match.group(1)
|
||||
if param_name in skipped_params_out or param_name in skipped_params_remove:
|
||||
if param_name in skipped_params_out:
|
||||
comment_line = re.sub(r'^:param\s+(\w+):\s*(.*?)$', r'\1: \2', comment_line)
|
||||
comment_line = re.sub(r'(?:optional|if specified|if given),?\s*', '',
|
||||
comment_line = re.sub(
|
||||
r'^:param\s+(\w+):\s*(.*?)$', r'\1: \2',
|
||||
comment_line)
|
||||
comment_line = re.sub(
|
||||
r'(?:optional|if specified|if given),?\s*',
|
||||
'',
|
||||
comment_line)
|
||||
out_params.append(comment_line)
|
||||
skipping_param = 2
|
||||
@ -2148,10 +2273,12 @@ while line_idx < line_count:
|
||||
|
||||
if ':return:' in comment_line and out_params:
|
||||
waiting_for_return_to_end = True
|
||||
comment_line = comment_line.replace(':return:', ':return: -')
|
||||
comment_line = comment_line.replace(':return:',
|
||||
':return: -')
|
||||
write_output("CM2", f"{doc_prepend}{comment_line}\n")
|
||||
for out_param in out_params:
|
||||
write_output("CM7", f"{doc_prepend} - {out_param}\n")
|
||||
write_output("CM7",
|
||||
f"{doc_prepend} - {out_param}\n")
|
||||
out_params = []
|
||||
else:
|
||||
write_output("CM2", f"{doc_prepend}{comment_line}\n")
|
||||
|
Loading…
x
Reference in New Issue
Block a user