Merge branch 'hy-repr'

This commit is contained in:
Kodi Arfer 2017-03-25 08:22:38 -07:00
commit 741b9b9bd6
10 changed files with 405 additions and 109 deletions

4
NEWS
View File

@ -22,6 +22,10 @@ Changes from 0.12.1
returns. returns.
* `setv` no longer unnecessarily tries to get attributes * `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 Changes from 0.12.0
[ Bug Fixes ] [ Bug Fixes ]

48
docs/contrib/hy_repr.rst Normal file
View File

@ -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``.

View File

@ -16,3 +16,4 @@ Contents:
profile profile
sequences sequences
walk walk
hy_repr

View File

@ -193,7 +193,6 @@ Hy. Let's experiment with this in the hy interpreter::
[1, 2, 3] [1, 2, 3]
=> {"dog" "bark" => {"dog" "bark"
... "cat" "meow"} ... "cat" "meow"}
...
{'dog': 'bark', 'cat': 'meow'} {'dog': 'bark', 'cat': 'meow'}
=> (, 1 2 3) => (, 1 2 3)
(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. 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 If you are familiar with other Lisps, you may be interested that Hy
supports the Common Lisp method of quoting: supports the Common Lisp method of quoting:

View File

@ -32,14 +32,16 @@ import code
import ast import ast
import sys import sys
import os import os
import importlib
import astor.codegen import astor.codegen
import hy import hy
from hy.lex import LexException, PrematureEndOfInput, tokenize from hy.lex import LexException, PrematureEndOfInput, tokenize
from hy.compiler import hy_compile, HyTypeError from hy.lex.parser import hy_symbol_mangle
from hy.importer import (ast_compile, import_buffer_to_module, from hy.compiler import HyTypeError
from hy.importer import (hy_eval, import_buffer_to_module,
import_file_to_ast, import_file_to_hst, import_file_to_ast, import_file_to_hst,
import_buffer_to_ast, import_buffer_to_hst) import_buffer_to_ast, import_buffer_to_hst)
from hy.completer import completion from hy.completer import completion
@ -73,25 +75,37 @@ builtins.quit = HyQuitter('quit')
builtins.exit = HyQuitter('exit') 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): class HyREPL(code.InteractiveConsole):
def __init__(self, spy=False, locals=None, filename="<input>"): def __init__(self, spy=False, output_fn=None, locals=None,
filename="<input>"):
self.spy = spy 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, code.InteractiveConsole.__init__(self, locals=locals,
filename=filename) filename=filename)
def runsource(self, source, filename='<input>', symbol='single'): def runsource(self, source, filename='<input>', symbol='single'):
global SIMPLE_TRACEBACKS global SIMPLE_TRACEBACKS
try: try:
tokens = tokenize(source) try:
except PrematureEndOfInput: tokens = tokenize(source)
return True 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: except LexException as e:
if e.source is None: if e.source is None:
e.source = source e.source = source
@ -100,10 +114,15 @@ class HyREPL(code.InteractiveConsole):
return False return False
try: try:
_ast = hy_compile(tokens, "__console__", root=ast.Interactive) def ast_callback(main_ast, expr_ast):
if self.spy: if self.spy:
print_python_code(_ast) # Mush the two AST chunks into a single module for
code = ast_compile(_ast, filename, symbol) # 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: except HyTypeError as e:
if e.source is None: if e.source is None:
e.source = source e.source = source
@ -117,7 +136,12 @@ class HyREPL(code.InteractiveConsole):
self.showtraceback() self.showtraceback()
return False 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 return False
@ -211,7 +235,7 @@ def run_file(filename):
return 0 return 0
def run_repl(hr=None, spy=False): def run_repl(hr=None, **kwargs):
import platform import platform
sys.ps1 = "=> " sys.ps1 = "=> "
sys.ps2 = "... " sys.ps2 = "... "
@ -221,7 +245,7 @@ def run_repl(hr=None, spy=False):
with completion(Completer(namespace)): with completion(Completer(namespace)):
if not hr: if not hr:
hr = HyREPL(spy, namespace) hr = HyREPL(locals=namespace, **kwargs)
hr.interact("{appname} {version} using " hr.interact("{appname} {version} using "
"{py}({build}) {pyversion} on {os}".format( "{py}({build}) {pyversion} on {os}".format(
@ -236,8 +260,8 @@ def run_repl(hr=None, spy=False):
return 0 return 0
def run_icommand(source, spy=False): def run_icommand(source, **kwargs):
hr = HyREPL(spy) hr = HyREPL(**kwargs)
if os.path.exists(source): if os.path.exists(source):
with open(source, "r") as f: with open(source, "r") as f:
source = f.read() source = f.read()
@ -272,7 +296,9 @@ def cmdline_handler(scriptname, argv):
help="program passed in as a string, then stay in REPL") help="program passed in as a string, then stay in REPL")
parser.add_argument("--spy", action="store_true", parser.add_argument("--spy", action="store_true",
help="print equivalent Python code before executing") 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("-v", "--version", action="version", version=VERSION)
parser.add_argument("--show-tracebacks", action="store_true", parser.add_argument("--show-tracebacks", action="store_true",
@ -315,7 +341,8 @@ def cmdline_handler(scriptname, argv):
if options.icommand: if options.icommand:
# User did "hy -i ..." # 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:
if options.args[0] == "-": if options.args[0] == "-":
@ -332,7 +359,7 @@ def cmdline_handler(scriptname, argv):
sys.exit(e.errno) sys.exit(e.errno)
# User did NOTHING! # 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" # entry point for cmd line script "hy"

77
hy/contrib/hy_repr.hy Normal file
View File

@ -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))

View File

@ -111,7 +111,7 @@ def import_buffer_to_module(module_name, buf):
return mod return mod
def hy_eval(hytree, namespace, module_name): def hy_eval(hytree, namespace, module_name, ast_callback=None):
foo = HyObject() foo = HyObject()
foo.start_line = 0 foo.start_line = 0
foo.end_line = 0 foo.end_line = 0
@ -133,6 +133,9 @@ def hy_eval(hytree, namespace, module_name):
node.lineno = 1 node.lineno = 1
node.col_offset = 1 node.col_offset = 1
if ast_callback:
ast_callback(_ast, expr)
if not isinstance(namespace, dict): if not isinstance(namespace, dict):
raise HyTypeError(foo, "Globals must be a dictionary") raise HyTypeError(foo, "Globals must be a dictionary")

View File

@ -19,6 +19,7 @@ from .native_tests.contrib.loop import * # noqa
from .native_tests.contrib.walk import * # noqa from .native_tests.contrib.walk import * # noqa
from .native_tests.contrib.multi import * # noqa from .native_tests.contrib.multi import * # noqa
from .native_tests.contrib.sequences import * # noqa from .native_tests.contrib.sequences import * # noqa
from .native_tests.contrib.hy_repr import * # noqa
if PY3: if PY3:
from .native_tests.py3_only_tests import * # noqa from .native_tests.py3_only_tests import * # noqa

View File

@ -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")))

View File

@ -23,116 +23,164 @@
# DEALINGS IN THE SOFTWARE. # DEALINGS IN THE SOFTWARE.
import os import os
import subprocess import subprocess
import re
from hy._compat import PY3 from hy._compat import PY3
hy_dir = os.environ.get('HY_DIR', '') 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), p = subprocess.Popen(os.path.join(hy_dir, cmd),
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
shell=True) shell=True)
stdout = ""
stderr = ""
if stdin_data is not None: if stdin_data is not None:
p.stdin.write(stdin_data.encode('ASCII')) p.stdin.write(stdin_data.encode('ASCII'))
p.stdin.flush() p.stdin.flush()
p.stdin.close() p.stdin.close()
# Read stdout and stderr otherwise if the PIPE buffer is full, we might # Read stdout and stderr otherwise if the PIPE buffer is full, we might
# wait for ever… # wait for ever…
stdout = ""
stderr = ""
while p.poll() is None: while p.poll() is None:
stdout += p.stdout.read().decode('utf-8') stdout += p.stdout.read().decode('utf-8')
stderr += p.stderr.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(): def test_bin_hy():
ret = run_cmd("hy", "") run_cmd("hy", "")
assert ret[0] == 0
def test_bin_hy_stdin(): def test_bin_hy_stdin():
ret = run_cmd("hy", '(koan)') output, _ = run_cmd("hy", '(koan)')
assert ret[0] == 0 assert "monk" in output
assert "monk" in ret[1]
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(): def test_bin_hy_cmd():
ret = run_cmd("hy -c \"(koan)\"") output, _ = run_cmd("hy -c \"(koan)\"")
assert ret[0] == 0 assert "monk" in output
assert "monk" in ret[1]
ret = run_cmd("hy -c \"(koan\"") _, err = run_cmd("hy -c \"(koan\"", expect=1)
assert ret[0] == 1 assert "Premature end of input" in err
assert "Premature end of input" in ret[2]
def test_bin_hy_icmd(): def test_bin_hy_icmd():
ret = run_cmd("hy -i \"(koan)\"", "(ideas)") output, _ = run_cmd("hy -i \"(koan)\"", "(ideas)")
assert ret[0] == 0
output = ret[1]
assert "monk" in output assert "monk" in output
assert "figlet" in output assert "figlet" in output
def test_bin_hy_icmd_file(): def test_bin_hy_icmd_file():
ret = run_cmd("hy -i resources/icmd_test_file.hy", "(ideas)") output, _ = run_cmd("hy -i resources/icmd_test_file.hy", "(ideas)")
assert ret[0] == 0
output = ret[1]
assert "Hy!" in output assert "Hy!" in output
def test_bin_hy_icmd_and_spy(): def test_bin_hy_icmd_and_spy():
ret = run_cmd("hy -i \"(+ [] [])\" --spy", "(+ 1 1)") output, _ = run_cmd("hy -i \"(+ [] [])\" --spy", "(+ 1 1)")
assert ret[0] == 0
output = ret[1]
assert "([] + [])" in output assert "([] + [])" in output
def test_bin_hy_missing_file(): def test_bin_hy_missing_file():
ret = run_cmd("hy foobarbaz") _, err = run_cmd("hy foobarbaz", expect=2)
assert ret[0] == 2 assert "No such file" in err
assert "No such file" in ret[2]
def test_bin_hy_file_with_args(): def test_bin_hy_file_with_args():
ret = run_cmd("hy tests/resources/argparse_ex.hy -h") assert "usage" in run_cmd("hy tests/resources/argparse_ex.hy -h")[0]
assert ret[0] == 0 assert "got c" in run_cmd("hy tests/resources/argparse_ex.hy -c bar")[0]
assert "usage" in ret[1] assert "foo" in run_cmd("hy tests/resources/argparse_ex.hy -i foo")[0]
ret = run_cmd("hy tests/resources/argparse_ex.hy -c bar") assert "foo" in run_cmd("hy tests/resources/argparse_ex.hy -i foo -c bar")[0] # noqa
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_bin_hyc(): def test_bin_hyc():
ret = run_cmd("hyc") _, err = run_cmd("hyc", expect=2)
assert ret[0] == 2 assert "usage" in err
assert "usage" in ret[2]
ret = run_cmd("hyc -h") output, _ = run_cmd("hyc -h")
assert ret[0] == 0 assert "usage" in output
assert "usage" in ret[1]
ret = run_cmd("hyc tests/resources/argparse_ex.hy") output, _ = run_cmd("hyc tests/resources/argparse_ex.hy")
assert ret[0] == 0 assert "Compiling" in output
assert "Compiling" in ret[1]
assert os.path.exists("tests/resources/argparse_ex.pyc") assert os.path.exists("tests/resources/argparse_ex.pyc")
def test_bin_hyc_missing_file(): def test_bin_hyc_missing_file():
ret = run_cmd("hyc foobarbaz") _, err = run_cmd("hyc foobarbaz", expect=2)
assert ret[0] == 2 assert "[Errno 2]" in err
assert "[Errno 2]" in ret[2]
def test_hy2py(): def test_hy2py():
@ -144,10 +192,10 @@ def test_hy2py():
continue continue
else: else:
i += 1 i += 1
ret = run_cmd("hy2py -s -a " + os.path.join(dirpath, f)) output, err = run_cmd("hy2py -s -a " +
assert ret[0] == 0, f os.path.join(dirpath, f))
assert len(ret[1]) > 1, f assert len(output) > 1, f
assert len(ret[2]) == 0, f assert len(err) == 0, f
assert i assert i
@ -159,48 +207,40 @@ def test_bin_hy_builtins():
def test_bin_hy_main(): def test_bin_hy_main():
ret = run_cmd("hy tests/resources/bin/main.hy") output, _ = run_cmd("hy tests/resources/bin/main.hy")
assert ret[0] == 0 assert "Hello World" in output
assert "Hello World" in ret[1]
def test_bin_hy_main_args(): def test_bin_hy_main_args():
ret = run_cmd("hy tests/resources/bin/main.hy test 123") output, _ = run_cmd("hy tests/resources/bin/main.hy test 123")
assert ret[0] == 0 assert "test" in output
assert "test" in ret[1] assert "123" in output
assert "123" in ret[1]
def test_bin_hy_main_exitvalue(): def test_bin_hy_main_exitvalue():
ret = run_cmd("hy tests/resources/bin/main.hy exit1") run_cmd("hy tests/resources/bin/main.hy exit1", expect=1)
assert ret[0] == 1
def test_bin_hy_no_main(): def test_bin_hy_no_main():
ret = run_cmd("hy tests/resources/bin/nomain.hy") output, _ = run_cmd("hy tests/resources/bin/nomain.hy")
assert ret[0] == 0 assert "This Should Still Work" in output
assert "This Should Still Work" in ret[1]
def test_bin_hy_module_main(): def test_bin_hy_module_main():
ret = run_cmd("hy -m tests.resources.bin.main") output, _ = run_cmd("hy -m tests.resources.bin.main")
assert ret[0] == 0 assert "Hello World" in output
assert "Hello World" in ret[1]
def test_bin_hy_module_main_args(): def test_bin_hy_module_main_args():
ret = run_cmd("hy -m tests.resources.bin.main test 123") output, _ = run_cmd("hy -m tests.resources.bin.main test 123")
assert ret[0] == 0 assert "test" in output
assert "test" in ret[1] assert "123" in output
assert "123" in ret[1]
def test_bin_hy_module_main_exitvalue(): def test_bin_hy_module_main_exitvalue():
ret = run_cmd("hy -m tests.resources.bin.main exit1") run_cmd("hy -m tests.resources.bin.main exit1", expect=1)
assert ret[0] == 1
def test_bin_hy_module_no_main(): def test_bin_hy_module_no_main():
ret = run_cmd("hy -m tests.resources.bin.nomain") output, _ = run_cmd("hy -m tests.resources.bin.nomain")
assert ret[0] == 0 assert "This Should Still Work" in output
assert "This Should Still Work" in ret[1]