Skip to content

Conversation

@toaction
Copy link
Contributor

@toaction toaction commented Oct 14, 2025

fix #47

[require]feat: pydump support Python defined class: #44

@gemini-code-assist
Copy link

Summary of Changes

Hello @toaction, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the llpyg project by adding comprehensive support for Python class definitions. It extends the pydump tool to parse detailed class structures, including inheritance, methods, and properties. This extracted information is then utilized by the pygen tool to automatically generate corresponding Go structs, constructors, and methods, thereby bridging the gap between Python's object-oriented features and Go's type system for improved interoperability.

Highlights

  • Python Class Parsing: Introduced a new class.go file within _xtool/pydump to enable detailed parsing of Python class definitions, including their base classes, __init__ methods, instance methods, class methods, static methods, and properties.
  • Enhanced pydump Functionality: The pydump.go file has been updated to integrate the new class parsing logic, allowing the tool to extract comprehensive information about Python classes present in a module. The getSignature function was also refined to handle method signatures more robustly.
  • Extended Symbol Representation: The symbol/symbol.go file now includes new Go struct definitions (Class, Base, Property) to accurately represent the structure and metadata of Python classes, their inheritance, and their attributes.
  • Go Code Generation for Classes: A new genclass.go file in tool/pygen provides the core logic for generating Go code from the parsed Python class symbols. This includes creating Go structs, constructors, and methods (including getters/setters for properties) with appropriate //go:linkname and //llgo:link directives for interoperability.
  • Integrated Class Generation into pygen: The main pygen.go file has been modified to incorporate the new class generation process, ensuring that Python classes are automatically converted into their Go equivalents during the code generation phase.
  • New Test Case for Class Generation: A new test (TestGenClass) has been added to pygen_test.go, along with example Python (demo.py) and expected Go output (expect.go) files, to validate the correct parsing and generation of Go code for Python classes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for generating Go bindings for Python classes. The changes include parsing class structures in pydump and generating corresponding Go code in pygen.

The implementation is comprehensive, covering methods, properties, and inheritance. However, I've found a few critical issues related to potential panics from nil pointer dereferences, which should be addressed. I've also identified a bug in how property link names are generated. Additionally, there are some opportunities for code improvement regarding consistency and avoiding side effects.

Overall, this is a great feature addition. Please see my detailed comments for suggestions.

