Merge branch 'master' into pr/637

Conflicts:
	hy/core/language.hy
This commit is contained in:
Paul Tagliamonte 2014-11-15 07:58:56 -05:00
commit ec593154fe
29 changed files with 498 additions and 84 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
.git

View File

@ -2,11 +2,10 @@ Paul R. Tagliamonte <paultag@debian.org> Paul Tagliamonte <paultag@debian.org>
Paul R. Tagliamonte <paultag@debian.org> Paul Tagliamonte <tag@pault.ag> Paul R. Tagliamonte <paultag@debian.org> Paul Tagliamonte <tag@pault.ag>
Paul R. Tagliamonte <paultag@debian.org> Paul Tagliamonte <paultag@sunlightfoundation.com> Paul R. Tagliamonte <paultag@debian.org> Paul Tagliamonte <paultag@sunlightfoundation.com>
Paul R. Tagliamonte <paultag@debian.org> Paul Tagliamonte <paultag@ubuntu.com> Paul R. Tagliamonte <paultag@debian.org> Paul Tagliamonte <paultag@ubuntu.com>
Morten Linderud <mcfoxax@gmail.com> Foxboron <mcfoxax@gmail.com> Morten Linderud <morten@linderud.pw> Foxboron <morten@linderud.pw>
Morten Linderud <mcfoxax@gmail.com> <fox@velox.pw>
James King <james@agentultra.com> agentultra <james@agentultra.com> James King <james@agentultra.com> agentultra <james@agentultra.com>
James King <james@agentultra.com> J Kenneth King <james@agentultra.com> James King <james@agentultra.com> J Kenneth King <james@agentultra.com>
Abhishek L <abhishek.lekshmanan@gmail.com> <abhishekl.2006@gmail.com> Abhishek L <abhishek.lekshmanan@gmail.com> <abhishekl.2006@gmail.com>
Bob Tolbert <bob@tolbert.org> Bob Tolbert <bob@eyesopen.com> Bob Tolbert <bob@tolbert.org> Bob Tolbert <bob@eyesopen.com>
Guillermo Vaya <guivaya@gmail.com> Guillermo Vaya <guillermo.vaya@gigas.com> Guillermo Vaya <guivaya@gmail.com> Guillermo Vaya <guillermo.vaya@gigas.com>
Gergely Nagy <algernon@balabit.hu> Gergely Nagy <algernon@madhouse-project.org> Gergely Nagy <algernon@balabit.hu> Gergely Nagy <algernon@madhouse-project.org>

View File

@ -8,15 +8,9 @@ python:
- "3.4" - "3.4"
cache: cache:
- $HOME/.pip-cache - $HOME/.pip-cache
# command to install dependencies
install:
- pip install -r requirements-travis.txt --download-cache $HOME/.pip-cache
- pip install coveralls --download-cache $HOME/.pip-cache
- 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 # command to run tests
script: make travis script: make travis
after_success: coveralls after_success: make coveralls
notifications: notifications:
email: email:
- paultag@gmail.com - paultag@gmail.com

View File

@ -42,9 +42,12 @@
* Halit Alptekin <info@halitalptekin.com> * Halit Alptekin <info@halitalptekin.com>
* Richard Parsons <richard.lee.parsons@gmail.com> * Richard Parsons <richard.lee.parsons@gmail.com>
* han semaj <sangho.nah@gmail.com> * han semaj <sangho.nah@gmail.com>
* kirbyfan64 <kirbyfan64@users.noreply.github.com> * Ryan Gonzalez <rymg19@gmail.com>
* Brendan Curran-Johnson <brendan@bcjbcj.ca> * Brendan Curran-Johnson <brendan@bcjbcj.ca>
* Ivan Kozik <ivan@ludios.org> * Ivan Kozik <ivan@ludios.org>
* Allison Kaptur <allison.kaptur@gmail.com> * Allison Kaptur <allison.kaptur@gmail.com>
* Matthew Wampler-Doty <matthew.wampler.doty@gmail.com> * Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
* Tianon Gravi <admwiggin@gmail.com> * Tianon Gravi <admwiggin@gmail.com>
* Ian Denhardt <ian@zenhack.net>
* Ruslan Prokopiev <bismigalis@gmail.com>
* Alexander Artemenko <svetlyak.40wt@gmail.com>

View File

