diff --git a/.travis.yml b/.travis.yml index b6e97e6..22c320e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,18 @@ language: python python: - "pypy" + - "2.6" - "2.7" - "3.2" - "3.3" - - "2.6" + - "3.4" # command to install dependencies install: - - pip install -r requirements.txt + - pip install -r requirements-dev.txt - pip install coveralls - - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install argparse importlib unittest2 astor; fi - - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install astor; fi - - if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then pip install astor; fi - - python setup.py -q install -# # command to run tests + - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi # needs for running tests + - pip install --allow-all-external -e . +# command to run tests script: make travis after_success: coveralls notifications: diff --git a/AUTHORS b/AUTHORS index e5b61cf..847740c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,3 +45,4 @@ * kirbyfan64 * Brendan Curran-Johnson * Ivan Kozik +* Allison Kaptur diff --git a/NEWS b/NEWS index 3670fca..0c01556 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,83 @@ +Changes from 0.9.12 + + 0.10.0 - the "oh man I'm late for PyCon" release + + Thanks to theanalyst (Abhi) for getting the release notes + together. You're the best! + - Hy Society + + [ Breaking Changes ] + + We're calling this release 0.10 because we broke + API. Sorry about that. We've removed kwapply in + favor of using `apply`. Please be sure to upgrade + all code to work with `apply`. + + (apply function-call args kwargs) ; is the signature + + [Thanks] + + Major shoutout to Clinton Dreisbach for implementing loop/recur. + As always, massive hugs to olasd for the constant reviews and for + implementing HyCons cells. Thanks to @kenanb for redesigning the + new Hy logo. + + Many thanks to algernon for working on adderall, which helped + push Hy further this cycle. Adderall is an implementation of miniKanren + in Hy. If you're interested in using Adderall, check out hydiomatic, + which prettfies Hy source using Adderall rules. + + This release saw an increase of about 11 contributors for a point + release, you guys rock! + + -Hy Society + + [ Language Changes ] + + * `for' revamped again (Last time, we hope!), this time using a saner + itertools.product when nesting + * `lisp-if'/`lif' added for the lisp-like everything is true if, giving + seasoned lispers a better if check (0 is a value, etc) + * Reader Macros are macros now! + * yield-from is now a proper yield from on Python 3. It also now breaks on + Python 2.x. + * Added if-not macro + * We finally have a lisp like cons cells + * Generator expressions, set & dict comprehensions are now supported + * (.) is a mini DSL for attribute access + * `macroexpand' & `macroexpand-1' added to core + * `disassemble' added to core, which dumps the AST or equivalent python code + * `coll?' added to core to check for a collection + * `identity' function added to core + + [ Misc. Fixes ] + * Lots of doc fixes. Reorganization as well as better docs on Hy internals + * Universal Wheel Support + * Pygments > 1.6 supports Hy now. All codeblocks in docs have been changed + from clojure to hy + * Hy REPL supports invoking with --spy & -i options [reword] + * `first' and `rest' are functions and not macros anymore + * "clean" target added to Makefile + * hy2py supports a bunch of commandline options to show AST, source etc. + * Sub-object mangling: every identifier is split along the dots & mangled + seperately + + [ Bug Fixes ] + * Empty MacroExpansions work as expected + * Python 3.4 port. Sorry this wasn't in a 3.4 release time, we forgot to do + a release. Whoops. + * eg/lxml/parse-tumblr.hy works with Python 3 + * hy2py works on Windows + * Fixed unicode encoding issue in REPL during unicode exceptions + * Fixed handling of comments at end of input (#382) + + [ Contrib changes ] + * Curry module added to contrib + * Loop/recur module added which provides TCO at tail position + * defmulti has been added - check out more in the docs -- thanks to Foxboron for this one! + * Walk module for walking the Hy AST, features a `macroexpand-all` as well + + Changes from Hy 0.9.11 tl;dr: diff --git a/bin/hy2py b/bin/hy2py deleted file mode 100755 index 69b151b..0000000 --- a/bin/hy2py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -from hy.importer import import_file_to_ast, import_file_to_hst - -import argparse -import sys - -import astor.codegen - -module_name = "" - -parser = argparse.ArgumentParser( - prog="hy2py", - usage="%(prog)s [options] FILE", - formatter_class=argparse.RawDescriptionHelpFormatter) -parser.add_argument("--with-source", "-s", action="store_true", - help="Show the parsed source structure") -parser.add_argument("--with-ast", "-a", action="store_true", - help="Show the generated AST") -parser.add_argument("--without-python", "-np", action="store_true", - help="Do not show the python code generated from the AST") -parser.add_argument('args', nargs=argparse.REMAINDER, help=argparse.SUPPRESS) - -options = parser.parse_args(sys.argv[1:]) - -if options.with_source: - hst = import_file_to_hst(options.args[0]) - print(hst) - print() - print() - -_ast = import_file_to_ast(options.args[0], module_name) -if options.with_ast: - print(astor.dump(_ast)) - print() - print() - -if not options.without_python: - print(astor.codegen.to_source(_ast)) diff --git a/docs/conf.py b/docs/conf.py index 4744800..9fe83a2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -96,7 +96,13 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +try: + import sphinx_rtd_theme +except ImportError: + html_theme = 'default' +else: + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/hacking.rst b/docs/hacking.rst index 4329c68..8b9ecc6 100644 --- a/docs/hacking.rst +++ b/docs/hacking.rst @@ -92,6 +92,12 @@ Write docs---docs are good! Even this doc! Core Development Rules ====================== +Pull requests are good! + +Before you submit a PR, please run the tests and check your code against the style guide. You can do both these things at once:: + + $ make d + All incoming changes need to be acked by 2 different members of Hylang's core team. Additional review is clearly welcome, but we need a minimum of 2 signoffs for any change. diff --git a/docs/language/api.rst b/docs/language/api.rst index f9d21ad..4501ebd 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -764,6 +764,36 @@ any numeric type, empty sequence and empty dictionary are considered `False`. Everything else is considered `True`. +lisp-if / lif +------------- + +.. versionadded:: 0.10.0 + +For those that prefer a more lisp-y if clause, we have lisp-if, or lif. This +*only* considers None/nil as false! All other values of python +"falseiness" are considered true. + + +.. code-block:: clj + + => (lisp-if True "true" "false") + "true" + => (lisp-if False "true" "false") + "true" + => (lisp-if 0 "true" "false") + "true" + => (lisp-if nil "true" "false") + "false" + => (lisp-if None "true" "false") + "false" + + ; And, same thing + => (lif True "true" "false") + "true" + => (lif nil "true" "false") + "false" + + import ------ @@ -824,11 +854,15 @@ Just as in normal function definitions, if the first element of the body is a string, it serves as docstring. This is useful for giving class methods docstrings. +.. code-block:: clj + => (setv times-three ... (fn [x] ... "Multiplies input by three and returns the result." ... (* x 3))) +Then test it via the Python built-in ``help`` function:: + => (help times-three) Help on function times_three: @@ -1265,23 +1299,15 @@ infinite series without consuming infinite amount of memory. => (list-comp x [x (take 15 (random-numbers 1 50))])]) [7, 41, 6, 22, 32, 17, 5, 38, 18, 38, 17, 14, 23, 23, 19] -.. _zipwith: -zipwith -------- +yield-from +---------- -.. versionadded:: 0.10.0 +.. versionadded:: 0.9.13 -`zipwith` zips multiple lists and maps the given function over the result. It is -equilavent to calling ``zip``, followed by calling ``map`` on the result. +**PYTHON 3.3 AND UP ONLY!** -In the following example, `zipwith` is used to add the contents of two lists -together. The equilavent ``map`` and ``zip`` calls follow. - -.. code-block:: clj - - => (import operator.add) - => (zipwith operator.add [1 2 3] [4 5 6]) ; using zipwith - [5, 7, 9] - => (map operator.add (zip [1 2 3] [4 5 6])) ; using map+zip - [5, 7, 9] +`yield-from` is used to call a subgenerator. This is useful if you +want your coroutine to be able to delegate its processes to another +coroutine, say if using something fancy like +`asyncio `_. diff --git a/docs/language/cli.rst b/docs/language/cli.rst index ab9d8aa..98e853e 100644 --- a/docs/language/cli.rst +++ b/docs/language/cli.rst @@ -2,6 +2,8 @@ Command Line Interface ====================== +.. _hy: + hy -- @@ -46,6 +48,8 @@ Command line options Print the Hy version number and exit. +.. _hyc: + hyc --- @@ -71,3 +75,29 @@ Command line options $ hyc hyname.hy $ python hyname.pyc Hy Afroman! + + +.. _hy2py: + +hy2py +----- + +.. versionadded:: 0.10.1 + +Command line options +^^^^^^^^^^^^^^^^^^^^ + +.. cmdoption:: -s + --with-source + + Show the parsed source structure. + +.. cmdoption:: -a + --with-ast + + Show the generated AST. + +.. cmdoption:: -np + --without-python + + Do not show the Python code generated from the AST. diff --git a/docs/language/core.rst b/docs/language/core.rst index 46b21b2..42070cf 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -495,8 +495,8 @@ nth Usage: ``(nth coll n)`` Return the `nth` item in a collection, counting from 0. Unlike -``get``, ``nth`` works on both iterators and iterables. Returns ``None`` -if the `n` is outside the range of `coll`. +``get``, ``nth`` works on both iterators and iterables. Raises ``IndexError`` +if the `n` is outside the range of ``coll`` or ``ValueError`` if it's negative. .. code-block:: hy @@ -506,8 +506,10 @@ if the `n` is outside the range of `coll`. => (nth [1 2 4 7] 3) 7 - => (none? (nth [1 2 4 7] 5)) - True + => (nth [1 2 4 7] 5) + Traceback (most recent call last): + ... + IndexError: 5 => (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2)) 5 @@ -764,6 +766,7 @@ drop Usage: ``(drop n coll)`` Return an iterator, skipping the first ``n`` members of ``coll`` +Raises ``ValueError`` if ``n`` is negative. .. code-block:: hy @@ -924,6 +927,7 @@ take Usage: ``(take n coll)`` Return an iterator containing the first ``n`` members of ``coll``. +Raises ``ValueError`` if ``n`` is negative. .. code-block:: hy @@ -979,3 +983,21 @@ Return an iterator from ``coll`` as long as predicate, ``pred`` returns True. => (list (take-while neg? [ 1 2 3 -4 5])) [] + +.. _zipwith: + +zipwith +------- + +.. versionadded:: 0.9.13 + +Usage: ``(zipwith fn coll ...)`` + +Equivalent to ``zip``, but uses a multi-argument function instead of creating a tuple. +If ``zipwith`` is called with N collections, then ``fn`` must accept N arguments. + +.. code-block:: clojure + + => (import operator) + => (list (zipwith operator.add [1 2 3] [4 5 6])) + [5, 7, 9] diff --git a/hy/cmdline.py b/hy/cmdline.py index 98f10cd..6ccbb8f 100644 --- a/hy/cmdline.py +++ b/hy/cmdline.py @@ -25,16 +25,21 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +from __future__ import print_function + import argparse import code import ast import sys +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.importer import (ast_compile, import_buffer_to_module, + import_file_to_ast, import_file_to_hst) from hy.completer import completion from hy.macros import macro, require @@ -66,7 +71,6 @@ builtins.exit = HyQuitter('exit') def print_python_code(_ast): - import astor.codegen # astor cannot handle ast.Interactive, so disguise it as a module _ast_for_print = ast.Module() _ast_for_print.body = _ast.body @@ -313,3 +317,43 @@ def hyc_main(): sys.stderr.write("hyc: Can't open file '%s': [Errno %d] %s\n" % (x.filename, x.errno, x.strerror)) sys.exit(x.errno) + + +# entry point for cmd line script "hy2py" +def hy2py_main(): + module_name = "" + + options = dict(prog="hy2py", usage="%(prog)s [options] FILE", + formatter_class=argparse.RawDescriptionHelpFormatter) + parser = argparse.ArgumentParser(**options) + parser.add_argument("--with-source", "-s", action="store_true", + help="Show the parsed source structure") + parser.add_argument("--with-ast", "-a", action="store_true", + help="Show the generated AST") + parser.add_argument("--without-python", "-np", action="store_true", + help=("Do not show the Python code generated " + "from the AST")) + parser.add_argument('args', nargs=argparse.REMAINDER, + help=argparse.SUPPRESS) + + options = parser.parse_args(sys.argv[1:]) + + if not options.args: + parser.exit(1, parser.format_help()) + + if options.with_source: + hst = import_file_to_hst(options.args[0]) + print(hst) + print() + print() + + _ast = import_file_to_ast(options.args[0], module_name) + if options.with_ast: + print(astor.dump(_ast)) + print() + print() + + if not options.without_python: + print(astor.codegen.to_source(_ast)) + + parser.exit(0) diff --git a/hy/compiler.py b/hy/compiler.py index dbb4782..032a49a 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -732,25 +732,10 @@ class HyASTCompiler(object): handler_results += self._compile_catch_expression(e, name) handlers.append(handler_results.stmts.pop()) elif e[0] == HySymbol("else"): - if orelse: - raise HyTypeError( - e, - "`try' cannot have more than one `else'") - else: - orelse = self._compile_branch(e[1:]) - # XXX tempvar magic - orelse += orelse.expr_as_stmt() - orelse = orelse.stmts + orelse = self.try_except_helper(e, HySymbol("else"), orelse) elif e[0] == HySymbol("finally"): - if finalbody: - raise HyTypeError( - e, - "`try' cannot have more than one `finally'") - else: - finalbody = self._compile_branch(e[1:]) - # XXX tempvar magic - finalbody += finalbody.expr_as_stmt() - finalbody = finalbody.stmts + finalbody = self.try_except_helper(e, HySymbol("finally"), + finalbody) else: raise HyTypeError(e, "Unknown expression in `try'") @@ -768,8 +753,8 @@ class HyASTCompiler(object): col_offset=expr.start_column, type=None, name=None, - body=[ast.Pass(lineno=expr.start_line, - col_offset=expr.start_column)])] + body=[ast.Raise(lineno=expr.start_line, + col_offset=expr.start_column)])] ret = handler_results @@ -809,6 +794,17 @@ class HyASTCompiler(object): body=body, orelse=orelse) + returnable + def try_except_helper(self, hy_obj, symbol, accumulated): + if accumulated: + raise HyTypeError( + hy_obj, + "`try' cannot have more than one `%s'" % symbol) + else: + accumulated = self._compile_branch(hy_obj[1:]) + accumulated += accumulated.expr_as_stmt() + accumulated = accumulated.stmts + return accumulated + @builds("except") @builds("catch") def magic_internal_form(self, expr): @@ -1017,6 +1013,28 @@ class HyASTCompiler(object): return ret + @builds("yield_from") + @checkargs(max=1) + def compile_yield_from_expression(self, expr): + if not PY33: + raise HyCompileError( + "yield-from only supported in python 3.3+!") + + expr.pop(0) + ret = Result(contains_yield=True) + + value = None + if expr != []: + ret += self.compile(expr.pop(0)) + value = ret.force_expr + + ret += ast.YieldFrom( + value=value, + lineno=expr.start_line, + col_offset=expr.start_column) + + return ret + @builds("import") def compile_import_expression(self, expr): def _compile_import(expr, module, names=None, importer=ast.Import): diff --git a/hy/core/language.hy b/hy/core/language.hy index cdef29e..ff44e80 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -23,7 +23,9 @@ ;;;; to make functional programming slightly easier. ;;;; - +(import itertools) +(import functools) +(import collections) (import [hy._compat [long-type]]) ; long for python2, int for python3 (import [hy.models.cons [HyCons]]) @@ -44,15 +46,11 @@ "Check whether c can be used as a cons object" (instance? HyCons c)) -(defn cycle [coll] - "Yield an infinite repetition of the items in coll" - (setv seen []) - (for* [x coll] - (yield x) - (.append seen x)) - (while seen - (for* [x seen] - (yield x)))) +(defn keyword? [k] + "Check whether k is a keyword" + (and (instance? (type :foo) k) + (.startswith k (get :foo 0)))) + (defn dec [n] "Decrement n by 1" @@ -82,22 +80,36 @@ (yield val) (.add seen val)))))) +(if-python2 + (do + (setv filterfalse itertools.ifilterfalse) + (setv zip_longest itertools.izip_longest) + (setv filter itertools.ifilter) + (setv map itertools.imap) + (setv zip itertools.izip) + (setv range xrange) + (setv input raw_input)) + (do + (setv reduce functools.reduce) + (setv filterfalse itertools.filterfalse) + (setv zip_longest itertools.zip_longest) + ; Someone can import these directly from `hy.core.language`; + ; we'll make some duplicates. + (setv filter filter) + (setv map map) + (setv zip zip) + (setv range range) + (setv input input))) + +(setv cycle itertools.cycle) +(setv repeat itertools.repeat) +(setv drop-while itertools.dropwhile) +(setv take-while itertools.takewhile) +(setv zipwith map) + (defn drop [count coll] "Drop `count` elements from `coll` and yield back the rest" - (let [[citer (iter coll)]] - (try (for* [i (range count)] - (next citer)) - (catch [StopIteration])) - citer)) - -(defn drop-while [pred coll] - "Drop all elements of `coll` until `pred` is False" - (let [[citer (iter coll)]] - (for* [val citer] - (if (not (pred val)) - (do (yield val) (break)))) - (for* [val citer] - (yield val)))) + (itertools.islice coll count nil)) (defn empty? [coll] "Return True if `coll` is empty" @@ -121,13 +133,6 @@ (if (not (hasattr tree attr)) (setattr tree attr 1)))) -(defn filter [pred coll] - "Return all elements from `coll` that pass `pred`" - (let [[citer (iter coll)]] - (for* [val citer] - (if (pred val) - (yield val))))) - (defn flatten [coll] "Return a single flat list expanding all members of coll" (if (coll? coll) @@ -169,7 +174,7 @@ (defn first [coll] "Return first item from `coll`" - (get coll 0)) + (nth coll 0)) (defn identity [x] "Returns the argument unchanged" @@ -194,14 +199,13 @@ (defn integer-char? [x] "Return True if char `x` parses as an integer" (try - (integer? (int x)) - (catch [e ValueError] False) - (catch [e TypeError] False))) + (integer? (int x)) + (catch [e ValueError] False) + (catch [e TypeError] False))) (defn iterable? [x] "Return true if x is iterable" - (try (do (iter x) true) - (catch [Exception] false))) + (isinstance x collections.Iterable)) (defn iterate [f x] (setv val x) @@ -211,8 +215,7 @@ (defn iterator? [x] "Return true if x is an iterator" - (try (= x (iter x)) - (catch [TypeError] false))) + (isinstance x collections.Iterator)) (defn list* [hd &rest tl] "Return a dotted list construed from the elements of the argument" @@ -253,13 +256,9 @@ (defn nth [coll index] "Return nth item in collection or sequence, counting from 0" - (if (not (neg? index)) - (if (iterable? coll) - (try (get (list (take 1 (drop index coll))) 0) - (catch [IndexError] None)) - (try (get coll index) - (catch [IndexError] None))) - None)) + (try + (next (drop index coll)) + (catch [e StopIteration] (raise (IndexError index))))) (defn odd? [n] "Return true if n is an odd number" @@ -280,14 +279,7 @@ (defn rest [coll] "Get all the elements of a coll, except the first." - (slice coll 1)) - -(defn repeat [x &optional n] - "Yield x forever or optionally n times" - (if (none? n) - (setv dispatch (fn [] (while true (yield x)))) - (setv dispatch (fn [] (for* [_ (range n)] (yield x))))) - (dispatch)) + (drop 1 coll)) (defn repeatedly [func] "Yield result of running func repeatedly" @@ -296,7 +288,7 @@ (defn second [coll] "Return second item from `coll`" - (get coll 1)) + (nth coll 1)) (defn some [pred coll] "Return true if (pred x) is logical true for any x in coll, else false" @@ -317,9 +309,7 @@ (defn take [count coll] "Take `count` elements from `coll`, or the whole set if the total number of entries in `coll` is less than `count`." - (let [[citer (iter coll)]] - (for* [_ (range count)] - (yield (next citer))))) + (itertools.islice coll nil count)) (defn take-nth [n coll] "Return every nth member of coll @@ -332,29 +322,15 @@ (next citer)))) (raise (ValueError "n must be positive")))) -(defn take-while [pred coll] - "Take all elements while `pred` is true" - (let [[citer (iter coll)]] - (for* [val citer] - (if (pred val) - (yield val) - (break))))) - (defn zero? [n] "Return true if n is 0" (_numeric_check n) (= n 0)) -(defn zipwith [func &rest lists] - "Zip the contents of several lists and map a function to the result" - (do - (import functools) - (map (functools.partial (fn [f args] (apply f args)) func) (apply zip lists)))) - (def *exports* '[calling-module-name coll? cons cons? cycle dec distinct disassemble drop drop-while empty? even? every? first filter flatten float? gensym identity inc instance? integer - integer? integer-char? iterable? iterate iterator? - list* macroexpand macroexpand-1 neg? nil? none? nth - numeric? odd? pos? remove repeat repeatedly rest second - some string string? take take-nth take-while zero? zipwith]) + integer? integer-char? iterable? iterate iterator? keyword? + list* macroexpand macroexpand-1 map neg? nil? none? nth + numeric? odd? pos? range remove repeat repeatedly rest second + some string string? take take-nth take-while zero? zip zipwith]) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 33f74de..d62e553 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -27,8 +27,8 @@ (import [hy.models.list [HyList]] - [hy.models.symbol [HySymbol]]) - + [hy.models.symbol [HySymbol]] + [hy._compat [PY33 PY34]]) (defmacro for [args &rest body] @@ -145,6 +145,11 @@ `(if (not ~test) ~not-branch ~yes-branch))) +(defmacro-alias [lisp-if lif] [test &rest branches] + "Like `if`, but anything that is not None/nil is considered true." + `(if (is-not ~test nil) ~@branches)) + + (defmacro when [test &rest body] "Execute `body` when `test` is true" `(if ~test (do ~@body))) @@ -155,12 +160,6 @@ `(if-not ~test (do ~@body))) -(defmacro yield-from [iterable] - "Yield all the items from iterable" - (let [[x (gensym)]] - `(for* [~x ~iterable] - (yield ~x)))) - (defmacro with-gensyms [args &rest body] `(let ~(HyList (map (fn [x] `[~x (gensym '~x)]) args)) ~@body)) diff --git a/hy/version.py b/hy/version.py index 2c33563..23a31bc 100644 --- a/hy/version.py +++ b/hy/version.py @@ -20,4 +20,4 @@ __appname__ = "hy" -__version__ = "0.9.12" +__version__ = "0.10.0" diff --git a/requirements-dev.txt b/requirements-dev.txt index fc712b3..88a91e9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,12 @@ --r requirements.txt -tox +# test tools nose +tox + +# code quality +flake8 +coverage + +# documentation Pygments>=1.6 Sphinx -coverage +sphinx_rtd_theme diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index ab8080a..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Check site / dev for more deps! -flake8 -rply>=0.7.0 diff --git a/setup.py b/setup.py index 40bb4e0..b357a9e 100755 --- a/setup.py +++ b/setup.py @@ -59,7 +59,8 @@ setup( entry_points={ 'console_scripts': [ 'hy = hy.cmdline:hy_main', - 'hyc = hy.cmdline:hyc_main' + 'hyc = hy.cmdline:hyc_main', + 'hy2py = hy.cmdline:hy2py_main', ] }, packages=find_packages(exclude=['tests*']), diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy index 15debf4..c6d8306 100644 --- a/tests/native_tests/core.hy +++ b/tests/native_tests/core.hy @@ -82,8 +82,8 @@ (assert-equal res [None 4 5]) (setv res (list (drop 0 [1 2 3 4 5]))) (assert-equal res [1 2 3 4 5]) - (setv res (list (drop -1 [1 2 3 4 5]))) - (assert-equal res [1 2 3 4 5]) + (try (do (list (drop -1 [1 2 3 4 5])) (assert False)) + (catch [e [ValueError]] nil)) (setv res (list (drop 6 (iter [1 2 3 4 5])))) (assert-equal res []) (setv res (list (take 5 (drop 2 (iterate inc 0))))) @@ -335,12 +335,15 @@ "NATIVE: testing the nth function" (assert-equal 2 (nth [1 2 4 7] 1)) (assert-equal 7 (nth [1 2 4 7] 3)) - (assert-true (none? (nth [1 2 4 7] 5))) - (assert-true (none? (nth [1 2 4 7] -1))) + (try (do (nth [1 2 4 7] 5) (assert False)) + (catch [e [IndexError]] nil)) + (try (do (nth [1 2 4 7] -1) (assert False)) + (catch [e [ValueError]] nil)) ;; now for iterators (assert-equal 2 (nth (iter [1 2 4 7]) 1)) (assert-equal 7 (nth (iter [1 2 4 7]) 3)) - (assert-true (none? (nth (iter [1 2 4 7]) -1))) + (try (do (nth (iter [1 2 4 7]) -1) (assert False)) + (catch [e [ValueError]] nil)) (assert-equal 5 (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2))) (defn test-numeric? [] @@ -429,8 +432,8 @@ (assert-equal res ["s" "s" "s" "s"]) (setv res (list (take 0 (repeat "s")))) (assert-equal res []) - (setv res (list (take -1 (repeat "s")))) - (assert-equal res []) + (try (do (list (take -1 (repeat "s"))) (assert False)) + (catch [e [ValueError]] nil)) (setv res (list (take 6 [1 2 None 4]))) (assert-equal res [1 2 None 4])) @@ -478,3 +481,14 @@ (assert-equal (list res) [4 4 4]) (setv res (zipwith operator.sub [3 7 9] [1 2 4])) (assert-equal (list res) [2 5 5])) + +(defn test-is-keyword [] + "NATIVE: testing the keyword? function" + (assert (keyword? ':bar)) + (assert (keyword? ':baz)) + (assert (keyword? :bar)) + (assert (keyword? :baz)) + (assert (not (keyword? "foo"))) + (assert (not (keyword? ":foo"))) + (assert (not (keyword? 1))) + (assert (not (keyword? nil)))) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index ccf81c5..f9f1181 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -236,6 +236,11 @@ "NATIVE: test do" (do)) +(defn test-bare-try [] (try + (try (raise ValueError)) + (except [ValueError]) + (else (assert false)))) + (defn test-exceptions [] "NATIVE: test Exceptions" @@ -469,7 +474,7 @@ (defn test-rest [] "NATIVE: test rest" - (assert (= (rest [1 2 3 4 5]) [2 3 4 5]))) + (assert (= (list (rest [1 2 3 4 5])) [2 3 4 5]))) (defn test-importas [] diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy index 5bdf821..571e824 100644 --- a/tests/native_tests/native_macros.hy +++ b/tests/native_tests/native_macros.hy @@ -1,3 +1,6 @@ +(import [hy._compat [PY33]]) +(import [hy.errors [HyCompileError]]) + (defmacro rev [&rest body] "Execute the `body` statements in reverse" (quasiquote (do (unquote-splice (list (reversed body)))))) @@ -99,11 +102,18 @@ (defn test-yield-from [] "NATIVE: testing yield from" - (defn yield-from-test [] - (for* [i (range 3)] - (yield i)) - (yield-from [1 2 3])) - (assert (= (list (yield-from-test)) [0 1 2 1 2 3]))) + + (try + (eval + '(do (defn yield-from-test [] + (for* [i (range 3)] + (yield i)) + (yield-from [1 2 3])) + (assert (= (list (yield-from-test)) [0 1 2 1 2 3])))) + (catch [e HyCompileError] + ;; Yup, this should happen on non-Python3.3 thingies + (assert (not PY33))) + (else (assert PY33)))) (defn test-if-python2 [] (import sys) @@ -191,6 +201,25 @@ :yes))) +(defn test-lisp-if [] + "test that lisp-if works as expected" + ; nil is false + (assert (= (lisp-if None "true" "false") "false")) + (assert (= (lisp-if nil "true" "false") "false")) + + ; But everything else is True! Even falsey things. + (assert (= (lisp-if True "true" "false") "true")) + (assert (= (lisp-if False "true" "false") "true")) + (assert (= (lisp-if 0 "true" "false") "true")) + (assert (= (lisp-if "some-string" "true" "false") "true")) + (assert (= (lisp-if "" "true" "false") "true")) + (assert (= (lisp-if (+ 1 2 3) "true" "false") "true")) + + ; Just to be sure, test the alias lif + (assert (= (lif nil "true" "false") "false")) + (assert (= (lif 0 "true" "false") "true"))) + + (defn test-defn-alias [] (defn-alias [tda-main tda-a1 tda-a2] [] :bazinga) (defun-alias [tda-main tda-a1 tda-a2] [] :bazinga) diff --git a/tests/test_bin.py b/tests/test_bin.py index f6fb11c..dcb2e4e 100644 --- a/tests/test_bin.py +++ b/tests/test_bin.py @@ -129,7 +129,7 @@ def test_hy2py(): for f in filenames: if f.endswith(".hy"): i += 1 - ret = run_cmd("python bin/hy2py -s -a " + ret = run_cmd("hy2py -s -a " + os.path.join(dirpath, f)) assert ret[0] == 0, f assert len(ret[1]) > 1, f diff --git a/tox.ini b/tox.ini index 44c1903..6c280fc 100644 --- a/tox.ini +++ b/tox.ini @@ -1,39 +1,18 @@ [tox] -envlist = py27,pypy,py32,py33,py26,flake8 +envlist = py26,py27,pypy,py32,py33,flake8 +skipsdist = True + [testenv] -commands = nosetests +commands = + pip install --allow-all-external -e . + nosetests deps = - nose - setuptools - rply - -[testenv:pypy] -commands = nosetests -deps = - astor - nose - setuptools - rply - -[testenv:py27] -commands = nosetests -deps = - astor - nose - setuptools - rply + -rrequirements-dev.txt [testenv:py26] deps = - astor - nose - setuptools + {[testenv]deps} unittest2 - importlib - rply [testenv:flake8] -deps = - flake8 - rply commands = flake8 hy bin tests