commit
a38cdb7ccc
@ -6,6 +6,7 @@ python:
|
|||||||
- "3.5"
|
- "3.5"
|
||||||
- "3.6"
|
- "3.6"
|
||||||
- "3.7"
|
- "3.7"
|
||||||
|
- 3.8-dev
|
||||||
- pypy2.7-6.0
|
- pypy2.7-6.0
|
||||||
- pypy3.5-6.0
|
- pypy3.5-6.0
|
||||||
install:
|
install:
|
||||||
|
2
NEWS.rst
2
NEWS.rst
@ -9,8 +9,10 @@ Removals
|
|||||||
|
|
||||||
New Features
|
New Features
|
||||||
------------------------------
|
------------------------------
|
||||||
|
* Python 3.8 is now supported.
|
||||||
* Format strings with embedded Hy code (e.g., `f"The sum is {(+ x y)}"`)
|
* Format strings with embedded Hy code (e.g., `f"The sum is {(+ x y)}"`)
|
||||||
are now supported, even on Pythons earlier than 3.6.
|
are now supported, even on Pythons earlier than 3.6.
|
||||||
|
* Added a special form `setx` to create Python 3.8 assignment expressions.
|
||||||
* New list? function.
|
* New list? function.
|
||||||
* New tuple? function.
|
* New tuple? function.
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import importlib
|
|||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
import hy
|
import hy
|
||||||
from hy._compat import PY3, PY36
|
from hy._compat import PY3, PY36, PY38
|
||||||
|
|
||||||
NATIVE_TESTS = os.path.join("", "tests", "native_tests", "")
|
NATIVE_TESTS = os.path.join("", "tests", "native_tests", "")
|
||||||
|
|
||||||
@ -13,7 +13,8 @@ _fspath_pyimport = py.path.local.pyimport
|
|||||||
|
|
||||||
def pytest_ignore_collect(path, config):
|
def pytest_ignore_collect(path, config):
|
||||||
return (("py3_only" in path.basename and not PY3) or
|
return (("py3_only" in path.basename and not PY3) or
|
||||||
("py36_only" in path.basename and not PY36) or None)
|
("py36_only" in path.basename and not PY36) or
|
||||||
|
("py38_only" in path.basename and not PY38) or None)
|
||||||
|
|
||||||
|
|
||||||
def pyimport_patch_mismatch(self, **kwargs):
|
def pyimport_patch_mismatch(self, **kwargs):
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# This file is execfile()d with the current directory set to its containing dir.
|
# This file is execfile()d with the current directory set to its containing dir.
|
||||||
|
|
||||||
import re, os, sys, time, cgi
|
import re, os, sys, time, html
|
||||||
sys.path.append(os.path.abspath(".."))
|
sys.path.append(os.path.abspath(".."))
|
||||||
|
|
||||||
extensions = ['sphinx.ext.intersphinx']
|
extensions = ['sphinx.ext.intersphinx']
|
||||||
@ -28,7 +28,7 @@ copyright = u'%s the authors' % time.strftime('%Y')
|
|||||||
version = ".".join(hy_version.split(".")[:-1])
|
version = ".".join(hy_version.split(".")[:-1])
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = hy_version
|
release = hy_version
|
||||||
hy_descriptive_version = cgi.escape(hy_version)
|
hy_descriptive_version = html.escape(hy_version)
|
||||||
if "+" in hy_version:
|
if "+" in hy_version:
|
||||||
hy_descriptive_version += " <strong style='color: red;'>(unstable)</strong>"
|
hy_descriptive_version += " <strong style='color: red;'>(unstable)</strong>"
|
||||||
|
|
||||||
|
@ -412,6 +412,21 @@ They can be used to assign multiple variables at once:
|
|||||||
=>
|
=>
|
||||||
|
|
||||||
|
|
||||||
|
``setv`` always returns ``None``.
|
||||||
|
|
||||||
|
|
||||||
|
setx
|
||||||
|
-----
|
||||||
|
|
||||||
|
Whereas ``setv`` creates an assignment statement, ``setx`` creates an assignment expression (see :pep:`572`). It requires Python 3.8 or later. Only one target–value pair is allowed, and the target must be a bare symbol, but the ``setx`` form returns the assigned value instead of ``None``.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
=> (when (> (setx x (+ 1 2)) 0)
|
||||||
|
... (print x "is greater than 0"))
|
||||||
|
3 is greater than 0
|
||||||
|
|
||||||
|
|
||||||
defclass
|
defclass
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import sys, keyword, textwrap
|
|||||||
PY3 = sys.version_info[0] >= 3
|
PY3 = sys.version_info[0] >= 3
|
||||||
PY36 = sys.version_info >= (3, 6)
|
PY36 = sys.version_info >= (3, 6)
|
||||||
PY37 = sys.version_info >= (3, 7)
|
PY37 = sys.version_info >= (3, 7)
|
||||||
|
PY38 = sys.version_info >= (3, 8)
|
||||||
|
|
||||||
# The value of UCS4 indicates whether Unicode strings are stored as UCS-4.
|
# The value of UCS4 indicates whether Unicode strings are stored as UCS-4.
|
||||||
# It is always true on Pythons >= 3.3, which use USC-4 on all systems.
|
# It is always true on Pythons >= 3.3, which use USC-4 on all systems.
|
||||||
|
@ -270,8 +270,9 @@ class HyREPL(code.InteractiveConsole, object):
|
|||||||
try:
|
try:
|
||||||
# Mush the two AST chunks into a single module for
|
# Mush the two AST chunks into a single module for
|
||||||
# conversion into Python.
|
# conversion into Python.
|
||||||
new_ast = ast.Module(exec_ast.body +
|
new_ast = ast.Module(
|
||||||
[ast.Expr(eval_ast.body)])
|
exec_ast.body + [ast.Expr(eval_ast.body)],
|
||||||
|
type_ignores=[])
|
||||||
print(astor.to_source(new_ast))
|
print(astor.to_source(new_ast))
|
||||||
except Exception:
|
except Exception:
|
||||||
msg = 'Exception in AST callback:\n{}\n'.format(
|
msg = 'Exception in AST callback:\n{}\n'.format(
|
||||||
|
@ -15,7 +15,7 @@ from hy.errors import (HyCompileError, HyTypeError, HyLanguageError,
|
|||||||
from hy.lex import mangle, unmangle, hy_parse, parse_one_thing, LexException
|
from hy.lex import mangle, unmangle, hy_parse, parse_one_thing, LexException
|
||||||
|
|
||||||
from hy._compat import (string_types, str_type, bytes_type, long_type, PY3,
|
from hy._compat import (string_types, str_type, bytes_type, long_type, PY3,
|
||||||
PY36, reraise)
|
PY36, PY38, reraise)
|
||||||
from hy.macros import require, load_macros, macroexpand, tag_macroexpand
|
from hy.macros import require, load_macros, macroexpand, tag_macroexpand
|
||||||
|
|
||||||
import hy.core
|
import hy.core
|
||||||
@ -1399,15 +1399,16 @@ class HyASTCompiler(object):
|
|||||||
expr, target=target, value=ret.force_expr, op=op())
|
expr, target=target, value=ret.force_expr, op=op())
|
||||||
|
|
||||||
@special("setv", [many(FORM + FORM)])
|
@special("setv", [many(FORM + FORM)])
|
||||||
|
@special((PY38, "setx"), [times(1, 1, SYM + FORM)])
|
||||||
def compile_def_expression(self, expr, root, pairs):
|
def compile_def_expression(self, expr, root, pairs):
|
||||||
if not pairs:
|
if not pairs:
|
||||||
return asty.Name(expr, id='None', ctx=ast.Load())
|
return asty.Name(expr, id='None', ctx=ast.Load())
|
||||||
result = Result()
|
result = Result()
|
||||||
for pair in pairs:
|
for pair in pairs:
|
||||||
result += self._compile_assign(*pair)
|
result += self._compile_assign(root, *pair)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _compile_assign(self, name, result):
|
def _compile_assign(self, root, name, result):
|
||||||
|
|
||||||
str_name = "%s" % name
|
str_name = "%s" % name
|
||||||
if str_name in (["None"] + (["True", "False"] if PY3 else [])):
|
if str_name in (["None"] + (["True", "False"] if PY3 else [])):
|
||||||
@ -1427,14 +1428,18 @@ class HyASTCompiler(object):
|
|||||||
and isinstance(name, HySymbol)
|
and isinstance(name, HySymbol)
|
||||||
and '.' not in name):
|
and '.' not in name):
|
||||||
result.rename(name)
|
result.rename(name)
|
||||||
# Throw away .expr to ensure that (setv ...) returns None.
|
if root != HySymbol("setx"):
|
||||||
result.expr = None
|
# Throw away .expr to ensure that (setv ...) returns None.
|
||||||
|
result.expr = None
|
||||||
else:
|
else:
|
||||||
st_name = self._storeize(name, ld_name)
|
st_name = self._storeize(name, ld_name)
|
||||||
result += asty.Assign(
|
node = (asty.NamedExpr
|
||||||
|
if root == HySymbol("setx")
|
||||||
|
else asty.Assign)
|
||||||
|
result += node(
|
||||||
name if hasattr(name, "start_line") else result,
|
name if hasattr(name, "start_line") else result,
|
||||||
targets=[st_name],
|
value=result.force_expr,
|
||||||
value=result.force_expr)
|
target=st_name, targets=[st_name])
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -2131,7 +2136,7 @@ def hy_compile(tree, module, root=ast.Module, get_expr=False,
|
|||||||
key=lambda a: not (isinstance(a, ast.ImportFrom) and
|
key=lambda a: not (isinstance(a, ast.ImportFrom) and
|
||||||
a.module == '__future__'))
|
a.module == '__future__'))
|
||||||
|
|
||||||
ret = root(body=body)
|
ret = root(body=body, type_ignores=[])
|
||||||
|
|
||||||
if get_expr:
|
if get_expr:
|
||||||
expr = ast.Expression(body=expr)
|
expr = ast.Expression(body=expr)
|
||||||
|
@ -22,3 +22,4 @@ python_functions=test_* is_test_* hyx_test_* hyx_is_test_*
|
|||||||
filterwarnings =
|
filterwarnings =
|
||||||
once::DeprecationWarning
|
once::DeprecationWarning
|
||||||
once::PendingDeprecationWarning
|
once::PendingDeprecationWarning
|
||||||
|
ignore::SyntaxWarning
|
||||||
|
7
setup.py
7
setup.py
@ -31,7 +31,11 @@ class Install(install):
|
|||||||
"." + filename[:-len(".hy")])
|
"." + filename[:-len(".hy")])
|
||||||
install.run(self)
|
install.run(self)
|
||||||
|
|
||||||
install_requires = ['rply>=0.7.7', 'astor>=0.7.1', 'funcparserlib>=0.3.6', 'clint>=0.4']
|
install_requires = [
|
||||||
|
'rply>=0.7.7',
|
||||||
|
'astor @ https://github.com/berkerpeksag/astor/archive/master.zip',
|
||||||
|
'funcparserlib>=0.3.6',
|
||||||
|
'clint>=0.4']
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
install_requires.append('pyreadline>=2.1')
|
install_requires.append('pyreadline>=2.1')
|
||||||
|
|
||||||
@ -79,6 +83,7 @@ setup(
|
|||||||
"Programming Language :: Python :: 3.5",
|
"Programming Language :: Python :: 3.5",
|
||||||
"Programming Language :: Python :: 3.6",
|
"Programming Language :: Python :: 3.6",
|
||||||
"Programming Language :: Python :: 3.7",
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
"Topic :: Software Development :: Code Generators",
|
"Topic :: Software Development :: Code Generators",
|
||||||
"Topic :: Software Development :: Compilers",
|
"Topic :: Software Development :: Compilers",
|
||||||
"Topic :: Software Development :: Libraries",
|
"Topic :: Software Development :: Libraries",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
pytest)
|
pytest)
|
||||||
(import sys)
|
(import sys)
|
||||||
|
|
||||||
(import [hy._compat [PY3 PY37]])
|
(import [hy._compat [PY3 PY37 PY38]])
|
||||||
|
|
||||||
(defn test-sys-argv []
|
(defn test-sys-argv []
|
||||||
"NATIVE: test sys.argv"
|
"NATIVE: test sys.argv"
|
||||||
@ -1550,10 +1550,11 @@ cee\"} dee" "ey bee\ncee dee"))
|
|||||||
(defn test-disassemble []
|
(defn test-disassemble []
|
||||||
"NATIVE: Test the disassemble function"
|
"NATIVE: Test the disassemble function"
|
||||||
(assert (= (disassemble '(do (leaky) (leaky) (macros))) (cond
|
(assert (= (disassemble '(do (leaky) (leaky) (macros))) (cond
|
||||||
[PY3 "Module(
|
[PY3 (.format "Module(
|
||||||
body=[Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
|
body=[Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
|
||||||
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
|
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
|
||||||
Expr(value=Call(func=Name(id='macros'), args=[], keywords=[]))])"]
|
Expr(value=Call(func=Name(id='macros'), args=[], keywords=[]))]{})"
|
||||||
|
(if PY38 ",\n type_ignores=[]" ""))]
|
||||||
[True "Module(
|
[True "Module(
|
||||||
body=[
|
body=[
|
||||||
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),
|
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),
|
||||||
|
29
tests/native_tests/py38_only_tests.hy
Normal file
29
tests/native_tests/py38_only_tests.hy
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
;; Copyright 2019 the authors.
|
||||||
|
;; This file is part of Hy, which is free software licensed under the Expat
|
||||||
|
;; license. See the LICENSE.
|
||||||
|
|
||||||
|
;; Tests where the emitted code relies on Python ≥3.8.
|
||||||
|
;; conftest.py skips this file when running on Python <3.8.
|
||||||
|
|
||||||
|
(import pytest)
|
||||||
|
|
||||||
|
(defn test-setx []
|
||||||
|
(setx y (+ (setx x (+ "a" "b")) "c"))
|
||||||
|
(assert (= x "ab"))
|
||||||
|
(assert (= y "abc"))
|
||||||
|
|
||||||
|
(setv l [])
|
||||||
|
(for [x [1 2 3]]
|
||||||
|
(when (>= (setx y (+ x 8)) 10)
|
||||||
|
(.append l y)))
|
||||||
|
(assert (= l [10 11]))
|
||||||
|
|
||||||
|
(setv a ["apple" None "banana"])
|
||||||
|
(setv filtered (lfor
|
||||||
|
i (range (len a))
|
||||||
|
:if (is-not (setx v (get a i)) None)
|
||||||
|
v))
|
||||||
|
(assert (= filtered ["apple" "banana"]))
|
||||||
|
(assert (= v "banana"))
|
||||||
|
(with [(pytest.raises NameError)]
|
||||||
|
i))
|
Loading…
Reference in New Issue
Block a user