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/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 ============================== 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) 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 =================================== diff --git a/hy/_compat.py b/hy/_compat.py index a2ab7a5..70a3187 100644 --- a/hy/_compat.py +++ b/hy/_compat.py @@ -2,108 +2,15 @@ # 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 +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) -# 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 -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. -# -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 - - 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) - - -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 - # keyword.iskeyword will count it as a keyword, but we - # use the __future__ feature print_function, which makes - # it a non-keyword. - 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 +def reraise(exc_type, value, traceback=None): 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 - -try: - FileNotFoundError = FileNotFoundError -except NameError: - FileNotFoundError = IOError + raise value.with_traceback(traceback) + finally: + traceback = None diff --git a/hy/cmdline.py b/hy/cmdline.py index d38ec08..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, PY3, 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)] @@ -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/compiler.py b/hy/compiler.py index 9a20daf..7638893 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 (PY36, PY38, reraise) from hy.macros import require, load_macros, macroexpand, tag_macroexpand import hy.core @@ -29,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') @@ -99,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. @@ -179,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: @@ -190,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)) @@ -277,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!" % ( @@ -315,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): @@ -390,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 @@ -458,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 @@ -469,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: @@ -512,7 +475,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)) @@ -520,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 @@ -564,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: @@ -650,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) @@ -681,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 @@ -739,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): @@ -784,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): @@ -900,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 @@ -991,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 @@ -1015,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()) @@ -1096,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. @@ -1104,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: @@ -1136,16 +1054,14 @@ 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( 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=[]) @@ -1347,18 +1263,17 @@ 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): 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: @@ -1411,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) @@ -1477,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. @@ -1501,29 +1413,19 @@ 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, vararg=rest, + posonlyargs=[], kwonlyargs=kwonly, kw_defaults=kw_defaults, kwarg=kwargs) @@ -1533,13 +1435,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() @@ -1585,7 +1481,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() @@ -1754,16 +1650,14 @@ 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): - f = {HyInteger: long_type, + f = {HyInteger: int, HyFloat: float, HyComplex: complex}[type(x)] return asty.Num(x, n=f(x)) @@ -1810,9 +1704,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)) - node = asty.Bytes if PY3 and type(string) is HyBytes else asty.Str - f = bytes_type if type(string) is HyBytes else str_type + return self._format_string(string, str(string)) + 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)) def _format_string(self, string, rest, allow_recursion=True): @@ -1859,7 +1753,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 +1827,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 +1992,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..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, string_types 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/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 53af7fb..80cba83 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -11,7 +11,7 @@ (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 [collections.abc :as cabc]) (import [hy.models [HySymbol HyKeyword]]) (import [hy.lex [tokenize mangle unmangle read read-str]]) (import [hy.lex.exceptions [LexException PrematureEndOfInput]]) @@ -21,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)) @@ -86,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 @@ -152,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)) @@ -252,13 +201,9 @@ Return series of accumulated sums (or other binary function results)." "Perform `isinstance` with reversed arguments." (isinstance x klass)) -(defn integer [x] - "Return Hy kind of integer for `x`." - (long-type 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." @@ -388,17 +333,9 @@ 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." - (if-python2 - (unicode 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`." @@ -433,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. @@ -446,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`." @@ -457,11 +394,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? - integer integer? integer-char? interleave interpose islice iterable? + disassemble drop drop-last drop-while empty? eval even? every? first + flatten float? fraction gensym group-by identity inc instance? + 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 - remove repeat repeatedly rest reduce second some string string? symbol? - take take-nth take-while tuple? unmangle xor tee zero? zip zip-longest]) + 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? symbol? + take take-nth take-while tuple? unmangle xor tee zero? zip-longest]) diff --git a/hy/core/shadow.hy b/hy/core/shadow.hy index 2135400..74ab08f 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/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(' 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/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", 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/importer/test_importer.py b/tests/importer/test_importer.py index dea1baf..a823c41 100644 --- a/tests/importer/test_importer.py +++ b/tests/importer/test_importer.py @@ -18,7 +18,7 @@ from hy.lex import hy_parse from hy.errors import HyLanguageError from hy.lex.exceptions import PrematureEndOfInput from hy.compiler import hy_eval, hy_compile -from hy.importer import HyLoader, cache_from_source +from hy.importer import HyLoader try: from importlib import reload @@ -101,7 +101,7 @@ def test_import_autocompiles(): f.write(b'(defn pyctest [s] (+ "X" s "Y"))') f.flush() - pyc_path = cache_from_source(f.name) + pyc_path = importlib.util.cache_from_source(f.name) try: os.remove(pyc_path) @@ -144,7 +144,7 @@ def test_reload(): def unlink(filename): os.unlink(source) - bytecode = cache_from_source(source) + bytecode = importlib.util.cache_from_source(source) if os.path.isfile(bytecode): os.unlink(bytecode) 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..0618766 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] @@ -287,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" @@ -324,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"))) @@ -334,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))) @@ -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 687376c..5593d01 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"))) @@ -1482,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) @@ -1549,17 +1531,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() @@ -1597,9 +1574,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)")) @@ -1716,3 +1691,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/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/native_macros.hy b/tests/native_tests/native_macros.hy index 835939e..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]]) @@ -409,9 +404,9 @@ in expansions." Additionally, we confirm that `require` statements are executed via loaded bytecode." (import os sys marshal types) - (import [hy.importer [cache-from-source]]) + (import importlib) - (setv pyc-file (cache-from-source + (setv pyc-file (importlib.util.cache-from-source (os.path.realpath (os.path.join "tests" "resources" "macro_with_require.hy")))) 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 << 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)))))) diff --git a/tests/test_bin.py b/tests/test_bin.py index 06b3af9..1af7ffe 100644 --- a/tests/test_bin.py +++ b/tests/test_bin.py @@ -8,14 +8,12 @@ import os import re import shlex import subprocess +import builtins -from hy.importer import cache_from_source -from hy._compat import PY3 +from importlib.util import cache_from_source import pytest -from hy._compat import builtins - hy_dir = os.environ.get('HY_DIR', '') @@ -497,9 +495,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 +509,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 +519,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 +532,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 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)])