@ -1,3 +1,21 @@
pypy_url=http://buildbot.pypy.org/nightly/trunk/pypy-c-jit-latest-linux64.tar.bz2
pip_url=https://bootstrap.pypa.io/get-pip.py
python=python
pip=pip
coveralls=coveralls
nose=nosetests
pcache=$(HOME)/.pip-cache
ifeq (PyPy 2.4,$(findstring PyPy 2.4,$(shell python -V 2>&1 | tail -1)))
bad_pypy=1
python=./pypy
pip=./pip
coveralls=./coveralls
nose=./nosetests
else
bad_pypy=
endif
all: all:
@echo "No default step. Use setup.py" @echo "No default step. Use setup.py"
@echo "" @echo ""
@ -53,14 +71,42 @@ diff:
r: d tox diff r: d tox diff
travis: python:
nosetests -s --with-coverage --cover-package hy ifeq ($(bad_pypy),1)
# Due to stupid PyPy 2.4 bugs, a custom version needs to be downloaded
curl $(pypy_url) -o pypy.tbz2
tar xf pypy.tbz2
ln -sf `pwd`/pypy-*/bin/pypy $(python)
curl $(pip_url) | $(python)
ln -sf `pwd`/pypy-*/bin/pip $(pip)
sudo $(pip) install nose
ln -sf `pwd`/pypy-*/bin/nosetests $(nose)
endif
ifeq (Python 2.6,$(findstring Python 2.6,$(shell python -V 2>&1)))
$(pip) install unittest2
endif
$(pip) install -r requirements-travis.txt --download-cache $(pcache)
$(pip) install coveralls --download-cache $(pcache)
$(pip) install --allow-all-external -e .
ifeq ($(bad_pypy),1)
ln -sf `pwd`/pypy-*/bin/coveralls $(coveralls)
endif
travis: python
ifeq ($(bad_pypy),1)
HY_DIR=`pwd`/pypy-*/bin $(nose) -s --with-coverage --cover-package hy
else
$(nose) -s --with-coverage --cover-package hy
endif
ifeq (PyPy,$(findstring PyPy,$(shell python -V 2>&1 | tail -1))) ifeq (PyPy,$(findstring PyPy,$(shell python -V 2>&1 | tail -1)))
@echo "skipping flake8 on pypy" @echo "skipping flake8 on pypy"
else else
flake8 hy bin tests flake8 hy bin tests
endif endif
coveralls:
$(coveralls)
clean: clean:
@find . -name "*.pyc" -exec rm {} \; @find . -name "*.pyc" -exec rm {} \;
@find -name __pycache__ -delete @find -name __pycache__ -delete

46
NEWS
View File

@ -1,3 +1,49 @@
Changes from 0.10.0
This release took some time (sorry, all my fault) but it's got a bunch of
really nice features. We hope you enjoy hacking with Hy as much as we enjoy
hacking on Hy.
In other news, we're Dockerized as an official library image!
<https://registry.hub.docker.com/_/hylang/>
$ docker run -it --rm hylang
hy 0.10.0 using CPython(default) 3.4.1 on Linux
=> ((lambda [] (print "Hello, World!")))
Hello, World!
- Hy Society
[ Language Changes ]
* Implement raise :from, Python 3 only.
* defmain macro
* name & keyword functions added to core
* (read) added to core
* shadow added to core
* New functions interleave interpose zip_longest added to core
* nth returns default value when out of bounds
* merge-with added
* doto macro added
* keyword? to findout keywords
* setv no longer allows "." in names
[Internals ]
* Builtins reimplemented in terms of python stdlib
* gensyms (defmacro/g!) handles non-string types better
[Tools]
* Added hy2py to installed scripts
[ Misc. Fixes ]
* Symbols like true, false, none can't be assigned
* Set sys.argv default to [''] like Python does
* REPL displays the the python version and platform at startup
* Dockerfile added for https://registry.hub.docker.com/_/hylang/
[ Contrib changes ]
* Fix ap-first and ap-last for failure conditions
Changes from 0.9.12 Changes from 0.9.12
0.10.0 - the "oh man I'm late for PyCon" release 0.10.0 - the "oh man I'm late for PyCon" release

View File

@ -11,13 +11,13 @@ args and/or kwargs. Inspired by clojures take on `defn`.
=> (require hy.contrib.multi) => (require hy.contrib.multi)
=> (defmulti fun => (defmulti fun
... ([a] a) ... ([a] "a")
... ([a b] "a b") ... ([a b] "a b")
... ([a b c] "a b c")) ... ([a b c] "a b c"))
=> (fun 1 2 3)
'a b c'
=> (fun a b)
"a b"
=> (fun 1) => (fun 1)
1 "a"
=> (fun 1 2)
"a b"
=> (fun 1 2 3)
"a b c"

View File

@ -13,3 +13,4 @@
* `Bob Tolbert <https://github.com/rwtolbert>`_ * `Bob Tolbert <https://github.com/rwtolbert>`_
* `Berker Peksag <https://github.com/berkerpeksag>`_ * `Berker Peksag <https://github.com/berkerpeksag>`_
* `Clinton N. Dreisbach <https://github.com/cndreisbach>`_ * `Clinton N. Dreisbach <https://github.com/cndreisbach>`_
* `han semaj <https://github.com/microamp>`_

View File

@ -818,14 +818,18 @@ any numeric type, empty sequence and empty dictionary are considered `False`.
Everything else is considered `True`. Everything else is considered `True`.
lisp-if / lif lisp-if / lif and lisp-if-not / lif-not
------------- ---------------------------------------
.. versionadded:: 0.10.0 .. versionadded:: 0.10.0
.. versionadded:: 0.10.2
lisp-if-not / lif-not
For those that prefer a more lisp-y if clause, we have lisp-if, or lif. This 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 *only* considers None/nil as false! All other values of python
"falseiness" are considered true. "falseiness" are considered true. Conversely, we have lisp-if-not or lif-not,
in parallel to if / if-not, which reverses the comparison.
.. code-block:: clj .. code-block:: clj
@ -840,12 +844,20 @@ For those that prefer a more lisp-y if clause, we have lisp-if, or lif. This
"false" "false"
=> (lisp-if None "true" "false") => (lisp-if None "true" "false")
"false" "false"
=> (lisp-if-not nil "true" "false")
"true"
=> (lisp-if-not None "true" "false")
"true"
=> (lisp-if-not False "true" "false")
"false"
; And, same thing ; And, same thing
=> (lif True "true" "false") => (lif True "true" "false")
"true" "true"
=> (lif nil "true" "false") => (lif nil "true" "false")
"false" "false"
=> (lif-not None "true" "false")
"true"
import import

