Merge pull request #1772 from Kodiologist/py38

Support Python 3.8
This commit is contained in:
Kodi Arfer 2019-04-23 17:31:38 -04:00 committed by GitHub
commit a38cdb7ccc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 81 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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))