Pebaz / nimporter

Compile Nim Extensions for Python On Import!
MIT License
824 stars 33 forks source link

Is it possible to create CLIs in Nim and distribute using Nimporter? #20

Closed Benjamin-Lee closed 4 years ago

Benjamin-Lee commented 4 years ago

When compiling a CLI created in Nim using docopt, I get the following error:

Obtaining file:///Users/BenjaminLee/Desktop/Python/Research/librtd
    ERROR: Command errored out with exit status 1:
     command: /Users/BenjaminLee/.virtualenvs/librtd/bin/python3.7 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/Users/BenjaminLee/Desktop/Python/Research/librtd/setup.py'"'"'; __file__='"'"'/Users/BenjaminLee/Desktop/Python/Research/librtd/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /private/var/folders/gw/sk03t1cn3wlgclyc55yst44w0000gn/T/pip-pip-egg-info-3e_yn9f1
         cwd: /Users/BenjaminLee/Desktop/Python/Research/librtd/
    Complete output (19 lines):
    /Users/BenjaminLee/.nimble/pkgs/nimpy-0.1.0/nimpy.nim(972, 32) Warning: Cannot prove that 'arg1k' is initialized. This will become a compile time error in the future. [ProveInit]
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/Users/BenjaminLee/Desktop/Python/Research/librtd/setup.py", line 11, in <module>
        ext_modules=nimporter.build_nim_extensions(),
      File "/Users/BenjaminLee/.virtualenvs/librtd/lib/python3.7/site-packages/nimporter.py", line 1070, in build_nim_extensions
        ext = cls._build_nim_extension(extension, root)
      File "/Users/BenjaminLee/.virtualenvs/librtd/lib/python3.7/site-packages/nimporter.py", line 969, in _build_nim_extension
        path, root, library=path.is_dir()
      File "/Users/BenjaminLee/.virtualenvs/librtd/lib/python3.7/site-packages/nimporter.py", line 465, in compile_nim_extension
        raise NimCompileException(errors[0])
    nimporter.NimCompileException:  commandLineParams() unsupported by dynamic libraries; usage of 'commandLineParams' is an {.error.} defined at /usr/local/Cellar/nim/1.2.0/nim/lib/pure/os.nim(2774, 3)
     |
     |
     -> proc docopt*(doc: string, argv: seq[string] = command_line_params(), help = true,
     |               version: string = "", options_first = false, quit = true
     |              ): Table[string, Value] {.gcsafe.} =

    At /Users/BenjaminLee/.nimble/pkgs/docopt-0.6.8/docopt.nim 608:47
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

This appears to be because functions like paramCount don't work in dynamic libraries. So, is there way to have pure Nim CLIs distributed in Python? Or do I need to rewrite the CLI in Python?

Pebaz commented 4 years ago

Definitely!

I am not familiar with using Docopt with Nim but this is definitely possible if you use a small (1 file) Python wrapper around the Nim code.

Basically, you have to pass the CLI arguments from Python sys.argv to a Nim function that runs your CLI handler code.

Example:

import sys, nimporter, nim_cli

def main(args=[]):
    return nim_cli.run_cli(args)

if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))

Inside your Nim file, you would just have a function that can accept CLI arguments and run with them:

# nim_cli.nim

import nimpy

proc run_cli(args: seq[string]): int {.exportpy.} =
    echo args
    return -1

Let me know if you have any other questions and I will be happy to help!

Benjamin-Lee commented 4 years ago

Thanks for the suggestion, that's exactly what I needed. For reference for anyone else having the issue, I solved the problem using the Python docopt package and just passed the parsed args in. All of my argument validation etc which I had done in Nim worked just as before.