View File

@ -474,6 +474,27 @@ Returns the single step macro expansion of form.
=> (macroexpand-1 '(-> (a b) (-> (c d) (e f)))) => (macroexpand-1 '(-> (a b) (-> (c d) (e f))))
(u'_>' (u'a' u'b') (u'c' u'd') (u'e' u'f')) (u'_>' (u'a' u'b') (u'c' u'd') (u'e' u'f'))
.. _merge-with-fn:
merge-with
----------
.. versionadded:: 0.10.1
Usage: ``(merge-with f &rest maps)
Returns a map that consist of the rest of the maps joined onto first.
If a key occurs in more than one map, the mapping(s) from the latter
(left-to-right) will be combined with the mapping in the result by
calling ``(f val-in-result val-in-latter)``.
.. code-block:: clojure
=> (merge-with (fn [x y] (+ x y)) {"a" 10 "b" 20} {"a" 1 "c" 30})
{u'a': 11L, u'c': 30L, u'b': 20L}
.. _neg?-fn: .. _neg?-fn:
neg? neg?
@ -557,11 +578,11 @@ Return True if x is None.
nth nth
--- ---
Usage: ``(nth coll n)`` Usage: ``(nth coll n &optional [default nil])``
Return the `nth` item in a collection, counting from 0. Unlike Return the `nth` item in a collection, counting from 0. Return the
``get``, ``nth`` works on both iterators and iterables. Raises ``IndexError`` default value, ``nil``, if out of bounds (unless specified otherwise).
if the `n` is outside the range of ``coll`` or ``ValueError`` if it's negative. Raise ``ValueError`` if ``n`` is negative.
.. code-block:: hy .. code-block:: hy
@ -571,14 +592,21 @@ if the `n` is outside the range of ``coll`` or ``ValueError`` if it's negative.
=> (nth [1 2 4 7] 3) => (nth [1 2 4 7] 3)
7 7
=> (nth [1 2 4 7] 5) => (nil? (nth [1 2 4 7] 5))
Traceback (most recent call last): True
...
IndexError: 5 => (nth [1 2 4 7] 5 "default")
'default'
=> (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2)) => (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2))
5 5
=> (nth [1 2 4 7] -1)
Traceback (most recent call last):
...
ValueError: Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize.
.. _numeric?-fn: .. _numeric?-fn:
numeric? numeric?
@ -672,21 +700,25 @@ some
Usage: ``(some pred coll)`` Usage: ``(some pred coll)``
Return True if ``(pred x)`` is logical true for any ``x`` in ``coll``, otherwise False. Return False if ``coll`` is empty. Return the first logical true value of ``(pred x)`` for any ``x`` in
``coll``, otherwise ``nil``. Return ``nil`` if ``coll`` is empty.
.. code-block:: hy .. code-block:: hy
=> (some even? [2 4 6]) => (some even? [2 4 6])
True True
=> (some even? [1 3 5]) => (nil? (some even? [1 3 5]))
False
=> (some even? [1 3 6])
True True
=> (some even? []) => (nil? (some identity [0 "" []]))
False True
=> (some identity [0 "non-empty-string" []])
'non-empty-string'
=> (nil? (some even? []))
True
.. _string?-fn: .. _string?-fn:
@ -958,7 +990,7 @@ Return an iterator of `x`, `fn(x)`, `fn(fn(x))`.
read read
---- ----
Usage: ``(read [from-file eof])`` Usage: ``(read &optional [from-file eof])``
Reads the next hy expression from `from-file` (defaults to `sys.stdin`), and Reads the next hy expression from `from-file` (defaults to `sys.stdin`), and
can take a single byte as EOF (defaults to an empty string). can take a single byte as EOF (defaults to an empty string).

View File

@ -52,3 +52,9 @@ if PY3:
long_type = int long_type = int
else: else:
long_type = long # NOQA long_type = long # NOQA
if PY3:
exec('def raise_empty(t, *args): raise t(*args) from None')
else:
def raise_empty(t, *args):
raise t(*args)

View File

