Possible way to handle enums.

Named enums with 'Flags' in the name use `enum.IntFlag`, all other enums
use `enum.IntEnum`.  We have to bring the enum members into the containing
scope to match the original behaviour. Further, since these enums are typing
information only, and not actually in wx proper, we prevent them from appearing
to leak the the user by marking as non-exported (prepend '_' to the name), then
make a TypeAlias to the enum or an int.  This way type signatures still claim
to accept integers as appropriate.

I like this solution best, because we preserved the information about which
enum members are expected for method/function parameters, while not leaking
non-existant classes out of the type-stubs, and not complaining about using
integers.

There's still a few undefined but referenced enums (ex:
richtext.TextAttrDimensionFlags). These are most likely a union of some of
the other flags/enum types already defined, but the work to pull that information
from the C++ source is probably too much.
This commit is contained in:
lojack5
2023-10-17 21:34:31 -06:00
parent e14be4fbb4
commit e84d7abcba
2 changed files with 35 additions and 2 deletions

View File

@@ -79,7 +79,8 @@ header_pyi = """\
typing_imports = """\
from __future__ import annotations
from typing import Any, overload
from enum import IntEnum, IntFlag, auto
from typing import Any, overload, TypeAlias
"""
@@ -224,11 +225,42 @@ class PiWrapperGenerator(generators.WrapperGeneratorBase, FixWxPrefix):
assert isinstance(enum, extractors.EnumDef)
if enum.ignored or piIgnored(enum):
return
# These enum classes aren't actually accessible from the real wx
# module, o we need to prepend _. But we want to make a type alias to
# the non-prefixed name, so method signatures can reference it without
# any special code, and also allow bare ints as inputs.
if '@' in enum.name or not enum.name:
# Anonymous enum
enum_name = f"_enum_{enum.name.replace('@', '').strip()}"
alias = ''
else:
alias = self.fixWxPrefix(enum.name)
enum_name = f'_{alias}'
if 'Flags' in enum_name:
enum_type = 'IntFlag'
else:
enum_type = 'IntEnum'
# Create the enum definition
stream.write(f'\n{indent}class {enum_name}({enum_type}):\n')
for v in enum.items:
if v.ignored or piIgnored(v):
continue
name = v.pyName or v.name
stream.write('%s%s = 0\n' % (indent, name))
stream.write(f'{indent} {name} = auto()\n')
# Create the alias if needed
if alias:
stream.write(f'{indent}{alias}: TypeAlias = {enum_name} | int\n')
# And bring the enum members into global scope. We can't use
# enum.global_enum for this because:
# 1. It's only available on Python 3.11+
# 2. FixWxPrefix wouldn't be able to pick up the names, since it's
# detecting based on AST parsing, not runtime changes (which
# enum.global_enum performs).
for v in enum.items:
if v.ignored or piIgnored(v):
continue
name = v.pyName or v.name
stream.write(f'{indent}{name} = {enum_name}.{name}\n')
#-----------------------------------------------------------------------
def generateGlobalVar(self, globalVar, stream):

View File

@@ -100,6 +100,7 @@ class FixWxPrefix(object):
testName = name
if '(' in name:
testName = name[:name.find('(')]
testName = testName.split('.')[0]
if testName in FixWxPrefix._coreTopLevelNames:
return 'wx.'+name