Merge pull request #1777 from Kodiologist/nopy2

Drop support for Python 2
This commit is contained in:
Kodi Arfer 2019-06-04 16:23:25 -04:00 committed by GitHub
commit c2cde0a821
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 498 additions and 1292 deletions

View File

@ -2,12 +2,10 @@ sudo: false
dist: xenial dist: xenial
language: python language: python
python: python:
- "2.7"
- "3.5" - "3.5"
- "3.6" - "3.6"
- "3.7" - "3.7"
- 3.8-dev - 3.8-dev
- pypy2.7-6.0
- pypy3.5-6.0 - pypy3.5-6.0
install: install:
- pip install -r requirements-travis.txt - pip install -r requirements-travis.txt

View File

@ -1,5 +1,12 @@
.. default-role:: code .. default-role:: code
Unreleased
==============================
Removals
------------------------------
* Python 2 is no longer supported.
0.17.0 0.17.0
============================== ==============================

View File

@ -4,7 +4,7 @@ import importlib
import py import py
import pytest import pytest
import hy import hy
from hy._compat import PY3, PY36, PY38 from hy._compat import PY36, PY38
NATIVE_TESTS = os.path.join("", "tests", "native_tests", "") NATIVE_TESTS = os.path.join("", "tests", "native_tests", "")
@ -12,8 +12,7 @@ _fspath_pyimport = py.path.local.pyimport
def pytest_ignore_collect(path, config): def pytest_ignore_collect(path, config):
return (("py3_only" in path.basename and not PY3) or return (("py36_only" in path.basename and not PY36) or
("py36_only" in path.basename and not PY36) or
("py38_only" in path.basename and not PY38) or None) ("py38_only" in path.basename and not PY38) or None)

View File

@ -52,5 +52,4 @@ html_context = dict(
hy_descriptive_version = hy_descriptive_version) hy_descriptive_version = hy_descriptive_version)
intersphinx_mapping = dict( intersphinx_mapping = dict(
py2 = ('https://docs.python.org/2/', None), py = ('https://docs.python.org/3/', None))
py = ('https://docs.python.org/3/', None))

View File