@ -38,7 +38,7 @@ from hy.models.cons import HyCons
from hy.errors import HyCompileError, HyTypeError from hy.errors import HyCompileError, HyTypeError
import hy.macros import hy.macros
from hy._compat import str_type, long_type, PY27, PY33, PY3, PY34 from hy._compat import str_type, long_type, PY27, PY33, PY3, PY34, raise_empty
from hy.macros import require, macroexpand, reader_macroexpand from hy.macros import require, macroexpand, reader_macroexpand
import hy.importer import hy.importer
@ -429,7 +429,7 @@ class HyASTCompiler(object):
except HyTypeError as e: except HyTypeError as e:
raise raise
except Exception as e: except Exception as e:
raise HyCompileError(e, sys.exc_info()[2]) raise_empty(HyCompileError, e, sys.exc_info()[2])
raise HyCompileError(Exception("Unknown type: `%s'" % _type)) raise HyCompileError(Exception("Unknown type: `%s'" % _type))
@ -1268,7 +1268,8 @@ class HyASTCompiler(object):
def compile_decorate_expression(self, expr): def compile_decorate_expression(self, expr):
expr.pop(0) # with-decorator expr.pop(0) # with-decorator
fn = self.compile(expr.pop(-1)) fn = self.compile(expr.pop(-1))
if not fn.stmts or not isinstance(fn.stmts[-1], ast.FunctionDef): if not fn.stmts or not (isinstance(fn.stmts[-1], ast.FunctionDef) or
isinstance(fn.stmts[-1], ast.ClassDef)):
raise HyTypeError(expr, "Decorated a non-function") raise HyTypeError(expr, "Decorated a non-function")
decorators, ret = self._compile_collect(expr) decorators, ret = self._compile_collect(expr)
fn.stmts[-1].decorator_list = decorators fn.stmts[-1].decorator_list = decorators
@ -2003,12 +2004,12 @@ class HyASTCompiler(object):
except TypeError: except TypeError:
raise HyTypeError( raise HyTypeError(
expression, expression,
"Wrong argument type for defclass slots definition.") "Wrong argument type for defclass attributes definition.")
for b in body_expression: for b in body_expression:
if len(b) != 2: if len(b) != 2:
raise HyTypeError( raise HyTypeError(
expression, expression,
"Wrong number of argument in defclass slot.") "Wrong number of argument in defclass attribute.")
body += self._compile_assign(b[0], b[1], body += self._compile_assign(b[0], b[1],
b.start_line, b.start_column) b.start_line, b.start_column)
body += body.expr_as_stmt() body += body.expr_as_stmt()

View File

@ -26,12 +26,12 @@
(import itertools) (import itertools)
(import functools) (import functools)
(import collections) (import collections)
(import sys) (import sys)
(import [hy._compat [long-type]]) ; long for python2, int for python3 (import [hy._compat [long-type]]) ; long for python2, int for python3
(import [hy.models.cons [HyCons]]) (import [hy.models.cons [HyCons]]
[hy.models.keyword [HyKeyword *keyword-prefix*]])
(import [hy.lex [LexException PrematureEndOfInput tokenize]]) (import [hy.lex [LexException PrematureEndOfInput tokenize]])
(defn _numeric-check [x] (defn _numeric-check [x]
(if (not (numeric? x)) (if (not (numeric? x))
(raise (TypeError (.format "{0!r} is not a number" x))))) (raise (TypeError (.format "{0!r} is not a number" x)))))
@ -258,6 +258,22 @@
(setv name (calling-module-name)) (setv name (calling-module-name))
(hy.macros.macroexpand-1 form name)) (hy.macros.macroexpand-1 form name))
(defn merge-with [f &rest maps]
"Returns a map that consists of the rest of the maps joined onto
the first. If a key occurs in more than one map, the mapping(s)
from the latter (left-to-right) will be combined with the mapping in
the result by calling (f val-in-result val-in-latter)."
(if (any maps)
(let [[merge-entry (fn [m e]
(let [[k (get e 0)] [v (get e 1)]]
(if (in k m)
(assoc m k (f (get m k) v))
(assoc m k v)))
m)]
[merge2 (fn [m1 m2]
(reduce merge-entry (.items m2) (or m1 {})))]]
(reduce merge2 maps))))
(defn neg? [n] (defn neg? [n]
"Return true if n is < 0" "Return true if n is < 0"
(_numeric-check n) (_numeric-check n)
@ -275,11 +291,10 @@
(import numbers) (import numbers)
(instance? numbers.Number x)) (instance? numbers.Number x))
(defn nth [coll index] (defn nth [coll n &optional [default nil]]
"Return nth item in collection or sequence, counting from 0" "Return nth item in collection or sequence, counting from 0.
(try Return nil if out of bounds unless specified otherwise."
(next (drop index coll)) (next (drop n coll) default))
(catch [e StopIteration] (raise (IndexError index)))))
(defn odd? [n] (defn odd? [n]
"Return true if n is an odd number" "Return true if n is an odd number"
@ -305,8 +320,8 @@
(nth coll 1)) (nth coll 1))
(defn some [pred coll] (defn some [pred coll]
"Return true if (pred x) is logical true for any x in coll, else false" "Return the first logical true value of (pred x) for any x in coll, else nil"
(any (map pred coll))) (first (filter nil (map pred coll))))
(defn string [x] (defn string [x]
"Cast x as current string implementation" "Cast x as current string implementation"
@ -353,17 +368,51 @@
(setv buff (+ buff inn)) (setv buff (+ buff inn))
(try (try
(def parsed (first (tokenize buff))) (def parsed (first (tokenize buff)))
(except [e [LexException PrematureEndOfInput IndexError]]) (except [e [LexException PrematureEndOfInput IndexError]])
(else (if parsed (break))))) (else (if parsed (break)))))
parsed) parsed)
(defun Botsbuildbots () (Botsbuildbots))
(def *exports* '[butlast calling-module-name coll? cons cons? cycle (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))))
(defn hyify [text]
"Convert text to match hy identifier"
(.replace (string text) "_" "-"))
(defn keyword [value]
"Create a keyword from the given value. Strings numbers and even objects
with the __name__ magic will work"
(if (and (string? value) (value.startswith *keyword-prefix*))
(hyify value)
(if (string? value)
(HyKeyword (+ ":" (hyify value)))
(try
(hyify (.__name__ value))
(catch [] (HyKeyword (+ ":" (string value))))))))
(defn name [value]
"Convert the given value to a string. Keyword special character will be stripped.
String will be used as is. Even objects with the __name__ magic will work"
(if (and (string? value) (value.startswith *keyword-prefix*))
(hyify (slice value 2))
(if (string? value)
(hyify value)
(try
(hyify (. value __name__))
(catch [] (string value))))))
(def *exports* '[Botsbuildbots
butlast calling-module-name coll? cons cons? cycle
dec distinct disassemble drop drop-last drop-while empty? even? dec distinct disassemble drop drop-last drop-while empty? even?
every? first filter filterfalse flatten float? gensym identity every? first filter filterfalse flatten float? gensym identity
inc input instance? integer integer? integer-char? interleave inc input instance? integer integer? integer-char? interleave
interpose iterable? iterate iterator? keyword? list* interpose iterable? iterate iterator? keyword keyword? list*
macroexpand macroexpand-1 map neg? nil? none? nth macroexpand macroexpand-1 map merge-with name neg? nil? none?
numeric? odd? pos? range read remove repeat repeatedly nth numeric? odd? pos? range read remove repeat repeatedly
rest reduce second some string string? take take-nth rest reduce second some string string? take take-nth
take-while zero? zip zip_longest zipwith]) take-while zero? zip zip_longest zipwith])

