From da855af5698bdbafc584d43b8f2680784ced6835 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 14:13:36 -0400 Subject: [PATCH 01/21] Remove Python 2 from trove classifiers --- setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.py b/setup.py index 71afe86..bd6718e 100755 --- a/setup.py +++ b/setup.py @@ -80,8 +80,6 @@ setup( "Operating System :: OS Independent", "Programming Language :: Lisp", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", From 4a2e7e1bd077ee07cf7cd2b471c0f0a897e28af2 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 14:17:10 -0400 Subject: [PATCH 02/21] Integrate py3_only_tests into native_tests/language --- tests/native_tests/language.hy | 201 ++++++++++++++++++++++++++ tests/native_tests/py3_only_tests.hy | 207 --------------------------- 2 files changed, 201 insertions(+), 207 deletions(-) delete mode 100644 tests/native_tests/py3_only_tests.hy diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 687376c..1494d14 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -1716,3 +1716,204 @@ macros() "Make sure relative imports work properly" (import [..resources [tlib]]) (assert (= tlib.SECRET-MESSAGE "Hello World"))) + + +(defn test-exception-cause [] + (try (raise ValueError :from NameError) + (except [e [ValueError]] + (assert (= (type (. e __cause__)) NameError))))) + + +(defn test-kwonly [] + "NATIVE: test keyword-only arguments" + ;; keyword-only with default works + (defn kwonly-foo-default-false [&kwonly [foo False]] foo) + (assert (= (kwonly-foo-default-false) False)) + (assert (= (kwonly-foo-default-false :foo True) True)) + ;; keyword-only without default ... + (defn kwonly-foo-no-default [&kwonly foo] foo) + (setv attempt-to-omit-default (try + (kwonly-foo-no-default) + (except [e [Exception]] e))) + ;; works + (assert (= (kwonly-foo-no-default :foo "quux") "quux")) + ;; raises TypeError with appropriate message if not supplied + (assert (isinstance attempt-to-omit-default TypeError)) + (assert (in "missing 1 required keyword-only argument: 'foo'" + (. attempt-to-omit-default args [0]))) + ;; keyword-only with other arg types works + (defn function-of-various-args [a b &rest args &kwonly foo &kwargs kwargs] + (, a b args foo kwargs)) + (assert (= (function-of-various-args 1 2 3 4 :foo 5 :bar 6 :quux 7) + (, 1 2 (, 3 4) 5 {"bar" 6 "quux" 7})))) + + +(defn test-extended-unpacking-1star-lvalues [] + (setv [x #*y] [1 2 3 4]) + (assert (= x 1)) + (assert (= y [2 3 4])) + (setv [a #*b c] "ghijklmno") + (assert (= a "g")) + (assert (= b (list "hijklmn"))) + (assert (= c "o"))) + + +(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]))) + + +(defn test-yield-from-exception-handling [] + "NATIVE: Ensure exception handling in yield from works right" + (defn yield-from-subgenerator-test [] + (yield 1) + (yield 2) + (yield 3) + (assert 0)) + (defn yield-from-test [] + (for [i (range 3)] + (yield i)) + (try + (yield-from (yield-from-subgenerator-test)) + (except [e AssertionError] + (yield 4)))) + (assert (= (list (yield-from-test)) [0 1 2 1 2 3 4]))) + +(require [hy.contrib.walk [let]]) + +(defn test-let-optional [] + (let [a 1 + b 6 + d 2] + (defn foo [&kwonly [a a] b [c d]] + (, a b c)) + (assert (= (foo :b "b") + (, 1 "b" 2))) + (assert (= (foo :b 20 :a 10 :c 30) + (, 10 20 30))))) + +(defn test-pep-3115 [] + (defclass member-table [dict] + [--init-- (fn [self] (setv self.member-names [])) + + --setitem-- (fn [self key value] + (if (not-in key self) + (.append self.member-names key)) + (dict.--setitem-- self key value))]) + + (defclass OrderedClass [type] + [--prepare-- (classmethod (fn [metacls name bases] (member-table))) + + --new-- (fn [cls name bases classdict] + (setv result (type.--new-- cls name bases (dict classdict))) + (setv result.member-names classdict.member-names) + result)]) + + (defclass MyClass [:metaclass OrderedClass] + [method1 (fn [self] (pass)) + method2 (fn [self] (pass))]) + + (assert (= (. (MyClass) member-names) + ["__module__" "__qualname__" "method1" "method2"]))) + + +(import [asyncio [get-event-loop sleep]]) + + +(defn test-unpacking-pep448-1star [] + (setv l [1 2 3]) + (setv p [4 5]) + (assert (= ["a" #*l "b" #*p #*l] ["a" 1 2 3 "b" 4 5 1 2 3])) + (assert (= (, "a" #*l "b" #*p #*l) (, "a" 1 2 3 "b" 4 5 1 2 3))) + (assert (= #{"a" #*l "b" #*p #*l} #{"a" "b" 1 2 3 4 5})) + (defn f [&rest args] args) + (assert (= (f "a" #*l "b" #*p #*l) (, "a" 1 2 3 "b" 4 5 1 2 3))) + (assert (= (+ #*l #*p) 15)) + (assert (= (and #*l) 3))) + + +(defn test-unpacking-pep448-2star [] + (setv d1 {"a" 1 "b" 2}) + (setv d2 {"c" 3 "d" 4}) + (assert (= {1 "x" #**d1 #**d2 2 "y"} {"a" 1 "b" 2 "c" 3 "d" 4 1 "x" 2 "y"})) + (defn fun [&optional a b c d e f] [a b c d e f]) + (assert (= (fun #**d1 :e "eee" #**d2) [1 2 3 4 "eee" None]))) + + +(defn run-coroutine [coro] + "Run a coroutine until its done in the default event loop.""" + (.run_until_complete (get-event-loop) (coro))) + + +(defn test-fn/a [] + (assert (= (run-coroutine (fn/a [] (await (sleep 0)) [1 2 3])) + [1 2 3]))) + + +(defn test-defn/a [] + (defn/a coro-test [] + (await (sleep 0)) + [1 2 3]) + (assert (= (run-coroutine coro-test) [1 2 3]))) + + +(defn test-decorated-defn/a [] + (defn decorator [func] (fn/a [] (/ (await (func)) 2))) + + #@(decorator + (defn/a coro-test [] + (await (sleep 0)) + 42)) + (assert (= (run-coroutine coro-test) 21))) + + +(defclass AsyncWithTest [] + (defn --init-- [self val] + (setv self.val val) + None) + + (defn/a --aenter-- [self] + self.val) + + (defn/a --aexit-- [self tyle value traceback] + (setv self.val None))) + + +(defn test-single-with/a [] + (run-coroutine + (fn/a [] + (with/a [t (AsyncWithTest 1)] + (assert (= t 1)))))) + +(defn test-two-with/a [] + (run-coroutine + (fn/a [] + (with/a [t1 (AsyncWithTest 1) + t2 (AsyncWithTest 2)] + (assert (= t1 1)) + (assert (= t2 2)))))) + +(defn test-thrice-with/a [] + (run-coroutine + (fn/a [] + (with/a [t1 (AsyncWithTest 1) + t2 (AsyncWithTest 2) + t3 (AsyncWithTest 3)] + (assert (= t1 1)) + (assert (= t2 2)) + (assert (= t3 3)))))) + +(defn test-quince-with/a [] + (run-coroutine + (fn/a [] + (with/a [t1 (AsyncWithTest 1) + t2 (AsyncWithTest 2) + t3 (AsyncWithTest 3) + _ (AsyncWithTest 4)] + (assert (= t1 1)) + (assert (= t2 2)) + (assert (= t3 3)))))) diff --git a/tests/native_tests/py3_only_tests.hy b/tests/native_tests/py3_only_tests.hy deleted file mode 100644 index 4c79ad5..0000000 --- a/tests/native_tests/py3_only_tests.hy +++ /dev/null @@ -1,207 +0,0 @@ -;; 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. -;; conftest.py skips this file when running on Python 2. - - -(defn test-exception-cause [] - (try (raise ValueError :from NameError) - (except [e [ValueError]] - (assert (= (type (. e __cause__)) NameError))))) - - -(defn test-kwonly [] - "NATIVE: test keyword-only arguments" - ;; keyword-only with default works - (defn kwonly-foo-default-false [&kwonly [foo False]] foo) - (assert (= (kwonly-foo-default-false) False)) - (assert (= (kwonly-foo-default-false :foo True) True)) - ;; keyword-only without default ... - (defn kwonly-foo-no-default [&kwonly foo] foo) - (setv attempt-to-omit-default (try - (kwonly-foo-no-default) - (except [e [Exception]] e))) - ;; works - (assert (= (kwonly-foo-no-default :foo "quux") "quux")) - ;; raises TypeError with appropriate message if not supplied - (assert (isinstance attempt-to-omit-default TypeError)) - (assert (in "missing 1 required keyword-only argument: 'foo'" - (. attempt-to-omit-default args [0]))) - ;; keyword-only with other arg types works - (defn function-of-various-args [a b &rest args &kwonly foo &kwargs kwargs] - (, a b args foo kwargs)) - (assert (= (function-of-various-args 1 2 3 4 :foo 5 :bar 6 :quux 7) - (, 1 2 (, 3 4) 5 {"bar" 6 "quux" 7})))) - - -(defn test-extended-unpacking-1star-lvalues [] - (setv [x #*y] [1 2 3 4]) - (assert (= x 1)) - (assert (= y [2 3 4])) - (setv [a #*b c] "ghijklmno") - (assert (= a "g")) - (assert (= b (list "hijklmn"))) - (assert (= c "o"))) - - -(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]))) - - -(defn test-yield-from-exception-handling [] - "NATIVE: Ensure exception handling in yield from works right" - (defn yield-from-subgenerator-test [] - (yield 1) - (yield 2) - (yield 3) - (assert 0)) - (defn yield-from-test [] - (for [i (range 3)] - (yield i)) - (try - (yield-from (yield-from-subgenerator-test)) - (except [e AssertionError] - (yield 4)))) - (assert (= (list (yield-from-test)) [0 1 2 1 2 3 4]))) - -(require [hy.contrib.walk [let]]) - -(defn test-let-optional [] - (let [a 1 - b 6 - d 2] - (defn foo [&kwonly [a a] b [c d]] - (, a b c)) - (assert (= (foo :b "b") - (, 1 "b" 2))) - (assert (= (foo :b 20 :a 10 :c 30) - (, 10 20 30))))) - -(defn test-pep-3115 [] - (defclass member-table [dict] - [--init-- (fn [self] (setv self.member-names [])) - - --setitem-- (fn [self key value] - (if (not-in key self) - (.append self.member-names key)) - (dict.--setitem-- self key value))]) - - (defclass OrderedClass [type] - [--prepare-- (classmethod (fn [metacls name bases] (member-table))) - - --new-- (fn [cls name bases classdict] - (setv result (type.--new-- cls name bases (dict classdict))) - (setv result.member-names classdict.member-names) - result)]) - - (defclass MyClass [:metaclass OrderedClass] - [method1 (fn [self] (pass)) - method2 (fn [self] (pass))]) - - (assert (= (. (MyClass) member-names) - ["__module__" "__qualname__" "method1" "method2"]))) - - -(import [asyncio [get-event-loop sleep]]) - - -(defn test-unpacking-pep448-1star [] - (setv l [1 2 3]) - (setv p [4 5]) - (assert (= ["a" #*l "b" #*p #*l] ["a" 1 2 3 "b" 4 5 1 2 3])) - (assert (= (, "a" #*l "b" #*p #*l) (, "a" 1 2 3 "b" 4 5 1 2 3))) - (assert (= #{"a" #*l "b" #*p #*l} #{"a" "b" 1 2 3 4 5})) - (defn f [&rest args] args) - (assert (= (f "a" #*l "b" #*p #*l) (, "a" 1 2 3 "b" 4 5 1 2 3))) - (assert (= (+ #*l #*p) 15)) - (assert (= (and #*l) 3))) - - -(defn test-unpacking-pep448-2star [] - (setv d1 {"a" 1 "b" 2}) - (setv d2 {"c" 3 "d" 4}) - (assert (= {1 "x" #**d1 #**d2 2 "y"} {"a" 1 "b" 2 "c" 3 "d" 4 1 "x" 2 "y"})) - (defn fun [&optional a b c d e f] [a b c d e f]) - (assert (= (fun #**d1 :e "eee" #**d2) [1 2 3 4 "eee" None]))) - - -(defn run-coroutine [coro] - "Run a coroutine until its done in the default event loop.""" - (.run_until_complete (get-event-loop) (coro))) - - -(defn test-fn/a [] - (assert (= (run-coroutine (fn/a [] (await (sleep 0)) [1 2 3])) - [1 2 3]))) - - -(defn test-defn/a [] - (defn/a coro-test [] - (await (sleep 0)) - [1 2 3]) - (assert (= (run-coroutine coro-test) [1 2 3]))) - - -(defn test-decorated-defn/a [] - (defn decorator [func] (fn/a [] (/ (await (func)) 2))) - - #@(decorator - (defn/a coro-test [] - (await (sleep 0)) - 42)) - (assert (= (run-coroutine coro-test) 21))) - - -(defclass AsyncWithTest [] - (defn --init-- [self val] - (setv self.val val) - None) - - (defn/a --aenter-- [self] - self.val) - - (defn/a --aexit-- [self tyle value traceback] - (setv self.val None))) - - -(defn test-single-with/a [] - (run-coroutine - (fn/a [] - (with/a [t (AsyncWithTest 1)] - (assert (= t 1)))))) - -(defn test-two-with/a [] - (run-coroutine - (fn/a [] - (with/a [t1 (AsyncWithTest 1) - t2 (AsyncWithTest 2)] - (assert (= t1 1)) - (assert (= t2 2)))))) - -(defn test-thrice-with/a [] - (run-coroutine - (fn/a [] - (with/a [t1 (AsyncWithTest 1) - t2 (AsyncWithTest 2) - t3 (AsyncWithTest 3)] - (assert (= t1 1)) - (assert (= t2 2)) - (assert (= t3 3)))))) - -(defn test-quince-with/a [] - (run-coroutine - (fn/a [] - (with/a [t1 (AsyncWithTest 1) - t2 (AsyncWithTest 2) - t3 (AsyncWithTest 3) - _ (AsyncWithTest 4)] - (assert (= t1 1)) - (assert (= t2 2)) - (assert (= t3 3)))))) From 5c7441b011768340e7a012fdba7cb7dd88446170 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 14:34:00 -0400 Subject: [PATCH 03/21] Remove non-native tests of Python 2 --- tests/compilers/test_ast.py | 102 ++++++++----------------------- tests/compilers/test_compiler.py | 12 +--- tests/test_bin.py | 15 ++--- 3 files changed, 33 insertions(+), 96 deletions(-) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 9d004da..70ac563 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -10,7 +10,7 @@ from hy.compiler import hy_compile, hy_eval from hy.errors import HyCompileError, HyLanguageError, HyError from hy.lex import hy_parse from hy.lex.exceptions import LexException, PrematureEndOfInput -from hy._compat import PY3, PY36 +from hy._compat import PY36 import ast import pytest @@ -121,9 +121,8 @@ def test_ast_good_raise(): can_compile("(raise e)") -if PY3: - def test_ast_raise_from(): - can_compile("(raise Exception :from NameError)") +def test_ast_raise_from(): + can_compile("(raise Exception :from NameError)") def test_ast_bad_raise(): @@ -205,16 +204,16 @@ def test_ast_bad_global(): cant_compile("(global (foo))") -if PY3: - def test_ast_good_nonlocal(): - "Make sure AST can compile valid nonlocal" - can_compile("(nonlocal a)") - can_compile("(nonlocal foo bar)") +def test_ast_good_nonlocal(): + "Make sure AST can compile valid nonlocal" + can_compile("(nonlocal a)") + can_compile("(nonlocal foo bar)") - def test_ast_bad_nonlocal(): - "Make sure AST can't compile invalid nonlocal" - cant_compile("(nonlocal)") - cant_compile("(nonlocal (foo))") + +def test_ast_bad_nonlocal(): + "Make sure AST can't compile invalid nonlocal" + cant_compile("(nonlocal)") + cant_compile("(nonlocal (foo))") def test_ast_good_defclass(): @@ -226,7 +225,6 @@ def test_ast_good_defclass(): can_compile("(defclass a [] None (print \"foo\"))") -@pytest.mark.skipif(not PY3, reason="Python 3 supports class keywords") def test_ast_good_defclass_with_metaclass(): "Make sure AST can compile valid defclass with keywords" can_compile("(defclass a [:metaclass b])") @@ -299,21 +297,6 @@ import a dotted name.""" cant_compile("(require [spam [foo.bar]])") -def test_ast_no_pointless_imports(): - def contains_import_from(code): - return any([isinstance(node, ast.ImportFrom) - for node in can_compile(code).body]) - # `reduce` is a builtin in Python 2, but not Python 3. - # The version of `map` that returns an iterator is a builtin in - # Python 3, but not Python 2. - if PY3: - assert contains_import_from("reduce") - assert not contains_import_from("map") - else: - assert not contains_import_from("reduce") - assert contains_import_from("map") - - def test_ast_good_get(): "Make sure AST can compile valid get" can_compile("(get x y)") @@ -454,29 +437,20 @@ def test_lambda_list_keywords_kwargs(): def test_lambda_list_keywords_kwonly(): - """Ensure we can compile functions with &kwonly if we're on Python - 3, or fail with an informative message on Python 2.""" kwonly_demo = "(fn [&kwonly a [b 2]] (print 1) (print a b))" - if PY3: - code = can_compile(kwonly_demo) - for i, kwonlyarg_name in enumerate(('a', 'b')): - assert kwonlyarg_name == code.body[0].args.kwonlyargs[i].arg - assert code.body[0].args.kw_defaults[0] is None - assert code.body[0].args.kw_defaults[1].n == 2 - else: - exception = cant_compile(kwonly_demo) - assert isinstance(exception, HyLanguageError) - message = exception.args[0] - assert message == "&kwonly parameters require Python 3" + code = can_compile(kwonly_demo) + for i, kwonlyarg_name in enumerate(('a', 'b')): + assert kwonlyarg_name == code.body[0].args.kwonlyargs[i].arg + assert code.body[0].args.kw_defaults[0] is None + assert code.body[0].args.kw_defaults[1].n == 2 def test_lambda_list_keywords_mixed(): """ Ensure we can mix them up.""" can_compile("(fn [x &rest xs &kwargs kw] (list x xs kw))") cant_compile("(fn [x &rest xs &fasfkey {bar \"baz\"}])") - if PY3: - can_compile("(fn [x &rest xs &kwonly kwoxs &kwargs kwxs]" - " (list x xs kwxs kwoxs))") + can_compile("(fn [x &rest xs &kwonly kwoxs &kwargs kwxs]" + " (list x xs kwxs kwoxs))") def test_missing_keyword_argument_value(): @@ -504,11 +478,11 @@ def test_ast_unicode_strings(): def test_ast_unicode_vs_bytes(): - assert s('"hello"') == u"hello" - assert type(s('"hello"')) is (str if PY3 else unicode) # noqa - assert s('b"hello"') == (eval('b"hello"') if PY3 else "hello") - assert type(s('b"hello"')) is (bytes if PY3 else str) - assert s('b"\\xa0"') == (bytes([160]) if PY3 else chr(160)) + assert s('"hello"') == "hello" + assert type(s('"hello"')) is str + assert s('b"hello"') == b"hello" + assert type(s('b"hello"')) is bytes + assert s('b"\\xa0"') == bytes([160]) @pytest.mark.skipif(not PY36, reason='f-strings require Python 3.6+') @@ -528,7 +502,7 @@ def test_ast_bracket_string(): assert s(r'#[my delim[fizzle]my delim]') == 'fizzle' assert s(r'#[[]]') == '' assert s(r'#[my delim[]my delim]') == '' - assert type(s('#[X[hello]X]')) is (str if PY3 else unicode) # noqa + assert type(s('#[X[hello]X]')) is str assert s(r'#[X[raw\nstring]X]') == 'raw\\nstring' assert s(r'#[foozle[aa foozli bb ]foozle]') == 'aa foozli bb ' assert s(r'#[([unbalanced](]') == 'unbalanced' @@ -623,30 +597,6 @@ def test_lots_of_comment_lines(): can_compile(1000 * ";\n") -def test_exec_star(): - - code = can_compile('(exec* "print(5)")').body[0] - assert type(code) == (ast.Expr if PY3 else ast.Exec) - if not PY3: - assert code.body.s == "print(5)" - assert code.globals is None - assert code.locals is None - - code = can_compile('(exec* "print(a)" {"a" 3})').body[0] - assert type(code) == (ast.Expr if PY3 else ast.Exec) - if not PY3: - assert code.body.s == "print(a)" - assert code.globals.keys[0].s == "a" - assert code.locals is None - - code = can_compile('(exec* "print(a + b)" {"a" "x"} {"b" "y"})').body[0] - assert type(code) == (ast.Expr if PY3 else ast.Exec) - if not PY3: - assert code.body.s == "print(a + b)" - assert code.globals.keys[0].s == "a" - assert code.locals.keys[0].s == "b" - - def test_compiler_macro_tag_try(): """Check that try forms within defmacro/deftag are compiled correctly""" # https://github.com/hylang/hy/issues/1350 @@ -654,13 +604,11 @@ def test_compiler_macro_tag_try(): can_compile("(deftag foo [] (try None (except [] None)) `())") -@pytest.mark.skipif(not PY3, reason="Python 3 required") def test_ast_good_yield_from(): "Make sure AST can compile valid yield-from" can_compile("(yield-from [1 2])") -@pytest.mark.skipif(not PY3, reason="Python 3 required") def test_ast_bad_yield_from(): "Make sure AST can't compile invalid yield-from" cant_compile("(yield-from)") diff --git a/tests/compilers/test_compiler.py b/tests/compilers/test_compiler.py index bbb2970..9648c8f 100644 --- a/tests/compilers/test_compiler.py +++ b/tests/compilers/test_compiler.py @@ -6,7 +6,6 @@ import ast from hy import compiler from hy.models import HyExpression, HyList, HySymbol, HyInteger -from hy._compat import PY3 def make_expression(*args): @@ -64,12 +63,5 @@ def test_compiler_yield_return(): assert len(body) == 2 assert isinstance(body[0], ast.Expr) assert isinstance(body[0].value, ast.Yield) - - if PY3: - # From 3.3+, the final statement becomes a return value - assert isinstance(body[1], ast.Return) - assert isinstance(body[1].value, ast.BinOp) - else: - # In earlier versions, the expression is not returned - assert isinstance(body[1], ast.Expr) - assert isinstance(body[1].value, ast.BinOp) + assert isinstance(body[1], ast.Return) + assert isinstance(body[1].value, ast.BinOp) diff --git a/tests/test_bin.py b/tests/test_bin.py index 06b3af9..1cfb3e5 100644 --- a/tests/test_bin.py +++ b/tests/test_bin.py @@ -10,7 +10,6 @@ import shlex import subprocess from hy.importer import cache_from_source -from hy._compat import PY3 import pytest @@ -497,9 +496,8 @@ def test_bin_hy_tracebacks(): os.environ['HY_DEBUG'] = '' def req_err(x): - assert x == '{}HyRequireError: No module named {}'.format( - 'hy.errors.' if PY3 else '', - (repr if PY3 else str)('not_a_real_module')) + assert (x == 'hy.errors.HyRequireError: No module named ' + "'not_a_real_module'") # Modeled after # > python -c 'import not_a_real_module' @@ -512,7 +510,7 @@ def test_bin_hy_tracebacks(): del error_lines[-1] assert len(error_lines) <= 10 # Rough check for the internal traceback filtering - req_err(error_lines[4 if PY3 else -1]) + req_err(error_lines[4]) _, error = run_cmd('hy -c "(require not-a-real-module)"', expect=1) error_lines = error.splitlines() @@ -522,7 +520,7 @@ def test_bin_hy_tracebacks(): output, error = run_cmd('hy -i "(require not-a-real-module)"') assert output.startswith('=> ') print(error.splitlines()) - req_err(error.splitlines()[2 if PY3 else -3]) + req_err(error.splitlines()[2]) # Modeled after # > python -c 'print("hi' @@ -535,9 +533,8 @@ def test_bin_hy_tracebacks(): r'Traceback \(most recent call last\):\n' r' File "(?:|string-[0-9a-f]+)", line 1\n' r' \(print "\n' - r' \^\n' + - r'{}PrematureEndOfInput: Partial string literal\n'.format( - r'hy\.lex\.exceptions\.' if PY3 else '')) + r' \^\n' + r'hy.lex.exceptions.PrematureEndOfInput: Partial string literal\n') assert re.search(peoi_re, error) # Modeled after From ea872c39835ce3f41685c031942d44fab3a58188 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 14:46:52 -0400 Subject: [PATCH 04/21] Remove native tests of Python 2 --- tests/native_tests/comprehensions.hy | 6 +-- tests/native_tests/contrib/hy_repr.hy | 28 +++++-------- tests/native_tests/core.hy | 8 +--- tests/native_tests/extra/reserved.hy | 5 +-- tests/native_tests/language.hy | 60 ++++++++++----------------- tests/native_tests/mangling.hy | 7 +--- tests/native_tests/mathematics.hy | 16 ++----- tests/native_tests/operators.hy | 6 +-- 8 files changed, 43 insertions(+), 93 deletions(-) diff --git a/tests/native_tests/comprehensions.hy b/tests/native_tests/comprehensions.hy index 9d97cbd..206de29 100644 --- a/tests/native_tests/comprehensions.hy +++ b/tests/native_tests/comprehensions.hy @@ -1,7 +1,6 @@ (import types - pytest - [hy._compat [PY3]]) + pytest) (defn test-comprehension-types [] @@ -134,8 +133,7 @@ ; An `lfor` that gets compiled to a real comprehension (setv x 0) (assert (= (lfor x [1 2 3] (inc x)) [2 3 4])) - (assert (= x (if PY3 0 3))) - ; Python 2 list comprehensions leak their variables. + (assert (= x 0)) ; An `lfor` that gets compiled to a loop (setv x 0 l []) diff --git a/tests/native_tests/contrib/hy_repr.hy b/tests/native_tests/contrib/hy_repr.hy index 7867181..4a0a1ec 100644 --- a/tests/native_tests/contrib/hy_repr.hy +++ b/tests/native_tests/contrib/hy_repr.hy @@ -3,7 +3,7 @@ ;; license. See the LICENSE. (import - [hy._compat [PY3 PY36 PY37]] + [hy._compat [PY36 PY37]] [math [isnan]] [hy.contrib.hy-repr [hy-repr hy-repr-register]]) @@ -79,10 +79,10 @@ (assert (is (type (get orig 1)) float)) (assert (is (type (get result 1)) HyFloat))) -(when PY3 (defn test-dict-views [] +(defn test-dict-views [] (assert (= (hy-repr (.keys {1 2})) "(dict-keys [1])")) (assert (= (hy-repr (.values {1 2})) "(dict-values [2])")) - (assert (= (hy-repr (.items {1 2})) "(dict-items [(, 1 2)])")))) + (assert (= (hy-repr (.items {1 2})) "(dict-items [(, 1 2)])"))) (defn test-datetime [] (import [datetime :as D]) @@ -91,9 +91,8 @@ "(datetime.datetime 2009 1 15 15 27 5)")) (assert (= (hy-repr (D.datetime 2009 1 15 15 27 5 123)) "(datetime.datetime 2009 1 15 15 27 5 123)")) - (when PY3 - (assert (= (hy-repr (D.datetime 2009 1 15 15 27 5 123 :tzinfo D.timezone.utc)) - "(datetime.datetime 2009 1 15 15 27 5 123 :tzinfo datetime.timezone.utc)"))) + (assert (= (hy-repr (D.datetime 2009 1 15 15 27 5 123 :tzinfo D.timezone.utc)) + "(datetime.datetime 2009 1 15 15 27 5 123 :tzinfo datetime.timezone.utc)")) (when PY36 (assert (= (hy-repr (D.datetime 2009 1 15 15 27 5 :fold 1)) "(datetime.datetime 2009 1 15 15 27 5 :fold 1)")) @@ -114,17 +113,11 @@ (defn test-collections [] (import collections) (assert (= (hy-repr (collections.defaultdict :a 8)) - (if PY3 - "(defaultdict None {\"a\" 8})" - "(defaultdict None {b\"a\" 8})"))) + "(defaultdict None {\"a\" 8})")) (assert (= (hy-repr (collections.defaultdict int :a 8)) - (if PY3 - "(defaultdict {\"a\" 8})" - "(defaultdict {b\"a\" 8})"))) + "(defaultdict {\"a\" 8})")) (assert (= (hy-repr (collections.Counter [15 15 15 15])) - (if PY3 - "(Counter {15 4})" - "(Counter {15 (int 4)})"))) + "(Counter {15 4})")) (setv C (collections.namedtuple "Fooey" ["cd" "a_b"])) (assert (= (hy-repr (C 11 12)) "(Fooey :cd 11 :a_b 12)"))) @@ -155,9 +148,8 @@ (setv mo (re.search "b+" "aaaabbbccc")) (assert (= (hy-repr mo) (.format - #[[<{} object; :span {} :match "bbb">]] - (if PY37 "re.Match" (+ (. (type mo) __module__) ".SRE_Match")) - (if PY3 "(, 4 7)" "(, (int 4) (int 7))"))))) + #[[<{} object; :span (, 4 7) :match "bbb">]] + (if PY37 "re.Match" (+ (. (type mo) __module__) ".SRE_Match")))))) (defn test-hy-repr-custom [] diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy index 39a389c..787a390 100644 --- a/tests/native_tests/core.hy +++ b/tests/native_tests/core.hy @@ -2,8 +2,6 @@ ;; This file is part of Hy, which is free software licensed under the Expat ;; license. See the LICENSE. -(import [hy._compat [PY3]]) - ;;;; some simple helpers (defn assert-true [x] @@ -429,8 +427,7 @@ result['y in globals'] = 'y' in globals()") (assert-true (neg? -2)) (assert-false (neg? 1)) (assert-false (neg? 0)) - (when PY3 - (assert-requires-num neg?))) + (assert-requires-num neg?)) (defn test-zero [] "NATIVE: testing the zero? function" @@ -519,8 +516,7 @@ result['y in globals'] = 'y' in globals()") (assert-true (pos? 2)) (assert-false (pos? -1)) (assert-false (pos? 0)) - (when PY3 - (assert-requires-num pos?))) + (assert-requires-num pos?)) (defn test-remove [] "NATIVE: testing the remove function" diff --git a/tests/native_tests/extra/reserved.hy b/tests/native_tests/extra/reserved.hy index ca67577..b1b9c7a 100644 --- a/tests/native_tests/extra/reserved.hy +++ b/tests/native_tests/extra/reserved.hy @@ -2,13 +2,12 @@ ;; This file is part of Hy, which is free software licensed under the Expat ;; license. See the LICENSE. -(import [hy.extra.reserved [names]] [hy._compat [PY3]]) +(import [hy.extra.reserved [names]]) (defn test-reserved [] (assert (is (type (names)) frozenset)) (assert (in "and" (names))) - (when PY3 - (assert (in "False" (names)))) + (assert (in "False" (names))) (assert (in "pass" (names))) (assert (in "class" (names))) (assert (in "defclass" (names))) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 1494d14..63b7599 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -11,7 +11,7 @@ pytest) (import sys) -(import [hy._compat [PY3 PY37 PY38]]) +(import [hy._compat [PY38]]) (defn test-sys-argv [] "NATIVE: test sys.argv" @@ -71,13 +71,12 @@ (except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) (try (eval '(defn None [] (print "hello"))) (except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) - (when PY3 - (try (eval '(setv False 1)) - (except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) - (try (eval '(setv True 0)) - (except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) - (try (eval '(defn True [] (print "hello"))) - (except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))))) + (try (eval '(setv False 1)) + (except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) + (try (eval '(setv True 0)) + (except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) + (try (eval '(defn True [] (print "hello"))) + (except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))) (defn test-setv-pairs [] @@ -513,9 +512,7 @@ (setv passed False) (try (raise) - ;; Python 2 raises IndexError here (due to the previous test) - ;; Python 3 raises RuntimeError - (except [[IndexError RuntimeError]] + (except [RuntimeError] (setv passed True))) (assert passed) @@ -747,16 +744,11 @@ (defn test-yield-with-return [] "NATIVE: test yield with return" (defn gen [] (yield 3) "goodbye") - (if PY3 - (do (setv gg (gen)) - (assert (= 3 (next gg))) - (try (next gg) - (except [e StopIteration] (assert (hasattr e "value")) - (assert (= (getattr e "value") "goodbye"))))) - (do (setv gg (gen)) - (assert (= 3 (next gg))) - (try (next gg) - (except [e StopIteration] (assert (not (hasattr e "value")))))))) + (setv gg (gen)) + (assert (= 3 (next gg))) + (try (next gg) + (except [e StopIteration] (assert (hasattr e "value")) + (assert (= (getattr e "value") "goodbye"))))) (defn test-yield-in-try [] @@ -1242,19 +1234,14 @@ cee\"} dee" "ey bee\ncee dee")) ; Conversion characters and format specifiers (setv p:9 "other") (setv !r "bar") - (defn u [s] - ; Add a "u" prefix for Python 2. - (if PY3 - s - (.replace (.replace s "'" "u'" 1) " " " " 1))) - (assert (= f"a{p !r}" (u "a'xyzzy'"))) + (assert (= f"a{p !r}" "a'xyzzy'")) (assert (= f"a{p :9}" "axyzzy ")) (assert (= f"a{p:9}" "aother")) - (assert (= f"a{p !r :9}" (u "a'xyzzy' "))) - (assert (= f"a{p !r:9}" (u "a'xyzzy' "))) + (assert (= f"a{p !r :9}" "a'xyzzy' ")) + (assert (= f"a{p !r:9}" "a'xyzzy' ")) (assert (= f"a{p:9 :9}" "aother ")) (assert (= f"a{!r}" "abar")) - (assert (= f"a{!r !r}" (u "a'bar'"))) + (assert (= f"a{!r !r}" "a'bar'")) ; Fun with `r` (assert (= f"hello {r\"\\n\"}" r"hello \n")) @@ -1278,7 +1265,7 @@ cee\"} dee" "ey bee\ncee dee")) (assert (= f"{(C) : {(str (+ 1 1)) !r :x<5}}" "C[ '2'xx]")) ; Format bracket strings - (assert (= #[f[a{p !r :9}]f] (u "a'xyzzy' "))) + (assert (= #[f[a{p !r :9}]f] "a'xyzzy' ")) (assert (= #[f-string[result: {value :{width}.{precision}}]f-string] "result: 12.34"))) @@ -1549,17 +1536,12 @@ cee\"} dee" "ey bee\ncee dee")) (defn test-disassemble [] "NATIVE: Test the disassemble function" - (assert (= (disassemble '(do (leaky) (leaky) (macros))) (cond - [PY3 (.format "Module( + (assert (= (disassemble '(do (leaky) (leaky) (macros))) + (.format "Module( 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='macros'), args=[], keywords=[]))]{})" - (if PY38 ",\n type_ignores=[]" ""))] - [True "Module( - 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)), - Expr(value=Call(func=Name(id='macros'), args=[], keywords=[], starargs=None, kwargs=None))])"]))) + (if PY38 ",\n type_ignores=[]" "")))) (assert (= (disassemble '(do (leaky) (leaky) (macros)) True) "leaky() leaky() diff --git a/tests/native_tests/mangling.hy b/tests/native_tests/mangling.hy index 832d236..22c1942 100644 --- a/tests/native_tests/mangling.hy +++ b/tests/native_tests/mangling.hy @@ -3,9 +3,6 @@ ;; license. See the LICENSE. -(import [hy._compat [PY3]]) - - (defn test-hyphen [] (setv a-b 1) (assert (= a-b 1)) @@ -63,9 +60,7 @@ (defn test-higher-unicode [] (setv 😂 "emoji") (assert (= 😂 "emoji")) - (if PY3 - (assert (= hyx_Xface_with_tears_of_joyX "emoji")) - (assert (= hyx_XU1f602X "emoji")))) + (assert (= hyx_Xface_with_tears_of_joyX "emoji"))) (defn test-nameless-unicode [] diff --git a/tests/native_tests/mathematics.hy b/tests/native_tests/mathematics.hy index aa4ad78..8ad8797 100644 --- a/tests/native_tests/mathematics.hy +++ b/tests/native_tests/mathematics.hy @@ -2,8 +2,6 @@ ;; This file is part of Hy, which is free software licensed under the Expat ;; license. See the LICENSE. -(import [hy._compat [PY3]]) - (setv square (fn [x] (* x x))) @@ -191,20 +189,12 @@ (defn test-matmul [] "NATIVE: test matrix multiplication" - (if PY3 - (assert (= (@ first-test-matrix second-test-matrix) - product-of-test-matrices)) - ;; Python <= 3.4 - (do - (setv matmul-attempt (try (@ first-test-matrix second-test-matrix) - (except [e [Exception]] e))) - (assert (isinstance matmul-attempt NameError))))) + (assert (= (@ first-test-matrix second-test-matrix) + product-of-test-matrices))) (defn test-augassign-matmul [] "NATIVE: test augmented-assignment matrix multiplication" (setv matrix first-test-matrix matmul-attempt (try (@= matrix second-test-matrix) (except [e [Exception]] e))) - (if PY3 - (assert (= product-of-test-matrices matrix)) - (assert (isinstance matmul-attempt NameError)))) + (assert (= product-of-test-matrices matrix))) diff --git a/tests/native_tests/operators.hy b/tests/native_tests/operators.hy index cb0851e..6f2ea4a 100644 --- a/tests/native_tests/operators.hy +++ b/tests/native_tests/operators.hy @@ -2,8 +2,6 @@ ;; This file is part of Hy, which is free software licensed under the Expat ;; license. See the LICENSE. -(import pytest [hy._compat [PY3]]) - (defmacro op-and-shadow-test [op &rest body] ; Creates two tests with the given `body`, one where all occurrences ; of the symbol `f` are syntactically replaced with `op` (a test of @@ -102,14 +100,14 @@ (forbid (f 1 2 3))) -(when PY3 (op-and-shadow-test @ +(op-and-shadow-test @ (defclass C [object] [ __init__ (fn [self content] (setv self.content content)) __matmul__ (fn [self other] (C (+ self.content other.content)))]) (forbid (f)) (assert (do (setv c (C "a")) (is (f c) c))) (assert (= (. (f (C "b") (C "c")) content) "bc")) - (assert (= (. (f (C "d") (C "e") (C "f")) content) "def")))) + (assert (= (. (f (C "d") (C "e") (C "f")) content) "def"))) (op-and-shadow-test << From b130e3284e0edb362ddce194babdb0b08a47f222 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 14:47:08 -0400 Subject: [PATCH 05/21] Don't test Python 2 --- .travis.yml | 2 -- conftest.py | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4234503..f7fe97b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,10 @@ sudo: false dist: xenial language: python python: - - "2.7" - "3.5" - "3.6" - "3.7" - 3.8-dev - - pypy2.7-6.0 - pypy3.5-6.0 install: - pip install -r requirements-travis.txt diff --git a/conftest.py b/conftest.py index 9f65b48..7ded83e 100644 --- a/conftest.py +++ b/conftest.py @@ -4,7 +4,7 @@ import importlib import py import pytest import hy -from hy._compat import PY3, PY36, PY38 +from hy._compat import PY36, PY38 NATIVE_TESTS = os.path.join("", "tests", "native_tests", "") @@ -12,8 +12,7 @@ _fspath_pyimport = py.path.local.pyimport def pytest_ignore_collect(path, config): - return (("py3_only" in path.basename and not PY3) or - ("py36_only" in path.basename and not PY36) or + return (("py36_only" in path.basename and not PY36) or ("py38_only" in path.basename and not PY38) or None) From bba97ab2a6f8356d96e06e4eb9241f40aec6a53f Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 15:18:12 -0400 Subject: [PATCH 06/21] Remove hy._compat's type aliases --- hy/_compat.py | 5 ----- hy/compiler.py | 19 +++++++++---------- hy/completer.py | 6 +++--- hy/contrib/hy_repr.hy | 12 ++++-------- hy/core/language.hy | 5 ++--- hy/lex/__init__.py | 8 ++++---- hy/lex/parser.py | 3 +-- hy/macros.py | 4 ++-- hy/models.py | 32 ++++++++++++++------------------ tests/test_models.py | 15 +++++++-------- 10 files changed, 46 insertions(+), 63 deletions(-) diff --git a/hy/_compat.py b/hy/_compat.py index a2ab7a5..0da0b09 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -17,11 +17,6 @@ PY38 = sys.version_info >= (3, 8) # It is always true on Pythons >= 3.3, which use USC-4 on all systems. UCS4 = sys.maxunicode == 0x10FFFF -str_type = str if PY3 else unicode # NOQA -bytes_type = bytes if PY3 else str # NOQA -long_type = int if PY3 else long # NOQA -string_types = str if PY3 else basestring # NOQA - # # Inspired by the same-named `six` functions. # diff --git a/hy/compiler.py b/hy/compiler.py index 9a20daf..b1d6e2d 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -14,8 +14,7 @@ from hy.errors import (HyCompileError, HyTypeError, HyLanguageError, 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, - PY36, PY38, reraise) +from hy._compat import (PY3, PY36, PY38, reraise) from hy.macros import require, load_macros, macroexpand, tag_macroexpand import hy.core @@ -512,7 +511,7 @@ class HyASTCompiler(object): compiled_value = self.compile(value) ret += compiled_value - arg = str_type(expr)[1:] + arg = str(expr)[1:] keywords.append(asty.keyword( expr, arg=ast_str(arg), value=compiled_value.force_expr)) @@ -1358,7 +1357,7 @@ class HyASTCompiler(object): def compile_maths_expression(self, expr, root, args): if len(args) == 0: # Return the identity element for this operator. - return asty.Num(expr, n=long_type( + return asty.Num(expr, n=( {"+": 0, "|": 0, "*": 1}[root])) if len(args) == 1: @@ -1763,7 +1762,7 @@ class HyASTCompiler(object): @builds_model(HyInteger, HyFloat, HyComplex) def compile_numeric_literal(self, x): - f = {HyInteger: long_type, + f = {HyInteger: int, HyFloat: float, HyComplex: complex}[type(x)] return asty.Num(x, n=f(x)) @@ -1810,9 +1809,9 @@ class HyASTCompiler(object): def compile_string(self, string): if type(string) is HyString and string.is_format: # This is a format string (a.k.a. an f-string). - return self._format_string(string, str_type(string)) + return self._format_string(string, str(string)) node = asty.Bytes if PY3 and type(string) is HyBytes else asty.Str - f = bytes_type if type(string) is HyBytes else str_type + f = bytes if type(string) is HyBytes else str return node(string, s=f(string)) def _format_string(self, string, rest, allow_recursion=True): @@ -1859,7 +1858,7 @@ class HyASTCompiler(object): try: model, item = parse_one_thing(item) except (ValueError, LexException) as e: - raise self._syntax_error(string, "f-string: " + str_type(e)) + raise self._syntax_error(string, "f-string: " + str(e)) # Look for a conversion character. item = item.lstrip() @@ -1933,7 +1932,7 @@ def get_compiler_module(module=None, compiler=None, calling_frame=False): module = getattr(compiler, 'module', None) or module - if isinstance(module, string_types): + if isinstance(module, str): if module.startswith('<') and module.endswith('>'): module = types.ModuleType(module) else: @@ -2098,7 +2097,7 @@ def hy_compile(tree, module, root=ast.Module, get_expr=False, """ module = get_compiler_module(module, compiler, False) - if isinstance(module, string_types): + if isinstance(module, str): if module.startswith('<') and module.endswith('>'): module = types.ModuleType(module) else: diff --git a/hy/completer.py b/hy/completer.py index 38cf8eb..0f65ae6 100644 --- a/hy/completer.py +++ b/hy/completer.py @@ -9,7 +9,7 @@ import sys import hy.macros import hy.compiler -from hy._compat import builtins, string_types +from hy._compat import builtins docomplete = True @@ -78,7 +78,7 @@ class Completer(object): matches = [] for p in self.path: for k in p.keys(): - if isinstance(k, string_types): + if isinstance(k, str): k = k.replace("_", "-") if k.startswith(text): matches.append(k) @@ -89,7 +89,7 @@ class Completer(object): matches = [] for p in self.tag_path: for k in p.keys(): - if isinstance(k, string_types): + if isinstance(k, str): if k.startswith(text): matches.append("#{}".format(k)) return matches diff --git a/hy/contrib/hy_repr.hy b/hy/contrib/hy_repr.hy index c99a45a..3baaebb 100644 --- a/hy/contrib/hy_repr.hy +++ b/hy/contrib/hy_repr.hy @@ -7,7 +7,7 @@ re datetime collections - [hy._compat [PY3 PY36 str-type bytes-type long-type]] + [hy._compat [PY36]] [hy.models [HyObject HyExpression HySymbol HyKeyword HyInteger HyFloat HyComplex HyList HyDict HySet HyString HyBytes]]) (try @@ -84,10 +84,10 @@ (+ "(" (-cat x) ")")))) (hy-repr-register [HySymbol HyKeyword] str) -(hy-repr-register [str-type bytes-type] (fn [x] +(hy-repr-register [str bytes] (fn [x] (setv r (.lstrip (-base-repr x) "ub")) (+ - (if (instance? bytes-type x) "b" "") + (if (instance? bytes x) "b" "") (if (.startswith "\"" r) ; If Python's built-in repr produced a double-quoted string, use ; that. @@ -96,10 +96,6 @@ ; convert it. (+ "\"" (.replace (cut r 1 -1) "\"" "\\\"") "\""))))) (hy-repr-register bool str) -(if (not PY3) (hy-repr-register int (fn [x] - (.format "(int {})" (-base-repr x))))) -(if (not PY3) (hy-repr-register long_type (fn [x] - (.rstrip (-base-repr x) "L")))) (hy-repr-register float (fn [x] (if (isnan x) "NaN" @@ -131,7 +127,7 @@ (-repr-time-innards x)))) (defn -repr-time-innards [x] (.rstrip (+ " " (.join " " (filter identity [ - (if x.microsecond (str-type x.microsecond)) + (if x.microsecond (str x.microsecond)) (if (not (none? x.tzinfo)) (+ ":tzinfo " (hy-repr x.tzinfo))) (if (and PY36 (!= x.fold 0)) (+ ":fold " (hy-repr x.fold)))]))))) (defn -strftime-0 [x fmt] diff --git a/hy/core/language.hy b/hy/core/language.hy index 53af7fb..687bd5c 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -11,7 +11,6 @@ (import [fractions [Fraction :as fraction]]) (import operator) ; shadow not available yet (import sys) -(import [hy._compat [long-type]]) ; long for python2, int for python3 (import [hy.models [HySymbol HyKeyword]]) (import [hy.lex [tokenize mangle unmangle read read-str]]) (import [hy.lex.exceptions [LexException PrematureEndOfInput]]) @@ -254,11 +253,11 @@ Return series of accumulated sums (or other binary function results)." (defn integer [x] "Return Hy kind of integer for `x`." - (long-type x)) + (int x)) (defn integer? [x] "Check if `x` is an integer." - (isinstance x (, int long-type))) + (isinstance x int)) (defn integer-char? [x] "Check if char `x` parses as an integer." diff --git a/hy/lex/__init__.py b/hy/lex/__init__.py index d133f5f..b29709c 100644 --- a/hy/lex/__init__.py +++ b/hy/lex/__init__.py @@ -8,7 +8,7 @@ import re import sys import unicodedata -from hy._compat import str_type, isidentifier, UCS4 +from hy._compat import isidentifier, UCS4 from hy.lex.exceptions import PrematureEndOfInput, LexException # NOQA from hy.models import HyExpression, HySymbol @@ -116,7 +116,7 @@ def mangle(s): assert s - s = str_type(s) + s = str(s) s = s.replace("-", "_") s2 = s.lstrip('_') leading_underscores = '_' * (len(s) - len(s2)) @@ -147,7 +147,7 @@ def unmangle(s): form. This may not round-trip, because different Hy symbol names can mangle to the same Python identifier.""" - s = str_type(s) + s = str(s) s2 = s.lstrip('_') leading_underscores = len(s) - len(s2) @@ -203,4 +203,4 @@ def read(from_file=sys.stdin, eof=""): def read_str(input): - return read(StringIO(str_type(input))) + return read(StringIO(str(input))) diff --git a/hy/lex/parser.py b/hy/lex/parser.py index 6a1acfb..1d5ce24 100755 --- a/hy/lex/parser.py +++ b/hy/lex/parser.py @@ -9,7 +9,6 @@ from functools import wraps from rply import ParserGenerator -from hy._compat import str_type from hy.models import (HyBytes, HyComplex, HyDict, HyExpression, HyFloat, HyInteger, HyKeyword, HyList, HySet, HyString, HySymbol) from .lexer import lexer @@ -214,7 +213,7 @@ def t_string(state, p): raise LexException.from_lexer("Can't convert {} to a HyString".format(p[0].value), state, p[0]) return (HyString(s, is_format = is_format) - if isinstance(s, str_type) + if isinstance(s, str) else HyBytes(s)) diff --git a/hy/macros.py b/hy/macros.py index e2cec31..97d0976 100644 --- a/hy/macros.py +++ b/hy/macros.py @@ -9,7 +9,7 @@ import traceback from contextlib import contextmanager -from hy._compat import PY3, string_types, reraise, rename_function +from hy._compat import PY3, reraise, rename_function from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value from hy.lex import mangle from hy.errors import (HyLanguageError, HyMacroExpansionError, HyTypeError, @@ -156,7 +156,7 @@ def require(source_module, target_module, assignments, prefix=""): parent_frame = inspect.stack()[1][0] target_namespace = parent_frame.f_globals target_module = target_namespace.get('__name__', None) - elif isinstance(target_module, string_types): + elif isinstance(target_module, str): target_module = importlib.import_module(target_module) target_namespace = target_module.__dict__ elif inspect.ismodule(target_module): diff --git a/hy/models.py b/hy/models.py index 458d615..fbced02 100644 --- a/hy/models.py +++ b/hy/models.py @@ -6,7 +6,6 @@ from __future__ import unicode_literals from contextlib import contextmanager from math import isnan, isinf from hy import _initialize_env_var -from hy._compat import PY3, str_type, bytes_type, long_type, string_types from hy.errors import HyWrapperError from fractions import Fraction from clint.textui import colored @@ -88,7 +87,7 @@ def repr_indent(obj): return repr(obj).replace("\n", "\n ") -class HyString(HyObject, str_type): +class HyString(HyObject, str): """ Generic Hy String object. Helpful to store string literals from Hy scripts. It's either a ``str`` or a ``unicode``, depending on the @@ -100,20 +99,20 @@ class HyString(HyObject, str_type): value.brackets = brackets return value -_wrappers[str_type] = HyString +_wrappers[str] = HyString -class HyBytes(HyObject, bytes_type): +class HyBytes(HyObject, bytes): """ Generic Hy Bytes object. It's either a ``bytes`` or a ``str``, depending on the Python version. """ pass -_wrappers[bytes_type] = HyBytes +_wrappers[bytes] = HyBytes -class HySymbol(HyObject, str_type): +class HySymbol(HyObject, str): """ Hy Symbol. Basically a string. """ @@ -170,42 +169,39 @@ def strip_digit_separators(number): # Don't strip a _ or , if it's the first character, as _42 and # ,42 aren't valid numbers return (number[0] + number[1:].replace("_", "").replace(",", "") - if isinstance(number, string_types) and len(number) > 1 + if isinstance(number, str) and len(number) > 1 else number) -class HyInteger(HyObject, long_type): +class HyInteger(HyObject, int): """ Internal representation of a Hy Integer. May raise a ValueError as if - int(foo) was called, given HyInteger(foo). On python 2.x long will - be used instead + int(foo) was called, given HyInteger(foo). """ def __new__(cls, number, *args, **kwargs): - if isinstance(number, string_types): + if isinstance(number, str): number = strip_digit_separators(number) bases = {"0x": 16, "0o": 8, "0b": 2} for leader, base in bases.items(): if number.startswith(leader): # We've got a string, known leader, set base. - number = long_type(number, base=base) + number = int(number, base=base) break else: # We've got a string, no known leader; base 10. - number = long_type(number, base=10) + number = int(number, base=10) else: # We've got a non-string; convert straight. - number = long_type(number) + number = int(number) return super(HyInteger, cls).__new__(cls, number) _wrappers[int] = HyInteger -if not PY3: # do not add long on python3 - _wrappers[long_type] = HyInteger def check_inf_nan_cap(arg, value): - if isinstance(arg, string_types): + if isinstance(arg, str): if isinf(value) and "i" in arg.lower() and "Inf" not in arg: raise ValueError('Inf must be capitalized as "Inf"') if isnan(value) and "NaN" not in arg: @@ -233,7 +229,7 @@ class HyComplex(HyObject, complex): """ def __new__(cls, real, imag=0, *args, **kwargs): - if isinstance(real, string_types): + if isinstance(real, str): value = super(HyComplex, cls).__new__( cls, strip_digit_separators(real) ) diff --git a/tests/test_models.py b/tests/test_models.py index fd1b615..b9e6a90 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -5,14 +5,13 @@ import copy import hy from clint.textui.colored import clean -from hy._compat import long_type, str_type from hy.models import (wrap_value, replace_hy_obj, HyString, HyInteger, HyList, HyDict, HySet, HyExpression, HyComplex, HyFloat, pretty) -def test_wrap_long_type(): +def test_wrap_int(): """ Test conversion of integers.""" - wrapped = wrap_value(long_type(0)) + wrapped = wrap_value(0) assert type(wrapped) == HyInteger @@ -26,27 +25,27 @@ def test_wrap_tuple(): def test_wrap_nested_expr(): """ Test conversion of HyExpressions with embedded non-HyObjects.""" - wrapped = wrap_value(HyExpression([long_type(0)])) + wrapped = wrap_value(HyExpression([0])) assert type(wrapped) == HyExpression assert type(wrapped[0]) == HyInteger assert wrapped == HyExpression([HyInteger(0)]) -def test_replace_long_type(): +def test_replace_int(): """ Test replacing integers.""" - replaced = replace_hy_obj(long_type(0), HyInteger(13)) + replaced = replace_hy_obj(0, HyInteger(13)) assert replaced == HyInteger(0) def test_replace_string_type(): """Test replacing python string""" - replaced = replace_hy_obj(str_type("foo"), HyString("bar")) + replaced = replace_hy_obj("foo", HyString("bar")) assert replaced == HyString("foo") def test_replace_tuple(): """ Test replacing tuples.""" - replaced = replace_hy_obj((long_type(0), ), HyInteger(13)) + replaced = replace_hy_obj((0, ), HyInteger(13)) assert type(replaced) == HyList assert type(replaced[0]) == HyInteger assert replaced == HyList([HyInteger(0)]) From 2685b01a4b6e00901d6d5ea0877809abaef4be1d Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 15:18:56 -0400 Subject: [PATCH 07/21] Remove miscellaneous PY3 checks --- hy/cmdline.py | 8 ++++---- hy/core/shadow.hy | 13 ++++--------- hy/macros.py | 5 +---- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/hy/cmdline.py b/hy/cmdline.py index d38ec08..e6d3a71 100644 --- a/hy/cmdline.py +++ b/hy/cmdline.py @@ -35,7 +35,7 @@ from hy.importer import runhy from hy.completer import completion, Completer from hy.macros import macro, require from hy.models import HyExpression, HyString, HySymbol -from hy._compat import builtins, PY3, FileNotFoundError +from hy._compat import builtins, FileNotFoundError sys.last_type = None @@ -661,7 +661,7 @@ def hy2py_main(): if options.with_source: # need special printing on Windows in case the # codepage doesn't support utf-8 characters - if PY3 and platform.system() == "Windows": + if platform.system() == "Windows": for h in hst: try: print(h) @@ -676,7 +676,7 @@ def hy2py_main(): _ast = hy_compile(hst, '__main__', filename=filename, source=source) if options.with_ast: - if PY3 and platform.system() == "Windows": + if platform.system() == "Windows": _print_for_windows(astor.dump_tree(_ast)) else: print(astor.dump_tree(_ast)) @@ -684,7 +684,7 @@ def hy2py_main(): print() if not options.without_python: - if PY3 and platform.system() == "Windows": + if platform.system() == "Windows": _print_for_windows(astor.code_gen.to_source(_ast)) else: print(astor.code_gen.to_source(_ast)) diff --git a/hy/core/shadow.hy b/hy/core/shadow.hy index 2135400..8a18c60 100644 --- a/hy/core/shadow.hy +++ b/hy/core/shadow.hy @@ -5,12 +5,10 @@ ;;;; Hy shadow functions (import operator) -(import [hy._compat [PY3]]) (require [hy.core.bootstrap [*]]) -(if PY3 - (import [functools [reduce]])) +(import [functools [reduce]]) (defn + [&rest args] "Shadowed `+` operator adds `args`." @@ -60,10 +58,9 @@ "Shadowed `%` operator takes `x` modulo `y`." (% x y)) -(if PY3 - (defn @ [a1 &rest a-rest] - "Shadowed `@` operator matrix multiples `a1` by each `a-rest`." - (reduce operator.matmul a-rest a1))) +(defn @ [a1 &rest a-rest] + "Shadowed `@` operator matrix multiples `a1` by each `a-rest`." + (reduce operator.matmul a-rest a1))) (defn << [a1 a2 &rest a-rest] "Shadowed `<<` operator performs left-shift on `a1` by `a2`, ..., `a-rest`." @@ -173,5 +170,3 @@ 'and 'or 'not 'is 'is-not 'in 'not-in 'get]) -(if (not PY3) - (.remove EXPORTS '@)) diff --git a/hy/macros.py b/hy/macros.py index 97d0976..32658d6 100644 --- a/hy/macros.py +++ b/hy/macros.py @@ -9,7 +9,7 @@ import traceback from contextlib import contextmanager -from hy._compat import PY3, reraise, rename_function +from hy._compat import reraise, rename_function from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value from hy.lex import mangle from hy.errors import (HyLanguageError, HyMacroExpansionError, HyTypeError, @@ -74,9 +74,6 @@ def tag(name): def _(fn): _name = mangle('#{}'.format(name)) - if not PY3: - _name = _name.encode('UTF-8') - fn = rename_function(fn, _name) module = inspect.getmodule(fn) From ecf0352d3702358d5ad3213280568e9292ce1f7b Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 16:12:00 -0400 Subject: [PATCH 08/21] Remove aliases: `builtins`, `FileNotFoundError` --- hy/_compat.py | 9 --------- hy/cmdline.py | 4 ++-- hy/compiler.py | 5 +---- hy/completer.py | 2 +- hy/core/shadow.hy | 2 +- tests/test_bin.py | 3 +-- 6 files changed, 6 insertions(+), 19 deletions(-) diff --git a/hy/_compat.py b/hy/_compat.py index 0da0b09..65be73c 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -2,10 +2,6 @@ # This file is part of Hy, which is free software licensed under the Expat # license. See the LICENSE. -try: - import __builtin__ as builtins -except ImportError: - import builtins # NOQA import sys, keyword, textwrap PY3 = sys.version_info[0] >= 3 @@ -97,8 +93,3 @@ def isidentifier(x): # https://bugs.python.org/issue33899 tokens = [t for t in tokens if t[0] != T.NEWLINE] return len(tokens) == 2 and tokens[0][0] == T.NAME - -try: - FileNotFoundError = FileNotFoundError -except NameError: - FileNotFoundError = IOError diff --git a/hy/cmdline.py b/hy/cmdline.py index e6d3a71..4c0c1fa 100644 --- a/hy/cmdline.py +++ b/hy/cmdline.py @@ -19,6 +19,7 @@ import time import linecache import hashlib import codeop +import builtins import astor.code_gen @@ -35,7 +36,6 @@ from hy.importer import runhy from hy.completer import completion, Completer from hy.macros import macro, require from hy.models import HyExpression, HyString, HySymbol -from hy._compat import builtins, FileNotFoundError sys.last_type = None @@ -256,7 +256,7 @@ class HyREPL(code.InteractiveConsole, object): module, f = '.'.join(parts[:-1]), parts[-1] self.output_fn = getattr(importlib.import_module(module), f) else: - self.output_fn = __builtins__[mangle(output_fn)] + self.output_fn = getattr(builtins, mangle(output_fn)) # Pre-mangle symbols for repl recent results: *1, *2, *3 self._repl_results_symbols = [mangle("*{}".format(i + 1)) for i in range(3)] diff --git a/hy/compiler.py b/hy/compiler.py index b1d6e2d..3b0d700 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -28,15 +28,12 @@ import types import ast import sys import copy +import builtins import __future__ from collections import defaultdict from functools import reduce -if PY3: - import builtins -else: - import __builtin__ as builtins Inf = float('inf') diff --git a/hy/completer.py b/hy/completer.py index 0f65ae6..b1969c1 100644 --- a/hy/completer.py +++ b/hy/completer.py @@ -6,10 +6,10 @@ import contextlib import os import re import sys +import builtins import hy.macros import hy.compiler -from hy._compat import builtins docomplete = True diff --git a/hy/core/shadow.hy b/hy/core/shadow.hy index 8a18c60..74ab08f 100644 --- a/hy/core/shadow.hy +++ b/hy/core/shadow.hy @@ -60,7 +60,7 @@ (defn @ [a1 &rest a-rest] "Shadowed `@` operator matrix multiples `a1` by each `a-rest`." - (reduce operator.matmul a-rest a1))) + (reduce operator.matmul a-rest a1)) (defn << [a1 a2 &rest a-rest] "Shadowed `<<` operator performs left-shift on `a1` by `a2`, ..., `a-rest`." diff --git a/tests/test_bin.py b/tests/test_bin.py index 1cfb3e5..297eade 100644 --- a/tests/test_bin.py +++ b/tests/test_bin.py @@ -8,13 +8,12 @@ import os import re import shlex import subprocess +import builtins from hy.importer import cache_from_source import pytest -from hy._compat import builtins - hy_dir = os.environ.get('HY_DIR', '') From c255f0d03c92a7d14ff379d6aee00a5360eeb143 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 15:43:39 -0400 Subject: [PATCH 09/21] Remove old hy._compat raising code --- hy/_compat.py | 46 ++++++++++------------------------------------ 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/hy/_compat.py b/hy/_compat.py index 65be73c..cbe0fad 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -2,7 +2,7 @@ # This file is part of Hy, which is free software licensed under the Expat # license. See the LICENSE. -import sys, keyword, textwrap +import sys, keyword PY3 = sys.version_info[0] >= 3 PY36 = sys.version_info >= (3, 6) @@ -13,44 +13,18 @@ PY38 = sys.version_info >= (3, 8) # It is always true on Pythons >= 3.3, which use USC-4 on all systems. UCS4 = sys.maxunicode == 0x10FFFF -# -# Inspired by the same-named `six` functions. -# -if PY3: - raise_src = textwrap.dedent(''' - def raise_from(value, from_value): - raise value from from_value - ''') - def reraise(exc_type, value, traceback=None): - try: - raise value.with_traceback(traceback) - finally: - traceback = None +def reraise(exc_type, value, traceback=None): + try: + raise value.with_traceback(traceback) + finally: + traceback = None - code_obj_args = ['argcount', 'kwonlyargcount', 'nlocals', 'stacksize', - 'flags', 'code', 'consts', 'names', 'varnames', - 'filename', 'name', 'firstlineno', 'lnotab', 'freevars', - 'cellvars'] -else: - def raise_from(value, from_value=None): - raise value - - raise_src = textwrap.dedent(''' - def reraise(exc_type, value, traceback=None): - try: - raise exc_type, value, traceback - finally: - traceback = None - ''') - - code_obj_args = ['argcount', 'nlocals', 'stacksize', 'flags', 'code', - 'consts', 'names', 'varnames', 'filename', 'name', - 'firstlineno', 'lnotab', 'freevars', 'cellvars'] - -raise_code = compile(raise_src, __file__, 'exec') -exec(raise_code) +code_obj_args = ['argcount', 'kwonlyargcount', 'nlocals', 'stacksize', + 'flags', 'code', 'consts', 'names', 'varnames', + 'filename', 'name', 'firstlineno', 'lnotab', 'freevars', + 'cellvars'] def rename_function(func, new_name): """Creates a copy of a function and [re]sets the name at the code-object From 7991c59480fa2c7ca8a0ca691e134381861d641e Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 15:48:29 -0400 Subject: [PATCH 10/21] Remove handling of UCS-2 --- hy/_compat.py | 4 ---- hy/lex/__init__.py | 17 ++--------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/hy/_compat.py b/hy/_compat.py index cbe0fad..518434c 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -9,10 +9,6 @@ PY36 = sys.version_info >= (3, 6) 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. -# It is always true on Pythons >= 3.3, which use USC-4 on all systems. -UCS4 = sys.maxunicode == 0x10FFFF - def reraise(exc_type, value, traceback=None): try: diff --git a/hy/lex/__init__.py b/hy/lex/__init__.py index b29709c..e2e5a26 100644 --- a/hy/lex/__init__.py +++ b/hy/lex/__init__.py @@ -8,7 +8,7 @@ import re import sys import unicodedata -from hy._compat import isidentifier, UCS4 +from hy._compat import isidentifier from hy.lex.exceptions import PrematureEndOfInput, LexException # NOQA from hy.models import HyExpression, HySymbol @@ -135,7 +135,7 @@ def mangle(s): else '{0}{1}{0}'.format(mangle_delim, unicodedata.name(c, '').lower().replace('-', 'H').replace(' ', '_') or 'U{}'.format(unicode_char_to_hex(c))) - for c in unicode_to_ucs4iter(s)) + for c in s) s = leading_underscores + s assert isidentifier(s) @@ -168,19 +168,6 @@ def unmangle(s): return '-' * leading_underscores + s -def unicode_to_ucs4iter(ustr): - # Covert a unicode string to an iterable object, - # elements in the object are single USC-4 unicode characters - if UCS4: - return ustr - ucs4_list = list(ustr) - for i, u in enumerate(ucs4_list): - if 0xD7FF < ord(u) < 0xDC00: - ucs4_list[i] += ucs4_list[i + 1] - del ucs4_list[i + 1] - return ucs4_list - - def read(from_file=sys.stdin, eof=""): """Read from input and returns a tokenized string. From e45cee575a4e6f9803f792fdab32c373571d4994 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 15:55:24 -0400 Subject: [PATCH 11/21] Move `rename_function` to hy.macros --- hy/_compat.py | 21 --------------------- hy/macros.py | 23 ++++++++++++++++++++++- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/hy/_compat.py b/hy/_compat.py index 518434c..1411521 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -17,27 +17,6 @@ def reraise(exc_type, value, traceback=None): traceback = None -code_obj_args = ['argcount', 'kwonlyargcount', 'nlocals', 'stacksize', - 'flags', 'code', 'consts', 'names', 'varnames', - 'filename', 'name', 'firstlineno', 'lnotab', 'freevars', - 'cellvars'] - -def rename_function(func, new_name): - """Creates a copy of a function and [re]sets the name at the code-object - level. - """ - c = func.__code__ - new_code = type(c)(*[getattr(c, 'co_{}'.format(a)) - if a != 'name' else str(new_name) - for a in code_obj_args]) - - _fn = type(func)(new_code, func.__globals__, str(new_name), - func.__defaults__, func.__closure__) - _fn.__dict__.update(func.__dict__) - - return _fn - - def isidentifier(x): if x in ('True', 'False', 'None', 'print'): # `print` is special-cased here because Python 2's diff --git a/hy/macros.py b/hy/macros.py index 32658d6..3c91f11 100644 --- a/hy/macros.py +++ b/hy/macros.py @@ -9,7 +9,7 @@ import traceback from contextlib import contextmanager -from hy._compat import reraise, rename_function +from hy._compat import reraise from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value from hy.lex import mangle from hy.errors import (HyLanguageError, HyMacroExpansionError, HyTypeError, @@ -381,3 +381,24 @@ def tag_macroexpand(tag, tree, module): expr.module = inspect.getmodule(tag_macro) return replace_hy_obj(expr, tree) + + +def rename_function(func, new_name): + """Creates a copy of a function and [re]sets the name at the code-object + level. + """ + c = func.__code__ + new_code = type(c)(*[getattr(c, 'co_{}'.format(a)) + if a != 'name' else str(new_name) + for a in code_obj_args]) + + _fn = type(func)(new_code, func.__globals__, str(new_name), + func.__defaults__, func.__closure__) + _fn.__dict__.update(func.__dict__) + + return _fn + +code_obj_args = ['argcount', 'kwonlyargcount', 'nlocals', 'stacksize', + 'flags', 'code', 'consts', 'names', 'varnames', + 'filename', 'name', 'firstlineno', 'lnotab', 'freevars', + 'cellvars'] From d7da03be12e8c28afd7d27c96d75c6a61107345d Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 15:56:16 -0400 Subject: [PATCH 12/21] Simplify hy._compat.isidentifier --- hy/_compat.py | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/hy/_compat.py b/hy/_compat.py index 1411521..57a67dd 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -18,27 +18,8 @@ def reraise(exc_type, value, traceback=None): def isidentifier(x): - if x in ('True', 'False', 'None', 'print'): - # `print` is special-cased here because Python 2's - # keyword.iskeyword will count it as a keyword, but we - # use the __future__ feature print_function, which makes - # it a non-keyword. + if x in ('True', 'False', 'None'): return True if keyword.iskeyword(x): return False - if PY3: - return x.isidentifier() - if x.rstrip() != x: - return False - import tokenize as T - from io import StringIO - try: - tokens = list(T.generate_tokens(StringIO(x).readline)) - except (T.TokenError, IndentationError): - return False - # Some versions of Python 2.7 (including one that made it into - # Ubuntu 18.10) have a Python 3 backport that adds a NEWLINE - # token. Remove it if it's present. - # https://bugs.python.org/issue33899 - tokens = [t for t in tokens if t[0] != T.NEWLINE] - return len(tokens) == 2 and tokens[0][0] == T.NAME + return x.isidentifier() From 5dcb03b64dac3b94122da63a6a623c4b9da7b4a8 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 16:13:07 -0400 Subject: [PATCH 13/21] Move `isidentifier` to hy.lex --- hy/_compat.py | 10 +--------- hy/lex/__init__.py | 10 +++++++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hy/_compat.py b/hy/_compat.py index 57a67dd..4b7d471 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -2,7 +2,7 @@ # This file is part of Hy, which is free software licensed under the Expat # license. See the LICENSE. -import sys, keyword +import sys PY3 = sys.version_info[0] >= 3 PY36 = sys.version_info >= (3, 6) @@ -15,11 +15,3 @@ def reraise(exc_type, value, traceback=None): raise value.with_traceback(traceback) finally: traceback = None - - -def isidentifier(x): - if x in ('True', 'False', 'None'): - return True - if keyword.iskeyword(x): - return False - return x.isidentifier() diff --git a/hy/lex/__init__.py b/hy/lex/__init__.py index e2e5a26..c32742e 100644 --- a/hy/lex/__init__.py +++ b/hy/lex/__init__.py @@ -4,11 +4,11 @@ from __future__ import unicode_literals +import keyword import re import sys import unicodedata -from hy._compat import isidentifier from hy.lex.exceptions import PrematureEndOfInput, LexException # NOQA from hy.models import HyExpression, HySymbol @@ -191,3 +191,11 @@ def read(from_file=sys.stdin, eof=""): def read_str(input): return read(StringIO(str(input))) + + +def isidentifier(x): + if x in ('True', 'False', 'None'): + return True + if keyword.iskeyword(x): + return False + return x.isidentifier() From 67def3359fdeff560181b536486a4887ec902664 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 16:14:07 -0400 Subject: [PATCH 14/21] Remove Python 2 support from hy.importer --- hy/importer.py | 408 ++-------------------------- tests/importer/test_importer.py | 6 +- tests/native_tests/native_macros.hy | 4 +- tests/test_bin.py | 2 +- 4 files changed, 32 insertions(+), 388 deletions(-) diff --git a/hy/importer.py b/hy/importer.py index 6d31004..f54803d 100644 --- a/hy/importer.py +++ b/hy/importer.py @@ -19,33 +19,6 @@ from contextlib import contextmanager from hy.compiler import hy_compile, hy_ast_compile_flags from hy.lex import hy_parse -from hy._compat import PY3 - - -def cache_from_source(source_path): - """Get the cached bytecode file name for a given source file name. - - This function's name is set to mirror Python 3.x's - `importlib.util.cache_from_source`, which is also used when available. - - Parameters - ---------- - source_path : str - Path of the source file - - Returns - ------- - out : str - Path of the corresponding bytecode file that may--or may - not--actually exist. - """ - if PY3: - return importlib.util.cache_from_source(source_path) - else: - # If source_path has a file extension, replace it with ".pyc". - # Otherwise, just append ".pyc". - d, f = os.path.split(source_path) - return os.path.join(d, re.sub(r"(?:\.[^.]+)?\Z", ".pyc", f)) @contextmanager @@ -135,370 +108,41 @@ def _get_code_from_file(run_name, fname=None, source = f.read().decode('utf-8') code = compile(source, fname, 'exec') - return (code, fname) if PY3 else code + return (code, fname) -if PY3: - importlib.machinery.SOURCE_SUFFIXES.insert(0, '.hy') - _py_source_to_code = importlib.machinery.SourceFileLoader.source_to_code +importlib.machinery.SOURCE_SUFFIXES.insert(0, '.hy') +_py_source_to_code = importlib.machinery.SourceFileLoader.source_to_code - def _could_be_hy_src(filename): - return (os.path.isfile(filename) and - (filename.endswith('.hy') or - not any(filename.endswith(ext) - for ext in importlib.machinery.SOURCE_SUFFIXES[1:]))) +def _could_be_hy_src(filename): + return (os.path.isfile(filename) and + (filename.endswith('.hy') or + not any(filename.endswith(ext) + for ext in importlib.machinery.SOURCE_SUFFIXES[1:]))) - def _hy_source_to_code(self, data, path, _optimize=-1): - if _could_be_hy_src(path): - source = data.decode("utf-8") - hy_tree = hy_parse(source, filename=path) - with loader_module_obj(self) as module: - data = hy_compile(hy_tree, module) +def _hy_source_to_code(self, data, path, _optimize=-1): + if _could_be_hy_src(path): + source = data.decode("utf-8") + hy_tree = hy_parse(source, filename=path) + with loader_module_obj(self) as module: + data = hy_compile(hy_tree, module) - return _py_source_to_code(self, data, path, _optimize=_optimize) + return _py_source_to_code(self, data, path, _optimize=_optimize) - importlib.machinery.SourceFileLoader.source_to_code = _hy_source_to_code +importlib.machinery.SourceFileLoader.source_to_code = _hy_source_to_code - # This is actually needed; otherwise, pre-created finders assigned to the - # current dir (i.e. `''`) in `sys.path` will not catch absolute imports of - # directory-local modules! - sys.path_importer_cache.clear() +# This is actually needed; otherwise, pre-created finders assigned to the +# current dir (i.e. `''`) in `sys.path` will not catch absolute imports of +# directory-local modules! +sys.path_importer_cache.clear() - # Do this one just in case? - importlib.invalidate_caches() - - # XXX: These and the 2.7 counterparts below aren't truly cross-compliant. - # They're useful for testing, though. - HyImporter = importlib.machinery.FileFinder - HyLoader = importlib.machinery.SourceFileLoader - -else: - import imp - import py_compile - import marshal - import struct - import traceback - - from pkgutil import ImpImporter, ImpLoader - - def _could_be_hy_src(filename): - return (filename.endswith('.hy') or - (os.path.isfile(filename) and - not any(filename.endswith(s[0]) for s in imp.get_suffixes()))) - - class HyLoader(ImpLoader, object): - def __init__(self, fullname, filename, fileobj=None, etc=None): - """This constructor is designed for some compatibility with - SourceFileLoader.""" - if etc is None and filename is not None: - if _could_be_hy_src(filename): - etc = ('.hy', 'U', imp.PY_SOURCE) - if fileobj is None: - fileobj = io.open(filename, 'rU', encoding='utf-8') - - super(HyLoader, self).__init__(fullname, fileobj, filename, etc) - - def __getattr__(self, item): - # We add these for Python >= 3.4 Loader interface compatibility. - if item == 'path': - return self.filename - elif item == 'name': - return self.fullname - else: - return super(HyLoader, self).__getattr__(item) - - def exec_module(self, module, fullname=None): - fullname = self._fix_name(fullname) - code = self.get_code(fullname) - eval(code, module.__dict__) - - def load_module(self, fullname=None): - """Same as `pkgutil.ImpLoader`, with an extra check for Hy - source and the option to not run `self.exec_module`.""" - fullname = self._fix_name(fullname) - ext_type = self.etc[0] - mod_type = self.etc[2] - mod = None - pkg_path = os.path.join(self.filename, '__init__.hy') - if ext_type == '.hy' or ( - mod_type == imp.PKG_DIRECTORY and - os.path.isfile(pkg_path)): - - was_in_sys = fullname in sys.modules - if was_in_sys: - mod = sys.modules[fullname] - else: - mod = sys.modules.setdefault( - fullname, types.ModuleType(fullname)) - - # TODO: Should we set these only when not in `sys.modules`? - if mod_type == imp.PKG_DIRECTORY: - mod.__file__ = pkg_path - mod.__path__ = [self.filename] - mod.__package__ = fullname - else: - # mod.__path__ = self.filename - mod.__file__ = self.get_filename(fullname) - mod.__package__ = '.'.join(fullname.split('.')[:-1]) - - mod.__name__ = fullname - - try: - self.exec_module(mod, fullname=fullname) - except Exception: - # Follow Python 2.7 logic and only remove a new, bad - # module; otherwise, leave the old--and presumably - # good--module in there. - if not was_in_sys: - del sys.modules[fullname] - raise - - if mod is None: - self._reopen() - try: - mod = imp.load_module(fullname, self.file, self.filename, - self.etc) - finally: - if self.file: - self.file.close() - - mod.__loader__ = self - return mod - - def _reopen(self): - """Same as `pkgutil.ImpLoader`, with an extra check for Hy - source""" - if self.file and self.file.closed: - ext_type = self.etc[0] - if ext_type == '.hy': - self.file = io.open(self.filename, 'rU', encoding='utf-8') - else: - super(HyLoader, self)._reopen() - - def byte_compile_hy(self, fullname=None): - fullname = self._fix_name(fullname) - if fullname is None: - fullname = self.fullname - - hy_source = self.get_source(fullname) - hy_tree = hy_parse(hy_source, filename=self.filename) - - with loader_module_obj(self) as module: - hy_ast = hy_compile(hy_tree, module) - - code = compile(hy_ast, self.filename, 'exec', - hy_ast_compile_flags) - - if not sys.dont_write_bytecode: - try: - hyc_compile(code, module=fullname) - except IOError: - pass - return code - - def get_code(self, fullname=None): - """Same as `pkgutil.ImpLoader`, with an extra check for Hy - source""" - fullname = self._fix_name(fullname) - ext_type = self.etc[0] - if ext_type == '.hy': - # Looks like we have to manually check for--and update-- - # the bytecode. - t_py = long(os.stat(self.filename).st_mtime) - pyc_file = cache_from_source(self.filename) - if os.path.isfile(pyc_file): - t_pyc = long(os.stat(pyc_file).st_mtime) - - if t_pyc is not None and t_pyc >= t_py: - with open(pyc_file, 'rb') as f: - if f.read(4) == imp.get_magic(): - t = struct.unpack(' Date: Mon, 20 May 2019 17:27:17 -0400 Subject: [PATCH 15/21] Remove Python 2 support in hy.compiler --- hy/compiler.py | 205 ++++++++++++------------------------------------- 1 file changed, 51 insertions(+), 154 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 3b0d700..2963496 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -14,7 +14,7 @@ from hy.errors import (HyCompileError, HyTypeError, HyLanguageError, from hy.lex import mangle, unmangle, hy_parse, parse_one_thing, LexException -from hy._compat import (PY3, PY36, PY38, reraise) +from hy._compat import (PY36, PY38, reraise) from hy.macros import require, load_macros, macroexpand, tag_macroexpand import hy.core @@ -95,15 +95,12 @@ def calling_module(n=1): def ast_str(x, piecewise=False): if piecewise: return ".".join(ast_str(s) if s else "" for s in x.split(".")) - x = mangle(x) - return x if PY3 else x.encode('UTF8') + return mangle(x) _special_form_compilers = {} _model_compilers = {} -_decoratables = (ast.FunctionDef, ast.ClassDef) -if PY3: - _decoratables += (ast.AsyncFunctionDef,) +_decoratables = (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef) # _bad_roots are fake special operators, which are used internally # by other special forms (e.g., `except` in `try`) but can't be # used to construct special forms themselves. @@ -175,7 +172,7 @@ class Result(object): object gets added to a Result object, it gets converted on-the-fly. """ __slots__ = ("imports", "stmts", "temp_variables", - "_expr", "__used_expr", "contains_yield") + "_expr", "__used_expr") def __init__(self, *args, **kwargs): if args: @@ -186,14 +183,12 @@ class Result(object): self.stmts = [] self.temp_variables = [] self._expr = None - self.contains_yield = False self.__used_expr = False # XXX: Make sure we only have AST where we should. for kwarg in kwargs: - if kwarg not in ["imports", "contains_yield", "stmts", "expr", - "temp_variables"]: + if kwarg not in ["imports", "stmts", "expr", "temp_variables"]: raise TypeError( "%s() got an unexpected keyword argument '%s'" % ( self.__class__.__name__, kwarg)) @@ -273,9 +268,7 @@ class Result(object): if isinstance(var, ast.Name): var.id = new_name var.arg = new_name - elif isinstance(var, ast.FunctionDef): - var.name = new_name - elif PY3 and isinstance(var, ast.AsyncFunctionDef): + elif isinstance(var, (ast.FunctionDef, ast.AsyncFunctionDef)): var.name = new_name else: raise TypeError("Don't know how to rename a %s!" % ( @@ -311,22 +304,17 @@ class Result(object): result.stmts = self.stmts + other.stmts result.expr = other.expr result.temp_variables = other.temp_variables - result.contains_yield = False - if self.contains_yield or other.contains_yield: - result.contains_yield = True return result def __str__(self): return ( - "Result(imports=[%s], stmts=[%s], " - "expr=%s, contains_yield=%s)" - ) % ( + "Result(imports=[%s], stmts=[%s], expr=%s)" + % ( ", ".join(ast.dump(x) for x in self.imports), ", ".join(ast.dump(x) for x in self.stmts), - ast.dump(self.expr) if self.expr else None, - self.contains_yield - ) + ast.dump(self.expr) if self.expr else None + )) def is_unpack(kind, x): @@ -386,11 +374,7 @@ class HyASTCompiler(object): for stdlib_module in hy.core.STDLIB: mod = importlib.import_module(stdlib_module) for e in map(ast_str, getattr(mod, 'EXPORTS', [])): - if getattr(mod, e) is not getattr(builtins, e, ''): - # Don't bother putting a name in _stdlib if it - # points to a builtin with the same name. This - # prevents pointless imports. - self._stdlib[e] = stdlib_module + self._stdlib[e] = stdlib_module def get_anon_var(self): self.anon_var_count += 1 @@ -454,8 +438,7 @@ class HyASTCompiler(object): def _syntax_error(self, expr, message): return HySyntaxError(message, expr, self.filename, self.source) - def _compile_collect(self, exprs, with_kwargs=False, dict_display=False, - oldpy_unpack=False): + def _compile_collect(self, exprs, with_kwargs=False, dict_display=False): """Collect the expression contexts from a list of compiled expression. This returns a list of the expression contexts, and the sum of the @@ -465,34 +448,18 @@ class HyASTCompiler(object): compiled_exprs = [] ret = Result() keywords = [] - oldpy_starargs = None - oldpy_kwargs = None exprs_iter = iter(exprs) for expr in exprs_iter: - if not PY3 and oldpy_unpack and is_unpack("iterable", expr): - if oldpy_starargs: - raise self._syntax_error(expr, - "Pythons < 3.5 allow only one `unpack-iterable` per call") - oldpy_starargs = self.compile(expr[1]) - ret += oldpy_starargs - oldpy_starargs = oldpy_starargs.force_expr - - elif is_unpack("mapping", expr): + if is_unpack("mapping", expr): ret += self.compile(expr[1]) - if PY3: - if dict_display: - compiled_exprs.append(None) - compiled_exprs.append(ret.force_expr) - elif with_kwargs: - keywords.append(asty.keyword( - expr, arg=None, value=ret.force_expr)) - elif oldpy_unpack: - if oldpy_kwargs: - raise self._syntax_error(expr, - "Pythons < 3.5 allow only one `unpack-mapping` per call") - oldpy_kwargs = ret.force_expr + if dict_display: + compiled_exprs.append(None) + compiled_exprs.append(ret.force_expr) + elif with_kwargs: + keywords.append(asty.keyword( + expr, arg=None, value=ret.force_expr)) elif with_kwargs and isinstance(expr, HyKeyword): try: @@ -516,10 +483,7 @@ class HyASTCompiler(object): ret += self.compile(expr) compiled_exprs.append(ret.force_expr) - if oldpy_unpack: - return compiled_exprs, ret, keywords, oldpy_starargs, oldpy_kwargs - else: - return compiled_exprs, ret, keywords + return compiled_exprs, ret, keywords def _compile_branch(self, exprs): """Make a branch out of an iterable of Result objects @@ -560,7 +524,7 @@ class HyASTCompiler(object): new_name = ast.Subscript(value=name.value, slice=name.slice) elif isinstance(name, ast.Attribute): new_name = ast.Attribute(value=name.value, attr=name.attr) - elif PY3 and isinstance(name, ast.Starred): + elif isinstance(name, ast.Starred): new_name = ast.Starred( value=self._storeize(expr, name.value, func)) else: @@ -646,23 +610,10 @@ class HyASTCompiler(object): @special("unpack-iterable", [FORM]) def compile_unpack_iterable(self, expr, root, arg): - if not PY3: - raise self._syntax_error(expr, - "`unpack-iterable` isn't allowed here") ret = self.compile(arg) ret += asty.Starred(expr, value=ret.force_expr, ctx=ast.Load()) return ret - @special([(not PY3, "exec*")], [FORM, maybe(FORM), maybe(FORM)]) - # Under Python 3, `exec` is a function rather than a statement type, so Hy - # doesn't need a special form for it. - def compile_exec(self, expr, root, body, globals_, locals_): - return asty.Exec( - expr, - body=self.compile(body).force_expr, - globals=self.compile(globals_).force_expr if globals_ is not None else None, - locals=self.compile(locals_).force_expr if locals_ is not None else None) - @special("do", [many(FORM)]) def compile_do(self, expr, root, body): return self._compile_branch(body) @@ -677,9 +628,6 @@ class HyASTCompiler(object): exc = exc.force_expr if cause is not None: - if not PY3: - raise self._syntax_error(expr, - "raise from only supported in python 3") cause = self.compile(cause) ret += cause cause = cause.force_expr @@ -735,35 +683,17 @@ class HyASTCompiler(object): returnable = Result( expr=asty.Name(expr, id=return_var.id, ctx=ast.Load()), - temp_variables=[return_var], - contains_yield=body.contains_yield) + temp_variables=[return_var]) body += body.expr_as_stmt() if orelse else asty.Assign( expr, targets=[return_var], value=body.force_expr) body = body.stmts or [asty.Pass(expr)] - if PY3: - # Python 3.3 features a merge of TryExcept+TryFinally into Try. - x = asty.Try( - expr, - body=body, - handlers=handlers, - orelse=orelse, - finalbody=finalbody) - elif finalbody and handlers: - x = asty.TryFinally( - expr, - body=[asty.TryExcept( - expr, - body=body, - handlers=handlers, - orelse=orelse)], - finalbody=finalbody) - elif finalbody: - x = asty.TryFinally( - expr, body=body, finalbody=finalbody) - else: - x = asty.TryExcept( - expr, body=body, handlers=handlers, orelse=orelse) + x = asty.Try( + expr, + body=body, + handlers=handlers, + orelse=orelse, + finalbody=finalbody) return handler_results + x + returnable def _compile_catch_expression(self, expr, var, exceptions, body): @@ -780,9 +710,7 @@ class HyASTCompiler(object): name = None if len(exceptions) == 2: - name = exceptions[0] - name = (ast_str(name) if PY3 - else self._storeize(name, self.compile(name))) + name = ast_str(exceptions[0]) exceptions_list = exceptions[-1] if exceptions else HyList() if isinstance(exceptions_list, HyList): @@ -896,19 +824,19 @@ class HyASTCompiler(object): msg = self.compile(msg).force_expr return ret + asty.Assert(expr, test=e, msg=msg) - @special(["global", (PY3, "nonlocal")], [oneplus(SYM)]) + @special(["global", "nonlocal"], [oneplus(SYM)]) def compile_global_or_nonlocal(self, expr, root, syms): node = asty.Global if root == "global" else asty.Nonlocal return node(expr, names=list(map(ast_str, syms))) @special("yield", [maybe(FORM)]) def compile_yield_expression(self, expr, root, arg): - ret = Result(contains_yield=(not PY3)) + ret = Result() if arg is not None: ret += self.compile(arg) return ret + asty.Yield(expr, value=ret.force_expr) - @special([(PY3, "yield-from"), (PY3, "await")], [FORM]) + @special(["yield-from", "await"], [FORM]) def compile_yield_from_or_await_expression(self, expr, root, arg): ret = Result() + self.compile(arg) node = asty.YieldFrom if root == "yield-from" else asty.Await @@ -987,7 +915,7 @@ class HyASTCompiler(object): fn.stmts[-1].decorator_list = decs + fn.stmts[-1].decorator_list return ret + fn - @special(["with*", (PY3, "with/a*")], + @special(["with*", "with/a*"], [brackets(FORM, maybe(FORM)), many(FORM)]) def compile_with_expression(self, expr, root, args, body): thing, ctx = (None, args[0]) if args[1] is None else args @@ -1011,14 +939,11 @@ class HyASTCompiler(object): the_with = node(expr, context_expr=ctx.force_expr, optional_vars=thing, - body=body.stmts) - - if PY3: - the_with.items = [ast.withitem(context_expr=ctx.force_expr, - optional_vars=thing)] + body=body.stmts, + items=[ast.withitem(context_expr=ctx.force_expr, + optional_vars=thing)]) ret = Result(stmts=[initial_assign]) + ctx + the_with - ret.contains_yield = ret.contains_yield or body.contains_yield # And make our expression context our temp variable expr_name = asty.Name(expr, id=ast_str(var), ctx=ast.Load()) @@ -1092,7 +1017,6 @@ class HyASTCompiler(object): # The desired comprehension can't be expressed as a # real Python comprehension. We'll write it as a nested # loop in a function instead. - contains_yield = [] def f(parts): # This function is called recursively to construct # the nested loop. @@ -1100,8 +1024,6 @@ class HyASTCompiler(object): if is_for: if body: bd = self._compile_branch(body) - if bd.contains_yield: - contains_yield.append(True) return bd + bd.expr_as_stmt() return Result(stmts=[asty.Pass(expr)]) if node_class is asty.DictComp: @@ -1132,9 +1054,7 @@ class HyASTCompiler(object): else: raise ValueError("can't happen") if is_for: - ret = f(parts) - ret.contains_yield = bool(contains_yield) - return ret + return f(parts) fname = self.get_anon_var() # Define the generator function. ret = Result() + asty.FunctionDef( @@ -1343,12 +1263,11 @@ class HyASTCompiler(object): ">>": ast.RShift, "|": ast.BitOr, "^": ast.BitXor, - "&": ast.BitAnd} - if PY3: - m_ops["@"] = ast.MatMult + "&": ast.BitAnd, + "@": ast.MatMult} @special(["+", "*", "|"], [many(FORM)]) - @special(["-", "/", "&", (PY3, "@")], [oneplus(FORM)]) + @special(["-", "/", "&", "@"], [oneplus(FORM)]) @special(["**", "//", "<<", ">>"], [times(2, Inf, FORM)]) @special(["%", "^"], [times(2, 2, FORM)]) def compile_maths_expression(self, expr, root, args): @@ -1407,9 +1326,7 @@ class HyASTCompiler(object): def _compile_assign(self, root, name, result): str_name = "%s" % name - if str_name in (["None"] + (["True", "False"] if PY3 else [])): - # Python 2 allows assigning to True and False, although - # this is rarely wise. + if str_name in ("None", "True", "False"): raise self._syntax_error(name, "Can't assign to `%s'" % str_name) @@ -1473,13 +1390,12 @@ class HyASTCompiler(object): expr, test=cond_compiled.force_expr, body=body.stmts or [asty.Pass(expr)], orelse=orel.stmts) - ret.contains_yield = body.contains_yield return ret NASYM = some(lambda x: isinstance(x, HySymbol) and x not in ( "&optional", "&rest", "&kwonly", "&kwargs")) - @special(["fn", "fn*", (PY3, "fn/a")], [ + @special(["fn", "fn*", "fn/a"], [ # The starred version is for internal use (particularly, in the # definition of `defn`). It ensures that a FunctionDef is # produced rather than a Lambda. @@ -1497,25 +1413,14 @@ class HyASTCompiler(object): mandatory, optional, rest, kwonly, kwargs = params optional, defaults, ret = self._parse_optional_args(optional) - if kwonly is not None and not PY3: - raise self._syntax_error(params, - "&kwonly parameters require Python 3") kwonly, kw_defaults, ret2 = self._parse_optional_args(kwonly, True) ret += ret2 main_args = mandatory + optional - if PY3: - # Python 3.4+ requires that args are an ast.arg object, rather - # than an ast.Name or bare string. - main_args, kwonly, [rest], [kwargs] = ( - [[x and asty.arg(x, arg=ast_str(x), annotation=None) - for x in o] - for o in (main_args or [], kwonly or [], [rest], [kwargs])]) - else: - main_args = [asty.Name(x, id=ast_str(x), ctx=ast.Param()) - for x in main_args] - rest = rest and ast_str(rest) - kwargs = kwargs and ast_str(kwargs) + main_args, kwonly, [rest], [kwargs] = ( + [[x and asty.arg(x, arg=ast_str(x), annotation=None) + for x in o] + for o in (main_args or [], kwonly or [], [rest], [kwargs])]) args = ast.arguments( args=main_args, defaults=defaults, @@ -1529,13 +1434,7 @@ class HyASTCompiler(object): return ret + asty.Lambda(expr, args=args, body=body.force_expr) if body.expr: - if body.contains_yield and not PY3: - # Prior to PEP 380 (introduced in Python 3.3) - # generators may not have a value in a return - # statement. - body += body.expr_as_stmt() - else: - body += asty.Return(body.expr, value=body.expr) + body += asty.Return(body.expr, value=body.expr) name = self.get_anon_var() @@ -1581,7 +1480,7 @@ class HyASTCompiler(object): base_list, docstring, attrs, body = rest or ([[]], None, None, []) bases_expr, bases, keywords = ( - self._compile_collect(base_list[0], with_kwargs=PY3)) + self._compile_collect(base_list[0], with_kwargs=True)) bodyr = Result() @@ -1750,12 +1649,10 @@ class HyASTCompiler(object): # a typecheck, eg (type :foo) with_kwargs = root not in ( "type", "HyKeyword", "keyword", "name", "keyword?", "identity") - args, ret, keywords, oldpy_star, oldpy_kw = self._compile_collect( - args, with_kwargs, oldpy_unpack=True) + args, ret, keywords = self._compile_collect(args, with_kwargs) return func + ret + asty.Call( - expr, func=func.expr, args=args, keywords=keywords, - starargs=oldpy_star, kwargs=oldpy_kw) + expr, func=func.expr, args=args, keywords=keywords) @builds_model(HyInteger, HyFloat, HyComplex) def compile_numeric_literal(self, x): @@ -1807,7 +1704,7 @@ class HyASTCompiler(object): if type(string) is HyString and string.is_format: # This is a format string (a.k.a. an f-string). return self._format_string(string, str(string)) - node = asty.Bytes if PY3 and type(string) is HyBytes else asty.Str + node = asty.Bytes if type(string) is HyBytes else asty.Str f = bytes if type(string) is HyBytes else str return node(string, s=f(string)) From 7ba140725706c07aca0b43bef2c40275f4b99f89 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 16:58:52 -0400 Subject: [PATCH 16/21] Remove hy._compat.PY3 --- hy/_compat.py | 1 - 1 file changed, 1 deletion(-) diff --git a/hy/_compat.py b/hy/_compat.py index 4b7d471..70a3187 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -4,7 +4,6 @@ import sys -PY3 = sys.version_info[0] >= 3 PY36 = sys.version_info >= (3, 6) PY37 = sys.version_info >= (3, 7) PY38 = sys.version_info >= (3, 8) From 6af6a2945a6740590582e3775f0da6178dbfa701 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 17:09:24 -0400 Subject: [PATCH 17/21] Remove `if-python2` and its uses --- hy/contrib/profile.hy | 4 +- hy/core/bootstrap.hy | 7 --- hy/core/language.hy | 82 +++++------------------------ tests/native_tests/language.hy | 4 +- tests/native_tests/native_macros.hy | 5 -- 5 files changed, 16 insertions(+), 86 deletions(-) diff --git a/hy/contrib/profile.hy b/hy/contrib/profile.hy index 8ff2ed6..cea3435 100644 --- a/hy/contrib/profile.hy +++ b/hy/contrib/profile.hy @@ -19,9 +19,7 @@ `(do (import cProfile pstats) - (if-python2 - (import [StringIO [StringIO]]) - (import [io [StringIO]])) + (import [io [StringIO]]) (setv ~g!hy-pr (.Profile cProfile)) (.enable ~g!hy-pr) diff --git a/hy/core/bootstrap.hy b/hy/core/bootstrap.hy index 0d3d737..d5b7e19 100644 --- a/hy/core/bootstrap.hy +++ b/hy/core/bootstrap.hy @@ -77,10 +77,3 @@ (if (not (isinstance lambda-list hy.HyList)) (macro-error name "defn/a takes a parameter list as second argument")) `(setv ~name (fn/a ~lambda-list ~@body))) - -(defmacro if-python2 [python2-form python3-form] - "If running on python2, execute python2-form, else, execute python3-form" - (import sys) - (if (< (get sys.version_info 0) 3) - python2-form - python3-form)) diff --git a/hy/core/language.hy b/hy/core/language.hy index 687bd5c..c08c00c 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -11,6 +11,7 @@ (import [fractions [Fraction :as fraction]]) (import operator) ; shadow not available yet (import sys) +(import [collections.abc :as cabc]) (import [hy.models [HySymbol HyKeyword]]) (import [hy.lex [tokenize mangle unmangle read read-str]]) (import [hy.lex.exceptions [LexException PrematureEndOfInput]]) @@ -20,10 +21,6 @@ (require [hy.core.bootstrap [*]]) -(if-python2 - (import [collections :as cabc]) - (import [collections.abc :as cabc])) - (defn butlast [coll] "Return an iterator of all but the last item in `coll`." (drop-last 1 coll)) @@ -85,47 +82,12 @@ If the second argument `codegen` is true, generate python code instead." (yield val) (.add seen val))))) -(if-python2 - (setv - remove itertools.ifilterfalse - zip-longest itertools.izip_longest - ;; not builtin in Python3 - reduce reduce - ;; hy is more like Python3 - filter itertools.ifilter - input raw_input - map itertools.imap - range xrange - zip itertools.izip) - (setv - remove itertools.filterfalse - zip-longest itertools.zip_longest - ;; was builtin in Python2 - reduce functools.reduce - ;; Someone can import these directly from `hy.core.language`; - ;; we'll make some duplicates. - filter filter - input input - map map - range range - zip zip)) - -(if-python2 - (defn exec [$code &optional $globals $locals] - "Execute Python code. - -The parameter names contain weird characters to discourage calling this -function with keyword arguments, which isn't supported by Python 3's `exec`." - (if - (none? $globals) (do - (setv frame (._getframe sys (int 1))) - (try - (setv $globals frame.f_globals $locals frame.f_locals) - (finally (del frame)))) - (none? $locals) - (setv $locals $globals)) - (exec* $code $globals $locals)) - (setv exec exec)) +(setv + remove itertools.filterfalse + zip-longest itertools.zip_longest + ;; was builtin in Python2 + reduce functools.reduce + accumulate itertools.accumulate) ;; infinite iterators (setv @@ -151,18 +113,6 @@ function with keyword arguments, which isn't supported by Python 3's `exec`." permutations itertools.permutations product itertools.product) -;; also from itertools, but not in Python2, and without func option until 3.3 -(defn accumulate [iterable &optional [func operator.add]] - "Accumulate `func` on `iterable`. - -Return series of accumulated sums (or other binary function results)." - (setv it (iter iterable) - total (next it)) - (yield total) - (for [element it] - (setv total (func total element)) - (yield total))) - (defn drop [count coll] "Drop `count` elements from `coll` and yield back the rest." (islice coll count None)) @@ -389,15 +339,11 @@ with overlap." (defn string [x] "Cast `x` as the current python version's string implementation." - (if-python2 - (unicode x) - (str x))) + (str x)) (defn string? [x] "Check if `x` is a string." - (if-python2 - (isinstance x (, str unicode)) - (isinstance x str))) + (isinstance x str)) (defn take [count coll] "Take `count` elements from `coll`." @@ -456,11 +402,11 @@ Even objects with the __name__ magic will work." (setv EXPORTS '[*map accumulate butlast calling-module calling-module-name chain coll? combinations comp complement compress constantly count cycle dec distinct - disassemble drop drop-last drop-while empty? eval even? every? exec first - filter flatten float? fraction gensym group-by identity inc input instance? + disassemble drop drop-last drop-while empty? eval even? every? first + flatten float? fraction gensym group-by identity inc instance? integer integer? integer-char? interleave interpose islice iterable? iterate iterator? juxt keyword keyword? last list? macroexpand - macroexpand-1 mangle map merge-with multicombinations name neg? none? nth - numeric? odd? partition permutations pos? product range read read-str + macroexpand-1 mangle merge-with multicombinations name neg? none? nth + numeric? odd? partition permutations pos? product read read-str remove repeat repeatedly rest reduce second some string string? symbol? - take take-nth take-while tuple? unmangle xor tee zero? zip zip-longest]) + take take-nth take-while tuple? unmangle xor tee zero? zip-longest]) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 63b7599..931e0d0 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -1579,9 +1579,7 @@ macros() (defn test-read [] "NATIVE: test that read takes something for stdin and reads" - (if-python2 - (import [StringIO [StringIO]]) - (import [io [StringIO]])) + (import [io [StringIO]]) (import [hy.models [HyExpression]]) (setv stdin-buffer (StringIO "(+ 2 2)\n(- 2 2)")) diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy index fec9ce0..61001db 100644 --- a/tests/native_tests/native_macros.hy +++ b/tests/native_tests/native_macros.hy @@ -140,11 +140,6 @@ (assert initialized) (assert (test-initialized)) -(defn test-if-python2 [] - (import sys) - (assert (= (get sys.version_info 0) - (if-python2 2 3)))) - (defn test-gensym-in-macros [] (import ast) (import [astor.code-gen [to-source]]) From 9b4178ebd0620f4c61d9e72d81af0c1d5d0488ba Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 17:17:39 -0400 Subject: [PATCH 18/21] Remove undocumented fns `integer` and `string` --- hy/core/language.hy | 16 ++++------------ tests/native_tests/core.hy | 8 ++++---- tests/native_tests/language.hy | 5 ----- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/hy/core/language.hy b/hy/core/language.hy index c08c00c..80cba83 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -201,10 +201,6 @@ If the second argument `codegen` is true, generate python code instead." "Perform `isinstance` with reversed arguments." (isinstance x klass)) -(defn integer [x] - "Return Hy kind of integer for `x`." - (int x)) - (defn integer? [x] "Check if `x` is an integer." (isinstance x int)) @@ -337,10 +333,6 @@ with overlap." "Return the first logical true value of applying `pred` in `coll`, else None." (first (filter None (map pred coll)))) -(defn string [x] - "Cast `x` as the current python version's string implementation." - (str x)) - (defn string? [x] "Check if `x` is a string." (isinstance x str)) @@ -378,7 +370,7 @@ Strings numbers and even objects with the __name__ magic will work." (HyKeyword (unmangle value)) (try (unmangle (.__name__ value)) - (except [] (HyKeyword (string value))))))) + (except [] (HyKeyword (str value))))))) (defn name [value] "Convert `value` to a string. @@ -391,7 +383,7 @@ Even objects with the __name__ magic will work." (unmangle value) (try (unmangle (. value __name__)) - (except [] (string value)))))) + (except [] (str value)))))) (defn xor [a b] "Perform exclusive or between `a` and `b`." @@ -404,9 +396,9 @@ Even objects with the __name__ magic will work." combinations comp complement compress constantly count cycle dec distinct disassemble drop drop-last drop-while empty? eval even? every? first flatten float? fraction gensym group-by identity inc instance? - integer integer? integer-char? interleave interpose islice iterable? + integer? integer-char? interleave interpose islice iterable? iterate iterator? juxt keyword keyword? last list? macroexpand macroexpand-1 mangle merge-with multicombinations name neg? none? nth numeric? odd? partition permutations pos? product read read-str - remove repeat repeatedly rest reduce second some string string? symbol? + remove repeat repeatedly rest reduce second some string? symbol? take take-nth take-while tuple? unmangle xor tee zero? zip-longest]) diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy index 787a390..0618766 100644 --- a/tests/native_tests/core.hy +++ b/tests/native_tests/core.hy @@ -285,7 +285,7 @@ result['y in globals'] = 'y' in globals()") (setv s3 (gensym "xx")) (assert (= 0 (.find s2 "_xx\uffff"))) (assert (not (= s2 s3))) - (assert (not (= (string s2) (string s3))))) + (assert (not (= (str s2) (str s3))))) (defn test-identity [] "NATIVE: testing the identity function" @@ -322,8 +322,8 @@ result['y in globals'] = 'y' in globals()") (assert-true (integer? 0)) (assert-true (integer? 3)) (assert-true (integer? -3)) - (assert-true (integer? (integer "-3"))) - (assert-true (integer? (integer 3))) + (assert-true (integer? (int "-3"))) + (assert-true (integer? (int 3))) (assert-false (integer? 4.2)) (assert-false (integer? None)) (assert-false (integer? "foo"))) @@ -332,7 +332,7 @@ result['y in globals'] = 'y' in globals()") "NATIVE: testing the integer-char? function" (assert-true (integer-char? "1")) (assert-true (integer-char? "-1")) - (assert-true (integer-char? (str (integer 300)))) + (assert-true (integer-char? (str (int 300)))) (assert-false (integer-char? "foo")) (assert-false (integer-char? None))) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 931e0d0..5593d01 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -1469,11 +1469,6 @@ cee\"} dee" "ey bee\ncee dee")) (assert (= y [5]))) -(defn test-string [] - (assert (string? (string "a"))) - (assert (string? (string 1))) - (assert (= u"unicode" (string "unicode")))) - (defn test-del [] "NATIVE: Test the behavior of del" (setv foo 42) From 081c22d50b993499dbb05bd2ba0f70e1db053389 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 20 May 2019 17:21:34 -0400 Subject: [PATCH 19/21] Update NEWS --- NEWS.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index 94db065..00103d4 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,5 +1,12 @@ .. default-role:: code +Unreleased +============================== + +Removals +------------------------------ +* Python 2 is no longer supported. + 0.17.0 ============================== From 9914e9010c753c23fc7cf96c0de0dd2988b30b92 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Thu, 23 May 2019 13:20:44 -0400 Subject: [PATCH 20/21] Update the docs for removing Python 2 support Some of the example output may still be from Python 2. --- docs/conf.py | 3 +-- docs/language/api.rst | 15 ++------------- docs/language/core.rst | 18 ++---------------- docs/language/internals.rst | 10 ++++------ docs/language/syntax.rst | 12 ++++-------- docs/style-guide.rst | 1 - docs/tutorial.rst | 3 --- 7 files changed, 13 insertions(+), 49 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 682dbcf..f4ae994 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,5 +52,4 @@ html_context = dict( hy_descriptive_version = hy_descriptive_version) intersphinx_mapping = dict( - py2 = ('https://docs.python.org/2/', None), - py = ('https://docs.python.org/3/', None)) + py = ('https://docs.python.org/3/', None)) diff --git a/docs/language/api.rst b/docs/language/api.rst index 2a690f6..6b243ba 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -564,8 +564,6 @@ requires. File "", line 1, in TypeError: compare() missing 1 required keyword-only argument: 'keyfn' - Availability: Python 3. - &kwargs Like ``&rest``, but for keyword arugments. The following parameter will contain 0 or more keyword arguments. @@ -1057,7 +1055,7 @@ if / if* / if-not ``if / if* / if-not`` respect Python *truthiness*, that is, a *test* fails if it evaluates to a "zero" (including values of ``len`` zero, ``None``, and ``False``), and passes otherwise, but values with a ``__bool__`` method -(``__nonzero__`` in Python 2) can overrides this. +can override this. The ``if`` macro is for conditionally selecting an expression for evaluation. The result of the selected expression becomes the result of the entire ``if`` @@ -1296,19 +1294,12 @@ fact, these forms are implemented as generator functions whenever they contain Python statements, with the attendant consequences for calling ``return``. By contrast, ``for`` shares the caller's scope. -.. note:: An exception to the above scoping rules occurs on Python 2 for - ``lfor`` specifically (and not ``sfor``, ``gfor``, or ``dfor``) when - Hy can implement the ``lfor`` as a Python list comprehension. Then, - variables will leak to the surrounding scope. - nonlocal -------- .. versionadded:: 0.11.1 -**PYTHON 3.0 AND UP ONLY!** - ``nonlocal`` can be used to mark a symbol as not local to the current scope. The parameters are the names of symbols to mark as nonlocal. This is necessary to modify variables through nested ``fn`` scopes: @@ -1693,7 +1684,7 @@ object (respectively) to provide positional or keywords arguments => (f #* [1 2] #** {"c" 3 "d" 4}) [1, 2, 3, 4] -With Python 3, unpacking is allowed in more contexts, and you can unpack +Unpacking is allowed in a variety of contexts, and you can unpack more than once in one expression (:pep:`3132`, :pep:`448`). .. code-block:: clj @@ -2038,8 +2029,6 @@ yield-from .. versionadded:: 0.9.13 -**PYTHON 3.3 AND UP ONLY!** - ``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 diff --git a/docs/language/core.rst b/docs/language/core.rst index 7e42691..cabb420 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -240,19 +240,6 @@ otherwise ``False``. Return ``True`` if *coll* is empty. True -.. _exec-fn: - -exec ----- - -Equivalent to Python 3's built-in function :py:func:`exec`. - -.. code-block:: clj - - => (exec "print(a + b)" {"a" 1} {"b" 2}) - 3 - - .. _float?-fn: float? @@ -385,8 +372,7 @@ integer? Usage: ``(integer? x)`` -Returns `True` if *x* is an integer. For Python 2, this is -either ``int`` or ``long``. For Python 3, this is ``int``. +Returns `True` if *x* is an integer (``int``). .. code-block:: hy @@ -924,7 +910,7 @@ string? Usage: ``(string? x)`` -Returns ``True`` if *x* is a string. +Returns ``True`` if *x* is a string (``str``). .. code-block:: hy diff --git a/docs/language/internals.rst b/docs/language/internals.rst index 1cd32ad..a89b356 100644 --- a/docs/language/internals.rst +++ b/docs/language/internals.rst @@ -120,9 +120,7 @@ HyString ~~~~~~~~ ``hy.models.HyString`` represents string literals (including bracket strings), -which compile down to unicode string literals in Python. ``HyStrings`` inherit -unicode objects in Python 2, and string objects in Python 3 (and are therefore -not encoding-dependent). +which compile down to unicode string literals (``str``) in Python. ``HyString``\s are immutable. @@ -140,15 +138,15 @@ HyBytes ~~~~~~~ ``hy.models.HyBytes`` is like ``HyString``, but for sequences of bytes. -It inherits from ``bytes`` on Python 3 and ``str`` on Python 2. +It inherits from ``bytes``. .. _hy_numeric_models: Numeric Models ~~~~~~~~~~~~~~ -``hy.models.HyInteger`` represents integer literals (using the -``long`` type on Python 2, and ``int`` on Python 3). +``hy.models.HyInteger`` represents integer literals, using the ``int`` +type. ``hy.models.HyFloat`` represents floating-point literals. diff --git a/docs/language/syntax.rst b/docs/language/syntax.rst index 2414499..16ed838 100644 --- a/docs/language/syntax.rst +++ b/docs/language/syntax.rst @@ -10,7 +10,7 @@ An identifier consists of a nonempty sequence of Unicode characters that are not numeric literals ---------------- -In addition to regular numbers, standard notation from Python 3 for non-base 10 +In addition to regular numbers, standard notation from Python for non-base 10 integers is used. ``0x`` for Hex, ``0o`` for Octal, ``0b`` for Binary. .. code-block:: clj @@ -60,13 +60,9 @@ Plain string literals support :ref:`a variety of backslash escapes literally, prefix the string with ``r``, as in ``r"slash\not"``. Bracket strings are always raw strings and don't allow the ``r`` prefix. -Whether running under Python 2 or Python 3, Hy treats all string literals as -sequences of Unicode characters by default, and allows you to prefix a plain -string literal (but not a bracket string) with ``b`` to treat it as a sequence -of bytes. So when running under Python 3, Hy translates ``"foo"`` and -``b"foo"`` to the identical Python code, but when running under Python 2, -``"foo"`` is translated to ``u"foo"`` and ``b"foo"`` is translated to -``"foo"``. +Like Python, Hy treats all string literals as sequences of Unicode characters +by default. You may prefix a plain string literal (but not a bracket string) +with ``b`` to treat it as a sequence of bytes. Unlike Python, Hy only recognizes string prefixes (``r``, etc.) in lowercase. diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 68f2eae..167cad4 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -39,7 +39,6 @@ into the making of Hy. + Look like a Lisp; DTRT with it (e.g. dashes turn to underscores). + We're still Python. Most of the internals translate 1:1 to Python internals. + Use Unicode everywhere. -+ Fix the bad decisions in Python 2 when we can (see ``true_division``). + When in doubt, defer to Python. + If you're still unsure, defer to Clojure. + If you're even more unsure, defer to Common Lisp. diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 96f8f7f..09cbd52 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -25,9 +25,6 @@ This is pretty cool because it means Hy is several things: comfort of Python! - For everyone: a pleasant language that has a lot of neat ideas! -Now this tutorial assumes you're running Hy on Python 3. So know things -are a bit different if you're still using Python 2. - Basic intro to Lisp for Pythonistas =================================== From ba9b0239c7fa2a02478e8456ed3867c6f2fec0c5 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Tue, 4 Jun 2019 16:03:52 -0400 Subject: [PATCH 21/21] Fix crashes on the new Python 3.8 alpha --- hy/compiler.py | 3 ++- hy/macros.py | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 2963496..7638893 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1061,7 +1061,7 @@ class HyASTCompiler(object): expr, name=fname, args=ast.arguments( - args=[], vararg=None, kwarg=None, + args=[], vararg=None, kwarg=None, posonlyargs=[], kwonlyargs=[], kw_defaults=[], defaults=[]), body=f(parts).stmts, decorator_list=[]) @@ -1425,6 +1425,7 @@ class HyASTCompiler(object): args = ast.arguments( args=main_args, defaults=defaults, vararg=rest, + posonlyargs=[], kwonlyargs=kwonly, kw_defaults=kw_defaults, kwarg=kwargs) diff --git a/hy/macros.py b/hy/macros.py index 3c91f11..66fc3ff 100644 --- a/hy/macros.py +++ b/hy/macros.py @@ -9,7 +9,7 @@ import traceback from contextlib import contextmanager -from hy._compat import reraise +from hy._compat import reraise, PY38 from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value from hy.lex import mangle from hy.errors import (HyLanguageError, HyMacroExpansionError, HyTypeError, @@ -398,7 +398,8 @@ def rename_function(func, new_name): return _fn -code_obj_args = ['argcount', 'kwonlyargcount', 'nlocals', 'stacksize', - 'flags', 'code', 'consts', 'names', 'varnames', - 'filename', 'name', 'firstlineno', 'lnotab', 'freevars', - 'cellvars'] +code_obj_args = ['argcount', 'posonlyargcount', 'kwonlyargcount', 'nlocals', 'stacksize', + 'flags', 'code', 'consts', 'names', 'varnames', 'filename', 'name', + 'firstlineno', 'lnotab', 'freevars', 'cellvars'] +if not PY38: + code_obj_args.remove("posonlyargcount")