Skip to content

Commit 3080a31

Browse files
committed
Support for Python dataclasses
1 parent af6c7f4 commit 3080a31

File tree

1 file changed

+62
-5
lines changed

1 file changed

+62
-5
lines changed

cli/python.go

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,10 @@ func pythonValueString(value interface{}) (string, error) {
4949
return pythonSetString(v)
5050
case *pythonArgumentsObject:
5151
return pythonArgumentsString(v)
52-
case *types.GenericClass:
52+
case *genericClass:
5353
return fmt.Sprintf("%s.%s", v.Module, v.Name), nil
54-
case *types.GenericObject:
55-
s, _ := pythonValueString(v.Class)
56-
return fmt.Sprintf("%s(?)", s), nil
54+
case *genericObject:
55+
return pythonGenericObjectString(v)
5756
default:
5857
return "", fmt.Errorf("unsupported Python value: %T", value)
5958
}
@@ -181,6 +180,42 @@ func pythonArgumentsString(a *pythonArgumentsObject) (string, error) {
181180
}
182181
}
183182

183+
b.WriteByte(')')
184+
return b.String(), nil
185+
}
186+
187+
func pythonGenericObjectString(o *genericObject) (string, error) {
188+
var b strings.Builder
189+
b.WriteString(o.class.Name)
190+
b.WriteByte('(')
191+
192+
for i, e := 0, o.dict.List.Front(); e != nil; i++ {
193+
if i > 0 {
194+
b.WriteString(", ")
195+
}
196+
entry := e.Value.(*types.OrderedDictEntry)
197+
198+
var keyStr string
199+
if s, ok := entry.Key.(string); ok {
200+
keyStr = s
201+
} else {
202+
var err error
203+
keyStr, err = pythonValueString(entry.Key)
204+
if err != nil {
205+
return "", err
206+
}
207+
}
208+
b.WriteString(kwargStyle.Render(keyStr + "="))
209+
210+
valueStr, err := pythonValueString(entry.Value)
211+
if err != nil {
212+
return "", err
213+
}
214+
b.WriteString(valueStr)
215+
216+
e = e.Next()
217+
}
218+
184219
b.WriteByte(')')
185220
return b.String(), nil
186221

@@ -191,8 +226,12 @@ func findPythonClass(module, name string) (interface{}, error) {
191226
if module == "dispatch.proto" && name == "Arguments" {
192227
return &pythonArgumentsClass{}, nil
193228
}
229+
// If a custom class is encountered, we don't have enough information
230+
// to be able to format it. In many cases though (e.g. dataclasses),
231+
// it's sufficient to collect and format the module/name of the class,
232+
// and then data that arrives through PyDictSettable interface.
194233
slog.Debug("parsing Python value", "module", module, "name", name)
195-
return types.NewGenericClass(module, name), nil
234+
return &genericClass{&types.GenericClass{Module: module, Name: name}}, nil
196235
}
197236

198237
type pythonArgumentsClass struct{}
@@ -224,3 +263,21 @@ func (a *pythonArgumentsObject) PyDictSet(key, value interface{}) error {
224263
}
225264
return nil
226265
}
266+
267+
type genericClass struct {
268+
*types.GenericClass
269+
}
270+
271+
func (c *genericClass) PyNew(args ...interface{}) (interface{}, error) {
272+
return &genericObject{c, types.NewOrderedDict()}, nil
273+
}
274+
275+
type genericObject struct {
276+
class *genericClass
277+
dict *types.OrderedDict
278+
}
279+
280+
func (o *genericObject) PyDictSet(key, value interface{}) error {
281+
o.dict.Set(key, value)
282+
return nil
283+
}

0 commit comments

Comments
 (0)