diff --git a/.gitignore b/.gitignore index 39a6fc8..e46a222 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.pyc *swp *hy*egg* +*pyreadline*egg* .tox *pycache* dist diff --git a/.travis.yml b/.travis.yml index 1b8ef6e..124afc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ python: # command to install dependencies install: - pip install -r requirements.txt --use-mirrors - - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install importlib unittest2 astor --use-mirrors; fi + - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install argparse importlib unittest2 astor --use-mirrors; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install astor --use-mirrors; fi - if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then pip install astor --use-mirrors; fi - python setup.py -q install diff --git a/AUTHORS b/AUTHORS index 3630a44..eb253ba 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,4 +14,5 @@ * Thomas Ballinger * Morten Linderud * Guillermo Vayá +* Bob Tolbert * Ralph Möritz diff --git a/bin/hy b/bin/hy deleted file mode 100755 index 14cf5e0..0000000 --- a/bin/hy +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python - -import sys -from hy.cmdline import cmdline_handler - - -if __name__ == '__main__': - sys.exit(cmdline_handler("hy", sys.argv)) diff --git a/bin/hyc b/bin/hyc deleted file mode 100755 index c371b4b..0000000 --- a/bin/hyc +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env hy - -(import sys) -(import [hy.importer [write-hy-as-pyc]]) - -(write-hy-as-pyc (get sys.argv 1)) diff --git a/docs/language/api.rst b/docs/language/api.rst index 3a41249..c1235a7 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -177,7 +177,7 @@ Some example usage: `do` can accept any number of arguments, from 1 to n. -def / setf / setv +def / setv ----------------- `def` and `setv` are used to bind value, object or a function to a symbol. For diff --git a/eg/python3/futures/hello-world.hy b/eg/python3/futures/hello-world.hy index 880acbc..19d99d4 100644 --- a/eg/python3/futures/hello-world.hy +++ b/eg/python3/futures/hello-world.hy @@ -6,6 +6,6 @@ (with-as (ThreadPoolExecutor 10) executor - (setf jobs (list-comp (.submit executor task-to-do) (x (range 0 10)))) + (setv jobs (list-comp (.submit executor task-to-do) (x (range 0 10)))) (for (future (as-completed jobs)) (.result future))) diff --git a/hy/cmdline.py b/hy/cmdline.py index 4eca900..48b5d8c 100644 --- a/hy/cmdline.py +++ b/hy/cmdline.py @@ -24,7 +24,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -import optparse +import argparse import code import ast import sys @@ -198,26 +198,40 @@ def run_icommand(source): return run_repl(hr) -USAGE = "usage: %prog [-h | -i cmd | -c cmd | file | -]" -VERSION = "%prog " + hy.__version__ +USAGE = "%(prog)s [-h | -i cmd | -c cmd | file | -] [arg] ..." +VERSION = "%(prog)s " + hy.__version__ EPILOG = """ file program read from script - program read from stdin + [arg] ... arguments passed to program in sys.argv[1:] """ def cmdline_handler(scriptname, argv): - parser = optparse.OptionParser(usage=USAGE, version=VERSION) - parser.add_option( - "-c", dest="command", metavar="COMMAND", - help="program passed in as string") - parser.add_option( - "-i", dest="icommand", metavar="ICOMMAND", - help="program passed in as string, then stay in repl") + parser = argparse.ArgumentParser( + prog="hy", + usage=USAGE, + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=EPILOG) + parser.add_argument("-c", dest="command", + help="program passed in as a string") + parser.add_argument( + "-i", dest="icommand", + help="program passed in as a string, then stay in REPL") - # Hylarious way of adding non-option options to help text - parser.format_epilog = lambda self: EPILOG + parser.add_argument("-v", action="version", version=VERSION) - (options, args) = parser.parse_args() + # this will contain the script/program name and any arguments for it. + parser.add_argument('args', nargs=argparse.REMAINDER, + help=argparse.SUPPRESS) + + # stash the hy exectuable in case we need it later + # mimics Python sys.executable + hy.executable = argv[0] + + options = parser.parse_args(argv[1:]) + + # reset sys.argv like Python + sys.argv = options.args if options.command: # User did "hy -c ..." @@ -227,14 +241,25 @@ def cmdline_handler(scriptname, argv): # User did "hy -i ..." return run_icommand(options.icommand) - if args: - if args[0] == "-": + if options.args: + if options.args[0] == "-": # Read the program from stdin return run_command(sys.stdin.read()) else: # User did "hy " - return run_file(args[0]) + return run_file(options.args[0]) # User did NOTHING! return run_repl() + + +# entry point for cmd line script "hy" +def hy_main(): + sys.exit(cmdline_handler("hy", sys.argv)) + + +# entry point for cmd line script "hyc" +def hyc_main(): + from hy.importer import write_hy_as_pyc + write_hy_as_pyc(sys.argv[1]) diff --git a/hy/compiler.py b/hy/compiler.py index ad67c28..2e4e2b7 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1481,7 +1481,6 @@ class HyASTCompiler(object): return func + ret @builds("def") - @builds("setf") @builds("setv") @checkargs(2) def compile_def_expression(self, expression): diff --git a/hy/core/bootstrap.py b/hy/core/bootstrap.py index 6b9171f..eaedb28 100644 --- a/hy/core/bootstrap.py +++ b/hy/core/bootstrap.py @@ -128,10 +128,10 @@ def let_macro(variables, *body): for var in variables: if isinstance(var, list): - expr.append(HyExpression([HySymbol("setf"), + expr.append(HyExpression([HySymbol("setv"), var[0], var[1]])) else: - expr.append(HyExpression([HySymbol("setf"), + expr.append(HyExpression([HySymbol("setv"), var, HySymbol("None")])) return HyExpression([expr + list(body)]) diff --git a/make.bat b/make.bat new file mode 100644 index 0000000..ccf6138 --- /dev/null +++ b/make.bat @@ -0,0 +1,116 @@ +@ECHO OFF + +REM Make batch file for Hy development + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo. No default step. Use setup.py + echo. + echo. Other targets: + echo. + echo. - docs + echo. - full + echo. + echo. - dev "test & flake" + echo. - flake + echo. - test + echo. - diff + echo. - tox + echo. - d + echo. - r + echo. + goto end +) + +if "%1" == "docs" ( +:docs + echo.docs not yet supported under Windows +goto :EOF +) + +if "%1" == "upload" ( +:upload + python setup.py sdist upload +goto :EOF +) + +if "%1" == "clear" ( +:clear + cls +goto :EOF +) + +if "%1" == "d" ( +:d + call :clear + call :dev +goto :EOF +) + +if "%1" == "test" ( +:test + call :venv + nosetests -sv +goto :EOF +) + +if "%1" == "venv" ( +:venv + echo.%VIRTUAL_ENV% | findstr /C:"hy" 1>nul + if errorlevel 1 ( + echo.You're not in a Hy virtualenv. FOR SHAME + ) ELSE ( + echo.We're properly in a virtualenv. Going ahead. + ) +goto :EOF +) + +if "%1" == "flake" ( +:flake + echo.flake8 hy + flake8 hy +goto :EOF +) + +if "%1" == "dev" ( +:dev + call :test + call :flake +goto :EOF +) + +if "%1" == "tox" ( +:tox + call :venv + tox -e "py26,py27,py32,py33,flake8" +goto :EOF +) + +if "%1" == "d" ( +:d + call :clear + call :dev +goto :EOF +) + +if "%i" == "diff" ( +:diff + git diff --color +goto :EOF +) + +if "%1" == "r" ( +:r + call :d + call :tox + call :diff +goto :EOF +) + +if "%1" == full ( + call :docs + call :d + call :tox +) \ No newline at end of file diff --git a/setup.py b/setup.py index c3bb159..8b6ba62 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright (c) 2012 Paul Tagliamonte +# Copyright (c) 2012, 2013 Paul Tagliamonte # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -23,12 +23,15 @@ from hy import __appname__, __version__ from setuptools import setup import os +import sys long_description = """Hy is a Python <--> Lisp layer. It helps make things work nicer, and lets Python and the Hy lisp variant play nice together. """ install_requires = [] +if sys.version_info[0] == 2: + install_requires.append('argparse>=1.2.1') if os.name == 'nt': install_requires.append('pyreadline==2.0') @@ -36,10 +39,12 @@ setup( name=__appname__, version=__version__, install_requires=install_requires, - scripts=[ - "bin/hy", - "bin/hyc", - ], + entry_points={ + 'console_scripts': [ + 'hy = hy.cmdline:hy_main', + 'hyc = hy.cmdline:hyc_main' + ] + }, packages=[ 'hy', 'hy.lex', diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index d8f3825..fa55e27 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -408,11 +408,11 @@ (defn test-for-doodle [] "NATIVE: test for-do" - (do (do (do (do (do (do (do (do (do (setf (, x y) (, 0 0))))))))))) + (do (do (do (do (do (do (do (do (do (setv (, x y) (, 0 0))))))))))) (foreach [- [1 2]] (do - (setf x (+ x 1)) - (setf y (+ y 1)))) + (setv x (+ x 1)) + (setv y (+ y 1)))) (assert (= y x 2))) @@ -597,10 +597,10 @@ (defn test-eval [] "NATIVE: test eval" (assert (= 2 (eval (quote (+ 1 1))))) - (setf x 2) + (setv x 2) (assert (= 4 (eval (quote (+ x 2))))) - (setf test-payload (quote (+ x 2))) - (setf x 4) + (setv test-payload (quote (+ x 2))) + (setv x 4) (assert (= 6 (eval test-payload))) (assert (= 9 ((eval (quote (fn [x] (+ 3 3 x)))) 3))) (assert (= 1 (eval (quote 1)))) @@ -689,9 +689,9 @@ (defn test-try-except-return [] "NATIVE: test we can return from in a try except" (assert (= ((fn [] (try xxx (except [NameError] (+ 1 1))))) 2)) - (setf foo (try xxx (except [NameError] (+ 1 1)))) + (setv foo (try xxx (except [NameError] (+ 1 1)))) (assert (= foo 2)) - (setf foo (try (+ 2 2) (except [NameError] (+ 1 1)))) + (setv foo (try (+ 2 2) (except [NameError] (+ 1 1)))) (assert (= foo 4))) diff --git a/tests/native_tests/quote.hy b/tests/native_tests/quote.hy index f180a98..0b3e1d0 100644 --- a/tests/native_tests/quote.hy +++ b/tests/native_tests/quote.hy @@ -3,22 +3,22 @@ (defn test-quote [] "NATIVE: test for quoting functionality" - (setf q (quote (a b c))) + (setv q (quote (a b c))) (assert (= (len q) 3)) (assert (= q [(quote a) (quote b) (quote c)]))) (defn test-quoted-hoistable [] "NATIVE: check whether quote works on hoisted things" - (setf f (quote (if true true true))) + (setv f (quote (if true true true))) (assert (= (car f) (quote if))) (assert (= (cdr f) (quote (true true true))))) (defn test-quoted-macroexpand [] "NATIVE: check that we don't expand macros in quoted expressions" - (setf q1 (quote (-> a b c))) - (setf q2 (quasiquote (-> a b c))) + (setv q1 (quote (-> a b c))) + (setv q2 (quasiquote (-> a b c))) (assert (= q1 q2)) (assert (= (car q1) (quote ->))) (assert (= (cdr q1) (quote (a b c))))) @@ -26,7 +26,7 @@ (defn test-quote-dicts [] "NATIVE: test quoting dicts" - (setf q (quote {foo bar baz quux})) + (setv q (quote {foo bar baz quux})) (assert (= (len q) 4)) (assert (= (get q 0) (quote foo))) (assert (= (get q 1) (quote bar))) @@ -37,41 +37,41 @@ (defn test-quote-expr-in-dict [] "NATIVE: test quoting nested exprs in dict" - (setf q (quote {(foo bar) 0})) + (setv q (quote {(foo bar) 0})) (assert (= (len q) 2)) - (setf qq (get q 0)) + (setv qq (get q 0)) (assert (= qq (quote (foo bar))))) (defn test-quasiquote [] "NATIVE: test that quasiquote and quote are equivalent for simple cases" - (setf q (quote (a b c))) - (setf qq (quasiquote (a b c))) + (setv q (quote (a b c))) + (setv qq (quasiquote (a b c))) (assert (= q qq))) (defn test-unquote [] "NATIVE: test that unquote works as expected" - (setf q (quote (unquote foo))) + (setv q (quote (unquote foo))) (assert (= (len q) 2)) (assert (= (get q 1) (quote foo))) - (setf qq (quasiquote (a b c (unquote (+ 1 2))))) + (setv qq (quasiquote (a b c (unquote (+ 1 2))))) (assert (= (len qq) 4)) (assert (= qq (quote (a b c 3))))) (defn test-unquote-splice [] "NATIVE: test splicing unquotes" - (setf q (quote (c d e))) - (setf qq (quasiquote (a b (unquote-splice q) f (unquote-splice q)))) + (setv q (quote (c d e))) + (setv qq (quasiquote (a b (unquote-splice q) f (unquote-splice q)))) (assert (= (len qq) 9)) (assert (= qq (quote (a b c d e f c d e))))) (defn test-nested-quasiquote [] "NATIVE: test nested quasiquotes" - (setf qq (quasiquote (1 (quasiquote (unquote (+ 1 (unquote (+ 2 3))))) 4))) - (setf q (quote (1 (quasiquote (unquote (+ 1 5))) 4))) + (setv qq (quasiquote (1 (quasiquote (unquote (+ 1 (unquote (+ 2 3))))) 4))) + (setv q (quote (1 (quasiquote (unquote (+ 1 5))) 4))) (assert (= (len q) 3)) (assert (= (get qq 1) (quote (quasiquote (unquote (+ 1 5)))))) (assert (= q qq))) diff --git a/tests/resources/argparse_ex.hy b/tests/resources/argparse_ex.hy new file mode 100755 index 0000000..9b2500f --- /dev/null +++ b/tests/resources/argparse_ex.hy @@ -0,0 +1,18 @@ +#!/usr/bin/env hy + +(import sys) +(import argparse) + +(setv parser (argparse.ArgumentParser)) + +(.add_argument parser "-i") +(.add_argument parser "-c") + +(setv args (.parse_args parser)) + +;; using (cond) allows -i to take precedence over -c + +(cond (args.i + (print (str args.i))) + (args.c + (print (str "got c")))) diff --git a/tests/test_bin.py b/tests/test_bin.py index 4cdfb58..3a4849c 100644 --- a/tests/test_bin.py +++ b/tests/test_bin.py @@ -25,13 +25,18 @@ import subprocess import sys -def run_cmd(cmd): +def run_cmd(cmd, stdin_data=None): p = subprocess.Popen(cmd, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) stdout = "" stderr = "" + if stdin_data is not None: + p.stdin.write(stdin_data.encode('ASCII')) + p.stdin.flush() + p.stdin.close() # Read stdout and stderr otherwise if the PIPE buffer is full, we might # wait for ever… while p.poll() is None: @@ -41,28 +46,28 @@ def run_cmd(cmd): def test_bin_hy(): - ret = run_cmd("echo | bin/hy") + ret = run_cmd("hy", "") assert ret[0] == 0 def test_bin_hy_stdin(): - ret = run_cmd("echo \"(koan)\" | bin/hy") + ret = run_cmd("hy", '(koan)') assert ret[0] == 0 assert "monk" in ret[1] def test_bin_hy_cmd(): - ret = run_cmd("bin/hy -c \"(koan)\"") + ret = run_cmd("hy -c \"(koan)\"") assert ret[0] == 0 assert "monk" in ret[1] - ret = run_cmd("bin/hy -c \"(koan\"") + ret = run_cmd("hy -c \"(koan\"") assert ret[0] == 1 assert "LexException" in ret[1] def test_bin_hy_icmd(): - ret = run_cmd("echo \"(ideas)\" | bin/hy -i \"(koan)\"") + ret = run_cmd("hy -i \"(koan)\"", "(ideas)") assert ret[0] == 0 output = ret[1] @@ -71,16 +76,41 @@ def test_bin_hy_icmd(): def test_bin_hy_file(): - ret = run_cmd("bin/hy eg/nonfree/halting-problem/halting.hy") + ret = run_cmd("hy eg/nonfree/halting-problem/halting.hy") assert ret[0] == 0 assert "27" in ret[1] +def test_bin_hy_missing_file(): + ret = run_cmd("hy foobarbaz") + assert ret[0] == 1 + assert "No such file" in ret[2] + + +def test_bin_hy_file_with_args(): + ret = run_cmd("hy tests/resources/argparse_ex.hy -h") + assert ret[0] == 0 + assert "usage" in ret[1] + ret = run_cmd("hy tests/resources/argparse_ex.hy -c bar") + assert ret[0] == 0 + assert "got c" in ret[1] + ret = run_cmd("hy tests/resources/argparse_ex.hy -i foo") + assert ret[0] == 0 + assert "foo" in ret[1] + ret = run_cmd("hy tests/resources/argparse_ex.hy -i foo -c bar") + assert ret[0] == 0 + assert "foo" in ret[1] + + def test_hy2py(): # XXX Astor doesn't seem to support Python3 :( if sys.version_info[0] == 3: return + # and running this script this way doesn't work on Windows + if os.name == "nt": + return + i = 0 for dirpath, dirnames, filenames in os.walk("tests/native_tests"): for f in filenames: