diff --git a/NEWS b/NEWS
index 7584628..b0d5d24 100644
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,10 @@ Changes from 0.12.1
returns.
* `setv` no longer unnecessarily tries to get attributes
+ [ Misc. Improvements ]
+ * New contrib module `hy-repr`
+ * Added a command-line option --hy-repr
+
Changes from 0.12.0
[ Bug Fixes ]
diff --git a/docs/contrib/hy_repr.rst b/docs/contrib/hy_repr.rst
new file mode 100644
index 0000000..51d8842
--- /dev/null
+++ b/docs/contrib/hy_repr.rst
@@ -0,0 +1,48 @@
+==================
+Hy representations
+==================
+
+.. versionadded:: 0.13.0
+
+``hy.contrib.hy-repr`` is a module containing a single function.
+To import it, say::
+
+ (import [hy.contrib.hy-repr [hy-repr]])
+
+To make the Hy REPL use it for output, invoke Hy like so::
+
+ $ hy --repl-output-fn=hy.contrib.hy-repr.hy-repr
+
+.. _hy-repr-fn:
+
+hy-repr
+-------
+
+Usage: ``(hy-repr x)``
+
+This function is Hy's equivalent of Python's built-in ``repr``.
+It returns a string representing the input object in Hy syntax.
+
+.. code-block:: hy
+
+ => (hy-repr [1 2 3])
+ '[1 2 3]'
+ => (repr [1 2 3])
+ '[1, 2, 3]'
+
+If the input object has a method ``__hy-repr__``, it will be called
+instead of doing anything else.
+
+.. code-block:: hy
+
+ => (defclass C [list] [__hy-repr__ (fn [self] "cuddles")])
+ => (hy-repr (C))
+ 'cuddles'
+
+When ``hy-repr`` doesn't know how to handle its input, it falls back
+on ``repr``.
+
+Like ``repr`` in Python, ``hy-repr`` can round-trip many kinds of
+values. Round-tripping implies that given an object ``x``,
+``(eval (read-str (hy-repr x)))`` returns ``x``, or at least a value
+that's equal to ``x``.
diff --git a/docs/contrib/index.rst b/docs/contrib/index.rst
index 67c8abe..917e12b 100644
--- a/docs/contrib/index.rst
+++ b/docs/contrib/index.rst
@@ -16,3 +16,4 @@ Contents:
profile
sequences
walk
+ hy_repr
diff --git a/docs/tutorial.rst b/docs/tutorial.rst
index 50f0e73..19edbb9 100644
--- a/docs/tutorial.rst
+++ b/docs/tutorial.rst
@@ -193,7 +193,6 @@ Hy. Let's experiment with this in the hy interpreter::
[1, 2, 3]
=> {"dog" "bark"
... "cat" "meow"}
- ...
{'dog': 'bark', 'cat': 'meow'}
=> (, 1 2 3)
(1, 2, 3)
@@ -204,6 +203,20 @@ Hy. Let's experiment with this in the hy interpreter::
Notice the last two lines: Hy has a fraction literal like Clojure.
+If you start Hy like this (a shell alias might be helpful)::
+
+ $ hy --repl-output-fn=hy.contrib.hy-repr.hy-repr
+
+the interactive mode will use :ref:`_hy-repr-fn` instead of Python's
+native ``repr`` function to print out values, so you'll see values in
+Hy syntax rather than Python syntax::
+
+ => [1 2 3]
+ [1 2 3]
+ => {"dog" "bark"
+ ... "cat" "meow"}
+ {"dog" "bark" "cat" "meow"}
+
If you are familiar with other Lisps, you may be interested that Hy
supports the Common Lisp method of quoting:
diff --git a/hy/cmdline.py b/hy/cmdline.py
index 87dbe67..beb84f7 100644
--- a/hy/cmdline.py
+++ b/hy/cmdline.py
@@ -32,14 +32,16 @@ import code
import ast
import sys
import os
+import importlib
import astor.codegen
import hy
from hy.lex import LexException, PrematureEndOfInput, tokenize
-from hy.compiler import hy_compile, HyTypeError
-from hy.importer import (ast_compile, import_buffer_to_module,
+from hy.lex.parser import hy_symbol_mangle
+from hy.compiler import HyTypeError
+from hy.importer import (hy_eval, import_buffer_to_module,
import_file_to_ast, import_file_to_hst,
import_buffer_to_ast, import_buffer_to_hst)
from hy.completer import completion
@@ -73,25 +75,37 @@ builtins.quit = HyQuitter('quit')
builtins.exit = HyQuitter('exit')
-def print_python_code(_ast):
- # astor cannot handle ast.Interactive, so disguise it as a module
- _ast_for_print = ast.Module()
- _ast_for_print.body = _ast.body
- print(astor.codegen.to_source(_ast_for_print))
-
-
class HyREPL(code.InteractiveConsole):
- def __init__(self, spy=False, locals=None, filename=""):
+ def __init__(self, spy=False, output_fn=None, locals=None,
+ filename=""):
+
self.spy = spy
+
+ if output_fn is None:
+ self.output_fn = repr
+ elif callable(output_fn):
+ self.output_fn = output_fn
+ else:
+ f = hy_symbol_mangle(output_fn)
+ if "." in output_fn:
+ module, f = f.rsplit(".", 1)
+ self.output_fn = getattr(importlib.import_module(module), f)
+ else:
+ self.output_fn = __builtins__[f]
+
code.InteractiveConsole.__init__(self, locals=locals,
filename=filename)
def runsource(self, source, filename='', symbol='single'):
global SIMPLE_TRACEBACKS
try:
- tokens = tokenize(source)
- except PrematureEndOfInput:
- return True
+ try:
+ tokens = tokenize(source)
+ except PrematureEndOfInput:
+ return True
+ do = HyExpression([HySymbol('do')] + tokens)
+ do.start_line = do.end_line = do.start_column = do.end_column = 1
+ do.replace(do)
except LexException as e:
if e.source is None:
e.source = source
@@ -100,10 +114,15 @@ class HyREPL(code.InteractiveConsole):
return False
try:
- _ast = hy_compile(tokens, "__console__", root=ast.Interactive)
- if self.spy:
- print_python_code(_ast)
- code = ast_compile(_ast, filename, symbol)
+ def ast_callback(main_ast, expr_ast):
+ if self.spy:
+ # Mush the two AST chunks into a single module for
+ # conversion into Python.
+ new_ast = ast.Module(main_ast.body +
+ [ast.Expr(expr_ast.body)])
+ print(astor.to_source(new_ast))
+ value = hy_eval(do, self.locals, "__console__",
+ ast_callback)
except HyTypeError as e:
if e.source is None:
e.source = source
@@ -117,7 +136,12 @@ class HyREPL(code.InteractiveConsole):
self.showtraceback()
return False
- self.runcode(code)
+ if value is not None:
+ # Make the last non-None value available to
+ # the user as `_`.
+ self.locals['_'] = value
+ # Print the value.
+ print(self.output_fn(value))
return False
@@ -211,7 +235,7 @@ def run_file(filename):
return 0
-def run_repl(hr=None, spy=False):
+def run_repl(hr=None, **kwargs):
import platform
sys.ps1 = "=> "
sys.ps2 = "... "
@@ -221,7 +245,7 @@ def run_repl(hr=None, spy=False):
with completion(Completer(namespace)):
if not hr:
- hr = HyREPL(spy, namespace)
+ hr = HyREPL(locals=namespace, **kwargs)
hr.interact("{appname} {version} using "
"{py}({build}) {pyversion} on {os}".format(
@@ -236,8 +260,8 @@ def run_repl(hr=None, spy=False):
return 0
-def run_icommand(source, spy=False):
- hr = HyREPL(spy)
+def run_icommand(source, **kwargs):
+ hr = HyREPL(**kwargs)
if os.path.exists(source):
with open(source, "r") as f:
source = f.read()
@@ -272,7 +296,9 @@ def cmdline_handler(scriptname, argv):
help="program passed in as a string, then stay in REPL")
parser.add_argument("--spy", action="store_true",
help="print equivalent Python code before executing")
-
+ parser.add_argument("--repl-output-fn",
+ help="function for printing REPL output "
+ "(e.g., hy.contrib.hy-repr.hy-repr)")
parser.add_argument("-v", "--version", action="version", version=VERSION)
parser.add_argument("--show-tracebacks", action="store_true",
@@ -315,7 +341,8 @@ def cmdline_handler(scriptname, argv):
if options.icommand:
# User did "hy -i ..."
- return run_icommand(options.icommand, spy=options.spy)
+ return run_icommand(options.icommand, spy=options.spy,
+ output_fn=options.repl_output_fn)
if options.args:
if options.args[0] == "-":
@@ -332,7 +359,7 @@ def cmdline_handler(scriptname, argv):
sys.exit(e.errno)
# User did NOTHING!
- return run_repl(spy=options.spy)
+ return run_repl(spy=options.spy, output_fn=options.repl_output_fn)
# entry point for cmd line script "hy"
diff --git a/hy/contrib/hy_repr.hy b/hy/contrib/hy_repr.hy
new file mode 100644
index 0000000..e70a5a8
--- /dev/null
+++ b/hy/contrib/hy_repr.hy
@@ -0,0 +1,77 @@
+(import [hy._compat [PY3 str-type bytes-type long-type]])
+(import [hy.models [HyObject HyExpression HySymbol HyKeyword HyInteger HyList HyDict HySet HyString HyBytes]])
+
+(defn hy-repr [obj]
+ (setv seen (set))
+ ; We keep track of objects we've already seen, and avoid
+ ; redisplaying their contents, so a self-referential object
+ ; doesn't send us into an infinite loop.
+ (defn f [x q]
+ ; `x` is the current object being stringified.
+ ; `q` is True if we're inside a single quote, False otherwise.
+ (setv old? (in (id x) seen))
+ (.add seen (id x))
+ (setv t (type x))
+ (defn catted []
+ (if old? "..." (.join " " (list-comp (f it q) [it x]))))
+ (setv prefix "")
+ (if (and (not q) (instance? HyObject x))
+ (setv prefix "'" q True))
+ (+ prefix (if
+ (hasattr x "__hy_repr__")
+ (.__hy-repr__ x)
+ (is t HyExpression)
+ (if (and x (symbol? (first x)))
+ (if
+ (= (first x) 'quote)
+ (+ "'" (f (second x) True))
+ (= (first x) 'quasiquote)
+ (+ "`" (f (second x) q))
+ (= (first x) 'unquote)
+ (+ "~" (f (second x) q))
+ (= (first x) 'unquote_splice)
+ (+ "~@" (f (second x) q))
+ ; else
+ (+ "(" (catted) ")"))
+ (+ "(" (catted) ")"))
+ (is t tuple)
+ (+ "(," (if x " " "") (catted) ")")
+ (in t [list HyList])
+ (+ "[" (catted) "]")
+ (is t HyDict)
+ (+ "{" (catted) "}")
+ (is t dict)
+ (+
+ "{"
+ (if old? "..." (.join " " (list-comp
+ (+ (f k q) " " (f v q))
+ [[k v] (.items x)])))
+ "}")
+ (in t [set HySet])
+ (+ "#{" (catted) "}")
+ (is t frozenset)
+ (+ "(frozenset #{" (catted) "})")
+ (is t HySymbol)
+ x
+ (or (is t HyKeyword) (and (is t str-type) (.startswith x HyKeyword.PREFIX)))
+ (cut x 1)
+ (in t [str-type HyString bytes-type HyBytes]) (do
+ (setv r (.lstrip (repr x) "ub"))
+ (+ (if (in t [bytes-type HyBytes]) "b" "") (if (.startswith "\"" r)
+ ; If Python's built-in repr produced a double-quoted string, use
+ ; that.
+ r
+ ; Otherwise, we have a single-quoted string, which isn't valid Hy, so
+ ; convert it.
+ (+ "\"" (.replace (cut r 1 -1) "\"" "\\\"") "\""))))
+ (and (not PY3) (is t int))
+ (.format "(int {})" (repr x))
+ (and (not PY3) (in t [long_type HyInteger]))
+ (.rstrip (repr x) "L")
+ (is t complex)
+ (.strip (repr x) "()")
+ (is t fraction)
+ (.format "{}/{}" (f x.numerator q) (f x.denominator q))
+ ; else
+ (repr x))))
+ (f obj False))
diff --git a/hy/importer.py b/hy/importer.py
index 8418b7c..c3c0192 100644
--- a/hy/importer.py
+++ b/hy/importer.py
@@ -111,7 +111,7 @@ def import_buffer_to_module(module_name, buf):
return mod
-def hy_eval(hytree, namespace, module_name):
+def hy_eval(hytree, namespace, module_name, ast_callback=None):
foo = HyObject()
foo.start_line = 0
foo.end_line = 0
@@ -133,6 +133,9 @@ def hy_eval(hytree, namespace, module_name):
node.lineno = 1
node.col_offset = 1
+ if ast_callback:
+ ast_callback(_ast, expr)
+
if not isinstance(namespace, dict):
raise HyTypeError(foo, "Globals must be a dictionary")
diff --git a/tests/__init__.py b/tests/__init__.py
index 88a1cad..74b8390 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -19,6 +19,7 @@ from .native_tests.contrib.loop import * # noqa
from .native_tests.contrib.walk import * # noqa
from .native_tests.contrib.multi import * # noqa
from .native_tests.contrib.sequences import * # noqa
+from .native_tests.contrib.hy_repr import * # noqa
if PY3:
from .native_tests.py3_only_tests import * # noqa
diff --git a/tests/native_tests/contrib/hy_repr.hy b/tests/native_tests/contrib/hy_repr.hy
new file mode 100644
index 0000000..04dbce6
--- /dev/null
+++ b/tests/native_tests/contrib/hy_repr.hy
@@ -0,0 +1,82 @@
+(import
+ [hy.contrib.hy-repr [hy-repr]])
+
+(defn test-hy-repr-roundtrip-from-value []
+ ; Test that a variety of values round-trip properly.
+ (setv values [
+ None False True
+ 5 5.1 '5 '5.1
+ (int 5)
+ 1/2
+ 5j 5.1j 2+1j 1.2+3.4j
+ "" b""
+ '"" 'b""
+ "apple bloom" b"apple bloom" "⚘"
+ '"apple bloom" 'b"apple bloom" '"⚘"
+ "single ' quotes" b"single ' quotes"
+ "\"double \" quotes\"" b"\"double \" quotes\""
+ 'mysymbol :mykeyword
+ [] (,) #{} (frozenset #{})
+ '[] '(,) '#{} '(frozenset #{})
+ '['[]]
+ '(+ 1 2)
+ [1 2 3] (, 1 2 3) #{1 2 3} (frozenset #{1 2 3})
+ '[1 2 3] '(, 1 2 3) '#{1 2 3} '(frozenset #{1 2 3})
+ {"a" 1 "b" 2 "a" 3} '{"a" 1 "b" 2 "a" 3}
+ [1 [2 3] (, 4 (, 'mysymbol :mykeyword)) {"a" b"hello"}]
+ '[1 [2 3] (, 4 (, mysymbol :mykeyword)) {"a" b"hello"}]])
+ (for [original-val values]
+ (setv evaled (eval (read-str (hy-repr original-val))))
+ (assert (= evaled original-val))
+ (assert (is (type evaled) (type original-val)))))
+
+(defn test-hy-repr-roundtrip-from-str []
+ (setv strs [
+ "[1 2 3]"
+ "'[1 2 3]"
+ "[1 'a 3]"
+ "'[1 a 3]"
+ "'[1 'a 3]"
+ "[1 '[2 3] 4]"
+ "'[1 [2 3] 4]"
+ "'[1 '[2 3] 4]"
+ "'[1 `[2 3] 4]"
+ "'[1 `[~foo ~@bar] 4]"
+ "'[1 `[~(+ 1 2) ~@(+ [1] [2])] 4]"
+ "'[1 `[~(do (print x 'y) 1)] 4]"
+ "{1 20}"
+ "'{1 10 1 20}"
+ "'asymbol"
+ ":akeyword"])
+ (for [original-str strs]
+ (setv rep (hy-repr (eval (read-str original-str))))
+ (assert (= rep original-str))))
+
+(defn test-hy-model-constructors []
+ (import hy)
+ (assert (= (hy-repr (hy.HyInteger 7)) "'7"))
+ (assert (= (hy-repr (hy.HyString "hello")) "'\"hello\""))
+ (assert (= (hy-repr (hy.HyList [1 2 3])) "'[1 2 3]"))
+ (assert (= (hy-repr (hy.HyDict [1 2 3])) "'{1 2 3}")))
+
+(defn test-hy-repr-self-reference []
+
+ (setv x [1 2 3])
+ (setv (get x 1) x)
+ (assert (= (hy-repr x) "[1 [...] 3]"))
+
+ (setv x {1 2 3 [4 5] 6 7})
+ (setv (get x 3 1) x)
+ (assert (in (hy-repr x) (list-comp
+ ; The ordering of a dictionary isn't guaranteed, so we need
+ ; to check for all possible orderings.
+ (+ "{" (.join " " p) "}")
+ [p (permutations ["1 2" "3 [4 {...}]" "6 7"])]))))
+
+(defn test-hy-repr-dunder-method []
+ (defclass C [list] [__hy-repr__ (fn [self] "cuddles")])
+ (assert (= (hy-repr (C)) "cuddles")))
+
+(defn test-hy-repr-fallback []
+ (defclass D [list] [__repr__ (fn [self] "cuddles")])
+ (assert (= (hy-repr (D)) "cuddles")))
diff --git a/tests/test_bin.py b/tests/test_bin.py
index e66ef3f..b355e63 100644
--- a/tests/test_bin.py
+++ b/tests/test_bin.py
@@ -23,116 +23,164 @@
# DEALINGS IN THE SOFTWARE.
import os
import subprocess
+import re
from hy._compat import PY3
hy_dir = os.environ.get('HY_DIR', '')
-def run_cmd(cmd, stdin_data=None):
+def hr(s=""):
+ return "hy --repl-output-fn=hy.contrib.hy-repr.hy-repr " + s
+
+
+def run_cmd(cmd, stdin_data=None, expect=0):
p = subprocess.Popen(os.path.join(hy_dir, 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…
+ stdout = ""
+ stderr = ""
while p.poll() is None:
stdout += p.stdout.read().decode('utf-8')
stderr += p.stderr.read().decode('utf-8')
- return p.returncode, stdout, stderr
+ assert p.returncode == expect
+ return stdout, stderr
def test_bin_hy():
- ret = run_cmd("hy", "")
- assert ret[0] == 0
+ run_cmd("hy", "")
def test_bin_hy_stdin():
- ret = run_cmd("hy", '(koan)')
- assert ret[0] == 0
- assert "monk" in ret[1]
+ output, _ = run_cmd("hy", '(koan)')
+ assert "monk" in output
+
+ output, _ = run_cmd("hy --spy", '(koan)')
+ assert "monk" in output
+ assert "\\n Ummon" in output
+
+ # --spy should work even when an exception is thrown
+ output, _ = run_cmd("hy --spy", '(foof)')
+ assert "foof()" in output
+
+
+def test_bin_hy_stdin_multiline():
+ output, _ = run_cmd("hy", '(+ "a" "b"\n"c" "d")')
+ assert "'abcd'" in output
+
+
+def test_bin_hy_stdin_comments():
+ _, err_empty = run_cmd("hy", '')
+
+ output, err = run_cmd("hy", '(+ "a" "b") ; "c"')
+ assert "'ab'" in output
+ assert err == err_empty
+
+ _, err = run_cmd("hy", '; 1')
+ assert err == err_empty
+
+
+def test_bin_hy_stdin_assignment():
+ # If the last form is an assignment, don't print the value.
+
+ output, _ = run_cmd("hy", '(setv x (+ "A" "Z"))')
+ assert "AZ" not in output
+
+ output, _ = run_cmd("hy", '(setv x (+ "A" "Z")) (+ "B" "Y")')
+ assert "AZ" not in output
+ assert "BY" in output
+
+ output, _ = run_cmd("hy", '(+ "B" "Y") (setv x (+ "A" "Z"))')
+ assert "AZ" not in output
+ assert "BY" not in output
+
+
+def test_bin_hy_stdin_as_arrow():
+ # https://github.com/hylang/hy/issues/1255
+ output, _ = run_cmd("hy", "(as-> 0 it (inc it) (inc it))")
+ assert re.match(r"=>\s+2L?\s+=>", output)
+
+
+def test_bin_hy_stdin_error_underline_alignment():
+ _, err = run_cmd("hy", "(defmacro mabcdefghi [x] x)\n(mabcdefghi)")
+ assert "\n (mabcdefghi)\n ^----------^" in err
+
+
+def test_bin_hy_stdin_hy_repr():
+ output, _ = run_cmd("hy", '(+ [1] [2])')
+ assert "[1, 2]" in output.replace('L', '')
+
+ output, _ = run_cmd(hr(), '(+ [1] [2])')
+ assert "[1 2]" in output
+
+ output, _ = run_cmd(hr("--spy"), '(+ [1] [2])')
+ assert "[1]+[2]" in output.replace('L', '').replace(' ', '')
+ assert "[1 2]" in output
+
+ # --spy should work even when an exception is thrown
+ output, _ = run_cmd(hr("--spy"), '(+ [1] [2] (foof))')
+ assert "[1]+[2]" in output.replace('L', '').replace(' ', '')
def test_bin_hy_cmd():
- ret = run_cmd("hy -c \"(koan)\"")
- assert ret[0] == 0
- assert "monk" in ret[1]
+ output, _ = run_cmd("hy -c \"(koan)\"")
+ assert "monk" in output
- ret = run_cmd("hy -c \"(koan\"")
- assert ret[0] == 1
- assert "Premature end of input" in ret[2]
+ _, err = run_cmd("hy -c \"(koan\"", expect=1)
+ assert "Premature end of input" in err
def test_bin_hy_icmd():
- ret = run_cmd("hy -i \"(koan)\"", "(ideas)")
- assert ret[0] == 0
- output = ret[1]
-
+ output, _ = run_cmd("hy -i \"(koan)\"", "(ideas)")
assert "monk" in output
assert "figlet" in output
def test_bin_hy_icmd_file():
- ret = run_cmd("hy -i resources/icmd_test_file.hy", "(ideas)")
- assert ret[0] == 0
- output = ret[1]
-
+ output, _ = run_cmd("hy -i resources/icmd_test_file.hy", "(ideas)")
assert "Hy!" in output
def test_bin_hy_icmd_and_spy():
- ret = run_cmd("hy -i \"(+ [] [])\" --spy", "(+ 1 1)")
- assert ret[0] == 0
- output = ret[1]
-
+ output, _ = run_cmd("hy -i \"(+ [] [])\" --spy", "(+ 1 1)")
assert "([] + [])" in output
def test_bin_hy_missing_file():
- ret = run_cmd("hy foobarbaz")
- assert ret[0] == 2
- assert "No such file" in ret[2]
+ _, err = run_cmd("hy foobarbaz", expect=2)
+ assert "No such file" in err
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]
+ assert "usage" in run_cmd("hy tests/resources/argparse_ex.hy -h")[0]
+ assert "got c" in run_cmd("hy tests/resources/argparse_ex.hy -c bar")[0]
+ assert "foo" in run_cmd("hy tests/resources/argparse_ex.hy -i foo")[0]
+ assert "foo" in run_cmd("hy tests/resources/argparse_ex.hy -i foo -c bar")[0] # noqa
def test_bin_hyc():
- ret = run_cmd("hyc")
- assert ret[0] == 2
- assert "usage" in ret[2]
- ret = run_cmd("hyc -h")
- assert ret[0] == 0
- assert "usage" in ret[1]
- ret = run_cmd("hyc tests/resources/argparse_ex.hy")
- assert ret[0] == 0
- assert "Compiling" in ret[1]
+ _, err = run_cmd("hyc", expect=2)
+ assert "usage" in err
+
+ output, _ = run_cmd("hyc -h")
+ assert "usage" in output
+
+ output, _ = run_cmd("hyc tests/resources/argparse_ex.hy")
+ assert "Compiling" in output
assert os.path.exists("tests/resources/argparse_ex.pyc")
def test_bin_hyc_missing_file():
- ret = run_cmd("hyc foobarbaz")
- assert ret[0] == 2
- assert "[Errno 2]" in ret[2]
+ _, err = run_cmd("hyc foobarbaz", expect=2)
+ assert "[Errno 2]" in err
def test_hy2py():
@@ -144,10 +192,10 @@ def test_hy2py():
continue
else:
i += 1
- ret = run_cmd("hy2py -s -a " + os.path.join(dirpath, f))
- assert ret[0] == 0, f
- assert len(ret[1]) > 1, f
- assert len(ret[2]) == 0, f
+ output, err = run_cmd("hy2py -s -a " +
+ os.path.join(dirpath, f))
+ assert len(output) > 1, f
+ assert len(err) == 0, f
assert i
@@ -159,48 +207,40 @@ def test_bin_hy_builtins():
def test_bin_hy_main():
- ret = run_cmd("hy tests/resources/bin/main.hy")
- assert ret[0] == 0
- assert "Hello World" in ret[1]
+ output, _ = run_cmd("hy tests/resources/bin/main.hy")
+ assert "Hello World" in output
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]
+ output, _ = run_cmd("hy tests/resources/bin/main.hy test 123")
+ assert "test" in output
+ assert "123" in output
def test_bin_hy_main_exitvalue():
- ret = run_cmd("hy tests/resources/bin/main.hy exit1")
- assert ret[0] == 1
+ run_cmd("hy tests/resources/bin/main.hy exit1", expect=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]
+ output, _ = run_cmd("hy tests/resources/bin/nomain.hy")
+ assert "This Should Still Work" in output
def test_bin_hy_module_main():
- ret = run_cmd("hy -m tests.resources.bin.main")
- assert ret[0] == 0
- assert "Hello World" in ret[1]
+ output, _ = run_cmd("hy -m tests.resources.bin.main")
+ assert "Hello World" in output
def test_bin_hy_module_main_args():
- ret = run_cmd("hy -m tests.resources.bin.main test 123")
- assert ret[0] == 0
- assert "test" in ret[1]
- assert "123" in ret[1]
+ output, _ = run_cmd("hy -m tests.resources.bin.main test 123")
+ assert "test" in output
+ assert "123" in output
def test_bin_hy_module_main_exitvalue():
- ret = run_cmd("hy -m tests.resources.bin.main exit1")
- assert ret[0] == 1
+ run_cmd("hy -m tests.resources.bin.main exit1", expect=1)
def test_bin_hy_module_no_main():
- ret = run_cmd("hy -m tests.resources.bin.nomain")
- assert ret[0] == 0
- assert "This Should Still Work" in ret[1]
+ output, _ = run_cmd("hy -m tests.resources.bin.nomain")
+ assert "This Should Still Work" in output