View File

@ -153,6 +153,10 @@
"Like `if`, but anything that is not None/nil is considered true." "Like `if`, but anything that is not None/nil is considered true."
`(if (is-not ~test nil) ~@branches)) `(if (is-not ~test nil) ~@branches))
(defmacro-alias [lisp-if-not lif-not] [test &rest branches]
"Like `if-not`, but anything that is not None/nil is considered true."
`(if (is ~test nil) ~@branches))
(defmacro when [test &rest body] (defmacro when [test &rest body]
"Execute `body` when `test` is true" "Execute `body` when `test` is true"
@ -198,3 +202,15 @@
(.append ret (.append ret
`(setv ~name ~main))) `(setv ~name ~main)))
ret)) ret))
(defmacro Botsbuildbots []
"Build bots, repeatedly.^W^W^WPrint the AUTHORS, forever."
`(try
(do
(import [requests])
(let [[r (requests.get
"https://raw.githubusercontent.com/hylang/hy/master/AUTHORS")]]
(repeat r.text)))
(catch [e ImportError]
(repeat "Botsbuildbots requires `requests' to function."))))

View File

@ -26,9 +26,12 @@
(defn + [&rest args] (defn + [&rest args]
"Shadow + operator for when we need to import / map it against something" "Shadow + operator for when we need to import / map it against something"
(if (= (len args) 0) (let [[count (len args)]]
0 (if (zero? count)
(sum args))) ; shortcut here. (raise (TypeError "Need at least 1 argument to add/concatenate"))
(if (= count 1)
(get args 0)
(reduce operator.add args)))))
(defn - [&rest args] (defn - [&rest args]

View File

@ -117,6 +117,7 @@ _wrappers = {
list: lambda l: HyList(_wrap_value(x) for x in l), list: lambda l: HyList(_wrap_value(x) for x in l),
tuple: lambda t: HyList(_wrap_value(x) for x in t), tuple: lambda t: HyList(_wrap_value(x) for x in t),
type(None): lambda foo: HySymbol("None"), type(None): lambda foo: HySymbol("None"),
HyExpression: lambda e: HyExpression(_wrap_value(x) for x in e),
} }
if sys.version_info[0] < 3: # do not add long on python3 if sys.version_info[0] < 3: # do not add long on python3

View File

@ -23,14 +23,17 @@ from hy.models import HyObject
from hy._compat import str_type from hy._compat import str_type
KEYWORD_PREFIX = "\uFDD0"
class HyKeyword(HyObject, str_type): class HyKeyword(HyObject, str_type):
"""Generic Hy Keyword object. It's either a ``str`` or a ``unicode``, """Generic Hy Keyword object. It's either a ``str`` or a ``unicode``,
depending on the Python version. depending on the Python version.
""" """
def __new__(cls, value): def __new__(cls, value):
if not value.startswith("\uFDD0"): if not value.startswith(KEYWORD_PREFIX):
value = "\uFDD0" + value value = KEYWORD_PREFIX + value
obj = str_type.__new__(cls, value) obj = str_type.__new__(cls, value)
return obj return obj

View File

@ -20,4 +20,4 @@
__appname__ = "hy" __appname__ = "hy"
__version__ = "0.10.0" __version__ = "0.10.1"

View File

@ -1,3 +1,5 @@
# for Botsbuildbots
requests
# code quality # code quality
flake8 flake8
coverage coverage

66
scripts/reformat-changelog Executable file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env hy
(import re)
(import pdb)
(import codecs)
(setv *maintainer-line*
" -- Alexander Artemenko <svetlyak.40wt@gmail.com> Thu, 30 Sep 2014 13:06:09 +0400")
(defun read-lines-from-file [filename]
(let [[f (codecs.open filename "r" "utf-8")]]
(fn [] (let [[line (.readline f) ]]
line))))
(defun get-version-number [line]
(let [[match (re.search r"Changes from.*(\d+\.\d+\.\d+)$" line)]]
(if match
(let [[version (.group match (int 1))]
[numbered (list (map int (.split version "."))) ]
[explicit-mapping {"0.9.12" "0.10.0"
"0.8.2" "0.9.0"}]]
(assoc numbered 2 (+ (get numbered 2) 1))
(.get explicit-mapping
version
(.join "." (map str numbered)))))))
(defun read-version-content [reader]
(setv line (reader))
(setv content [])
(while (and line (not (get-version-number line)))
(.append content (.strip line))
(setv line (reader)))
[content line])
(defun read-versions-from-file [filename]
(let [[reader (read-lines-from-file filename)]]
(read-versions-rec (reader)
reader)))
(defun read-versions-rec [line reader]
(if line
(let [[version (get-version-number line)]
[[content next-line] (read-version-content reader)]]
(+ [{"from" version
"content" content}]
(read-versions-rec next-line reader)))
[]))
(defun format-deb-version [version]
(setv result [(.format "hy ({}) unstable; urgency=low"
(get version "from"))])
(for [line (get version "content")]
(.append result (+ " " line)))
(.append result *maintainer-line*)
(.append result "")
(.join "\n" result))
(defmain [&rest args]
(let ((versions (read-versions-from-file "NEWS")))
(for [version versions]
(print (.encode (format-deb-version version) "utf-8")))))

View File

@ -12,6 +12,7 @@ from .native_tests.when import * # noqa
from .native_tests.with_decorator import * # noqa from .native_tests.with_decorator import * # noqa
from .native_tests.core import * # noqa from .native_tests.core import * # noqa
from .native_tests.reader_macros import * # noqa from .native_tests.reader_macros import * # noqa
from .native_tests.shadow import * # noqa
from .native_tests.with_test import * # noqa from .native_tests.with_test import * # noqa
from .native_tests.contrib.anaphoric import * # noqa from .native_tests.contrib.anaphoric import * # noqa
from .native_tests.contrib.loop import * # noqa from .native_tests.contrib.loop import * # noqa

View File

@ -0,0 +1,28 @@
from hy._compat import long_type
from hy.models.integer import HyInteger
from hy.models.list import HyList
from hy.models.expression import HyExpression
from hy.macros import _wrap_value
def test_wrap_long_type():
""" Test conversion of integers."""
wrapped = _wrap_value(long_type(0))
assert type(wrapped) == HyInteger
def test_wrap_tuple():
""" Test conversion of tuples."""
wrapped = _wrap_value((HyInteger(0),))
assert type(wrapped) == HyList
assert type(wrapped[0]) == HyInteger
assert wrapped == HyList([HyInteger(0)])
def test_wrap_nested_expr():
""" Test conversion of HyExpressions with embedded non-HyObjects."""
wrapped = _wrap_value(HyExpression([long_type(0)]))
assert type(wrapped) == HyExpression
assert type(wrapped[0]) == HyInteger
assert wrapped == HyExpression([HyInteger(0)])

View File

@ -30,6 +30,9 @@
(defn assert-equal [x y] (defn assert-equal [x y]
(assert (= x y))) (assert (= x y)))
(defn assert-nil [x]
(assert (is x nil)))
(defn test-coll? [] (defn test-coll? []
"NATIVE: testing coll?" "NATIVE: testing coll?"
(assert-true (coll? [1 2 3])) (assert-true (coll? [1 2 3]))
@ -416,13 +419,17 @@
"NATIVE: testing the nth function" "NATIVE: testing the nth function"
(assert-equal 2 (nth [1 2 4 7] 1)) (assert-equal 2 (nth [1 2 4 7] 1))
(assert-equal 7 (nth [1 2 4 7] 3)) (assert-equal 7 (nth [1 2 4 7] 3))
(try (do (nth [1 2 4 7] 5) (assert False)) (assert-nil (nth [1 2 4 7] 5))
(catch [e [IndexError]] nil)) (assert-equal (nth [1 2 4 7] 5 "some default value")
"some default value") ; with default specified
(try (do (nth [1 2 4 7] -1) (assert False)) (try (do (nth [1 2 4 7] -1) (assert False))
(catch [e [ValueError]] nil)) (catch [e [ValueError]] nil))
;; now for iterators ;; now for iterators
(assert-equal 2 (nth (iter [1 2 4 7]) 1)) (assert-equal 2 (nth (iter [1 2 4 7]) 1))
(assert-equal 7 (nth (iter [1 2 4 7]) 3)) (assert-equal 7 (nth (iter [1 2 4 7]) 3))
(assert-nil (nth (iter [1 2 4 7]) 5))
(assert-equal (nth (iter [1 2 4 7]) 5 "some default value")
"some default value") ; with default specified
(try (do (nth (iter [1 2 4 7]) -1) (assert False)) (try (do (nth (iter [1 2 4 7]) -1) (assert False))
(catch [e [ValueError]] nil)) (catch [e [ValueError]] nil))
(assert-equal 5 (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2))) (assert-equal 5 (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2)))
@ -493,9 +500,16 @@
(defn test-some [] (defn test-some []
"NATIVE: testing the some function" "NATIVE: testing the some function"
(assert-true (some even? [2 4 6])) (assert-true (some even? [2 4 6]))
(assert-false (some even? [1 3 5])) (assert-nil (some even? [1 3 5]))
(assert-true (some even? [1 3 6])) (assert-true (some even? [1 2 3]))
(assert-false (some even? []))) (assert-nil (some even? []))
; 0, "" (empty string) and [] (empty list) are all logical false
(assert-nil (some identity [0 "" []]))
; non-empty string is logical true
(assert-equal (some identity [0 "this string is non-empty" []])
"this string is non-empty")
; nil if collection is empty
(assert-nil (some even? [])))
(defn test-string? [] (defn test-string? []
"NATIVE: testing string?" "NATIVE: testing string?"

View File

@ -20,16 +20,16 @@
(assert (not (isinstance (A) D)))) (assert (not (isinstance (A) D))))
(defn test-defclass-slots [] (defn test-defclass-attrs []
"NATIVE: test defclass slots" "NATIVE: test defclass attributes"
(defclass A [] (defclass A []
[[x 42]]) [[x 42]])
(assert (= A.x 42)) (assert (= A.x 42))
(assert (= (getattr (A) "x") 42))) (assert (= (getattr (A) "x") 42)))
(defn test-defclass-slots-fn [] (defn test-defclass-attrs-fn []
"NATIVE: test defclass slots with fn" "NATIVE: test defclass attributes with fn"
(defclass B [] (defclass B []
[[x 42] [[x 42]
[y (fn [self value] [y (fn [self value]
@ -52,7 +52,7 @@
(defn test-defclass-no-fn-leak [] (defn test-defclass-no-fn-leak []
"NATIVE: test defclass slots with fn" "NATIVE: test defclass attributes with fn"
(defclass A [] (defclass A []
[[x (fn [] 1)]]) [[x (fn [] 1)]])
(try (try

View File

@ -1,6 +1,7 @@
(import [tests.resources [kwtest function-with-a-dash]] (import [tests.resources [kwtest function-with-a-dash]]
[os.path [exists isdir isfile]] [os.path [exists isdir isfile]]
[sys :as systest]) [sys :as systest]
[operator [or_]])
(import sys) (import sys)
(import [hy._compat [PY33 PY34]]) (import [hy._compat [PY33 PY34]])
@ -495,9 +496,11 @@
(setv output (list (gen))) (setv output (list (gen)))
(assert (= [1] output))) (assert (= [1] output)))
(defn test-first [] (defn test-first []
"NATIVE: test firsty things" "NATIVE: test firsty things"
(assert (= (first [1 2 3 4 5]) 1)) (assert (= (first [1 2 3 4 5]) 1))
(assert (is (first []) nil))
(assert (= (car [1 2 3 4 5]) 1))) (assert (= (car [1 2 3 4 5]) 1)))
@ -983,6 +986,24 @@
(assert (= (macroexpand-1 '(-> (a b) (-> (c d) (e f)))) (assert (= (macroexpand-1 '(-> (a b) (-> (c d) (e f))))
'(-> (a b) (c d) (e f))))) '(-> (a b) (c d) (e f)))))
(defn test-merge-with []
"NATIVE: test merge-with"
(assert (= (merge-with + {} {}) nil))
(assert (= (merge-with + {"a" 10 "b" 20} {}) {"a" 10 "b" 20}))
(assert (= (merge-with + {} {"a" 10 "b" 20}) {"a" 10 "b" 20}))
(assert (= (merge-with + {"a" 10 "b" 20} {"a" 1 "c" 30})
{"a" 11 "b" 20 "c" 30}))
(assert (= (merge-with +
{:a 1 :b 2}
{:a 9 :b 98 :c 0}
{:a 10 :b 100 :c 10}
{:a 5}
{:c 5 :d 42})
{:d 42 :c 15 :a 25 :b 200}))
(assert (= (merge-with or_
{"a" (set [1 2 3]) "b" (set [4 5 6])}
{"a" (set [2 3 7 8]) "c" (set [1 2 3])})
{"a" (set [1 2 3 7 8]) "c" (set [1 2 3]) "b" (set [4 5 6])})))
(defn test-calling-module-name [] (defn test-calling-module-name []
"NATIVE: Test the calling-module-name function" "NATIVE: Test the calling-module-name function"
@ -1035,11 +1056,11 @@
(import [StringIO [StringIO]]) (import [StringIO [StringIO]])
(import [io [StringIO]])) (import [io [StringIO]]))
(import [hy.models.expression [HyExpression]]) (import [hy.models.expression [HyExpression]])
(def stdin-buffer (StringIO "(+ 2 2)\n(- 2 2)")) (def stdin-buffer (StringIO "(+ 2 2)\n(- 2 2)"))
(assert (= (eval (read stdin-buffer)) 4)) (assert (= (eval (read stdin-buffer)) 4))
(assert (isinstance (read stdin-buffer) HyExpression)) (assert (isinstance (read stdin-buffer) HyExpression))
"Multiline test" "Multiline test"
(def stdin-buffer (StringIO "(\n+\n41\n1\n)\n(-\n2\n1\n)")) (def stdin-buffer (StringIO "(\n+\n41\n1\n)\n(-\n2\n1\n)"))
(assert (= (eval (read stdin-buffer)) 42)) (assert (= (eval (read stdin-buffer)) 42))
@ -1048,9 +1069,33 @@
"EOF test" "EOF test"
(def stdin-buffer (StringIO "(+ 2 2)")) (def stdin-buffer (StringIO "(+ 2 2)"))
(read stdin-buffer) (read stdin-buffer)
(try (try
(read stdin-buffer) (read stdin-buffer)
(catch [e Exception] (catch [e Exception]
(assert (isinstance e EOFError))))) (assert (isinstance e EOFError)))))
(defn test-keyword-creation []
"NATIVE: Test keyword creation"
(assert (= (keyword "foo") :foo))
(assert (= (keyword "foo_bar") :foo-bar))
(assert (= (keyword `foo) :foo))
(assert (= (keyword `foo-bar) :foo-bar))
(assert (= (keyword 'foo) :foo))
(assert (= (keyword 'foo-bar) :foo-bar))
(assert (= (keyword 1) :1))
(assert (= (keyword 1.0) :1.0))
(assert (= (keyword :foo_bar) :foo-bar)))
(defn test-name-conversion []
"NATIVE: Test name conversion"
(assert (= (name "foo") "foo"))
(assert (= (name "foo_bar") "foo-bar"))
(assert (= (name `foo) "foo"))
(assert (= (name `foo_bar) "foo-bar"))
(assert (= (name 'foo) "foo"))
(assert (= (name 'foo_bar) "foo-bar"))
(assert (= (name 1) "1"))
(assert (= (name 1.0) "1.0"))
(assert (= (name :foo) "foo"))
(assert (= (name :foo_bar) "foo-bar"))
(assert (= (name test-name-conversion) "test-name-conversion")))

View File

@ -206,6 +206,24 @@
(assert (= (lif nil "true" "false") "false")) (assert (= (lif nil "true" "false") "false"))
(assert (= (lif 0 "true" "false") "true"))) (assert (= (lif 0 "true" "false") "true")))
(defn test-lisp-if-not []
"test that lisp-if-not works as expected"
; nil is false
(assert (= (lisp-if-not None "false" "true") "false"))
(assert (= (lisp-if-not nil "false" "true") "false"))
; But everything else is True! Even falsey things.
(assert (= (lisp-if-not True "false" "true") "true"))
(assert (= (lisp-if-not False "false" "true") "true"))
(assert (= (lisp-if-not 0 "false" "true") "true"))
(assert (= (lisp-if-not "some-string" "false" "true") "true"))
(assert (= (lisp-if-not "" "false" "true") "true"))
(assert (= (lisp-if-not (+ 1 2 3) "false" "true") "true"))
; Just to be sure, test the alias lif-not
(assert (= (lif-not nil "false" "true") "false"))
(assert (= (lif-not 0 "false" "true") "true")))
(defn test-defn-alias [] (defn test-defn-alias []
(defn-alias [tda-main tda-a1 tda-a2] [] :bazinga) (defn-alias [tda-main tda-a1 tda-a2] [] :bazinga)
@ -214,3 +232,6 @@
(assert (= (tda-a1) :bazinga)) (assert (= (tda-a1) :bazinga))
(assert (= (tda-a2) :bazinga)) (assert (= (tda-a2) :bazinga))
(assert (= tda-main tda-a1 tda-a2))) (assert (= tda-main tda-a1 tda-a2)))
(defn test-botsbuildbots []
(assert (> (len (first (Botsbuildbots))) 50)))

View File

@ -1,11 +1,22 @@
(defn test-shadow-addition [] (defn test-shadow-addition []
"NATIVE: test shadow addition" "NATIVE: test shadow addition"
(let [[x +]] (let [[x +]]
(assert (= (x) 0)) (assert (try
(x)
(catch [TypeError] True)
(else (throw AssertionError))))
(assert (= (x 1 2 3 4) 10)) (assert (= (x 1 2 3 4) 10))
(assert (= (x 1 2 3 4 5) 15)))) (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"]))))
(defn test-shadow-subtraction [] (defn test-shadow-subtraction []

View File

@ -7,6 +7,16 @@
(* 2 2))) (* 2 2)))
(defn bardec [cls]
(setv cls.my_attr 123)
cls)
(with-decorator bardec
(defclass cls []
[[my_attr 456]]))
(defn test-decorators [] (defn test-decorators []
"NATIVE: test decorators." "NATIVE: test decorators."
(assert (= (tfunction) 2))) (assert (= (tfunction) 2))
(assert (= cls.my_attr 123)))

View File

@ -26,8 +26,11 @@ import subprocess
from hy._compat import PY3 from hy._compat import PY3
hy_dir = os.environ.get('HY_DIR', '')
def run_cmd(cmd, stdin_data=None): def run_cmd(cmd, stdin_data=None):
p = subprocess.Popen(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,