@ -564,8 +564,6 @@ requires.
File "<input>", line 1, in <module> File "<input>", line 1, in <module>
TypeError: compare() missing 1 required keyword-only argument: 'keyfn' TypeError: compare() missing 1 required keyword-only argument: 'keyfn'
Availability: Python 3.
&kwargs &kwargs
Like ``&rest``, but for keyword arugments. Like ``&rest``, but for keyword arugments.
The following parameter will contain 0 or more keyword arguments. 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 ``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 evaluates to a "zero" (including values of ``len`` zero, ``None``, and
``False``), and passes otherwise, but values with a ``__bool__`` method ``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 ``if`` macro is for conditionally selecting an expression for evaluation.
The result of the selected expression becomes the result of the entire ``if`` 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 contain Python statements, with the attendant consequences for calling
``return``. By contrast, ``for`` shares the caller's scope. ``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 nonlocal
-------- --------
.. versionadded:: 0.11.1 .. 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. ``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 The parameters are the names of symbols to mark as nonlocal. This is necessary
to modify variables through nested ``fn`` scopes: 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}) => (f #* [1 2] #** {"c" 3 "d" 4})
[1, 2, 3, 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`). more than once in one expression (:pep:`3132`, :pep:`448`).
.. code-block:: clj .. code-block:: clj
@ -2038,8 +2029,6 @@ yield-from
.. versionadded:: 0.9.13 .. versionadded:: 0.9.13
**PYTHON 3.3 AND UP ONLY!**
``yield-from`` is used to call a subgenerator. This is useful if you ``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 want your coroutine to be able to delegate its processes to another
coroutine, say, if using something fancy like coroutine, say, if using something fancy like

View File

@ -240,19 +240,6 @@ otherwise ``False``. Return ``True`` if *coll* is empty.
True 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?-fn:
float? float?
@ -385,8 +372,7 @@ integer?
Usage: ``(integer? x)`` Usage: ``(integer? x)``
Returns `True` if *x* is an integer. For Python 2, this is Returns `True` if *x* is an integer (``int``).
either ``int`` or ``long``. For Python 3, this is ``int``.
.. code-block:: hy .. code-block:: hy
@ -924,7 +910,7 @@ string?
Usage: ``(string? x)`` Usage: ``(string? x)``
Returns ``True`` if *x* is a string. Returns ``True`` if *x* is a string (``str``).
.. code-block:: hy .. code-block:: hy

View File

@ -120,9 +120,7 @@ HyString
~~~~~~~~ ~~~~~~~~
``hy.models.HyString`` represents string literals (including bracket strings), ``hy.models.HyString`` represents string literals (including bracket strings),
which compile down to unicode string literals in Python. ``HyStrings`` inherit which compile down to unicode string literals (``str``) in Python.
unicode objects in Python 2, and string objects in Python 3 (and are therefore
not encoding-dependent).
``HyString``\s are immutable. ``HyString``\s are immutable.
@ -140,15 +138,15 @@ HyBytes
~~~~~~~ ~~~~~~~
``hy.models.HyBytes`` is like ``HyString``, but for sequences of bytes. ``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: .. _hy_numeric_models:
Numeric Models Numeric Models
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
``hy.models.HyInteger`` represents integer literals (using the ``hy.models.HyInteger`` represents integer literals, using the ``int``
``long`` type on Python 2, and ``int`` on Python 3). type.
``hy.models.HyFloat`` represents floating-point literals. ``hy.models.HyFloat`` represents floating-point literals.

View File

@ -10,7 +10,7 @@ An identifier consists of a nonempty sequence of Unicode characters that are not
numeric literals 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. integers is used. ``0x`` for Hex, ``0o`` for Octal, ``0b`` for Binary.
.. code-block:: clj .. 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 literally, prefix the string with ``r``, as in ``r"slash\not"``. Bracket
strings are always raw strings and don't allow the ``r`` prefix. 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 Like Python, Hy treats all string literals as sequences of Unicode characters
sequences of Unicode characters by default, and allows you to prefix a plain by default. You may prefix a plain string literal (but not a bracket string)
string literal (but not a bracket string) with ``b`` to treat it as a sequence with ``b`` to treat it as a sequence of bytes.
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"``.
Unlike Python, Hy only recognizes string prefixes (``r``, etc.) in lowercase. Unlike Python, Hy only recognizes string prefixes (``r``, etc.) in lowercase.

View File

@ -39,7 +39,6 @@ into the making of Hy.
+ Look like a Lisp; DTRT with it (e.g. dashes turn to underscores). + 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. + We're still Python. Most of the internals translate 1:1 to Python internals.
+ Use Unicode everywhere. + Use Unicode everywhere.
+ Fix the bad decisions in Python 2 when we can (see ``true_division``).
+ When in doubt, defer to Python. + When in doubt, defer to Python.
+ If you're still unsure, defer to Clojure. + If you're still unsure, defer to Clojure.
+ If you're even more unsure, defer to Common Lisp. + If you're even more unsure, defer to Common Lisp.

View File

@ -25,9 +25,6 @@ This is pretty cool because it means Hy is several things:
comfort of Python! comfort of Python!
- For everyone: a pleasant language that has a lot of neat ideas! - 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 Basic intro to Lisp for Pythonistas
=================================== ===================================

View File

@ -2,108 +2,15 @@
# This file is part of Hy, which is free software licensed under the Expat # This file is part of Hy, which is free software licensed under the Expat
# license. See the LICENSE. # license. See the LICENSE.
try: import sys
import __builtin__ as builtins
except ImportError:
import builtins # NOQA
import sys, keyword, textwrap
PY3 = sys.version_info[0] >= 3
PY36 = sys.version_info >= (3, 6) PY36 = sys.version_info >= (3, 6)
PY37 = sys.version_info >= (3, 7) PY37 = sys.version_info >= (3, 7)
PY38 = sys.version_info >= (3, 8) 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 def reraise(exc_type, value, traceback=None):
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
try: try:
tokens = list(T.generate_tokens(StringIO(x).readline)) raise value.with_traceback(traceback)
except (T.TokenError, IndentationError): finally:
return False traceback = None
# 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

View File

@ -19,6 +19,7 @@ import time
import linecache import linecache
import hashlib import hashlib
import codeop import codeop
import builtins
import astor.code_gen import astor.code_gen
@ -35,7 +36,6 @@ from hy.importer import runhy
from hy.completer import completion, Completer from hy.completer import completion, Completer
from hy.macros import macro, require from hy.macros import macro, require
from hy.models import HyExpression, HyString, HySymbol from hy.models import HyExpression, HyString, HySymbol
from hy._compat import builtins, PY3, FileNotFoundError
sys.last_type = None sys.last_type = None
@ -256,7 +256,7 @@ class HyREPL(code.InteractiveConsole, object):
module, f = '.'.join(parts[:-1]), parts[-1] module, f = '.'.join(parts[:-1]), parts[-1]
self.output_fn = getattr(importlib.import_module(module), f) self.output_fn = getattr(importlib.import_module(module), f)
else: 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 # Pre-mangle symbols for repl recent results: *1, *2, *3
self._repl_results_symbols = [mangle("*{}".format(i + 1)) for i in range(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: if options.with_source:
# need special printing on Windows in case the # need special printing on Windows in case the
# codepage doesn't support utf-8 characters # codepage doesn't support utf-8 characters
if PY3 and platform.system() == "Windows": if platform.system() == "Windows":
for h in hst: for h in hst:
try: try:
print(h) print(h)
@ -676,7 +676,7 @@ def hy2py_main():
_ast = hy_compile(hst, '__main__', filename=filename, source=source) _ast = hy_compile(hst, '__main__', filename=filename, source=source)
if options.with_ast: if options.with_ast:
if PY3 and platform.system() == "Windows": if platform.system() == "Windows":
_print_for_windows(astor.dump_tree(_ast)) _print_for_windows(astor.dump_tree(_ast))
else: else:
print(astor.dump_tree(_ast)) print(astor.dump_tree(_ast))
@ -684,7 +684,7 @@ def hy2py_main():
print() print()
if not options.without_python: if not options.without_python:
if PY3 and platform.system() == "Windows": if platform.system() == "Windows":
_print_for_windows(astor.code_gen.to_source(_ast)) _print_for_windows(astor.code_gen.to_source(_ast))
else: else:
print(astor.code_gen.to_source(_ast)) print(astor.code_gen.to_source(_ast))

View File

@ -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.lex import mangle, unmangle, hy_parse, parse_one_thing, LexException
from hy._compat import (string_types, str_type, bytes_type, long_type, PY3, from hy._compat import (PY36, PY38, reraise)
PY36, PY38, reraise)
from hy.macros import require, load_macros, macroexpand, tag_macroexpand from hy.macros import require, load_macros, macroexpand, tag_macroexpand
import hy.core import hy.core
@ -29,15 +28,12 @@ import types
import ast import ast
import sys import sys
import copy import copy
import builtins
import __future__ import __future__
from collections import defaultdict from collections import defaultdict
from functools import reduce from functools import reduce
if PY3:
import builtins
else:
import __builtin__ as builtins
Inf = float('inf') Inf = float('inf')
@ -99,15 +95,12 @@ def calling_module(n=1):
def ast_str(x, piecewise=False): def ast_str(x, piecewise=False):
if piecewise: if piecewise:
return ".".join(ast_str(s) if s else "" for s in x.split(".")) return ".".join(ast_str(s) if s else "" for s in x.split("."))
x = mangle(x) return mangle(x)
return x if PY3 else x.encode('UTF8')
_special_form_compilers = {} _special_form_compilers = {}
_model_compilers = {} _model_compilers = {}
_decoratables = (ast.FunctionDef, ast.ClassDef) _decoratables = (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)
if PY3:
_decoratables += (ast.AsyncFunctionDef,)
# _bad_roots are fake special operators, which are used internally # _bad_roots are fake special operators, which are used internally
# by other special forms (e.g., `except` in `try`) but can't be # by other special forms (e.g., `except` in `try`) but can't be
# used to construct special forms themselves. # 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. object gets added to a Result object, it gets converted on-the-fly.
""" """
__slots__ = ("imports", "stmts", "temp_variables", __slots__ = ("imports", "stmts", "temp_variables",
"_expr", "__used_expr", "contains_yield") "_expr", "__used_expr")
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if args: if args:
@ -190,14 +183,12 @@ class Result(object):
self.stmts = [] self.stmts = []
self.temp_variables = [] self.temp_variables = []
self._expr = None self._expr = None
self.contains_yield = False
self.__used_expr = False self.__used_expr = False
# XXX: Make sure we only have AST where we should. # XXX: Make sure we only have AST where we should.
for kwarg in kwargs: for kwarg in kwargs:
if kwarg not in ["imports", "contains_yield", "stmts", "expr", if kwarg not in ["imports", "stmts", "expr", "temp_variables"]:
"temp_variables"]:
raise TypeError( raise TypeError(
"%s() got an unexpected keyword argument '%s'" % ( "%s() got an unexpected keyword argument '%s'" % (
self.__class__.__name__, kwarg)) self.__class__.__name__, kwarg))
@ -277,9 +268,7 @@ class Result(object):
if isinstance(var, ast.Name): if isinstance(var, ast.Name):
var.id = new_name var.id = new_name
var.arg = new_name var.arg = new_name
elif isinstance(var, ast.FunctionDef): elif isinstance(var, (ast.FunctionDef, ast.AsyncFunctionDef)):
var.name = new_name
elif PY3 and isinstance(var, ast.AsyncFunctionDef):
var.name = new_name var.name = new_name
else: else:
raise TypeError("Don't know how to rename a %s!" % ( raise TypeError("Don't know how to rename a %s!" % (
@ -315,22 +304,17 @@ class Result(object):
result.stmts = self.stmts + other.stmts result.stmts = self.stmts + other.stmts
result.expr = other.expr result.expr = other.expr
result.temp_variables = other.temp_variables result.temp_variables = other.temp_variables
result.contains_yield = False
if self.contains_yield or other.contains_yield:
result.contains_yield = True
return result return result
def __str__(self): def __str__(self):
return ( return (
"Result(imports=[%s], stmts=[%s], " "Result(imports=[%s], stmts=[%s], expr=%s)"
"expr=%s, contains_yield=%s)" % (
) % (
", ".join(ast.dump(x) for x in self.imports), ", ".join(ast.dump(x) for x in self.imports),
", ".join(ast.dump(x) for x in self.stmts), ", ".join(ast.dump(x) for x in self.stmts),
ast.dump(self.expr) if self.expr else None, ast.dump(self.expr) if self.expr else None
self.contains_yield ))
)
def is_unpack(kind, x): def is_unpack(kind, x):
@ -390,11 +374,7 @@ class HyASTCompiler(object):
for stdlib_module in hy.core.STDLIB: for stdlib_module in hy.core.STDLIB:
mod = importlib.import_module(stdlib_module) mod = importlib.import_module(stdlib_module)
for e in map(ast_str, getattr(mod, 'EXPORTS', [])): for e in map(ast_str, getattr(mod, 'EXPORTS', [])):
if getattr(mod, e) is not getattr(builtins, e, ''): self._stdlib[e] = stdlib_module
# 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
def get_anon_var(self): def get_anon_var(self):
self.anon_var_count += 1 self.anon_var_count += 1
@ -458,8 +438,7 @@ class HyASTCompiler(object):
def _syntax_error(self, expr, message): def _syntax_error(self, expr, message):
return HySyntaxError(message, expr, self.filename, self.source) return HySyntaxError(message, expr, self.filename, self.source)
def _compile_collect(self, exprs, with_kwargs=False, dict_display=False, def _compile_collect(self, exprs, with_kwargs=False, dict_display=False):
oldpy_unpack=False):
"""Collect the expression contexts from a list of compiled expression. """Collect the expression contexts from a list of compiled expression.
This returns a list of the expression contexts, and the sum of the This returns a list of the expression contexts, and the sum of the
@ -469,34 +448,18 @@ class HyASTCompiler(object):
compiled_exprs = [] compiled_exprs = []
ret = Result() ret = Result()
keywords = [] keywords = []
oldpy_starargs = None
oldpy_kwargs = None
exprs_iter = iter(exprs) exprs_iter = iter(exprs)
for expr in exprs_iter: for expr in exprs_iter:
if not PY3 and oldpy_unpack and is_unpack("iterable", expr): if is_unpack("mapping", 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):
ret += self.compile(expr[1]) ret += self.compile(expr[1])
if PY3: if dict_display:
if dict_display: compiled_exprs.append(None)
compiled_exprs.append(None) compiled_exprs.append(ret.force_expr)
compiled_exprs.append(ret.force_expr) elif with_kwargs:
elif with_kwargs: keywords.append(asty.keyword(
keywords.append(asty.keyword( expr, arg=None, value=ret.force_expr))
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
elif with_kwargs and isinstance(expr, HyKeyword): elif with_kwargs and isinstance(expr, HyKeyword):
try: try:
@ -512,7 +475,7 @@ class HyASTCompiler(object):
compiled_value = self.compile(value) compiled_value = self.compile(value)
ret += compiled_value ret += compiled_value
arg = str_type(expr)[1:] arg = str(expr)[1:]
keywords.append(asty.keyword( keywords.append(asty.keyword(
expr, arg=ast_str(arg), value=compiled_value.force_expr)) expr, arg=ast_str(arg), value=compiled_value.force_expr))
@ -520,10 +483,7 @@ class HyASTCompiler(object):
ret += self.compile(expr) ret += self.compile(expr)
compiled_exprs.append(ret.force_expr) compiled_exprs.append(ret.force_expr)
if oldpy_unpack: return compiled_exprs, ret, keywords
return compiled_exprs, ret, keywords, oldpy_starargs, oldpy_kwargs
else:
return compiled_exprs, ret, keywords
def _compile_branch(self, exprs): def _compile_branch(self, exprs):
"""Make a branch out of an iterable of Result objects """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) new_name = ast.Subscript(value=name.value, slice=name.slice)
elif isinstance(name, ast.Attribute): elif isinstance(name, ast.Attribute):
new_name = ast.Attribute(value=name.value, attr=name.attr) 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( new_name = ast.Starred(
value=self._storeize(expr, name.value, func)) value=self._storeize(expr, name.value, func))
else: else:
@ -650,23 +610,10 @@ class HyASTCompiler(object):
@special("unpack-iterable", [FORM]) @special("unpack-iterable", [FORM])
def compile_unpack_iterable(self, expr, root, arg): 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 = self.compile(arg)
ret += asty.Starred(expr, value=ret.force_expr, ctx=ast.Load()) ret += asty.Starred(expr, value=ret.force_expr, ctx=ast.Load())
return ret 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)]) @special("do", [many(FORM)])
def compile_do(self, expr, root, body): def compile_do(self, expr, root, body):
return self._compile_branch(body) return self._compile_branch(body)
@ -681,9 +628,6 @@ class HyASTCompiler(object):
exc = exc.force_expr exc = exc.force_expr
if cause is not None: if cause is not None:
if not PY3:
raise self._syntax_error(expr,
"raise from only supported in python 3")
cause = self.compile(cause) cause = self.compile(cause)
ret += cause ret += cause
cause = cause.force_expr cause = cause.force_expr
@ -739,35 +683,17 @@ class HyASTCompiler(object):
returnable = Result( returnable = Result(
expr=asty.Name(expr, id=return_var.id, ctx=ast.Load()), expr=asty.Name(expr, id=return_var.id, ctx=ast.Load()),
temp_variables=[return_var], temp_variables=[return_var])
contains_yield=body.contains_yield)
body += body.expr_as_stmt() if orelse else asty.Assign( body += body.expr_as_stmt() if orelse else asty.Assign(
expr, targets=[return_var], value=body.force_expr) expr, targets=[return_var], value=body.force_expr)
body = body.stmts or [asty.Pass(expr)] body = body.stmts or [asty.Pass(expr)]
if PY3: x = asty.Try(
# Python 3.3 features a merge of TryExcept+TryFinally into Try. expr,
x = asty.Try( body=body,
expr, handlers=handlers,
body=body, orelse=orelse,
handlers=handlers, finalbody=finalbody)
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)
return handler_results + x + returnable return handler_results + x + returnable
def _compile_catch_expression(self, expr, var, exceptions, body): def _compile_catch_expression(self, expr, var, exceptions, body):
@ -784,9 +710,7 @@ class HyASTCompiler(object):
name = None name = None
if len(exceptions) == 2: if len(exceptions) == 2:
name = exceptions[0] name = ast_str(exceptions[0])
name = (ast_str(name) if PY3
else self._storeize(name, self.compile(name)))
exceptions_list = exceptions[-1] if exceptions else HyList() exceptions_list = exceptions[-1] if exceptions else HyList()
if isinstance(exceptions_list, HyList): if isinstance(exceptions_list, HyList):
@ -900,19 +824,19 @@ class HyASTCompiler(object):
msg = self.compile(msg).force_expr msg = self.compile(msg).force_expr
return ret + asty.Assert(expr, test=e, msg=msg) 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): def compile_global_or_nonlocal(self, expr, root, syms):
node = asty.Global if root == "global" else asty.Nonlocal node = asty.Global if root == "global" else asty.Nonlocal
return node(expr, names=list(map(ast_str, syms))) return node(expr, names=list(map(ast_str, syms)))
@special("yield", [maybe(FORM)]) @special("yield", [maybe(FORM)])
def compile_yield_expression(self, expr, root, arg): def compile_yield_expression(self, expr, root, arg):
ret = Result(contains_yield=(not PY3)) ret = Result()
if arg is not None: if arg is not None:
ret += self.compile(arg) ret += self.compile(arg)
return ret + asty.Yield(expr, value=ret.force_expr) 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): def compile_yield_from_or_await_expression(self, expr, root, arg):
ret = Result() + self.compile(arg) ret = Result() + self.compile(arg)
node = asty.YieldFrom if root == "yield-from" else asty.Await 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 fn.stmts[-1].decorator_list = decs + fn.stmts[-1].decorator_list
return ret + fn return ret + fn
@special(["with*", (PY3, "with/a*")], @special(["with*", "with/a*"],
[brackets(FORM, maybe(FORM)), many(FORM)]) [brackets(FORM, maybe(FORM)), many(FORM)])
def compile_with_expression(self, expr, root, args, body): def compile_with_expression(self, expr, root, args, body):
thing, ctx = (None, args[0]) if args[1] is None else args thing, ctx = (None, args[0]) if args[1] is None else args
@ -1015,14 +939,11 @@ class HyASTCompiler(object):
the_with = node(expr, the_with = node(expr,
context_expr=ctx.force_expr, context_expr=ctx.force_expr,
optional_vars=thing, optional_vars=thing,
body=body.stmts) body=body.stmts,
items=[ast.withitem(context_expr=ctx.force_expr,
if PY3: optional_vars=thing)])
the_with.items = [ast.withitem(context_expr=ctx.force_expr,
optional_vars=thing)]
ret = Result(stmts=[initial_assign]) + ctx + the_with 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 # And make our expression context our temp variable
expr_name = asty.Name(expr, id=ast_str(var), ctx=ast.Load()) 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 # The desired comprehension can't be expressed as a
# real Python comprehension. We'll write it as a nested # real Python comprehension. We'll write it as a nested
# loop in a function instead. # loop in a function instead.
contains_yield = []
def f(parts): def f(parts):
# This function is called recursively to construct # This function is called recursively to construct
# the nested loop. # the nested loop.
@ -1104,8 +1024,6 @@ class HyASTCompiler(object):
if is_for: if is_for:
if body: if body:
bd = self._compile_branch(body) bd = self._compile_branch(body)
if bd.contains_yield:
contains_yield.append(True)
return bd + bd.expr_as_stmt() return bd + bd.expr_as_stmt()
return Result(stmts=[asty.Pass(expr)]) return Result(stmts=[asty.Pass(expr)])
if node_class is asty.DictComp: if node_class is asty.DictComp:
@ -1136,16 +1054,14 @@ class HyASTCompiler(object):
else: else:
raise ValueError("can't happen") raise ValueError("can't happen")
if is_for: if is_for:
ret = f(parts) return f(parts)
ret.contains_yield = bool(contains_yield)
return ret
fname = self.get_anon_var() fname = self.get_anon_var()
# Define the generator function. # Define the generator function.
ret = Result() + asty.FunctionDef( ret = Result() + asty.FunctionDef(
expr, expr,
name=fname, name=fname,
args=ast.arguments( args=ast.arguments(
args=[], vararg=None, kwarg=None, args=[], vararg=None, kwarg=None, posonlyargs=[],
kwonlyargs=[], kw_defaults=[], defaults=[]), kwonlyargs=[], kw_defaults=[], defaults=[]),
body=f(parts).stmts, body=f(parts).stmts,
decorator_list=[]) decorator_list=[])
@ -1347,18 +1263,17 @@ class HyASTCompiler(object):
">>": ast.RShift, ">>": ast.RShift,
"|": ast.BitOr, "|": ast.BitOr,
"^": ast.BitXor, "^": ast.BitXor,
"&": ast.BitAnd} "&": ast.BitAnd,
if PY3: "@": ast.MatMult}
m_ops["@"] = ast.MatMult
@special(["+", "*", "|"], [many(FORM)]) @special(["+", "*", "|"], [many(FORM)])
@special(["-", "/", "&", (PY3, "@")], [oneplus(FORM)]) @special(["-", "/", "&", "@"], [oneplus(FORM)])
@special(["**", "//", "<<", ">>"], [times(2, Inf, FORM)]) @special(["**", "//", "<<", ">>"], [times(2, Inf, FORM)])
@special(["%", "^"], [times(2, 2, FORM)]) @special(["%", "^"], [times(2, 2, FORM)])
def compile_maths_expression(self, expr, root, args): def compile_maths_expression(self, expr, root, args):
if len(args) == 0: if len(args) == 0:
# Return the identity element for this operator. # Return the identity element for this operator.
return asty.Num(expr, n=long_type( return asty.Num(expr, n=(
{"+": 0, "|": 0, "*": 1}[root])) {"+": 0, "|": 0, "*": 1}[root]))
if len(args) == 1: if len(args) == 1:
@ -1411,9 +1326,7 @@ class HyASTCompiler(object):
def _compile_assign(self, root, name, result): def _compile_assign(self, root, name, result):
str_name = "%s" % name str_name = "%s" % name
if str_name in (["None"] + (["True", "False"] if PY3 else [])): if str_name in ("None", "True", "False"):
# Python 2 allows assigning to True and False, although
# this is rarely wise.
raise self._syntax_error(name, raise self._syntax_error(name,
"Can't assign to `%s'" % str_name) "Can't assign to `%s'" % str_name)
@ -1477,13 +1390,12 @@ class HyASTCompiler(object):
expr, test=cond_compiled.force_expr, expr, test=cond_compiled.force_expr,
body=body.stmts or [asty.Pass(expr)], body=body.stmts or [asty.Pass(expr)],
orelse=orel.stmts) orelse=orel.stmts)
ret.contains_yield = body.contains_yield
return ret return ret
NASYM = some(lambda x: isinstance(x, HySymbol) and x not in ( NASYM = some(lambda x: isinstance(x, HySymbol) and x not in (
"&optional", "&rest", "&kwonly", "&kwargs")) "&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 # The starred version is for internal use (particularly, in the
# definition of `defn`). It ensures that a FunctionDef is # definition of `defn`). It ensures that a FunctionDef is
# produced rather than a Lambda. # produced rather than a Lambda.
@ -1501,29 +1413,19 @@ class HyASTCompiler(object):
mandatory, optional, rest, kwonly, kwargs = params mandatory, optional, rest, kwonly, kwargs = params
optional, defaults, ret = self._parse_optional_args(optional) 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) kwonly, kw_defaults, ret2 = self._parse_optional_args(kwonly, True)
ret += ret2 ret += ret2
main_args = mandatory + optional main_args = mandatory + optional
if PY3: main_args, kwonly, [rest], [kwargs] = (
# Python 3.4+ requires that args are an ast.arg object, rather [[x and asty.arg(x, arg=ast_str(x), annotation=None)
# than an ast.Name or bare string. for x in o]
main_args, kwonly, [rest], [kwargs] = ( for o in (main_args or [], kwonly or [], [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)
args = ast.arguments( args = ast.arguments(
args=main_args, defaults=defaults, args=main_args, defaults=defaults,
vararg=rest, vararg=rest,
posonlyargs=[],
kwonlyargs=kwonly, kw_defaults=kw_defaults, kwonlyargs=kwonly, kw_defaults=kw_defaults,
kwarg=kwargs) kwarg=kwargs)
@ -1533,13 +1435,7 @@ class HyASTCompiler(object):
return ret + asty.Lambda(expr, args=args, body=body.force_expr) return ret + asty.Lambda(expr, args=args, body=body.force_expr)
if body.expr: if body.expr:
if body.contains_yield and not PY3: body += asty.Return(body.expr, value=body.expr)
# 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)
name = self.get_anon_var() name = self.get_anon_var()
@ -1585,7 +1481,7 @@ class HyASTCompiler(object):
base_list, docstring, attrs, body = rest or ([[]], None, None, []) base_list, docstring, attrs, body = rest or ([[]], None, None, [])
bases_expr, bases, keywords = ( bases_expr, bases, keywords = (
self._compile_collect(base_list[0], with_kwargs=PY3)) self._compile_collect(base_list[0], with_kwargs=True))
bodyr = Result() bodyr = Result()
@ -1754,16 +1650,14 @@ class HyASTCompiler(object):
# a typecheck, eg (type :foo) # a typecheck, eg (type :foo)
with_kwargs = root not in ( with_kwargs = root not in (
"type", "HyKeyword", "keyword", "name", "keyword?", "identity") "type", "HyKeyword", "keyword", "name", "keyword?", "identity")
args, ret, keywords, oldpy_star, oldpy_kw = self._compile_collect( args, ret, keywords = self._compile_collect(args, with_kwargs)
args, with_kwargs, oldpy_unpack=True)
return func + ret + asty.Call( return func + ret + asty.Call(
expr, func=func.expr, args=args, keywords=keywords, expr, func=func.expr, args=args, keywords=keywords)
starargs=oldpy_star, kwargs=oldpy_kw)
@builds_model(HyInteger, HyFloat, HyComplex) @builds_model(HyInteger, HyFloat, HyComplex)
def compile_numeric_literal(self, x): def compile_numeric_literal(self, x):
f = {HyInteger: long_type, f = {HyInteger: int,
HyFloat: float, HyFloat: float,
HyComplex: complex}[type(x)] HyComplex: complex}[type(x)]
return asty.Num(x, n=f(x)) return asty.Num(x, n=f(x))
@ -1810,9 +1704,9 @@ class HyASTCompiler(object):
def compile_string(self, string): def compile_string(self, string):
if type(string) is HyString and string.is_format: if type(string) is HyString and string.is_format:
# This is a format string (a.k.a. an f-string). # 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 node = asty.Bytes if 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)) return node(string, s=f(string))
def _format_string(self, string, rest, allow_recursion=True): def _format_string(self, string, rest, allow_recursion=True):
@ -1859,7 +1753,7 @@ class HyASTCompiler(object):
try: try:
model, item = parse_one_thing(item) model, item = parse_one_thing(item)
except (ValueError, LexException) as e: 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. # Look for a conversion character.
item = item.lstrip() 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 module = getattr(compiler, 'module', None) or module
if isinstance(module, string_types): if isinstance(module, str):
if module.startswith('<') and module.endswith('>'): if module.startswith('<') and module.endswith('>'):
module = types.ModuleType(module) module = types.ModuleType(module)
else: else:
@ -2098,7 +1992,7 @@ def hy_compile(tree, module, root=ast.Module, get_expr=False,
""" """
module = get_compiler_module(module, compiler, False) module = get_compiler_module(module, compiler, False)
if isinstance(module, string_types): if isinstance(module, str):
if module.startswith('<') and module.endswith('>'): if module.startswith('<') and module.endswith('>'):
module = types.ModuleType(module) module = types.ModuleType(module)
else: else:

View File

@ -6,10 +6,10 @@ import contextlib
import os import os
import re import re
import sys import sys
import builtins
import hy.macros import hy.macros
import hy.compiler import hy.compiler
from hy._compat import builtins, string_types
docomplete = True docomplete = True
@ -78,7 +78,7 @@ class Completer(object):
matches = [] matches = []
for p in self.path: for p in self.path:
for k in p.keys(): for k in p.keys():
if isinstance(k, string_types): if isinstance(k, str):
k = k.replace("_", "-") k = k.replace("_", "-")
if k.startswith(text): if k.startswith(text):
matches.append(k) matches.append(k)
@ -89,7 +89,7 @@ class Completer(object):
matches = [] matches = []
for p in self.tag_path: for p in self.tag_path:
for k in p.keys(): for k in p.keys():
if isinstance(k, string_types): if isinstance(k, str):
if k.startswith(text): if k.startswith(text):
matches.append("#{}".format(k)) matches.append("#{}".format(k))
return matches return matches

View File

@ -7,7 +7,7 @@
re re
datetime datetime
collections 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]]) [hy.models [HyObject HyExpression HySymbol HyKeyword HyInteger HyFloat HyComplex HyList HyDict HySet HyString HyBytes]])
(try (try
@ -84,10 +84,10 @@
(+ "(" (-cat x) ")")))) (+ "(" (-cat x) ")"))))
(hy-repr-register [HySymbol HyKeyword] str) (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")) (setv r (.lstrip (-base-repr x) "ub"))
(+ (+
(if (instance? bytes-type x) "b" "") (if (instance? bytes x) "b" "")
(if (.startswith "\"" r) (if (.startswith "\"" r)
; If Python's built-in repr produced a double-quoted string, use ; If Python's built-in repr produced a double-quoted string, use
; that. ; that.
@ -96,10 +96,6 @@
; convert it. ; convert it.
(+ "\"" (.replace (cut r 1 -1) "\"" "\\\"") "\""))))) (+ "\"" (.replace (cut r 1 -1) "\"" "\\\"") "\"")))))
(hy-repr-register bool str) (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] (hy-repr-register float (fn [x]
(if (if
(isnan x) "NaN" (isnan x) "NaN"
@ -131,7 +127,7 @@
(-repr-time-innards x)))) (-repr-time-innards x))))
(defn -repr-time-innards [x] (defn -repr-time-innards [x]
(.rstrip (+ " " (.join " " (filter identity [ (.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 (not (none? x.tzinfo)) (+ ":tzinfo " (hy-repr x.tzinfo)))
(if (and PY36 (!= x.fold 0)) (+ ":fold " (hy-repr x.fold)))]))))) (if (and PY36 (!= x.fold 0)) (+ ":fold " (hy-repr x.fold)))])))))
(defn -strftime-0 [x fmt] (defn -strftime-0 [x fmt]

View File

@ -19,9 +19,7 @@
`(do `(do
(import cProfile pstats) (import cProfile pstats)
(if-python2 (import [io [StringIO]])
(import [StringIO [StringIO]])
(import [io [StringIO]]))
(setv ~g!hy-pr (.Profile cProfile)) (setv ~g!hy-pr (.Profile cProfile))
(.enable ~g!hy-pr) (.enable ~g!hy-pr)

View File

@ -77,10 +77,3 @@
(if (not (isinstance lambda-list hy.HyList)) (if (not (isinstance lambda-list hy.HyList))
(macro-error name "defn/a takes a parameter list as second argument")) (macro-error name "defn/a takes a parameter list as second argument"))
`(setv ~name (fn/a ~lambda-list ~@body))) `(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))

View File

@ -11,7 +11,7 @@
(import [fractions [Fraction :as fraction]]) (import [fractions [Fraction :as fraction]])
(import operator) ; shadow not available yet (import operator) ; shadow not available yet
(import sys) (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.models [HySymbol HyKeyword]])
(import [hy.lex [tokenize mangle unmangle read read-str]]) (import [hy.lex [tokenize mangle unmangle read read-str]])
(import [hy.lex.exceptions [LexException PrematureEndOfInput]]) (import [hy.lex.exceptions [LexException PrematureEndOfInput]])
@ -21,10 +21,6 @@
(require [hy.core.bootstrap [*]]) (require [hy.core.bootstrap [*]])
(if-python2
(import [collections :as cabc])
(import [collections.abc :as cabc]))
(defn butlast [coll] (defn butlast [coll]
"Return an iterator of all but the last item in `coll`." "Return an iterator of all but the last item in `coll`."
(drop-last 1 coll)) (drop-last 1 coll))
@ -86,47 +82,12 @@ If the second argument `codegen` is true, generate python code instead."
(yield val) (yield val)
(.add seen val))))) (.add seen val)))))
(if-python2 (setv
(setv remove itertools.filterfalse
remove itertools.ifilterfalse zip-longest itertools.zip_longest
zip-longest itertools.izip_longest ;; was builtin in Python2
;; not builtin in Python3 reduce functools.reduce
reduce reduce accumulate itertools.accumulate)
;; 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))
;; infinite iterators ;; infinite iterators
(setv (setv
@ -152,18 +113,6 @@ function with keyword arguments, which isn't supported by Python 3's `exec`."
permutations itertools.permutations permutations itertools.permutations
product itertools.product) 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] (defn drop [count coll]
"Drop `count` elements from `coll` and yield back the rest." "Drop `count` elements from `coll` and yield back the rest."
(islice coll count None)) (islice coll count None))
@ -252,13 +201,9 @@ Return series of accumulated sums (or other binary function results)."
"Perform `isinstance` with reversed arguments." "Perform `isinstance` with reversed arguments."
(isinstance x klass)) (isinstance x klass))
(defn integer [x]
"Return Hy kind of integer for `x`."
(long-type x))
(defn integer? [x] (defn integer? [x]
"Check if `x` is an integer." "Check if `x` is an integer."
(isinstance x (, int long-type))) (isinstance x int))
(defn integer-char? [x] (defn integer-char? [x]
"Check if char `x` parses as an integer." "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." "Return the first logical true value of applying `pred` in `coll`, else None."
(first (filter None (map pred coll)))) (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] (defn string? [x]
"Check if `x` is a string." "Check if `x` is a string."
(if-python2 (isinstance x str))
(isinstance x (, str unicode))
(isinstance x str)))
(defn take [count coll] (defn take [count coll]
"Take `count` elements from `coll`." "Take `count` elements from `coll`."
@ -433,7 +370,7 @@ Strings numbers and even objects with the __name__ magic will work."
(HyKeyword (unmangle value)) (HyKeyword (unmangle value))
(try (try
(unmangle (.__name__ value)) (unmangle (.__name__ value))
(except [] (HyKeyword (string value))))))) (except [] (HyKeyword (str value)))))))
(defn name [value] (defn name [value]
"Convert `value` to a string. "Convert `value` to a string.
@ -446,7 +383,7 @@ Even objects with the __name__ magic will work."
(unmangle value) (unmangle value)
(try (try
(unmangle (. value __name__)) (unmangle (. value __name__))
(except [] (string value)))))) (except [] (str value))))))
(defn xor [a b] (defn xor [a b]
"Perform exclusive or between `a` and `b`." "Perform exclusive or between `a` and `b`."
@ -457,11 +394,11 @@ Even objects with the __name__ magic will work."
(setv EXPORTS (setv EXPORTS
'[*map accumulate butlast calling-module calling-module-name chain coll? '[*map accumulate butlast calling-module calling-module-name chain coll?
combinations comp complement compress constantly count cycle dec distinct combinations comp complement compress constantly count cycle dec distinct
disassemble drop drop-last drop-while empty? eval even? every? exec first disassemble drop drop-last drop-while empty? eval even? every? first
filter flatten float? fraction gensym group-by identity inc input instance? 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 iterate iterator? juxt keyword keyword? last list? macroexpand
macroexpand-1 mangle map merge-with multicombinations name neg? none? nth macroexpand-1 mangle merge-with multicombinations name neg? none? nth
numeric? odd? partition permutations pos? product range read read-str 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 zip-longest]) take take-nth take-while tuple? unmangle xor tee zero? zip-longest])

View File

@ -5,12 +5,10 @@
;;;; Hy shadow functions ;;;; Hy shadow functions
(import operator) (import operator)
(import [hy._compat [PY3]])
(require [hy.core.bootstrap [*]]) (require [hy.core.bootstrap [*]])
(if PY3 (import [functools [reduce]])
(import [functools [reduce]]))
(defn + [&rest args] (defn + [&rest args]
"Shadowed `+` operator adds `args`." "Shadowed `+` operator adds `args`."
@ -60,10 +58,9 @@
"Shadowed `%` operator takes `x` modulo `y`." "Shadowed `%` operator takes `x` modulo `y`."
(% x y)) (% x y))
(if PY3 (defn @ [a1 &rest a-rest]
(defn @ [a1 &rest a-rest] "Shadowed `@` operator matrix multiples `a1` by each `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] (defn << [a1 a2 &rest a-rest]
"Shadowed `<<` operator performs left-shift on `a1` by `a2`, ..., `a-rest`." "Shadowed `<<` operator performs left-shift on `a1` by `a2`, ..., `a-rest`."
@ -173,5 +170,3 @@
'and 'or 'not 'and 'or 'not
'is 'is-not 'in 'not-in 'is 'is-not 'in 'not-in
'get]) 'get])
(if (not PY3)
(.remove EXPORTS '@))

View File

@ -19,33 +19,6 @@ from contextlib import contextmanager
from hy.compiler import hy_compile, hy_ast_compile_flags from hy.compiler import hy_compile, hy_ast_compile_flags
from hy.lex import hy_parse 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 @contextmanager
@ -135,370 +108,41 @@ def _get_code_from_file(run_name, fname=None,
source = f.read().decode('utf-8') source = f.read().decode('utf-8')
code = compile(source, fname, 'exec') code = compile(source, fname, 'exec')
return (code, fname) if PY3 else code return (code, fname)
if PY3: importlib.machinery.SOURCE_SUFFIXES.insert(0, '.hy')
importlib.machinery.SOURCE_SUFFIXES.insert(0, '.hy') _py_source_to_code = importlib.machinery.SourceFileLoader.source_to_code
_py_source_to_code = importlib.machinery.SourceFileLoader.source_to_code
def _could_be_hy_src(filename): def _could_be_hy_src(filename):
return (os.path.isfile(filename) and return (os.path.isfile(filename) and
(filename.endswith('.hy') or (filename.endswith('.hy') or
not any(filename.endswith(ext) not any(filename.endswith(ext)
for ext in importlib.machinery.SOURCE_SUFFIXES[1:]))) for ext in importlib.machinery.SOURCE_SUFFIXES[1:])))
def _hy_source_to_code(self, data, path, _optimize=-1): def _hy_source_to_code(self, data, path, _optimize=-1):
if _could_be_hy_src(path): if _could_be_hy_src(path):
source = data.decode("utf-8") source = data.decode("utf-8")
hy_tree = hy_parse(source, filename=path) hy_tree = hy_parse(source, filename=path)
with loader_module_obj(self) as module: with loader_module_obj(self) as module:
data = hy_compile(hy_tree, 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 # This is actually needed; otherwise, pre-created finders assigned to the
# current dir (i.e. `''`) in `sys.path` will not catch absolute imports of # current dir (i.e. `''`) in `sys.path` will not catch absolute imports of
# directory-local modules! # directory-local modules!
sys.path_importer_cache.clear() sys.path_importer_cache.clear()
# Do this one just in case? # Do this one just in case?
importlib.invalidate_caches() 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('<I', f.read(4))[0]
if t == t_py:
self.code = marshal.load(f)
if self.code is None:
# There's no existing bytecode, or bytecode timestamp
# is older than the source file's.
self.code = self.byte_compile_hy(fullname)
if self.code is None:
super(HyLoader, self).get_code(fullname=fullname)
return self.code
def _get_delegate(self):
return HyImporter(self.filename).find_module('__init__')
class HyImporter(ImpImporter, object):
def __init__(self, path=None):
# We need to be strict about the types of files this importer will
# handle. To start, if the path is not the current directory in
# (represented by '' in `sys.path`), then it must be a supported
# file type or a directory. If it isn't, this importer is not
# suitable: throw an exception.
if path == '' or os.path.isdir(path) or (
os.path.isfile(path) and path.endswith('.hy')):
self.path = path
else:
raise ImportError('Invalid path: {}'.format(path))
def find_loader(self, fullname):
return self.find_module(fullname, path=None)
def find_module(self, fullname, path=None):
subname = fullname.split(".")[-1]
if subname != fullname and self.path is None:
return None
if self.path is None:
path = None
else:
path = [os.path.realpath(self.path)]
fileobj, file_path, etc = None, None, None
# The following are excerpts from the later pure Python
# implementations of the `imp` module (e.g. in Python 3.6).
if path is None:
path = sys.path
for entry in path:
if (os.path.isfile(entry) and subname == '__main__' and
entry.endswith('.hy')):
file_path = entry
fileobj = io.open(file_path, 'rU', encoding='utf-8')
etc = ('.hy', 'U', imp.PY_SOURCE)
break
else:
file_path = os.path.join(entry, subname)
path_init = os.path.join(file_path, '__init__.hy')
if os.path.isfile(path_init):
fileobj = None
etc = ('', '', imp.PKG_DIRECTORY)
break
file_path = file_path + '.hy'
if os.path.isfile(file_path):
fileobj = io.open(file_path, 'rU', encoding='utf-8')
etc = ('.hy', 'U', imp.PY_SOURCE)
break
else:
try:
fileobj, file_path, etc = imp.find_module(subname, path)
except (ImportError, IOError):
return None
return HyLoader(fullname, file_path, fileobj, etc)
sys.path_hooks.append(HyImporter)
sys.path_importer_cache.clear()
_py_compile_compile = py_compile.compile
def hyc_compile(file_or_code, cfile=None, dfile=None, doraise=False,
module=None):
"""Write a Hy file, or code object, to pyc.
This is a patched version of Python 2.7's `py_compile.compile`.
Also, it tries its best to write the bytecode file atomically.
Parameters
----------
file_or_code : str or instance of `types.CodeType`
A filename for a Hy or Python source file or its corresponding code
object.
cfile : str, optional
The filename to use for the bytecode file. If `None`, use the
standard bytecode filename determined by `cache_from_source`.
dfile : str, optional
The filename to use for compile-time errors.
doraise : bool, default False
If `True` raise compilation exceptions; otherwise, ignore them.
module : str or types.ModuleType, optional
The module, or module name, in which the Hy tree is expanded.
Default is the caller's module.
Returns
-------
out : str
The resulting bytecode file name. Python 3.x returns this, but
Python 2.7 doesn't; this function does for convenience.
"""
if isinstance(file_or_code, types.CodeType):
codeobject = file_or_code
filename = codeobject.co_filename
else:
filename = file_or_code
with open(filename, 'rb') as f:
source_str = f.read().decode('utf-8')
try:
flags = None
if _could_be_hy_src(filename):
hy_tree = hy_parse(source_str, filename=filename)
if module is None:
module = inspect.getmodule(inspect.stack()[1][0])
elif not inspect.ismodule(module):
module = importlib.import_module(module)
source = hy_compile(hy_tree, module)
flags = hy_ast_compile_flags
codeobject = compile(source, dfile or filename, 'exec', flags)
except Exception as err:
py_exc = py_compile.PyCompileError(err.__class__, err,
dfile or filename)
if doraise:
raise py_exc
else:
traceback.print_exc()
return
timestamp = long(os.stat(filename).st_mtime)
if cfile is None:
cfile = cache_from_source(filename)
f = None
try:
f = tempfile.NamedTemporaryFile('wb', dir=os.path.split(cfile)[0],
delete=False)
f.write('\0\0\0\0')
f.write(struct.pack('<I', timestamp))
f.write(marshal.dumps(codeobject))
f.flush()
f.seek(0, 0)
f.write(imp.get_magic())
# Make sure it's written to disk.
f.flush()
os.fsync(f.fileno())
f.close()
# Rename won't replace an existing dest on Windows.
if os.name == 'nt' and os.path.isfile(cfile):
os.unlink(cfile)
os.rename(f.name, cfile)
except OSError:
try:
if f is not None:
os.unlink(f.name)
except OSError:
pass
return cfile
py_compile.compile = hyc_compile
# XXX: These aren't truly cross-compliant.
# They're useful for testing, though.
HyImporter = importlib.machinery.FileFinder
HyLoader = importlib.machinery.SourceFileLoader
# We create a separate version of runpy, "runhy", that prefers Hy source over # We create a separate version of runpy, "runhy", that prefers Hy source over
# Python. # Python.

View File

@ -4,11 +4,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import keyword
import re import re
import sys import sys
import unicodedata import unicodedata
from hy._compat import str_type, isidentifier, UCS4
from hy.lex.exceptions import PrematureEndOfInput, LexException # NOQA from hy.lex.exceptions import PrematureEndOfInput, LexException # NOQA
from hy.models import HyExpression, HySymbol from hy.models import HyExpression, HySymbol
@ -116,7 +116,7 @@ def mangle(s):
assert s assert s
s = str_type(s) s = str(s)
s = s.replace("-", "_") s = s.replace("-", "_")
s2 = s.lstrip('_') s2 = s.lstrip('_')
leading_underscores = '_' * (len(s) - len(s2)) leading_underscores = '_' * (len(s) - len(s2))
@ -135,7 +135,7 @@ def mangle(s):
else '{0}{1}{0}'.format(mangle_delim, else '{0}{1}{0}'.format(mangle_delim,
unicodedata.name(c, '').lower().replace('-', 'H').replace(' ', '_') unicodedata.name(c, '').lower().replace('-', 'H').replace(' ', '_')
or 'U{}'.format(unicode_char_to_hex(c))) or 'U{}'.format(unicode_char_to_hex(c)))
for c in unicode_to_ucs4iter(s)) for c in s)
s = leading_underscores + s s = leading_underscores + s
assert isidentifier(s) assert isidentifier(s)
@ -147,7 +147,7 @@ def unmangle(s):
form. This may not round-trip, because different Hy symbol names can form. This may not round-trip, because different Hy symbol names can
mangle to the same Python identifier.""" mangle to the same Python identifier."""
s = str_type(s) s = str(s)
s2 = s.lstrip('_') s2 = s.lstrip('_')
leading_underscores = len(s) - len(s2) leading_underscores = len(s) - len(s2)
@ -168,19 +168,6 @@ def unmangle(s):
return '-' * leading_underscores + 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=""): def read(from_file=sys.stdin, eof=""):
"""Read from input and returns a tokenized string. """Read from input and returns a tokenized string.
@ -203,4 +190,12 @@ def read(from_file=sys.stdin, eof=""):
def read_str(input): def read_str(input):
return read(StringIO(str_type(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()

View File

@ -9,7 +9,6 @@ from functools import wraps
from rply import ParserGenerator from rply import ParserGenerator
from hy._compat import str_type
from hy.models import (HyBytes, HyComplex, HyDict, HyExpression, HyFloat, from hy.models import (HyBytes, HyComplex, HyDict, HyExpression, HyFloat,
HyInteger, HyKeyword, HyList, HySet, HyString, HySymbol) HyInteger, HyKeyword, HyList, HySet, HyString, HySymbol)
from .lexer import lexer 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), raise LexException.from_lexer("Can't convert {} to a HyString".format(p[0].value),
state, p[0]) state, p[0])
return (HyString(s, is_format = is_format) return (HyString(s, is_format = is_format)
if isinstance(s, str_type) if isinstance(s, str)
else HyBytes(s)) else HyBytes(s))

View File

@ -9,7 +9,7 @@ import traceback
from contextlib import contextmanager from contextlib import contextmanager
from hy._compat import PY3, string_types, reraise, rename_function from hy._compat import reraise, PY38
from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value
from hy.lex import mangle from hy.lex import mangle
from hy.errors import (HyLanguageError, HyMacroExpansionError, HyTypeError, from hy.errors import (HyLanguageError, HyMacroExpansionError, HyTypeError,
@ -74,9 +74,6 @@ def tag(name):
def _(fn): def _(fn):
_name = mangle('#{}'.format(name)) _name = mangle('#{}'.format(name))
if not PY3:
_name = _name.encode('UTF-8')
fn = rename_function(fn, _name) fn = rename_function(fn, _name)
module = inspect.getmodule(fn) module = inspect.getmodule(fn)
@ -156,7 +153,7 @@ def require(source_module, target_module, assignments, prefix=""):
parent_frame = inspect.stack()[1][0] parent_frame = inspect.stack()[1][0]
target_namespace = parent_frame.f_globals target_namespace = parent_frame.f_globals
target_module = target_namespace.get('__name__', None) 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_module = importlib.import_module(target_module)
target_namespace = target_module.__dict__ target_namespace = target_module.__dict__
elif inspect.ismodule(target_module): elif inspect.ismodule(target_module):
@ -384,3 +381,25 @@ def tag_macroexpand(tag, tree, module):
expr.module = inspect.getmodule(tag_macro) expr.module = inspect.getmodule(tag_macro)
return replace_hy_obj(expr, tree) 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', 'posonlyargcount', 'kwonlyargcount', 'nlocals', 'stacksize',
'flags', 'code', 'consts', 'names', 'varnames', 'filename', 'name',
'firstlineno', 'lnotab', 'freevars', 'cellvars']
if not PY38:
code_obj_args.remove("posonlyargcount")

View File

@ -6,7 +6,6 @@ from __future__ import unicode_literals
from contextlib import contextmanager from contextlib import contextmanager
from math import isnan, isinf from math import isnan, isinf
from hy import _initialize_env_var 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 hy.errors import HyWrapperError
from fractions import Fraction from fractions import Fraction
from clint.textui import colored from clint.textui import colored
@ -88,7 +87,7 @@ def repr_indent(obj):
return repr(obj).replace("\n", "\n ") 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 Generic Hy String object. Helpful to store string literals from Hy
scripts. It's either a ``str`` or a ``unicode``, depending on the scripts. It's either a ``str`` or a ``unicode``, depending on the
@ -100,20 +99,20 @@ class HyString(HyObject, str_type):
value.brackets = brackets value.brackets = brackets
return value 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 Generic Hy Bytes object. It's either a ``bytes`` or a ``str``, depending
on the Python version. on the Python version.
""" """
pass pass
_wrappers[bytes_type] = HyBytes _wrappers[bytes] = HyBytes
class HySymbol(HyObject, str_type): class HySymbol(HyObject, str):
""" """
Hy Symbol. Basically a string. 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 # Don't strip a _ or , if it's the first character, as _42 and
# ,42 aren't valid numbers # ,42 aren't valid numbers
return (number[0] + number[1:].replace("_", "").replace(",", "") 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) else number)
class HyInteger(HyObject, long_type): class HyInteger(HyObject, int):
""" """
Internal representation of a Hy Integer. May raise a ValueError as if 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 int(foo) was called, given HyInteger(foo).
be used instead
""" """
def __new__(cls, number, *args, **kwargs): def __new__(cls, number, *args, **kwargs):
if isinstance(number, string_types): if isinstance(number, str):
number = strip_digit_separators(number) number = strip_digit_separators(number)
bases = {"0x": 16, "0o": 8, "0b": 2} bases = {"0x": 16, "0o": 8, "0b": 2}
for leader, base in bases.items(): for leader, base in bases.items():
if number.startswith(leader): if number.startswith(leader):
# We've got a string, known leader, set base. # We've got a string, known leader, set base.
number = long_type(number, base=base) number = int(number, base=base)
break break
else: else:
# We've got a string, no known leader; base 10. # We've got a string, no known leader; base 10.
number = long_type(number, base=10) number = int(number, base=10)
else: else:
# We've got a non-string; convert straight. # We've got a non-string; convert straight.
number = long_type(number) number = int(number)
return super(HyInteger, cls).__new__(cls, number) return super(HyInteger, cls).__new__(cls, number)
_wrappers[int] = HyInteger _wrappers[int] = HyInteger
if not PY3: # do not add long on python3
_wrappers[long_type] = HyInteger
def check_inf_nan_cap(arg, value): 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: if isinf(value) and "i" in arg.lower() and "Inf" not in arg:
raise ValueError('Inf must be capitalized as "Inf"') raise ValueError('Inf must be capitalized as "Inf"')
if isnan(value) and "NaN" not in arg: if isnan(value) and "NaN" not in arg:
@ -233,7 +229,7 @@ class HyComplex(HyObject, complex):
""" """
def __new__(cls, real, imag=0, *args, **kwargs): def __new__(cls, real, imag=0, *args, **kwargs):
if isinstance(real, string_types): if isinstance(real, str):
value = super(HyComplex, cls).__new__( value = super(HyComplex, cls).__new__(
cls, strip_digit_separators(real) cls, strip_digit_separators(real)
) )

View File

@ -80,8 +80,6 @@ setup(
"Operating System :: OS Independent", "Operating System :: OS Independent",
"Programming Language :: Lisp", "Programming Language :: Lisp",
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.6",

View File

@ -10,7 +10,7 @@ from hy.compiler import hy_compile, hy_eval
from hy.errors import HyCompileError, HyLanguageError, HyError from hy.errors import HyCompileError, HyLanguageError, HyError
from hy.lex import hy_parse from hy.lex import hy_parse
from hy.lex.exceptions import LexException, PrematureEndOfInput from hy.lex.exceptions import LexException, PrematureEndOfInput
from hy._compat import PY3, PY36 from hy._compat import PY36
import ast import ast
import pytest import pytest
@ -121,9 +121,8 @@ def test_ast_good_raise():
can_compile("(raise e)") can_compile("(raise e)")
if PY3: def test_ast_raise_from():
def test_ast_raise_from(): can_compile("(raise Exception :from NameError)")
can_compile("(raise Exception :from NameError)")
def test_ast_bad_raise(): def test_ast_bad_raise():
@ -205,16 +204,16 @@ def test_ast_bad_global():
cant_compile("(global (foo))") cant_compile("(global (foo))")
if PY3: def test_ast_good_nonlocal():
def test_ast_good_nonlocal(): "Make sure AST can compile valid nonlocal"
"Make sure AST can compile valid nonlocal" can_compile("(nonlocal a)")
can_compile("(nonlocal a)") can_compile("(nonlocal foo bar)")
can_compile("(nonlocal foo bar)")
def test_ast_bad_nonlocal():
"Make sure AST can't compile invalid nonlocal" def test_ast_bad_nonlocal():
cant_compile("(nonlocal)") "Make sure AST can't compile invalid nonlocal"
cant_compile("(nonlocal (foo))") cant_compile("(nonlocal)")
cant_compile("(nonlocal (foo))")
def test_ast_good_defclass(): def test_ast_good_defclass():
@ -226,7 +225,6 @@ def test_ast_good_defclass():
can_compile("(defclass a [] None (print \"foo\"))") 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(): def test_ast_good_defclass_with_metaclass():
"Make sure AST can compile valid defclass with keywords" "Make sure AST can compile valid defclass with keywords"
can_compile("(defclass a [:metaclass b])") can_compile("(defclass a [:metaclass b])")
@ -299,21 +297,6 @@ import a dotted name."""
cant_compile("(require [spam [foo.bar]])") 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(): def test_ast_good_get():
"Make sure AST can compile valid get" "Make sure AST can compile valid get"
can_compile("(get x y)") can_compile("(get x y)")
@ -454,29 +437,20 @@ def test_lambda_list_keywords_kwargs():
def test_lambda_list_keywords_kwonly(): 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))" kwonly_demo = "(fn [&kwonly a [b 2]] (print 1) (print a b))"
if PY3: code = can_compile(kwonly_demo)
code = can_compile(kwonly_demo) for i, kwonlyarg_name in enumerate(('a', 'b')):
for i, kwonlyarg_name in enumerate(('a', 'b')): assert kwonlyarg_name == code.body[0].args.kwonlyargs[i].arg
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[0] is None assert code.body[0].args.kw_defaults[1].n == 2
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"
def test_lambda_list_keywords_mixed(): def test_lambda_list_keywords_mixed():
""" Ensure we can mix them up.""" """ Ensure we can mix them up."""
can_compile("(fn [x &rest xs &kwargs kw] (list x xs kw))") can_compile("(fn [x &rest xs &kwargs kw] (list x xs kw))")
cant_compile("(fn [x &rest xs &fasfkey {bar \"baz\"}])") cant_compile("(fn [x &rest xs &fasfkey {bar \"baz\"}])")
if PY3: can_compile("(fn [x &rest xs &kwonly kwoxs &kwargs kwxs]"
can_compile("(fn [x &rest xs &kwonly kwoxs &kwargs kwxs]" " (list x xs kwxs kwoxs))")
" (list x xs kwxs kwoxs))")
def test_missing_keyword_argument_value(): def test_missing_keyword_argument_value():
@ -504,11 +478,11 @@ def test_ast_unicode_strings():
def test_ast_unicode_vs_bytes(): def test_ast_unicode_vs_bytes():
assert s('"hello"') == u"hello" assert s('"hello"') == "hello"
assert type(s('"hello"')) is (str if PY3 else unicode) # noqa assert type(s('"hello"')) is str
assert s('b"hello"') == (eval('b"hello"') if PY3 else "hello") assert s('b"hello"') == b"hello"
assert type(s('b"hello"')) is (bytes if PY3 else str) assert type(s('b"hello"')) is bytes
assert s('b"\\xa0"') == (bytes([160]) if PY3 else chr(160)) assert s('b"\\xa0"') == bytes([160])
@pytest.mark.skipif(not PY36, reason='f-strings require Python 3.6+') @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'#[my delim[fizzle]my delim]') == 'fizzle'
assert s(r'#[[]]') == '' assert s(r'#[[]]') == ''
assert s(r'#[my delim[]my delim]') == '' 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'#[X[raw\nstring]X]') == 'raw\\nstring'
assert s(r'#[foozle[aa foozli bb ]foozle]') == 'aa foozli bb ' assert s(r'#[foozle[aa foozli bb ]foozle]') == 'aa foozli bb '
assert s(r'#[([unbalanced](]') == 'unbalanced' assert s(r'#[([unbalanced](]') == 'unbalanced'
@ -623,30 +597,6 @@ def test_lots_of_comment_lines():
can_compile(1000 * ";\n") 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(): def test_compiler_macro_tag_try():
"""Check that try forms within defmacro/deftag are compiled correctly""" """Check that try forms within defmacro/deftag are compiled correctly"""
# https://github.com/hylang/hy/issues/1350 # 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)) `())") can_compile("(deftag foo [] (try None (except [] None)) `())")
@pytest.mark.skipif(not PY3, reason="Python 3 required")
def test_ast_good_yield_from(): def test_ast_good_yield_from():
"Make sure AST can compile valid yield-from" "Make sure AST can compile valid yield-from"
can_compile("(yield-from [1 2])") can_compile("(yield-from [1 2])")
@pytest.mark.skipif(not PY3, reason="Python 3 required")
def test_ast_bad_yield_from(): def test_ast_bad_yield_from():
"Make sure AST can't compile invalid yield-from" "Make sure AST can't compile invalid yield-from"
cant_compile("(yield-from)") cant_compile("(yield-from)")

View File

@ -6,7 +6,6 @@ import ast
from hy import compiler from hy import compiler
from hy.models import HyExpression, HyList, HySymbol, HyInteger from hy.models import HyExpression, HyList, HySymbol, HyInteger
from hy._compat import PY3
def make_expression(*args): def make_expression(*args):
@ -64,12 +63,5 @@ def test_compiler_yield_return():
assert len(body) == 2 assert len(body) == 2
assert isinstance(body[0], ast.Expr) assert isinstance(body[0], ast.Expr)
assert isinstance(body[0].value, ast.Yield) assert isinstance(body[0].value, ast.Yield)
assert isinstance(body[1], ast.Return)
if PY3: assert isinstance(body[1].value, ast.BinOp)
# 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)

View File

@ -18,7 +18,7 @@ from hy.lex import hy_parse
from hy.errors import HyLanguageError from hy.errors import HyLanguageError
from hy.lex.exceptions import PrematureEndOfInput from hy.lex.exceptions import PrematureEndOfInput
from hy.compiler import hy_eval, hy_compile from hy.compiler import hy_eval, hy_compile
from hy.importer import HyLoader, cache_from_source from hy.importer import HyLoader
try: try:
from importlib import reload from importlib import reload
@ -101,7 +101,7 @@ def test_import_autocompiles():
f.write(b'(defn pyctest [s] (+ "X" s "Y"))') f.write(b'(defn pyctest [s] (+ "X" s "Y"))')
f.flush() f.flush()
pyc_path = cache_from_source(f.name) pyc_path = importlib.util.cache_from_source(f.name)
try: try:
os.remove(pyc_path) os.remove(pyc_path)
@ -144,7 +144,7 @@ def test_reload():
def unlink(filename): def unlink(filename):
os.unlink(source) os.unlink(source)
bytecode = cache_from_source(source) bytecode = importlib.util.cache_from_source(source)
if os.path.isfile(bytecode): if os.path.isfile(bytecode):
os.unlink(bytecode) os.unlink(bytecode)

View File

@ -1,7 +1,6 @@
(import (import
types types
pytest pytest)
[hy._compat [PY3]])
(defn test-comprehension-types [] (defn test-comprehension-types []
@ -134,8 +133,7 @@
; An `lfor` that gets compiled to a real comprehension ; An `lfor` that gets compiled to a real comprehension
(setv x 0) (setv x 0)
(assert (= (lfor x [1 2 3] (inc x)) [2 3 4])) (assert (= (lfor x [1 2 3] (inc x)) [2 3 4]))
(assert (= x (if PY3 0 3))) (assert (= x 0))
; Python 2 list comprehensions leak their variables.
; An `lfor` that gets compiled to a loop ; An `lfor` that gets compiled to a loop
(setv x 0 l []) (setv x 0 l [])

View File

@ -3,7 +3,7 @@
;; license. See the LICENSE. ;; license. See the LICENSE.
(import (import
[hy._compat [PY3 PY36 PY37]] [hy._compat [PY36 PY37]]
[math [isnan]] [math [isnan]]
[hy.contrib.hy-repr [hy-repr hy-repr-register]]) [hy.contrib.hy-repr [hy-repr hy-repr-register]])
@ -79,10 +79,10 @@
(assert (is (type (get orig 1)) float)) (assert (is (type (get orig 1)) float))
(assert (is (type (get result 1)) HyFloat))) (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 (.keys {1 2})) "(dict-keys [1])"))
(assert (= (hy-repr (.values {1 2})) "(dict-values [2])")) (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 [] (defn test-datetime []
(import [datetime :as D]) (import [datetime :as D])
@ -91,9 +91,8 @@
"(datetime.datetime 2009 1 15 15 27 5)")) "(datetime.datetime 2009 1 15 15 27 5)"))
(assert (= (hy-repr (D.datetime 2009 1 15 15 27 5 123)) (assert (= (hy-repr (D.datetime 2009 1 15 15 27 5 123))
"(datetime.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))
(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)"))
"(datetime.datetime 2009 1 15 15 27 5 123 :tzinfo datetime.timezone.utc)")))
(when PY36 (when PY36
(assert (= (hy-repr (D.datetime 2009 1 15 15 27 5 :fold 1)) (assert (= (hy-repr (D.datetime 2009 1 15 15 27 5 :fold 1))
"(datetime.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 [] (defn test-collections []
(import collections) (import collections)
(assert (= (hy-repr (collections.defaultdict :a 8)) (assert (= (hy-repr (collections.defaultdict :a 8))
(if PY3 "(defaultdict None {\"a\" 8})"))
"(defaultdict None {\"a\" 8})"
"(defaultdict None {b\"a\" 8})")))
(assert (= (hy-repr (collections.defaultdict int :a 8)) (assert (= (hy-repr (collections.defaultdict int :a 8))
(if PY3 "(defaultdict <class 'int'> {\"a\" 8})"))
"(defaultdict <class 'int'> {\"a\" 8})"
"(defaultdict <type 'int'> {b\"a\" 8})")))
(assert (= (hy-repr (collections.Counter [15 15 15 15])) (assert (= (hy-repr (collections.Counter [15 15 15 15]))
(if PY3 "(Counter {15 4})"))
"(Counter {15 4})"
"(Counter {15 (int 4)})")))
(setv C (collections.namedtuple "Fooey" ["cd" "a_b"])) (setv C (collections.namedtuple "Fooey" ["cd" "a_b"]))
(assert (= (hy-repr (C 11 12)) (assert (= (hy-repr (C 11 12))
"(Fooey :cd 11 :a_b 12)"))) "(Fooey :cd 11 :a_b 12)")))
@ -155,9 +148,8 @@
(setv mo (re.search "b+" "aaaabbbccc")) (setv mo (re.search "b+" "aaaabbbccc"))
(assert (= (hy-repr mo) (assert (= (hy-repr mo)
(.format (.format
#[[<{} object; :span {} :match "bbb">]] #[[<{} object; :span (, 4 7) :match "bbb">]]
(if PY37 "re.Match" (+ (. (type mo) __module__) ".SRE_Match")) (if PY37 "re.Match" (+ (. (type mo) __module__) ".SRE_Match"))))))
(if PY3 "(, 4 7)" "(, (int 4) (int 7))")))))
(defn test-hy-repr-custom [] (defn test-hy-repr-custom []

View File

@ -2,8 +2,6 @@
;; This file is part of Hy, which is free software licensed under the Expat ;; This file is part of Hy, which is free software licensed under the Expat
;; license. See the LICENSE. ;; license. See the LICENSE.
(import [hy._compat [PY3]])
;;;; some simple helpers ;;;; some simple helpers
(defn assert-true [x] (defn assert-true [x]
@ -287,7 +285,7 @@ result['y in globals'] = 'y' in globals()")
(setv s3 (gensym "xx")) (setv s3 (gensym "xx"))
(assert (= 0 (.find s2 "_xx\uffff"))) (assert (= 0 (.find s2 "_xx\uffff")))
(assert (not (= s2 s3))) (assert (not (= s2 s3)))
(assert (not (= (string s2) (string s3))))) (assert (not (= (str s2) (str s3)))))
(defn test-identity [] (defn test-identity []
"NATIVE: testing the identity function" "NATIVE: testing the identity function"
@ -324,8 +322,8 @@ result['y in globals'] = 'y' in globals()")
(assert-true (integer? 0)) (assert-true (integer? 0))
(assert-true (integer? 3)) (assert-true (integer? 3))
(assert-true (integer? -3)) (assert-true (integer? -3))
(assert-true (integer? (integer "-3"))) (assert-true (integer? (int "-3")))
(assert-true (integer? (integer 3))) (assert-true (integer? (int 3)))
(assert-false (integer? 4.2)) (assert-false (integer? 4.2))
(assert-false (integer? None)) (assert-false (integer? None))
(assert-false (integer? "foo"))) (assert-false (integer? "foo")))
@ -334,7 +332,7 @@ result['y in globals'] = 'y' in globals()")
"NATIVE: testing the integer-char? function" "NATIVE: testing the integer-char? function"
(assert-true (integer-char? "1")) (assert-true (integer-char? "1"))
(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? "foo"))
(assert-false (integer-char? None))) (assert-false (integer-char? None)))
@ -429,8 +427,7 @@ result['y in globals'] = 'y' in globals()")
(assert-true (neg? -2)) (assert-true (neg? -2))
(assert-false (neg? 1)) (assert-false (neg? 1))
(assert-false (neg? 0)) (assert-false (neg? 0))
(when PY3 (assert-requires-num neg?))
(assert-requires-num neg?)))
(defn test-zero [] (defn test-zero []
"NATIVE: testing the zero? function" "NATIVE: testing the zero? function"
@ -519,8 +516,7 @@ result['y in globals'] = 'y' in globals()")
(assert-true (pos? 2)) (assert-true (pos? 2))
(assert-false (pos? -1)) (assert-false (pos? -1))
(assert-false (pos? 0)) (assert-false (pos? 0))
(when PY3 (assert-requires-num pos?))
(assert-requires-num pos?)))
(defn test-remove [] (defn test-remove []
"NATIVE: testing the remove function" "NATIVE: testing the remove function"

View File

@ -2,13 +2,12 @@
;; This file is part of Hy, which is free software licensed under the Expat ;; This file is part of Hy, which is free software licensed under the Expat
;; license. See the LICENSE. ;; license. See the LICENSE.
(import [hy.extra.reserved [names]] [hy._compat [PY3]]) (import [hy.extra.reserved [names]])
(defn test-reserved [] (defn test-reserved []
(assert (is (type (names)) frozenset)) (assert (is (type (names)) frozenset))
(assert (in "and" (names))) (assert (in "and" (names)))
(when PY3 (assert (in "False" (names)))
(assert (in "False" (names))))
(assert (in "pass" (names))) (assert (in "pass" (names)))
(assert (in "class" (names))) (assert (in "class" (names)))
(assert (in "defclass" (names))) (assert (in "defclass" (names)))

View File

@ -11,7 +11,7 @@
pytest) pytest)
(import sys) (import sys)
(import [hy._compat [PY3 PY37 PY38]]) (import [hy._compat [PY38]])
(defn test-sys-argv [] (defn test-sys-argv []
"NATIVE: test sys.argv" "NATIVE: test sys.argv"
@ -71,13 +71,12 @@
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) (except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(try (eval '(defn None [] (print "hello"))) (try (eval '(defn None [] (print "hello")))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) (except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(when PY3 (try (eval '(setv False 1))
(try (eval '(setv False 1)) (except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) (try (eval '(setv True 0))
(try (eval '(setv True 0)) (except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))) (try (eval '(defn True [] (print "hello")))
(try (eval '(defn True [] (print "hello"))) (except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))))
(defn test-setv-pairs [] (defn test-setv-pairs []
@ -513,9 +512,7 @@
(setv passed False) (setv passed False)
(try (try
(raise) (raise)
;; Python 2 raises IndexError here (due to the previous test) (except [RuntimeError]
;; Python 3 raises RuntimeError
(except [[IndexError RuntimeError]]
(setv passed True))) (setv passed True)))
(assert passed) (assert passed)
@ -747,16 +744,11 @@
(defn test-yield-with-return [] (defn test-yield-with-return []
"NATIVE: test yield with return" "NATIVE: test yield with return"
(defn gen [] (yield 3) "goodbye") (defn gen [] (yield 3) "goodbye")
(if PY3 (setv gg (gen))
(do (setv gg (gen)) (assert (= 3 (next gg)))
(assert (= 3 (next gg))) (try (next gg)
(try (next gg) (except [e StopIteration] (assert (hasattr e "value"))
(except [e StopIteration] (assert (hasattr e "value")) (assert (= (getattr e "value") "goodbye")))))
(assert (= (getattr e "value") "goodbye")))))
(do (setv gg (gen))
(assert (= 3 (next gg)))
(try (next gg)
(except [e StopIteration] (assert (not (hasattr e "value"))))))))
(defn test-yield-in-try [] (defn test-yield-in-try []
@ -1242,19 +1234,14 @@ cee\"} dee" "ey bee\ncee dee"))
; Conversion characters and format specifiers ; Conversion characters and format specifiers
(setv p:9 "other") (setv p:9 "other")
(setv !r "bar") (setv !r "bar")
(defn u [s] (assert (= f"a{p !r}" "a'xyzzy'"))
; 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 :9}" "axyzzy ")) (assert (= f"a{p :9}" "axyzzy "))
(assert (= f"a{p:9}" "aother")) (assert (= f"a{p:9}" "aother"))
(assert (= f"a{p !r :9}" (u "a'xyzzy' "))) (assert (= f"a{p !r :9}" "a'xyzzy' "))
(assert (= f"a{p !r:9}" (u "a'xyzzy' "))) (assert (= f"a{p !r:9}" "a'xyzzy' "))
(assert (= f"a{p:9 :9}" "aother ")) (assert (= f"a{p:9 :9}" "aother "))
(assert (= f"a{!r}" "abar")) (assert (= f"a{!r}" "abar"))
(assert (= f"a{!r !r}" (u "a'bar'"))) (assert (= f"a{!r !r}" "a'bar'"))
; Fun with `r` ; Fun with `r`
(assert (= f"hello {r\"\\n\"}" r"hello \n")) (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]")) (assert (= f"{(C) : {(str (+ 1 1)) !r :x<5}}" "C[ '2'xx]"))
; Format bracket strings ; 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] (assert (= #[f-string[result: {value :{width}.{precision}}]f-string]
"result: 12.34"))) "result: 12.34")))
@ -1482,11 +1469,6 @@ cee\"} dee" "ey bee\ncee dee"))
(assert (= y [5]))) (assert (= y [5])))
(defn test-string []
(assert (string? (string "a")))
(assert (string? (string 1)))
(assert (= u"unicode" (string "unicode"))))
(defn test-del [] (defn test-del []
"NATIVE: Test the behavior of del" "NATIVE: Test the behavior of del"
(setv foo 42) (setv foo 42)
@ -1549,17 +1531,12 @@ cee\"} dee" "ey bee\ncee dee"))
(defn test-disassemble [] (defn test-disassemble []
"NATIVE: Test the disassemble function" "NATIVE: Test the disassemble function"
(assert (= (disassemble '(do (leaky) (leaky) (macros))) (cond (assert (= (disassemble '(do (leaky) (leaky) (macros)))
[PY3 (.format "Module( (.format "Module(
body=[Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])), body=[Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])), Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
Expr(value=Call(func=Name(id='macros'), args=[], keywords=[]))]{})" Expr(value=Call(func=Name(id='macros'), args=[], keywords=[]))]{})"
(if PY38 ",\n type_ignores=[]" ""))] (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))])"])))
(assert (= (disassemble '(do (leaky) (leaky) (macros)) True) (assert (= (disassemble '(do (leaky) (leaky) (macros)) True)
"leaky() "leaky()
leaky() leaky()
@ -1597,9 +1574,7 @@ macros()
(defn test-read [] (defn test-read []
"NATIVE: test that read takes something for stdin and reads" "NATIVE: test that read takes something for stdin and reads"
(if-python2 (import [io [StringIO]])
(import [StringIO [StringIO]])
(import [io [StringIO]]))
(import [hy.models [HyExpression]]) (import [hy.models [HyExpression]])
(setv stdin-buffer (StringIO "(+ 2 2)\n(- 2 2)")) (setv stdin-buffer (StringIO "(+ 2 2)\n(- 2 2)"))
@ -1716,3 +1691,204 @@ macros()
"Make sure relative imports work properly" "Make sure relative imports work properly"
(import [..resources [tlib]]) (import [..resources [tlib]])
(assert (= tlib.SECRET-MESSAGE "Hello World"))) (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))))))

View File

@ -3,9 +3,6 @@
;; license. See the LICENSE. ;; license. See the LICENSE.
(import [hy._compat [PY3]])
(defn test-hyphen [] (defn test-hyphen []
(setv a-b 1) (setv a-b 1)
(assert (= a-b 1)) (assert (= a-b 1))
@ -63,9 +60,7 @@
(defn test-higher-unicode [] (defn test-higher-unicode []
(setv 😂 "emoji") (setv 😂 "emoji")
(assert (= 😂 "emoji")) (assert (= 😂 "emoji"))
(if PY3 (assert (= hyx_Xface_with_tears_of_joyX "emoji")))
(assert (= hyx_Xface_with_tears_of_joyX "emoji"))
(assert (= hyx_XU1f602X "emoji"))))
(defn test-nameless-unicode [] (defn test-nameless-unicode []

View File

@ -2,8 +2,6 @@
;; This file is part of Hy, which is free software licensed under the Expat ;; This file is part of Hy, which is free software licensed under the Expat
;; license. See the LICENSE. ;; license. See the LICENSE.
(import [hy._compat [PY3]])
(setv square (fn [x] (setv square (fn [x]
(* x x))) (* x x)))
@ -191,20 +189,12 @@
(defn test-matmul [] (defn test-matmul []
"NATIVE: test matrix multiplication" "NATIVE: test matrix multiplication"
(if PY3 (assert (= (@ first-test-matrix second-test-matrix)
(assert (= (@ first-test-matrix second-test-matrix) product-of-test-matrices)))
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)))))
(defn test-augassign-matmul [] (defn test-augassign-matmul []
"NATIVE: test augmented-assignment matrix multiplication" "NATIVE: test augmented-assignment matrix multiplication"
(setv matrix first-test-matrix (setv matrix first-test-matrix
matmul-attempt (try (@= matrix second-test-matrix) matmul-attempt (try (@= matrix second-test-matrix)
(except [e [Exception]] e))) (except [e [Exception]] e)))
(if PY3 (assert (= product-of-test-matrices matrix)))
(assert (= product-of-test-matrices matrix))
(assert (isinstance matmul-attempt NameError))))

View File

@ -140,11 +140,6 @@
(assert initialized) (assert initialized)
(assert (test-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 [] (defn test-gensym-in-macros []
(import ast) (import ast)
(import [astor.code-gen [to-source]]) (import [astor.code-gen [to-source]])
@ -409,9 +404,9 @@ in expansions."
Additionally, we confirm that `require` statements are executed via loaded bytecode." Additionally, we confirm that `require` statements are executed via loaded bytecode."
(import os sys marshal types) (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.realpath
(os.path.join (os.path.join
"tests" "resources" "macro_with_require.hy")))) "tests" "resources" "macro_with_require.hy"))))

View File

@ -2,8 +2,6 @@
;; This file is part of Hy, which is free software licensed under the Expat ;; This file is part of Hy, which is free software licensed under the Expat
;; license. See the LICENSE. ;; license. See the LICENSE.
(import pytest [hy._compat [PY3]])
(defmacro op-and-shadow-test [op &rest body] (defmacro op-and-shadow-test [op &rest body]
; Creates two tests with the given `body`, one where all occurrences ; Creates two tests with the given `body`, one where all occurrences
; of the symbol `f` are syntactically replaced with `op` (a test of ; of the symbol `f` are syntactically replaced with `op` (a test of
@ -102,14 +100,14 @@
(forbid (f 1 2 3))) (forbid (f 1 2 3)))
(when PY3 (op-and-shadow-test @ (op-and-shadow-test @
(defclass C [object] [ (defclass C [object] [
__init__ (fn [self content] (setv self.content content)) __init__ (fn [self content] (setv self.content content))
__matmul__ (fn [self other] (C (+ self.content other.content)))]) __matmul__ (fn [self other] (C (+ self.content other.content)))])
(forbid (f)) (forbid (f))
(assert (do (setv c (C "a")) (is (f c) c))) (assert (do (setv c (C "a")) (is (f c) c)))
(assert (= (. (f (C "b") (C "c")) content) "bc")) (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 << (op-and-shadow-test <<

View File

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

View File

@ -8,14 +8,12 @@ import os
import re import re
import shlex import shlex
import subprocess import subprocess
import builtins
from hy.importer import cache_from_source from importlib.util import cache_from_source
from hy._compat import PY3
import pytest import pytest
from hy._compat import builtins
hy_dir = os.environ.get('HY_DIR', '') hy_dir = os.environ.get('HY_DIR', '')
@ -497,9 +495,8 @@ def test_bin_hy_tracebacks():
os.environ['HY_DEBUG'] = '' os.environ['HY_DEBUG'] = ''
def req_err(x): def req_err(x):
assert x == '{}HyRequireError: No module named {}'.format( assert (x == 'hy.errors.HyRequireError: No module named '
'hy.errors.' if PY3 else '', "'not_a_real_module'")
(repr if PY3 else str)('not_a_real_module'))
# Modeled after # Modeled after
# > python -c 'import not_a_real_module' # > python -c 'import not_a_real_module'
@ -512,7 +509,7 @@ def test_bin_hy_tracebacks():
del error_lines[-1] del error_lines[-1]
assert len(error_lines) <= 10 assert len(error_lines) <= 10
# Rough check for the internal traceback filtering # 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 = run_cmd('hy -c "(require not-a-real-module)"', expect=1)
error_lines = error.splitlines() error_lines = error.splitlines()
@ -522,7 +519,7 @@ def test_bin_hy_tracebacks():
output, error = run_cmd('hy -i "(require not-a-real-module)"') output, error = run_cmd('hy -i "(require not-a-real-module)"')
assert output.startswith('=> ') assert output.startswith('=> ')
print(error.splitlines()) print(error.splitlines())
req_err(error.splitlines()[2 if PY3 else -3]) req_err(error.splitlines()[2])
# Modeled after # Modeled after
# > python -c 'print("hi' # > python -c 'print("hi'
@ -535,9 +532,8 @@ def test_bin_hy_tracebacks():
r'Traceback \(most recent call last\):\n' r'Traceback \(most recent call last\):\n'
r' File "(?:<string>|string-[0-9a-f]+)", line 1\n' r' File "(?:<string>|string-[0-9a-f]+)", line 1\n'
r' \(print "\n' r' \(print "\n'
r' \^\n' + r' \^\n'
r'{}PrematureEndOfInput: Partial string literal\n'.format( r'hy.lex.exceptions.PrematureEndOfInput: Partial string literal\n')
r'hy\.lex\.exceptions\.' if PY3 else ''))
assert re.search(peoi_re, error) assert re.search(peoi_re, error)
# Modeled after # Modeled after

View File

@ -5,14 +5,13 @@
import copy import copy
import hy import hy
from clint.textui.colored import clean 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, from hy.models import (wrap_value, replace_hy_obj, HyString, HyInteger, HyList,
HyDict, HySet, HyExpression, HyComplex, HyFloat, pretty) HyDict, HySet, HyExpression, HyComplex, HyFloat, pretty)
def test_wrap_long_type(): def test_wrap_int():
""" Test conversion of integers.""" """ Test conversion of integers."""
wrapped = wrap_value(long_type(0)) wrapped = wrap_value(0)
assert type(wrapped) == HyInteger assert type(wrapped) == HyInteger
@ -26,27 +25,27 @@ def test_wrap_tuple():
def test_wrap_nested_expr(): def test_wrap_nested_expr():
""" Test conversion of HyExpressions with embedded non-HyObjects.""" """ 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) == HyExpression
assert type(wrapped[0]) == HyInteger assert type(wrapped[0]) == HyInteger
assert wrapped == HyExpression([HyInteger(0)]) assert wrapped == HyExpression([HyInteger(0)])
def test_replace_long_type(): def test_replace_int():
""" Test replacing integers.""" """ Test replacing integers."""
replaced = replace_hy_obj(long_type(0), HyInteger(13)) replaced = replace_hy_obj(0, HyInteger(13))
assert replaced == HyInteger(0) assert replaced == HyInteger(0)
def test_replace_string_type(): def test_replace_string_type():
"""Test replacing python string""" """Test replacing python string"""
replaced = replace_hy_obj(str_type("foo"), HyString("bar")) replaced = replace_hy_obj("foo", HyString("bar"))
assert replaced == HyString("foo") assert replaced == HyString("foo")
def test_replace_tuple(): def test_replace_tuple():
""" Test replacing tuples.""" """ 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) == HyList
assert type(replaced[0]) == HyInteger assert type(replaced[0]) == HyInteger
assert replaced == HyList([HyInteger(0)]) assert replaced == HyList([HyInteger(0)])