yglukhov / nimpy

Nim - Python bridge
MIT License
1.47k stars 61 forks source link

No way of initializing type with kwargs #299

Closed realratchet closed 10 months ago

realratchet commented 10 months ago

Hey I've encountered a bug with nimpy caused by nimlang templating. Is there a work around other than passing the arguments as variables?

import nimpy
let tqdm = pyImport("tqdm").tqdm

discard tqdm(total=5)                # won't compile: attempting to call routine: 'tqdm'
discard tqdm.callObject(total=5)     # won't compile: falls through to `.()` template which assumes that the python object has a method `callObject` attached to it
discard tqdm.callObject(nil, nil, 5) # works but suboptimal
realratchet commented 10 months ago

In case there isn't a way to do this currently, how about something like a bang operator.

template toBangArg(inNode: NimNode, plainArgs: NimNode, kwArgs: NimNode) =
  var node: NimNode

  if inNode.kind == nnkPar:
    node = inNode[0]
  else:
    node = inNode

  if node.kind == nnkHiddenStdConv and node[0].kind == nnkEmpty:
    discard
  elif node.kind == nnkExprEqExpr or node.kind == nnkAsgn or node.kind == nnkExprColonExpr:
    kwArgs.add(newTree(nnkPar,
      newCall("cstring", newLit($node[0])),
      newCall("toPyObjectArgument", node[1])))
  else:
    plainArgs.add(newCall("toPyObjectArgument", node))

macro `!`*(o: PyObject, args: varargs[untyped]): PyObject =
  expectKind(o, nnkSym)

  let plainArgs = newTree(nnkBracket)
  let kwArgs = newTree(nnkBracket)

  expectKind(args, nnkArgList)
  expectLen(args, 1)

  if args[0].kind == nnkTupleConstr:
    for arg in args[0]:
      toBangArg(arg, plainArgs, kwArgs)
  else:
    toBangArg(args[0], plainArgs, kwArgs)

  result = newCall(bindSym"newPyObjectConsumingRef",
    newCall(bindSym"callObjectAux", newDotExpr(o, newIdentNode("rawPyObj")), plainArgs, kwArgs))

This would allow the object to be called like this discard tqdm!(nil, desc: "column", total: 5) I wanted to make it work like this discard tqdm!(nil, desc="column", total=5) but it seems that nim has issue parsing macros with multiple kwargs if no arg is passed, e.g., discard tqdm!(desc="column", total=5) doesn't compile. However, the colon does align nim's constructor style.

If you think this is completely stupid, no problem, I can always work around this on my own. If you think this is something worth pursuing, I can make a PR.