[Python] Enhance object API __init__ with typed keyword arguments (#8615)

This commit significantly improves the developer experience for the Python Object-Based API by overhauling the generated `__init__` method for `T`-suffixed classes.

Previously, `T` objects had to be instantiated with an empty constructor, and their fields had to be populated manually one by one. This was verbose and not idiomatic Python.

This change modifies the Python code generator (`GenInitialize`) to produce `__init__` methods that are:

1.  **Keyword-Argument-Friendly**: The constructor now accepts all table/struct fields as keyword arguments, allowing for concise, single-line object creation.

2.  **Fully Typed**: The signature of the `__init__` method is now annotated with Python type hints. This provides immediate benefits for static analysis tools (like Mypy) and IDEs, enabling better autocompletion and type checking.

3.  **Correctly Optional**: The generator now correctly wraps types in `Optional[...]` if their default value is `None`. This applies to strings, vectors, and other nullable fields, ensuring strict type safety.

The new approach remains **fully backward-compatible**, as all arguments have default values. Existing code that uses the empty constructor will continue to work without modification.

#### Example of a Generated `__init__`

**Before:**

```python
class KeyValueT(object):
    def __init__(self):
        self.key = None  # type: str
        self.value = None  # type: str
```

**After:**

```python
class KeyValueT(object):
    def __init__(self, key: Optional[str] = None, value: Optional[str] = None):
        self.key = key
        self.value = value
```

#### Example of User Code

**Before:**

```python
# Old, verbose way
kv = KeyValueT()
kv.key = "instrument"
kv.value = "EUR/USD"
```

**After:**

```python
# New, Pythonic way
kv = KeyValueT(key="instrument", value="EUR/USD")
```
This commit is contained in:
Łukasz Kurowski
2025-07-23 08:57:39 +02:00
committed by GitHub
parent ca73ff34b7
commit c526cb640b
31 changed files with 745 additions and 293 deletions

View File

@@ -119,7 +119,9 @@ def InParentNamespaceEnd(builder):
class InParentNamespaceT(object):
# InParentNamespaceT
def __init__(self):
def __init__(
self,
):
pass
@classmethod
@@ -184,7 +186,9 @@ def MonsterEnd(builder):
class MonsterT(object):
# MonsterT
def __init__(self):
def __init__(
self,
):
pass
@classmethod
@@ -243,9 +247,13 @@ def CreateTest(builder, a, b):
class TestT(object):
# TestT
def __init__(self):
self.a = 0 # type: int
self.b = 0 # type: int
def __init__(
self,
a = 0,
b = 0,
):
self.a = a # type: int
self.b = b # type: int
@classmethod
def InitFromBuf(cls, buf, pos):
@@ -319,8 +327,11 @@ def TestSimpleTableWithEnumEnd(builder):
class TestSimpleTableWithEnumT(object):
# TestSimpleTableWithEnumT
def __init__(self):
self.color = 2 # type: int
def __init__(
self,
color = 2,
):
self.color = color # type: int
@classmethod
def InitFromBuf(cls, buf, pos):
@@ -404,13 +415,21 @@ except:
class Vec3T(object):
# Vec3T
def __init__(self):
self.x = 0.0 # type: float
self.y = 0.0 # type: float
self.z = 0.0 # type: float
self.test1 = 0.0 # type: float
self.test2 = 0 # type: int
self.test3 = None # type: Optional[TestT]
def __init__(
self,
x = 0.0,
y = 0.0,
z = 0.0,
test1 = 0.0,
test2 = 0,
test3 = None,
):
self.x = x # type: float
self.y = y # type: float
self.z = z # type: float
self.test1 = test1 # type: float
self.test2 = test2 # type: int
self.test3 = test3 # type: Optional[TestT]
@classmethod
def InitFromBuf(cls, buf, pos):
@@ -472,9 +491,13 @@ def CreateAbility(builder, id, distance):
class AbilityT(object):
# AbilityT
def __init__(self):
self.id = 0 # type: int
self.distance = 0 # type: int
def __init__(
self,
id = 0,
distance = 0,
):
self.id = id # type: int
self.distance = distance # type: int
@classmethod
def InitFromBuf(cls, buf, pos):
@@ -554,10 +577,15 @@ except:
class StructOfStructsT(object):
# StructOfStructsT
def __init__(self):
self.a = None # type: Optional[AbilityT]
self.b = None # type: Optional[TestT]
self.c = None # type: Optional[AbilityT]
def __init__(
self,
a = None,
b = None,
c = None,
):
self.a = a # type: Optional[AbilityT]
self.b = b # type: Optional[TestT]
self.c = c # type: Optional[AbilityT]
@classmethod
def InitFromBuf(cls, buf, pos):
@@ -632,8 +660,11 @@ except:
class StructOfStructsOfStructsT(object):
# StructOfStructsOfStructsT
def __init__(self):
self.a = None # type: Optional[StructOfStructsT]
def __init__(
self,
a = None,
):
self.a = a # type: Optional[StructOfStructsT]
@classmethod
def InitFromBuf(cls, buf, pos):
@@ -727,10 +758,15 @@ def StatEnd(builder):
class StatT(object):
# StatT
def __init__(self):
self.id = None # type: Optional[str]
self.val = 0 # type: int
self.count = 0 # type: int
def __init__(
self,
id = None,
val = 0,
count = 0,
):
self.id = id # type: Optional[str]
self.val = val # type: int
self.count = count # type: int
@classmethod
def InitFromBuf(cls, buf, pos):
@@ -813,8 +849,11 @@ def ReferrableEnd(builder):
class ReferrableT(object):
# ReferrableT
def __init__(self):
self.id = 0 # type: int
def __init__(
self,
id = 0,
):
self.id = id # type: int
@classmethod
def InitFromBuf(cls, buf, pos):
@@ -1969,68 +2008,131 @@ except:
class MonsterT(object):
# MonsterT
def __init__(self):
self.pos = None # type: Optional[Vec3T]
self.mana = 150 # type: int
self.hp = 100 # type: int
self.name = None # type: Optional[str]
self.inventory = None # type: Optional[List[int]]
self.color = 8 # type: int
self.testType = 0 # type: int
self.test = None # type: Union[None, MonsterT, TestSimpleTableWithEnumT, MonsterT]
self.test4 = None # type: Optional[List[TestT]]
self.testarrayofstring = None # type: Optional[List[Optional[str]]]
self.testarrayoftables = None # type: Optional[List[MonsterT]]
self.enemy = None # type: Optional[MonsterT]
self.testnestedflatbuffer = None # type: Optional[List[int]]
self.testempty = None # type: Optional[StatT]
self.testbool = False # type: bool
self.testhashs32Fnv1 = 0 # type: int
self.testhashu32Fnv1 = 0 # type: int
self.testhashs64Fnv1 = 0 # type: int
self.testhashu64Fnv1 = 0 # type: int
self.testhashs32Fnv1a = 0 # type: int
self.testhashu32Fnv1a = 0 # type: int
self.testhashs64Fnv1a = 0 # type: int
self.testhashu64Fnv1a = 0 # type: int
self.testarrayofbools = None # type: Optional[List[bool]]
self.testf = 3.14159 # type: float
self.testf2 = 3.0 # type: float
self.testf3 = 0.0 # type: float
self.testarrayofstring2 = None # type: Optional[List[Optional[str]]]
self.testarrayofsortedstruct = None # type: Optional[List[AbilityT]]
self.flex = None # type: Optional[List[int]]
self.test5 = None # type: Optional[List[TestT]]
self.vectorOfLongs = None # type: Optional[List[int]]
self.vectorOfDoubles = None # type: Optional[List[float]]
self.parentNamespaceTest = None # type: Optional[InParentNamespaceT]
self.vectorOfReferrables = None # type: Optional[List[ReferrableT]]
self.singleWeakReference = 0 # type: int
self.vectorOfWeakReferences = None # type: Optional[List[int]]
self.vectorOfStrongReferrables = None # type: Optional[List[ReferrableT]]
self.coOwningReference = 0 # type: int
self.vectorOfCoOwningReferences = None # type: Optional[List[int]]
self.nonOwningReference = 0 # type: int
self.vectorOfNonOwningReferences = None # type: Optional[List[int]]
self.anyUniqueType = 0 # type: int
self.anyUnique = None # type: Union[None, MonsterT, TestSimpleTableWithEnumT, MonsterT]
self.anyAmbiguousType = 0 # type: int
self.anyAmbiguous = None # type: Union[None, MonsterT, MonsterT, MonsterT]
self.vectorOfEnums = None # type: Optional[List[int]]
self.signedEnum = -1 # type: int
self.testrequirednestedflatbuffer = None # type: Optional[List[int]]
self.scalarKeySortedTables = None # type: Optional[List[StatT]]
self.nativeInline = None # type: Optional[TestT]
self.longEnumNonEnumDefault = 0 # type: int
self.longEnumNormalDefault = 2 # type: int
self.nanDefault = float('nan') # type: float
self.infDefault = float('inf') # type: float
self.positiveInfDefault = float('inf') # type: float
self.infinityDefault = float('inf') # type: float
self.positiveInfinityDefault = float('inf') # type: float
self.negativeInfDefault = float('-inf') # type: float
self.negativeInfinityDefault = float('-inf') # type: float
self.doubleInfDefault = float('inf') # type: float
def __init__(
self,
pos = None,
mana = 150,
hp = 100,
name = None,
inventory = None,
color = 8,
testType = 0,
test = None,
test4 = None,
testarrayofstring = None,
testarrayoftables = None,
enemy = None,
testnestedflatbuffer = None,
testempty = None,
testbool = False,
testhashs32Fnv1 = 0,
testhashu32Fnv1 = 0,
testhashs64Fnv1 = 0,
testhashu64Fnv1 = 0,
testhashs32Fnv1a = 0,
testhashu32Fnv1a = 0,
testhashs64Fnv1a = 0,
testhashu64Fnv1a = 0,
testarrayofbools = None,
testf = 3.14159,
testf2 = 3.0,
testf3 = 0.0,
testarrayofstring2 = None,
testarrayofsortedstruct = None,
flex = None,
test5 = None,
vectorOfLongs = None,
vectorOfDoubles = None,
parentNamespaceTest = None,
vectorOfReferrables = None,
singleWeakReference = 0,
vectorOfWeakReferences = None,
vectorOfStrongReferrables = None,
coOwningReference = 0,
vectorOfCoOwningReferences = None,
nonOwningReference = 0,
vectorOfNonOwningReferences = None,
anyUniqueType = 0,
anyUnique = None,
anyAmbiguousType = 0,
anyAmbiguous = None,
vectorOfEnums = None,
signedEnum = -1,
testrequirednestedflatbuffer = None,
scalarKeySortedTables = None,
nativeInline = None,
longEnumNonEnumDefault = 0,
longEnumNormalDefault = 2,
nanDefault = float('nan'),
infDefault = float('inf'),
positiveInfDefault = float('inf'),
infinityDefault = float('inf'),
positiveInfinityDefault = float('inf'),
negativeInfDefault = float('-inf'),
negativeInfinityDefault = float('-inf'),
doubleInfDefault = float('inf'),
):
self.pos = pos # type: Optional[Vec3T]
self.mana = mana # type: int
self.hp = hp # type: int
self.name = name # type: Optional[str]
self.inventory = inventory # type: Optional[List[int]]
self.color = color # type: int
self.testType = testType # type: int
self.test = test # type: Union[None, 'MonsterT', 'TestSimpleTableWithEnumT', 'MonsterT']
self.test4 = test4 # type: Optional[List[TestT]]
self.testarrayofstring = testarrayofstring # type: Optional[List[Optional[str]]]
self.testarrayoftables = testarrayoftables # type: Optional[List[MonsterT]]
self.enemy = enemy # type: Optional[MonsterT]
self.testnestedflatbuffer = testnestedflatbuffer # type: Optional[List[int]]
self.testempty = testempty # type: Optional[StatT]
self.testbool = testbool # type: bool
self.testhashs32Fnv1 = testhashs32Fnv1 # type: int
self.testhashu32Fnv1 = testhashu32Fnv1 # type: int
self.testhashs64Fnv1 = testhashs64Fnv1 # type: int
self.testhashu64Fnv1 = testhashu64Fnv1 # type: int
self.testhashs32Fnv1a = testhashs32Fnv1a # type: int
self.testhashu32Fnv1a = testhashu32Fnv1a # type: int
self.testhashs64Fnv1a = testhashs64Fnv1a # type: int
self.testhashu64Fnv1a = testhashu64Fnv1a # type: int
self.testarrayofbools = testarrayofbools # type: Optional[List[bool]]
self.testf = testf # type: float
self.testf2 = testf2 # type: float
self.testf3 = testf3 # type: float
self.testarrayofstring2 = testarrayofstring2 # type: Optional[List[Optional[str]]]
self.testarrayofsortedstruct = testarrayofsortedstruct # type: Optional[List[AbilityT]]
self.flex = flex # type: Optional[List[int]]
self.test5 = test5 # type: Optional[List[TestT]]
self.vectorOfLongs = vectorOfLongs # type: Optional[List[int]]
self.vectorOfDoubles = vectorOfDoubles # type: Optional[List[float]]
self.parentNamespaceTest = parentNamespaceTest # type: Optional[InParentNamespaceT]
self.vectorOfReferrables = vectorOfReferrables # type: Optional[List[ReferrableT]]
self.singleWeakReference = singleWeakReference # type: int
self.vectorOfWeakReferences = vectorOfWeakReferences # type: Optional[List[int]]
self.vectorOfStrongReferrables = vectorOfStrongReferrables # type: Optional[List[ReferrableT]]
self.coOwningReference = coOwningReference # type: int
self.vectorOfCoOwningReferences = vectorOfCoOwningReferences # type: Optional[List[int]]
self.nonOwningReference = nonOwningReference # type: int
self.vectorOfNonOwningReferences = vectorOfNonOwningReferences # type: Optional[List[int]]
self.anyUniqueType = anyUniqueType # type: int
self.anyUnique = anyUnique # type: Union[None, 'MonsterT', 'TestSimpleTableWithEnumT', 'MonsterT']
self.anyAmbiguousType = anyAmbiguousType # type: int
self.anyAmbiguous = anyAmbiguous # type: Union[None, 'MonsterT', 'MonsterT', 'MonsterT']
self.vectorOfEnums = vectorOfEnums # type: Optional[List[int]]
self.signedEnum = signedEnum # type: int
self.testrequirednestedflatbuffer = testrequirednestedflatbuffer # type: Optional[List[int]]
self.scalarKeySortedTables = scalarKeySortedTables # type: Optional[List[StatT]]
self.nativeInline = nativeInline # type: Optional[TestT]
self.longEnumNonEnumDefault = longEnumNonEnumDefault # type: int
self.longEnumNormalDefault = longEnumNormalDefault # type: int
self.nanDefault = nanDefault # type: float
self.infDefault = infDefault # type: float
self.positiveInfDefault = positiveInfDefault # type: float
self.infinityDefault = infinityDefault # type: float
self.positiveInfinityDefault = positiveInfinityDefault # type: float
self.negativeInfDefault = negativeInfDefault # type: float
self.negativeInfinityDefault = negativeInfinityDefault # type: float
self.doubleInfDefault = doubleInfDefault # type: float
@classmethod
def InitFromBuf(cls, buf, pos):
@@ -2708,19 +2810,33 @@ except:
class TypeAliasesT(object):
# TypeAliasesT
def __init__(self):
self.i8 = 0 # type: int
self.u8 = 0 # type: int
self.i16 = 0 # type: int
self.u16 = 0 # type: int
self.i32 = 0 # type: int
self.u32 = 0 # type: int
self.i64 = 0 # type: int
self.u64 = 0 # type: int
self.f32 = 0.0 # type: float
self.f64 = 0.0 # type: float
self.v8 = None # type: Optional[List[int]]
self.vf64 = None # type: Optional[List[float]]
def __init__(
self,
i8 = 0,
u8 = 0,
i16 = 0,
u16 = 0,
i32 = 0,
u32 = 0,
i64 = 0,
u64 = 0,
f32 = 0.0,
f64 = 0.0,
v8 = None,
vf64 = None,
):
self.i8 = i8 # type: int
self.u8 = u8 # type: int
self.i16 = i16 # type: int
self.u16 = u16 # type: int
self.i32 = i32 # type: int
self.u32 = u32 # type: int
self.i64 = i64 # type: int
self.u64 = u64 # type: int
self.f32 = f32 # type: float
self.f64 = f64 # type: float
self.v8 = v8 # type: Optional[List[int]]
self.vf64 = vf64 # type: Optional[List[float]]
@classmethod
def InitFromBuf(cls, buf, pos):