pytago icon indicating copy to clipboard operation
pytago copied to clipboard

Optional type annotations

Open jtran opened this issue 3 years ago • 2 comments

Describe the bug Hi, I would like to transpile Python code with type annotations that include Optional.

Python code example

Here's an example using a parameter, but ideally it would work anywhere type annotations are allowed.

from typing import Optional

def main(x: Optional[int] = None):
    print("Hello world!")

Current behavior

Exception: [(<class 'pytago.go_ast.core.FuncLit'>, NotImplementedError(&ast.Ident { Name: "Optional" })), (<class 'pytago.go_ast.core.FuncType'>, NotImplementedError(&ast.Ident { Name: "Optional" }))]

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/username/pytago/pytago/go_ast/core.py", line 373, in _build_x_list
    result = getattr(x_type, method)(x_node, **kwargs)
  File "/Users/username/pytago/pytago/go_ast/core.py", line 893, in from_FunctionDef
    rhs = build_expr_list([node])
  File "/Users/username/pytago/pytago/go_ast/core.py", line 417, in build_expr_list
    return _build_x_list(_EXPR_TYPES, "Expr", nodes, **kwargs)
  File "/Users/username/pytago/pytago/go_ast/core.py", line 391, in _build_x_list
    raise ValueError(f"No {x_name} type in {x_types} with {method}: "
ValueError: No Expr type in [<class 'pytago.go_ast.core.Expr'>, <class 'pytago.go_ast.core.ArrayType'>, <class 'pytago.go_ast.core.BadExpr'>, <class 'pytago.go_ast.core.BasicLit'>, <class 'pytago.go_ast.core.BinaryExpr'>, <class 'pytago.go_ast.core.Ident'>, <class 'pytago.go_ast.core.CallExpr'>, <class 'pytago.go_ast.core.ChanType'>, <class 'pytago.go_ast.core.CompositeLit'>, <class 'pytago.go_ast.core.Ellipsis'>, <class 'pytago.go_ast.core.FieldList'>, <class 'pytago.go_ast.core.FuncType'>, <class 'pytago.go_ast.core.FuncLit'>, <class 'pytago.go_ast.core.IndexExpr'>, <class 'pytago.go_ast.core.InterfaceType'>, <class 'pytago.go_ast.core.KeyValueExpr'>, <class 'pytago.go_ast.core.MapType'>, <class 'pytago.go_ast.core.ParenExpr'>, <class 'pytago.go_ast.core.SelectorExpr'>, <class 'pytago.go_ast.core.SliceExpr'>, <class 'pytago.go_ast.core.StarExpr'>, <class 'pytago.go_ast.core.StructType'>, <class 'pytago.go_ast.core.TypeAssertExpr'>, <class 'pytago.go_ast.core.UnaryExpr'>, <class 'pytago.go_ast.core.ValueSpec'>] with from_FunctionDef:
\```
def main(x: Optional[int]=None):
    print('Hello world!')
\```

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/username/.pyenv/versions/pytago-venv-3.10.0/bin/pytago", line 33, in <module>
    sys.exit(load_entry_point('pytago', 'console_scripts', 'pytago')())
  File "/Users/username/pytago/pytago/cmd.py", line 14, in main
    go = python_to_go(f.read(), debug=False)
  File "/Users/username/pytago/pytago/core.py", line 30, in python_to_go
    go_tree = go_ast.File.from_Module(py_tree)
  File "/Users/username/pytago/pytago/go_ast/core.py", line 2520, in from_Module
    go_module.Decls[:] = build_decl_list(node.body)
  File "/Users/username/pytago/pytago/go_ast/core.py", line 431, in build_decl_list
    decls.append(FuncDecl.from_global_code(node))
  File "/Users/username/pytago/pytago/go_ast/core.py", line 2708, in from_global_code
    body = from_this(BlockStmt, [node])
  File "/Users/username/pytago/pytago/go_ast/core.py", line 343, in from_this
    result = getattr(cls, from_method(node))(node)
  File "/Users/username/pytago/pytago/go_ast/core.py", line 1280, in from_list
    stmts = build_stmt_list(node_list)
  File "/Users/username/pytago/pytago/go_ast/core.py", line 421, in build_stmt_list
    return _build_x_list(_STMT_TYPES, "Stmt", nodes, **kwargs)
  File "/Users/username/pytago/pytago/go_ast/core.py", line 378, in _build_x_list
    raise type(e)(f"Unhandled exception for {x_name} type in {x_types} with {method}: "
ValueError: Unhandled exception for Stmt type in [<class 'pytago.go_ast.core.Stmt'>, <class 'pytago.go_ast.core.AssignStmt'>, <class 'pytago.go_ast.core.BadStmt'>, <class 'pytago.go_ast.core.BlockStmt'>, <class 'pytago.go_ast.core.BranchStmt'>, <class 'pytago.go_ast.core.CaseClause'>, <class 'pytago.go_ast.core.DeclStmt'>, <class 'pytago.go_ast.core.DeferStmt'>, <class 'pytago.go_ast.core.EmptyStmt'>, <class 'pytago.go_ast.core.ExprStmt'>, <class 'pytago.go_ast.core.ForStmt'>, <class 'pytago.go_ast.core.GoStmt'>, <class 'pytago.go_ast.core.IfStmt'>, <class 'pytago.go_ast.core.IncDecStmt'>, <class 'pytago.go_ast.core.LabeledStmt'>, <class 'pytago.go_ast.core.RangeStmt'>, <class 'pytago.go_ast.core.ReturnStmt'>, <class 'pytago.go_ast.core.SelectStmt'>, <class 'pytago.go_ast.core.SendStmt'>, <class 'pytago.go_ast.core.SwitchStmt'>, <class 'pytago.go_ast.core.TypeSwitchStmt'>] with from_FunctionDef:
\```
def main(x: Optional[int]=None):
    print('Hello world!')
\```

Expected behavior I expected that it would not raise an exception. In terms of output, perhaps a pointer to the type, an int in this case, would work. This would allow it to be nil.

func main(x *int) {
	fmt.Println("Hello world!")
}

jtran avatar Dec 27 '21 17:12 jtran

That's not exactly how pointers work. If you wanted to use this a substitute for optional parameters, you'd have to modify your code anyways to check if the pointer is nil, dereference it, etc... at the point where you're modifying your go code you might as well just be modifying the python code.

IoIxD avatar Jun 22 '22 19:06 IoIxD

You're missing the point of the issue. The request isn't to convert optional parameters to pointers; it's to transpile Optional types in a way that makes sense. (Converting to a pointer without also converting if x is not None: and properly dereferencing clearly wouldn't make sense.)

jtran avatar Jun 22 '22 21:06 jtran