diff --git a/.travis.yml b/.travis.yml index 9f8dea7..bd99240 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ matrix: include: - python: 3.5 env: TOXENV=py35 + - python: 3.6-dev + env: TOXENV=py36 env: - TOXENV=py27 - TOXENV=py33 diff --git a/AUTHORS b/AUTHORS index e4a45bc..e88a417 100644 --- a/AUTHORS +++ b/AUTHORS @@ -69,3 +69,4 @@ * Andrew Savchyn * Lev Kravinsky * Luna Lunapiena +* Jakub Wilk diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 01ba87a..0e01d57 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -7,7 +7,9 @@ helps in making Hy more awesome. Pull requests are great! We love them; here is a quick guide: - `Fork the repo`_ and create a topic branch for a feature/fix. Avoid - making changes directly on the master branch. + making changes directly on the master branch. If you would like to + contribute but don't know how to begin, the `good-first-bug`_ label + of the `issue tracker`_ is the place to go. (If you're new to Git: `Start Here`_) - All incoming features should be accompanied with tests. @@ -36,7 +38,7 @@ Pull requests are great! We love them; here is a quick guide: + Try sticking to the 50 character limit for the first line of Git commit messages. - + For more detail/explainations, follow this up with a blank line and + + For more detail/explanations, follow this up with a blank line and continue describing the commit in detail. - Finally, add yourself to the AUTHORS file (as a separate commit): you @@ -93,4 +95,5 @@ http://contributor-covenant.org/version/1/1/0/. .. _Contributor Covenant: http://contributor-covenant.org .. _issue tracker: https://github.com/hylang/hy/issues .. _Fork the Repo: https://help.github.com/articles/fork-a-repo/ -.. _Start Here: http://rogerdudler.github.io/git-guide/) \ No newline at end of file +.. _Start Here: http://rogerdudler.github.io/git-guide/ +.. _good-first-bug: http://github.com/hylang/hy/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-bug diff --git a/Makefile b/Makefile index ccf8456..40d8eaf 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ all: @echo "" docs: - make -C docs html + $(MAKE) -C docs html upload: r python setup.py sdist upload @@ -72,10 +72,10 @@ coveralls: clean: @find . -name "*.pyc" -exec rm {} \; - @find -name __pycache__ -delete + @find . -name __pycache__ -delete @${RM} -r -f .tox @${RM} -r -f dist @${RM} -r -f *.egg-info @${RM} -r -f docs/_build -.PHONY: docs +.PHONY: all docs upload full venv dev test tox flake clear d diff r python coveralls clean diff --git a/NEWS b/NEWS index ece2092..319e391 100644 --- a/NEWS +++ b/NEWS @@ -280,7 +280,7 @@ Changes from Hy 0.9.10 * Clarified rest / cdr, cleaned up require * Make with return the last expression from its branch * Fix yielding to not suck (#151) - * Make assoc accept multiple values, also added a even/odd check for + * Make assoc accept multiple values, also added an even/odd check for checkargs * Added ability to parse doc strings set in defclass declarations, * Provide bin scripts for both Windows and *nix diff --git a/README.md b/README.md index 3c1eb38..a487982 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Hy [![Version](https://img.shields.io/pypi/v/hy.svg)](https://pypi.python.org/pypi/hy) [![Coverage Status](https://img.shields.io/coveralls/hylang/hy/master.svg)](https://coveralls.io/r/hylang/hy) -[![XKCD #224](https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png)](https://xkcd.com/224/) +XKCD #224 Lisp and Python should love each other. Let's make it happen. [Try it](http://try-hy.appspot.com/). diff --git a/docs/contrib/anaphoric.rst b/docs/contrib/anaphoric.rst index cce39e0..51ece00 100644 --- a/docs/contrib/anaphoric.rst +++ b/docs/contrib/anaphoric.rst @@ -11,7 +11,7 @@ concise and easy to read. deliberately captures some form supplied to the macro which may be referred to by an anaphor (an expression referring to another). - -- Wikipedia (http://en.wikipedia.org/wiki/Anaphoric_macro) + -- Wikipedia (https://en.wikipedia.org/wiki/Anaphoric_macro) To use these macros you need to require the hy.contrib.anaphoric module like so: diff --git a/docs/contrib/index.rst b/docs/contrib/index.rst index 612ccdd..ed75add 100644 --- a/docs/contrib/index.rst +++ b/docs/contrib/index.rst @@ -7,8 +7,9 @@ Contents: .. toctree:: :maxdepth: 3 + alias anaphoric + flow loop multi - flow - alias + walk diff --git a/docs/contrib/loop.rst b/docs/contrib/loop.rst index 9968aab..aaef239 100644 --- a/docs/contrib/loop.rst +++ b/docs/contrib/loop.rst @@ -24,7 +24,7 @@ tail-call optimization (TCO) in their Hy code. position to be implemented as efficiently as goto statements, thus allowing efficient structured programming. - -- Wikipedia (http://en.wikipedia.org/wiki/Tail_call) + -- Wikipedia (https://en.wikipedia.org/wiki/Tail_call) Macros ====== @@ -39,7 +39,7 @@ rebinds the variables set in the recursion point and sends code execution back to that recursion point. If ``recur`` is used in a non-tail position, an exception is raised. -Usage: `(loop bindings &rest body)` +Usage: ``(loop bindings &rest body)`` Example: diff --git a/docs/contrib/walk.rst b/docs/contrib/walk.rst new file mode 100644 index 0000000..01e2bc0 --- /dev/null +++ b/docs/contrib/walk.rst @@ -0,0 +1,94 @@ +==== +walk +==== + +.. versionadded:: 0.11.0 + +Functions +========= + +.. _walk: + +walk +----- + +Usage: `(walk inner outer form)` + +``walk`` traverses ``form``, an arbitrary data structure. Applies +``inner`` to each element of form, building up a data structure of the +same type. Applies ``outer`` to the result. + +Example: + +.. code-block:: hy + + => (import [hy.contrib.walk [walk]]) + => (setv a '(a b c d e f)) + => (walk ord identity a) + (97 98 99 100 101 102) + => (walk ord first a) + 97 + +postwalk +--------- + +.. _postwalk: + +Usage: `(postwalk f form)` + +Performs depth-first, post-order traversal of ``form``. Calls ``f`` on +each sub-form, uses ``f`` 's return value in place of the original. + +.. code-block:: hy + + => (import [hy.contrib.walk [postwalk]]) + => (def trail '([1 2 3] [4 [5 6 [7]]])) + => (defn walking [x] + (print "Walking:" x) + x ) + => (postwalk walking trail) + Walking: 1 + Walking: 2 + Walking: 3 + Walking: (1 2 3) + Walking: 4 + Walking: 5 + Walking: 6 + Walking: 7 + Walking: (7) + Walking: (5 6 [7]) + Walking: (4 [5 6 [7]]) + Walking: ([1 2 3] [4 [5 6 [7]]]) + ([1 2 3] [4 [5 6 [7]]]) + +prewalk +-------- + +.. _prewalk: + +Usage: `(prewalk f form)` + +Performs depth-first, pre-order traversal of ``form``. Calls ``f`` on +each sub-form, uses ``f`` 's return value in place of the original. + +.. code-block:: hy + + => (import [hy.contrib.walk [prewalk]]) + => (def trail '([1 2 3] [4 [5 6 [7]]])) + => (defn walking [x] + (print "Walking:" x) + x ) + => (prewalk walking trail) + Walking: ([1 2 3] [4 [5 6 [7]]]) + Walking: [1 2 3] + Walking: 1 + Walking: 2 + Walking: 3 + Walking: [4 [5 6 [7]]] + Walking: 4 + Walking: [5 6 [7]] + Walking: 5 + Walking: 6 + Walking: [7] + Walking: 7 + ([1 2 3] [4 [5 6 [7]]]) diff --git a/docs/hacking.rst b/docs/hacking.rst index b902a68..4042208 100644 --- a/docs/hacking.rst +++ b/docs/hacking.rst @@ -32,7 +32,7 @@ Do this: $ . venv/bin/activate - or use `virtualenvwrapper `_ + or use `virtualenvwrapper `_ to create and manage your virtual environment:: $ mkvirtualenv hy @@ -63,7 +63,7 @@ Test! ===== Tests are located in ``tests/``. We use `nose -`_. +`_. To run the tests:: diff --git a/docs/language/api.rst b/docs/language/api.rst index c96e0b6..fa09c82 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -22,7 +22,7 @@ languages. string. For example, ``foo`` will become ``FOO``. * UTF-8 entities will be encoded using - `punycode `_ and prefixed with + `punycode `_ and prefixed with ``hy_``. For instance, ``⚘`` will become ``hy_w7h``, ``♥`` will become ``hy_g6h``, and ``i♥u`` will become ``hy_iu_t0x``. @@ -953,11 +953,20 @@ that ``import`` can be used. (import [sys :as systest]) ;; You can list as many imports as you like of different types. + ;; + ;; Python: + ;; from tests.resources import kwtest, function_with_a_dash + ;; from os.path import exists, isdir as is_dir, isfile as is_file + ;; import sys as systest (import [tests.resources [kwtest function-with-a-dash]] - [os.path [exists isdir isfile]] + [os.path [exists + isdir :as dir? + isfile :as file?]] [sys :as systest]) ;; Import all module functions into current namespace + ;; + ;; Python: from sys import * (import [sys [*]]) diff --git a/docs/language/cli.rst b/docs/language/cli.rst index 28bd067..ae9b620 100644 --- a/docs/language/cli.rst +++ b/docs/language/cli.rst @@ -35,7 +35,7 @@ Command Line Options .. cmdoption:: --spy - Print equivalent Python code before executing. For example:: + Print equivalent Python code before executing in REPL. For example:: => (defn salutationsnm [name] (print (+ "Hy " name "!"))) def salutationsnm(name): @@ -45,6 +45,7 @@ Command Line Options Hy YourName! => + `--spy` only works on REPL mode. .. versionadded:: 0.9.11 .. cmdoption:: --show-tracebacks diff --git a/docs/language/core.rst b/docs/language/core.rst index b3bb65f..80b2c0e 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -141,7 +141,7 @@ is ``True``, the function prints Python code instead. print('Hello World!') -.. _emtpy?-fn: +.. _empty?-fn: empty? ------ diff --git a/docs/language/internals.rst b/docs/language/internals.rst index d543847..c4b9db6 100644 --- a/docs/language/internals.rst +++ b/docs/language/internals.rst @@ -170,7 +170,7 @@ Cons Cells cells`_. Cons cells are especially useful to mimic features of "usual" LISP variants such as Scheme or Common Lisp. -.. _cons cells: http://en.wikipedia.org/wiki/Cons +.. _cons cells: https://en.wikipedia.org/wiki/Cons A cons cell is a 2-item object, containing a ``car`` (head) and a ``cdr`` (tail). In some Lisp variants, the cons cell is the fundamental diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 24bca21..7ee72e0 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -13,7 +13,7 @@ Quickstart 1. Create a `Virtual Python Environment `_. 2. Activate your Virtual Python Environment. -3. Install `hy from PyPI `_ with ``pip install hy``. +3. Install `hy from GitHub `_ with ``$ pip install git+https://github.com/hylang/hy.git``. 4. Start a REPL with ``hy``. 5. Type stuff in the REPL:: diff --git a/docs/style-guide.rst b/docs/style-guide.rst index b577d79..40fa86a 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -105,7 +105,7 @@ Layout & Indentation .. code-block:: clj - (let [foo (bar)] + (let [foo (bar) qux (baz)] (foo qux)) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index f455bd3..834332d 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -267,7 +267,7 @@ In Hy, you would do: [true (print "That variable is jussssst right!")]) -What you'll notice is that ``cond`` switches off between a some statement +What you'll notice is that ``cond`` switches off between a statement that is executed and checked conditionally for true or falseness, and then a bit of code to execute if it turns out to be true. You'll also notice that the ``else`` is implemented at the end simply by checking @@ -292,7 +292,7 @@ You can do the following: (if (try-some-thing) (do (print "this is if true") - (print "and why not, let's keep talking about how true it is!)) + (print "and why not, let's keep talking about how true it is!")) (print "this one's still simply just false")) You can see that we used ``do`` to wrap multiple statements. If you're diff --git a/hy/cmdline.py b/hy/cmdline.py index 195a5bc..35f75c6 100644 --- a/hy/cmdline.py +++ b/hy/cmdline.py @@ -274,7 +274,7 @@ def cmdline_handler(scriptname, argv): parser.add_argument("--spy", action="store_true", help="print equivalent Python code before executing") - parser.add_argument("-v", action="version", version=VERSION) + parser.add_argument("-v", "--version", action="version", version=VERSION) parser.add_argument("--show-tracebacks", action="store_true", help="show complete tracebacks for Hy exceptions") diff --git a/hy/compiler.py b/hy/compiler.py index c2e4dc3..b6c6d27 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -503,19 +503,13 @@ class HyASTCompiler(object): for expr in exprs: if expr in ll_keywords: - if expr == "&rest" and lambda_keyword is None: - lambda_keyword = expr - elif expr == "&optional": + if expr == "&optional": if len(defaults) > 0: raise HyTypeError(expr, "There can only be &optional " "arguments or one &key argument") lambda_keyword = expr - elif expr == "&key": - lambda_keyword = expr - elif expr == "&kwonly": - lambda_keyword = expr - elif expr == "&kwargs": + elif expr in ("&rest", "&key", "&kwonly", "&kwargs"): lambda_keyword = expr else: raise HyTypeError(expr, @@ -1516,7 +1510,8 @@ class HyASTCompiler(object): gen.append(ast.comprehension( target=target, iter=gen_res.force_expr, - ifs=[])) + ifs=[], + is_async=False)) if cond.expr: gen[-1].ifs.append(cond.expr) @@ -1910,7 +1905,6 @@ class HyASTCompiler(object): col_offset=child.start_column) return ret - @builds("+") @builds("*") @builds("/") @builds("//") @@ -1918,8 +1912,7 @@ class HyASTCompiler(object): if len(expression) > 2: return self.compile_maths_expression(expression) else: - id_op = {"+": HyInteger(0), "*": HyInteger(1), "/": HyInteger(1), - "//": HyInteger(1)} + id_op = {"*": HyInteger(1), "/": HyInteger(1), "//": HyInteger(1)} op = expression.pop(0) arg = expression.pop(0) if expression else id_op[op] @@ -1930,20 +1923,34 @@ class HyASTCompiler(object): ]).replace(expression) return self.compile_maths_expression(expr) - @builds("-") - @checkargs(min=1) - def compile_maths_expression_sub(self, expression): + def compile_maths_expression_additive(self, expression): if len(expression) > 2: return self.compile_maths_expression(expression) else: - arg = expression[1] + op = {"+": ast.UAdd, "-": ast.USub}[expression.pop(0)]() + arg = expression.pop(0) ret = self.compile(arg) - ret += ast.UnaryOp(op=ast.USub(), + ret += ast.UnaryOp(op=op, operand=ret.force_expr, lineno=arg.start_line, col_offset=arg.start_column) return ret + @builds("+") + def compile_maths_expression_add(self, expression): + if len(expression) == 1: + # Nullary + + return ast.Num(n=long_type(0), + lineno=expression.start_line, + col_offset=expression.start_column) + else: + return self.compile_maths_expression_additive(expression) + + @builds("-") + @checkargs(min=1) + def compile_maths_expression_sub(self, expression): + return self.compile_maths_expression_additive(expression) + @builds("+=") @builds("/=") @builds("//=") @@ -2449,6 +2456,9 @@ class HyASTCompiler(object): raise HyTypeError(name, ("received a `%s' instead of a symbol " "for macro name" % type(name).__name__)) name = HyString(name).replace(name) + for kw in ("&kwonly", "&kwargs", "&key"): + if kw in expression[0]: + raise HyTypeError(name, "macros cannot use %s" % kw) new_expression = HyExpression([ HySymbol("with_decorator"), HyExpression([HySymbol("hy.macros.macro"), name]), diff --git a/hy/core/language.hy b/hy/core/language.hy index 07bc202..b7fce7e 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -39,10 +39,6 @@ (import [hy.lex [LexException PrematureEndOfInput tokenize]]) (import [hy.compiler [HyASTCompiler]]) -(defn _numeric-check [x] - (if (not (numeric? x)) - (raise (TypeError (.format "{0!r} is not a number" x))))) - (defn butlast [coll] "Returns coll except of last element." (drop-last 1 coll)) @@ -66,7 +62,6 @@ (defn dec [n] "Decrement n by 1" - (_numeric-check n) (- n 1)) (defn disassemble [tree &optional [codegen false]] @@ -170,7 +165,6 @@ (defn even? [n] "Return true if n is an even number" - (_numeric-check n) (= (% n 2) 0)) (defn every? [pred coll] @@ -239,7 +233,6 @@ (defn inc [n] "Increment n by 1" - (_numeric-check n) (+ n 1)) (defn instance? [klass x] @@ -325,7 +318,6 @@ (defn neg? [n] "Return true if n is < 0" - (_numeric-check n) (< n 0)) (defn none? [x] @@ -347,7 +339,6 @@ (defn odd? [n] "Return true if n is an odd number" - (_numeric-check n) (= (% n 2) 1)) (def -sentinel (object)) @@ -364,7 +355,6 @@ (defn pos? [n] "Return true if n is > 0" - (_numeric_check n) (> n 0)) (defn rest [coll] @@ -415,7 +405,6 @@ (defn zero? [n] "Return true if n is 0" - (_numeric_check n) (= n 0)) (defn read [&optional [from-file sys.stdin] diff --git a/hy/core/shadow.hy b/hy/core/shadow.hy index b63219d..e66d075 100644 --- a/hy/core/shadow.hy +++ b/hy/core/shadow.hy @@ -30,7 +30,7 @@ (if (zero? count) (raise (TypeError "Need at least 1 argument to add/concatenate")) (if (= count 1) - (get args 0) + (operator.pos (get args 0)) (reduce operator.add args))))) diff --git a/hy/importer.py b/hy/importer.py index 8fcbd27..6eca35a 100644 --- a/hy/importer.py +++ b/hy/importer.py @@ -45,12 +45,12 @@ def ast_compile(ast, filename, mode): def import_buffer_to_hst(buf): - """Import content from buf and return an Hy AST.""" + """Import content from buf and return a Hy AST.""" return tokenize(buf + "\n") def import_file_to_hst(fpath): - """Import content from fpath and return an Hy AST.""" + """Import content from fpath and return a Hy AST.""" try: with open(fpath, 'r', encoding='utf-8') as f: return import_buffer_to_hst(f.read()) diff --git a/hy/models/set.py b/hy/models/set.py index 3a81aed..fda7b52 100644 --- a/hy/models/set.py +++ b/hy/models/set.py @@ -18,19 +18,16 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +from hy.models import _wrappers, wrap_value from hy.models.list import HyList -from functools import reduce class HySet(HyList): """ - Hy set (actually a list that pretends to be a set) + Hy set (just a representation of a set) """ - def __init__(self, items): - items = sorted(items) - items = list(reduce(lambda r, v: v in r and r or r+[v], items, [])) - super(HySet, self).__init__(items) - def __repr__(self): return "#{%s}" % (" ".join([repr(x) for x in self])) + +_wrappers[set] = lambda s: HySet(wrap_value(x) for x in s) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 4898c40..31c90bd 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -397,6 +397,7 @@ def test_lambda_list_keywords_rest(): """ Ensure we can compile functions with lambda list keywords.""" can_compile("(fn (x &rest xs) (print xs))") cant_compile("(fn (x &rest xs &rest ys) (print xs))") + can_compile("(fn (&optional a &rest xs) (print xs))") def test_lambda_list_keywords_key(): @@ -410,6 +411,7 @@ def test_lambda_list_keywords_kwargs(): """ Ensure we can compile functions with &kwargs.""" can_compile("(fn (x &kwargs kw) (list x kw))") cant_compile("(fn (x &kwargs xs &kwargs ys) (list x xs ys))") + can_compile("(fn (&optional x &kwargs kw) (list x kw))") def test_lambda_list_keywords_kwonly(): diff --git a/tests/lex/test_lex.py b/tests/lex/test_lex.py index 4413a6b..81407cc 100644 --- a/tests/lex/test_lex.py +++ b/tests/lex/test_lex.py @@ -230,6 +230,16 @@ def test_sets(): HyExpression([HySymbol("baz"), HySymbol("quux")]) ])] + # Duplicate items in a literal set should be okay (and should + # be preserved). + objs = tokenize("#{1 2 1 1 2 1}") + assert objs == [HySet([HyInteger(n) for n in [1, 2, 1, 1, 2, 1]])] + assert len(objs[0]) == 6 + + # https://github.com/hylang/hy/issues/1120 + objs = tokenize("#{a 1}") + assert objs == [HySet([HySymbol("a"), HyInteger(1)])] + def test_nospace(): """ Ensure we can tokenize without spaces if we have to """ diff --git a/tests/models/test_set.py b/tests/models/test_set.py index ceea726..0462c2c 100644 --- a/tests/models/test_set.py +++ b/tests/models/test_set.py @@ -5,4 +5,4 @@ hyset = HySet([3, 1, 2, 2]) def test_set(): - assert hyset == [1, 2, 3] + assert hyset == [3, 1, 2, 2] diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy index 9e3ad52..0bf1d4f 100644 --- a/tests/native_tests/core.hy +++ b/tests/native_tests/core.hy @@ -19,6 +19,8 @@ ;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;; DEALINGS IN THE SOFTWARE. +(import [hy._compat [PY3]]) + ;;;; some simple helpers (defn assert-true [x] @@ -33,6 +35,12 @@ (defn assert-nil [x] (assert (is x nil))) +(defn assert-requires-num [f] + (for [x ["foo" [] None]] + (try (f x) + (except [TypeError] True) + (else (assert False))))) + (defn test-coll? [] "NATIVE: testing coll?" (assert-true (coll? [1 2 3])) @@ -66,12 +74,7 @@ (assert-equal 0 (dec 1)) (assert-equal -1 (dec 0)) (assert-equal 0 (dec (dec 2))) - (try (do (dec "foo") (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (dec []) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (dec None) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e)))))) + (assert-requires-num dec)) (defn test-setv [] "NATIVE: testing setv mutation" @@ -173,12 +176,7 @@ (assert-true (even? -2)) (assert-false (even? 1)) (assert-true (even? 0)) - (try (even? "foo") - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (even? []) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (even? None) - (except [e [TypeError]] (assert (in "not a number" (str e)))))) + (assert-requires-num even?)) (defn test-every? [] "NATIVE: testing the every? function" @@ -263,12 +261,11 @@ "NATIVE: testing the inc function" (assert-equal 3 (inc 2)) (assert-equal 0 (inc -1)) - (try (do (inc "foo") (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (inc []) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (inc None) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e)))))) + (assert-requires-num inc) + + (defclass X [object] + [__add__ (fn [self other] (.format "__add__ got {}" other))]) + (assert-equal (inc (X)) "__add__ got 1")) (defn test-instance [] "NATIVE: testing instance? function" @@ -394,24 +391,14 @@ (assert-true (neg? -2)) (assert-false (neg? 1)) (assert-false (neg? 0)) - (try (do (neg? "foo") (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (neg? []) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (neg? None) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e)))))) + (when PY3 + (assert-requires-num neg?))) (defn test-zero [] "NATIVE: testing the zero? function" (assert-false (zero? -2)) (assert-false (zero? 1)) - (assert-true (zero? 0)) - (try (do (zero? "foo") (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (zero? []) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (zero? None) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e)))))) + (assert-true (zero? 0))) (defn test-none [] "NATIVE: testing for `is None`" @@ -463,12 +450,7 @@ (assert-true (odd? -3)) (assert-true (odd? 1)) (assert-false (odd? 0)) - (try (do (odd? "foo") (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (odd? []) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (odd? None) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e)))))) + (assert-requires-num odd?)) (defn test-partition [] "NATIVE: testing the partition function" @@ -500,12 +482,8 @@ (assert-true (pos? 2)) (assert-false (pos? -1)) (assert-false (pos? 0)) - (try (do (pos? "foo") (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (pos? []) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e))))) - (try (do (pos? None) (assert False)) - (except [e [TypeError]] (assert (in "not a number" (str e)))))) + (when PY3 + (assert-requires-num pos?))) (defn test-remove [] "NATIVE: testing the remove function" diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 0300ffb..54b3e61 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -47,6 +47,7 @@ (defn test-sets [] "NATIVE: test sets work right" (assert (= #{1 2 3 4} (| #{1 2} #{3 4}))) + (assert (= (type #{1 2 3 4}) set)) (assert (= #{} (set)))) diff --git a/tests/native_tests/mathematics.hy b/tests/native_tests/mathematics.hy index 58c5457..5ab90c4 100644 --- a/tests/native_tests/mathematics.hy +++ b/tests/native_tests/mathematics.hy @@ -28,6 +28,18 @@ (assert (= 0 (+))))) +(defn test-add-unary [] + "NATIVE: test that unary + calls __pos__" + + (defclass X [object] + [__pos__ (fn [self] "called __pos__")]) + (assert (= (+ (X)) "called __pos__")) + + ; Make sure the shadowed version works, too. + (setv f +) + (assert (= (f (X)) "called __pos__"))) + + (setv test_div (fn [] "NATIVE: Test division" (assert (= 25 (/ 100 2 2))) diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy index f833a30..788e52b 100644 --- a/tests/native_tests/native_macros.hy +++ b/tests/native_tests/native_macros.hy @@ -1,3 +1,5 @@ +(import [hy.errors [HyTypeError]]) + (defmacro rev [&rest body] "Execute the `body` statements in reverse" (quasiquote (do (unquote-splice (list (reversed body)))))) @@ -37,6 +39,9 @@ (defmacro a-dict [] {1 2}) (assert (= (a-dict) {1 2})) +(defmacro a-set [] #{1 2}) +(assert (= (a-set) #{1 2})) + (defmacro a-none []) (assert (= (a-none) None)) @@ -48,6 +53,26 @@ (defmacro bar [x y] (foo x y)) +(defn test-macro-kw [] + "NATIVE: test that an error is raised when &kwonly, &kwargs, or &key is used in a macro" + (try + (eval '(defmacro f [&kwonly a b])) + (except [e HyTypeError] + (assert (= e.message "macros cannot use &kwonly"))) + (else (assert false))) + + (try + (eval '(defmacro f [&kwargs kw])) + (except [e HyTypeError] + (assert (= e.message "macros cannot use &kwargs"))) + (else (assert false))) + + (try + (eval '(defmacro f [&key {"kw" "xyz"}])) + (except [e HyTypeError] + (assert (= e.message "macros cannot use &key"))) + (else (assert false)))) + (defn test-fn-calling-macro [] "NATIVE: test macro calling a plain function" (assert (= 3 (bar 1 2)))) diff --git a/tests/native_tests/shadow.hy b/tests/native_tests/shadow.hy index 0ca08ba..ce578bf 100644 --- a/tests/native_tests/shadow.hy +++ b/tests/native_tests/shadow.hy @@ -8,13 +8,9 @@ (assert (= (x 1 2 3 4) 10)) (assert (= (x 1 2 3 4 5) 15)) ; with strings - (assert (= (x "a") - "a")) (assert (= (x "a" "b" "c") "abc")) ; with lists - (assert (= (x ["a"]) - ["a"])) (assert (= (x ["a"] ["b"] ["c"]) ["a" "b" "c"])))) diff --git a/tox.ini b/tox.ini index 2bea02d..0162794 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,pypy,py33,py34,py35,flake8 +envlist = py27,pypy,py33,py34,py35,py36,flake8 skipsdist = True [testenv]