Merge branch 'master' into pr/666

This commit is contained in:
Paul Tagliamonte 2014-11-15 07:51:58 -05:00
commit 79b4456119
16 changed files with 279 additions and 26 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
.git

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

@ -48,3 +48,5 @@
* 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>

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

@ -449,6 +449,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?

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

View File

@ -28,10 +28,10 @@
(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)))))
@ -253,6 +253,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)
@ -354,13 +370,45 @@
(defun Botsbuildbots () (Botsbuildbots)) (defun Botsbuildbots () (Botsbuildbots))
(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 (def *exports* '[Botsbuildbots
butlast calling-module-name coll? cons cons? cycle butlast calling-module-name coll? cons cons? cycle
dec distinct disassemble drop drop-while empty? even? dec distinct disassemble drop 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

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

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

@ -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]])
@ -985,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"
@ -1055,4 +1074,28 @@
(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

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