Comment on lines +40 to +42
base := &symbol.Base{
Name: c.GoString(baseObj.GetAttrString(c.Str("__name__")).CStr()),
Module: c.GoString(baseObj.GetAttrString(c.Str("__module__")).CStr()),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: Nil pointer dereference risk

GetAttrString() can return nil, which would cause a panic when calling .CStr(). This needs defensive error handling:

Suggested change
base := &symbol.Base{
Name: c.GoString(baseObj.GetAttrString(c.Str("__name__")).CStr()),
Module: c.GoString(baseObj.GetAttrString(c.Str("__module__")).CStr()),
nameObj := baseObj.GetAttrString(c.Str("__name__"))
if nameObj == nil {
return nil, fmt.Errorf("base class of %s missing __name__ attribute", name)
}
defer nameObj.DecRef()
moduleObj := baseObj.GetAttrString(c.Str("__module__"))
if moduleObj == nil {
return nil, fmt.Errorf("base class of %s missing __module__ attribute", name)
}
defer moduleObj.DecRef()
base := &symbol.Base{
Name: c.GoString(nameObj.CStr()),
Module: c.GoString(moduleObj.CStr()),

Additionally: Missing DecRef() calls cause memory leaks. Python C API objects need explicit reference counting management.


// get class parents
func parseBases(class *py.Object, name string) ([]*symbol.Base, error) {
basesObj := class.GetAttrString(c.Str("__bases__")) // tuple
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: Memory leak - missing DecRef()

All Python objects returned by GetAttrString() need their reference counts decremented:

Suggested change
basesObj := class.GetAttrString(c.Str("__bases__")) // tuple
basesObj := class.GetAttrString(c.Str("__bases__")) // tuple
if basesObj == nil {
return nil, fmt.Errorf("can't get __bases__ from %s", name)
}
defer basesObj.DecRef()

func (ctx *context) genProperties(pkg *gogen.Package, cls *symbol.Class) {
for _, property := range cls.Properties {
name := cls.Name + property.Name
if property.Getter != "" {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical bug: Missing dot separator in property name

This generates incorrect linkname directives like py.Dogage.__get__ instead of py.Dog.age.__get__. Missing dot causes runtime linking failures.

Suggested change
if property.Getter != "" {
name := cls.Name + "." + property.Name

Comment on lines +14 to +16
for _, cls := range classes {
if len(cls.Bases) == 0 {
log.Fatalf("Class %s has no base", cls.Name)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code quality: Use error return instead of log.Fatal

log.Fatalf terminates the entire program. In library code, return an error to let the caller decide how to handle it:

Suggested change
for _, cls := range classes {
if len(cls.Bases) == 0 {
log.Fatalf("Class %s has no base", cls.Name)
for _, cls := range classes {
if len(cls.Bases) == 0 {
return fmt.Errorf("class %s has no base", cls.Name)
}

This requires changing genClasses signature to return error.

Comment on lines +84 to +97
switch typeName {
case "classmethod":
val = val.GetAttrString(c.Str("__func__"))
if val == nil {
return nil, fmt.Errorf("can't get __func__ of %s", name)
}
sym, err := parseMethod(val, name, typeName, true)
if err != nil {
return nil, err
}
cls.ClassMethods = append(cls.ClassMethods, sym)
case "staticmethod":
val = val.GetAttrString(c.Str("__func__"))
if val == nil {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code quality: Code duplication for classmethod/staticmethod

Both cases duplicate the __func__ retrieval pattern. Extract to a helper:

func getFunc(obj *py.Object, name string) (*py.Object, error) {
    funcObj := obj.GetAttrString(c.Str("__func__"))
    if funcObj == nil {
        return nil, fmt.Errorf("can't get __func__ of %s", name)
    }
    return funcObj, nil
}

Then use: val, err := getFunc(val, name)

Comment on lines +139 to +143
getter := val.GetAttrString(c.Str("fget"))
if getter != nil {
// (self) -> value
property.Getter = "()"
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code quality: Property getter signature hardcoded

The getter signature is hardcoded to "()" without parsing the actual function signature, losing type information:

Suggested change
getter := val.GetAttrString(c.Str("fget"))
if getter != nil {
// (self) -> value
property.Getter = "()"
}
getter := val.GetAttrString(c.Str("fget"))
if getter != nil {
defer getter.DecRef()
sym := &symbol.Symbol{
Name: name,
Type: "property",
}
sig, err := getSignature(getter, sym, true)
if err != nil {
return nil, err
}
property.Getter = sig

objPtr *types.Pointer
ret *types.Tuple
py gogen.PkgRef
structs map[string]*types.Named
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code style: Inconsistent spacing

Use spaces consistently (tabs mixed with spaces):

Suggested change
structs map[string]*types.Named
structs map[string]*types.Named

@xgopilot
Copy link

xgopilot bot commented Oct 14, 2025

Code Review Summary

Good work adding Python class support! The implementation is well-structured with clear separation between parsing and generation. However, critical issues must be fixed before merge:

🔴 Must Fix

  • Memory leaks: Missing DecRef() calls throughout class.go cause Python object leaks
  • Nil pointer risks: Missing nil checks in _xtool/pydump/class.go:40-42 can cause crashes
  • Property name bug: genclass.go:78 missing dot separator breaks linkname generation

🟡 Should Fix

  • Replace log.Fatal with error returns in genclass.go:16
  • Implement proper getter signature parsing (not hardcoded "()")
  • Extract duplicate __func__ retrieval logic

✅ Strengths

  • Clear code organization
  • Good test coverage
  • Descriptive naming conventions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LLGo Bindings design for Python class

1 participant