defmain macro; handles the whole if __name__ == __main__ / main function dance

Example:

  (defmain [&rest args]
    (print "now we're having a fun time!")
    (print args))

Which outputs:

  $ hy test.hy
  now we're having a fun time!
  (['test.hy'],)

Includes documentation and tests.
This commit is contained in:
Christopher Allan Webber 2014-03-11 13:37:29 -05:00
parent 5de39a4e1d
commit 774aad2ca8
5 changed files with 86 additions and 0 deletions

View File

@ -441,6 +441,44 @@ symbols for function names as the first parameter, `defn-alias` and
=> (alias) => (alias)
"Hello!" "Hello!"
defmain
-------
.. versionadded:: 0.9.13
The `defmain` macro defines a main function that is immediately called
with sys.argv as arguments if and only if this file is being executed
as a script. In other words this:
.. code-block:: clj
(defmain [&rest args]
(do-something-with args))
is the equivalent of::
def main(*args):
do_something_with(args)
return 0
if __name__ == "__main__":
import sys
retval = main(*sys.arg)
if isinstance(retval, int):
sys.exit(retval)
Note, as you can see above, if you return an integer from this
function, this will be used as the exit status for your script.
(Python defaults to exit status 0 otherwise, which means everything's
okay!)
(Since (sys.exit 0) is not run explicitly in case of a non-integer
return from defmain, it's good to put (defmain) as the last bit of
code in your file.)
.. _defmacro: .. _defmacro:
defmacro defmacro

View File

@ -171,6 +171,21 @@
(let ~(HyList (map (fn [x] `[~x (gensym (slice '~x 2))]) syms)) (let ~(HyList (map (fn [x] `[~x (gensym (slice '~x 2))]) syms))
~@body)))) ~@body))))
(defmacro defmain [args &rest body]
"Write a function named \"main\" and do the if __main__ dance"
(let [[retval (gensym)]]
`(do
(defn main [~@args]
~@body)
(when (= --name-- "__main__")
(import sys)
(setv ~retval (apply main sys.argv))
(if (integer? ~retval)
(sys.exit ~retval))))))
(defmacro-alias [defn-alias defun-alias] [names lambda-list &rest body] (defmacro-alias [defn-alias defun-alias] [names lambda-list &rest body]
"define one function with several names" "define one function with several names"
(let [[main (first names)] (let [[main (first names)]

View File

@ -0,0 +1,5 @@
(defmain [&rest args]
(print args)
(print "Hello World")
(if (in "exit1" args)
1))

View File

@ -0,0 +1,4 @@
(print "This Should Still Works")
(defn main []
(print "This Should Not Work"))

View File

@ -142,3 +142,27 @@ def test_bin_hy_builtins():
assert str(exit) == "Use (exit) or Ctrl-D (i.e. EOF) to exit" assert str(exit) == "Use (exit) or Ctrl-D (i.e. EOF) to exit"
assert str(quit) == "Use (quit) or Ctrl-D (i.e. EOF) to exit" assert str(quit) == "Use (quit) or Ctrl-D (i.e. EOF) to exit"
def test_bin_hy_main():
ret = run_cmd("hy tests/resources/bin/main.hy")
assert ret[0] == 0
assert "Hello World" in ret[1]
def test_bin_hy_main_args():
ret = run_cmd("hy tests/resources/bin/main.hy test 123")
assert ret[0] == 0
assert "test" in ret[1]
assert "123" in ret[1]
def test_bin_hy_main_exitvalue():
ret = run_cmd("hy tests/resources/bin/main.hy exit1")
assert ret[0] == 1
def test_bin_hy_no_main():
ret = run_cmd("hy tests/resources/bin/nomain.hy")
assert ret[0] == 0
assert "This Should Still Work" in ret[1]