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
language: python
python:
- "2.7"
- "3.5"
- "3.6"
- "3.7"
- 3.8-dev
- pypy2.7-6.0
- pypy3.5-6.0
install:
- pip install -r requirements-travis.txt

View File

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

View File

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

View File

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

View File

@ -564,8 +564,6 @@ requires.
File "<input>", line 1, in <module>
TypeError: compare() missing 1 required keyword-only argument: 'keyfn'
Availability: Python 3.
&kwargs
Like ``&rest``, but for keyword arugments.
The following parameter will contain 0 or more keyword arguments.
@ -1057,7 +1055,7 @@ if / if* / if-not
``if / if* / if-not`` respect Python *truthiness*, that is, a *test* fails if it
evaluates to a "zero" (including values of ``len`` zero, ``None``, and
``False``), and passes otherwise, but values with a ``__bool__`` method
(``__nonzero__`` in Python 2) can overrides this.
can override this.
The ``if`` macro is for conditionally selecting an expression for evaluation.
The result of the selected expression becomes the result of the entire ``if``
@ -1296,19 +1294,12 @@ fact, these forms are implemented as generator functions whenever they
contain Python statements, with the attendant consequences for calling
``return``. By contrast, ``for`` shares the caller's scope.
.. note:: An exception to the above scoping rules occurs on Python 2 for
``lfor`` specifically (and not ``sfor``, ``gfor``, or ``dfor``) when
Hy can implement the ``lfor`` as a Python list comprehension. Then,
variables will leak to the surrounding scope.
nonlocal
--------
.. versionadded:: 0.11.1
**PYTHON 3.0 AND UP ONLY!**
``nonlocal`` can be used to mark a symbol as not local to the current scope.
The parameters are the names of symbols to mark as nonlocal. This is necessary
to modify variables through nested ``fn`` scopes:
@ -1693,7 +1684,7 @@ object (respectively) to provide positional or keywords arguments
=> (f #* [1 2] #** {"c" 3 "d" 4})
[1, 2, 3, 4]
With Python 3, unpacking is allowed in more contexts, and you can unpack
Unpacking is allowed in a variety of contexts, and you can unpack
more than once in one expression (:pep:`3132`, :pep:`448`).
.. code-block:: clj
@ -2038,8 +2029,6 @@ yield-from
.. versionadded:: 0.9.13
**PYTHON 3.3 AND UP ONLY!**
``yield-from`` is used to call a subgenerator. This is useful if you
want your coroutine to be able to delegate its processes to another
coroutine, say, if using something fancy like

View File

@ -240,19 +240,6 @@ otherwise ``False``. Return ``True`` if *coll* is empty.
True
.. _exec-fn:
exec
----
Equivalent to Python 3's built-in function :py:func:`exec`.
.. code-block:: clj
=> (exec "print(a + b)" {"a" 1} {"b" 2})
3
.. _float?-fn:
float?
@ -385,8 +372,7 @@ integer?
Usage: ``(integer? x)``
Returns `True` if *x* is an integer. For Python 2, this is
either ``int`` or ``long``. For Python 3, this is ``int``.
Returns `True` if *x* is an integer (``int``).
.. code-block:: hy
@ -924,7 +910,7 @@ string?
Usage: ``(string? x)``
Returns ``True`` if *x* is a string.
Returns ``True`` if *x* is a string (``str``).
.. code-block:: hy

View File

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

View File

@ -10,7 +10,7 @@ An identifier consists of a nonempty sequence of Unicode characters that are not
numeric literals
----------------
In addition to regular numbers, standard notation from Python 3 for non-base 10
In addition to regular numbers, standard notation from Python for non-base 10
integers is used. ``0x`` for Hex, ``0o`` for Octal, ``0b`` for Binary.
.. code-block:: clj
@ -60,13 +60,9 @@ Plain string literals support :ref:`a variety of backslash escapes
literally, prefix the string with ``r``, as in ``r"slash\not"``. Bracket
strings are always raw strings and don't allow the ``r`` prefix.
Whether running under Python 2 or Python 3, Hy treats all string literals as
sequences of Unicode characters by default, and allows you to prefix a plain
string literal (but not a bracket string) with ``b`` to treat it as a sequence
of bytes. So when running under Python 3, Hy translates ``"foo"`` and
``b"foo"`` to the identical Python code, but when running under Python 2,
``"foo"`` is translated to ``u"foo"`` and ``b"foo"`` is translated to
``"foo"``.
Like Python, Hy treats all string literals as sequences of Unicode characters
by default. You may prefix a plain string literal (but not a bracket string)
with ``b`` to treat it as a sequence of bytes.
Unlike Python, Hy only recognizes string prefixes (``r``, etc.) in lowercase.

View File

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

View File

@ -25,9 +25,6 @@ This is pretty cool because it means Hy is several things:
comfort of Python!
- For everyone: a pleasant language that has a lot of neat ideas!
Now this tutorial assumes you're running Hy on Python 3. So know things
are a bit different if you're still using Python 2.
Basic intro to Lisp for Pythonistas
===================================

View File

@ -2,108 +2,15 @@
# This file is part of Hy, which is free software licensed under the Expat
# license. See the LICENSE.
try:
import __builtin__ as builtins
except ImportError:
import builtins # NOQA
import sys, keyword, textwrap
import sys
PY3 = sys.version_info[0] >= 3
PY36 = sys.version_info >= (3, 6)
PY37 = sys.version_info >= (3, 7)
PY38 = sys.version_info >= (3, 8)
# The value of UCS4 indicates whether Unicode strings are stored as UCS-4.
# It is always true on Pythons >= 3.3, which use USC-4 on all systems.
UCS4 = sys.maxunicode == 0x10FFFF
str_type = str if PY3 else unicode # NOQA
bytes_type = bytes if PY3 else str # NOQA
long_type = int if PY3 else long # NOQA
string_types = str if PY3 else basestring # NOQA
#
# Inspired by the same-named `six` functions.
#
if PY3:
raise_src = textwrap.dedent('''
def raise_from(value, from_value):
raise value from from_value
''')
def reraise(exc_type, value, traceback=None):
try:
raise value.with_traceback(traceback)
finally:
traceback = None
code_obj_args = ['argcount', 'kwonlyargcount', 'nlocals', 'stacksize',
'flags', 'code', 'consts', 'names', 'varnames',
'filename', 'name', 'firstlineno', 'lnotab', 'freevars',
'cellvars']
else:
def raise_from(value, from_value=None):
raise value
raise_src = textwrap.dedent('''
def reraise(exc_type, value, traceback=None):
try:
raise exc_type, value, traceback
finally:
traceback = None
''')
code_obj_args = ['argcount', 'nlocals', 'stacksize', 'flags', 'code',
'consts', 'names', 'varnames', 'filename', 'name',
'firstlineno', 'lnotab', 'freevars', 'cellvars']
raise_code = compile(raise_src, __file__, 'exec')
exec(raise_code)
def rename_function(func, new_name):
"""Creates a copy of a function and [re]sets the name at the code-object
level.
"""
c = func.__code__
new_code = type(c)(*[getattr(c, 'co_{}'.format(a))
if a != 'name' else str(new_name)
for a in code_obj_args])
_fn = type(func)(new_code, func.__globals__, str(new_name),
func.__defaults__, func.__closure__)
_fn.__dict__.update(func.__dict__)
return _fn
def isidentifier(x):
if x in ('True', 'False', 'None', 'print'):
# `print` is special-cased here because Python 2's
# keyword.iskeyword will count it as a keyword, but we
# use the __future__ feature print_function, which makes
# it a non-keyword.
return True
if keyword.iskeyword(x):
return False
if PY3:
return x.isidentifier()
if x.rstrip() != x:
return False
import tokenize as T
from io import StringIO
def reraise(exc_type, value, traceback=None):
try:
tokens = list(T.generate_tokens(StringIO(x).readline))
except (T.TokenError, IndentationError):
return False
# Some versions of Python 2.7 (including one that made it into
# Ubuntu 18.10) have a Python 3 backport that adds a NEWLINE
# token. Remove it if it's present.
# https://bugs.python.org/issue33899
tokens = [t for t in tokens if t[0] != T.NEWLINE]
return len(tokens) == 2 and tokens[0][0] == T.NAME
try:
FileNotFoundError = FileNotFoundError
except NameError:
FileNotFoundError = IOError
raise value.with_traceback(traceback)
finally:
traceback = None

View File

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

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

View File

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

View File

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

View File

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

View File

@ -77,10 +77,3 @@
(if (not (isinstance lambda-list hy.HyList))
(macro-error name "defn/a takes a parameter list as second argument"))
`(setv ~name (fn/a ~lambda-list ~@body)))
(defmacro if-python2 [python2-form python3-form]
"If running on python2, execute python2-form, else, execute python3-form"
(import sys)
(if (< (get sys.version_info 0) 3)
python2-form
python3-form))

View File

@ -11,7 +11,7 @@
(import [fractions [Fraction :as fraction]])
(import operator) ; shadow not available yet
(import sys)
(import [hy._compat [long-type]]) ; long for python2, int for python3
(import [collections.abc :as cabc])
(import [hy.models [HySymbol HyKeyword]])
(import [hy.lex [tokenize mangle unmangle read read-str]])
(import [hy.lex.exceptions [LexException PrematureEndOfInput]])
@ -21,10 +21,6 @@
(require [hy.core.bootstrap [*]])
(if-python2
(import [collections :as cabc])
(import [collections.abc :as cabc]))
(defn butlast [coll]
"Return an iterator of all but the last item in `coll`."
(drop-last 1 coll))
@ -86,47 +82,12 @@ If the second argument `codegen` is true, generate python code instead."
(yield val)
(.add seen val)))))
(if-python2
(setv
remove itertools.ifilterfalse
zip-longest itertools.izip_longest
;; not builtin in Python3
reduce reduce
;; hy is more like Python3
filter itertools.ifilter
input raw_input
map itertools.imap
range xrange
zip itertools.izip)
(setv
remove itertools.filterfalse
zip-longest itertools.zip_longest
;; was builtin in Python2
reduce functools.reduce
;; Someone can import these directly from `hy.core.language`;
;; we'll make some duplicates.
filter filter
input input
map map
range range
zip zip))
(if-python2
(defn exec [$code &optional $globals $locals]
"Execute Python code.
The parameter names contain weird characters to discourage calling this
function with keyword arguments, which isn't supported by Python 3's `exec`."
(if
(none? $globals) (do
(setv frame (._getframe sys (int 1)))
(try
(setv $globals frame.f_globals $locals frame.f_locals)
(finally (del frame))))
(none? $locals)
(setv $locals $globals))
(exec* $code $globals $locals))
(setv exec exec))
(setv
remove itertools.filterfalse
zip-longest itertools.zip_longest
;; was builtin in Python2
reduce functools.reduce
accumulate itertools.accumulate)
;; infinite iterators
(setv
@ -152,18 +113,6 @@ function with keyword arguments, which isn't supported by Python 3's `exec`."
permutations itertools.permutations
product itertools.product)
;; also from itertools, but not in Python2, and without func option until 3.3
(defn accumulate [iterable &optional [func operator.add]]
"Accumulate `func` on `iterable`.
Return series of accumulated sums (or other binary function results)."
(setv it (iter iterable)
total (next it))
(yield total)
(for [element it]
(setv total (func total element))
(yield total)))
(defn drop [count coll]
"Drop `count` elements from `coll` and yield back the rest."
(islice coll count None))
@ -252,13 +201,9 @@ Return series of accumulated sums (or other binary function results)."
"Perform `isinstance` with reversed arguments."
(isinstance x klass))
(defn integer [x]
"Return Hy kind of integer for `x`."
(long-type x))
(defn integer? [x]
"Check if `x` is an integer."
(isinstance x (, int long-type)))
(isinstance x int))
(defn integer-char? [x]
"Check if char `x` parses as an integer."
@ -388,17 +333,9 @@ with overlap."
"Return the first logical true value of applying `pred` in `coll`, else None."
(first (filter None (map pred coll))))
(defn string [x]
"Cast `x` as the current python version's string implementation."
(if-python2
(unicode x)
(str x)))
(defn string? [x]
"Check if `x` is a string."
(if-python2
(isinstance x (, str unicode))
(isinstance x str)))
(isinstance x str))
(defn take [count coll]
"Take `count` elements from `coll`."
@ -433,7 +370,7 @@ Strings numbers and even objects with the __name__ magic will work."
(HyKeyword (unmangle value))
(try
(unmangle (.__name__ value))
(except [] (HyKeyword (string value)))))))
(except [] (HyKeyword (str value)))))))
(defn name [value]
"Convert `value` to a string.
@ -446,7 +383,7 @@ Even objects with the __name__ magic will work."
(unmangle value)
(try
(unmangle (. value __name__))
(except [] (string value))))))
(except [] (str value))))))
(defn xor [a b]
"Perform exclusive or between `a` and `b`."
@ -457,11 +394,11 @@ Even objects with the __name__ magic will work."
(setv EXPORTS
'[*map accumulate butlast calling-module calling-module-name chain coll?
combinations comp complement compress constantly count cycle dec distinct
disassemble drop drop-last drop-while empty? eval even? every? exec first
filter flatten float? fraction gensym group-by identity inc input instance?
integer integer? integer-char? interleave interpose islice iterable?
disassemble drop drop-last drop-while empty? eval even? every? first
flatten float? fraction gensym group-by identity inc instance?
integer? integer-char? interleave interpose islice iterable?
iterate iterator? juxt keyword keyword? last list? macroexpand
macroexpand-1 mangle map merge-with multicombinations name neg? none? nth
numeric? odd? partition permutations pos? product range read read-str
remove repeat repeatedly rest reduce second some string string? symbol?
take take-nth take-while tuple? unmangle xor tee zero? zip zip-longest])
macroexpand-1 mangle merge-with multicombinations name neg? none? nth
numeric? odd? partition permutations pos? product read read-str
remove repeat repeatedly rest reduce second some string? symbol?
take take-nth take-while tuple? unmangle xor tee zero? zip-longest])

View File

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

View File

@ -19,33 +19,6 @@ from contextlib import contextmanager
from hy.compiler import hy_compile, hy_ast_compile_flags
from hy.lex import hy_parse
from hy._compat import PY3
def cache_from_source(source_path):
"""Get the cached bytecode file name for a given source file name.
This function's name is set to mirror Python 3.x's
`importlib.util.cache_from_source`, which is also used when available.
Parameters
----------
source_path : str
Path of the source file
Returns
-------
out : str
Path of the corresponding bytecode file that may--or may
not--actually exist.
"""
if PY3:
return importlib.util.cache_from_source(source_path)
else:
# If source_path has a file extension, replace it with ".pyc".
# Otherwise, just append ".pyc".
d, f = os.path.split(source_path)
return os.path.join(d, re.sub(r"(?:\.[^.]+)?\Z", ".pyc", f))
@contextmanager
@ -135,370 +108,41 @@ def _get_code_from_file(run_name, fname=None,
source = f.read().decode('utf-8')
code = compile(source, fname, 'exec')
return (code, fname) if PY3 else code
return (code, fname)
if PY3:
importlib.machinery.SOURCE_SUFFIXES.insert(0, '.hy')
_py_source_to_code = importlib.machinery.SourceFileLoader.source_to_code
importlib.machinery.SOURCE_SUFFIXES.insert(0, '.hy')
_py_source_to_code = importlib.machinery.SourceFileLoader.source_to_code
def _could_be_hy_src(filename):
return (os.path.isfile(filename) and
(filename.endswith('.hy') or
not any(filename.endswith(ext)
for ext in importlib.machinery.SOURCE_SUFFIXES[1:])))
def _could_be_hy_src(filename):
return (os.path.isfile(filename) and
(filename.endswith('.hy') or
not any(filename.endswith(ext)
for ext in importlib.machinery.SOURCE_SUFFIXES[1:])))
def _hy_source_to_code(self, data, path, _optimize=-1):
if _could_be_hy_src(path):
source = data.decode("utf-8")
hy_tree = hy_parse(source, filename=path)
with loader_module_obj(self) as module:
data = hy_compile(hy_tree, module)
def _hy_source_to_code(self, data, path, _optimize=-1):
if _could_be_hy_src(path):
source = data.decode("utf-8")
hy_tree = hy_parse(source, filename=path)
with loader_module_obj(self) as module:
data = hy_compile(hy_tree, module)
return _py_source_to_code(self, data, path, _optimize=_optimize)
return _py_source_to_code(self, data, path, _optimize=_optimize)
importlib.machinery.SourceFileLoader.source_to_code = _hy_source_to_code
importlib.machinery.SourceFileLoader.source_to_code = _hy_source_to_code
# This is actually needed; otherwise, pre-created finders assigned to the
# current dir (i.e. `''`) in `sys.path` will not catch absolute imports of
# directory-local modules!
sys.path_importer_cache.clear()
# This is actually needed; otherwise, pre-created finders assigned to the
# current dir (i.e. `''`) in `sys.path` will not catch absolute imports of
# directory-local modules!
sys.path_importer_cache.clear()
# Do this one just in case?
importlib.invalidate_caches()
# XXX: These and the 2.7 counterparts below aren't truly cross-compliant.
# They're useful for testing, though.
HyImporter = importlib.machinery.FileFinder
HyLoader = importlib.machinery.SourceFileLoader
else:
import imp
import py_compile
import marshal
import struct
import traceback
from pkgutil import ImpImporter, ImpLoader
def _could_be_hy_src(filename):
return (filename.endswith('.hy') or
(os.path.isfile(filename) and
not any(filename.endswith(s[0]) for s in imp.get_suffixes())))
class HyLoader(ImpLoader, object):
def __init__(self, fullname, filename, fileobj=None, etc=None):
"""This constructor is designed for some compatibility with
SourceFileLoader."""
if etc is None and filename is not None:
if _could_be_hy_src(filename):
etc = ('.hy', 'U', imp.PY_SOURCE)
if fileobj is None:
fileobj = io.open(filename, 'rU', encoding='utf-8')
super(HyLoader, self).__init__(fullname, fileobj, filename, etc)
def __getattr__(self, item):
# We add these for Python >= 3.4 Loader interface compatibility.
if item == 'path':
return self.filename
elif item == 'name':
return self.fullname
else:
return super(HyLoader, self).__getattr__(item)
def exec_module(self, module, fullname=None):
fullname = self._fix_name(fullname)
code = self.get_code(fullname)
eval(code, module.__dict__)
def load_module(self, fullname=None):
"""Same as `pkgutil.ImpLoader`, with an extra check for Hy
source and the option to not run `self.exec_module`."""
fullname = self._fix_name(fullname)
ext_type = self.etc[0]
mod_type = self.etc[2]
mod = None
pkg_path = os.path.join(self.filename, '__init__.hy')
if ext_type == '.hy' or (
mod_type == imp.PKG_DIRECTORY and
os.path.isfile(pkg_path)):
was_in_sys = fullname in sys.modules
if was_in_sys:
mod = sys.modules[fullname]
else:
mod = sys.modules.setdefault(
fullname, types.ModuleType(fullname))
# TODO: Should we set these only when not in `sys.modules`?
if mod_type == imp.PKG_DIRECTORY:
mod.__file__ = pkg_path
mod.__path__ = [self.filename]
mod.__package__ = fullname
else:
# mod.__path__ = self.filename
mod.__file__ = self.get_filename(fullname)
mod.__package__ = '.'.join(fullname.split('.')[:-1])
mod.__name__ = fullname
try:
self.exec_module(mod, fullname=fullname)
except Exception:
# Follow Python 2.7 logic and only remove a new, bad
# module; otherwise, leave the old--and presumably
# good--module in there.
if not was_in_sys:
del sys.modules[fullname]
raise
if mod is None:
self._reopen()
try:
mod = imp.load_module(fullname, self.file, self.filename,
self.etc)
finally:
if self.file:
self.file.close()
mod.__loader__ = self
return mod
def _reopen(self):
"""Same as `pkgutil.ImpLoader`, with an extra check for Hy
source"""
if self.file and self.file.closed:
ext_type = self.etc[0]
if ext_type == '.hy':
self.file = io.open(self.filename, 'rU', encoding='utf-8')
else:
super(HyLoader, self)._reopen()
def byte_compile_hy(self, fullname=None):
fullname = self._fix_name(fullname)
if fullname is None:
fullname = self.fullname
hy_source = self.get_source(fullname)
hy_tree = hy_parse(hy_source, filename=self.filename)
with loader_module_obj(self) as module:
hy_ast = hy_compile(hy_tree, module)
code = compile(hy_ast, self.filename, 'exec',
hy_ast_compile_flags)
if not sys.dont_write_bytecode:
try:
hyc_compile(code, module=fullname)
except IOError:
pass
return code
def get_code(self, fullname=None):
"""Same as `pkgutil.ImpLoader`, with an extra check for Hy
source"""
fullname = self._fix_name(fullname)
ext_type = self.etc[0]
if ext_type == '.hy':
# Looks like we have to manually check for--and update--
# the bytecode.
t_py = long(os.stat(self.filename).st_mtime)
pyc_file = cache_from_source(self.filename)
if os.path.isfile(pyc_file):
t_pyc = long(os.stat(pyc_file).st_mtime)
if t_pyc is not None and t_pyc >= t_py:
with open(pyc_file, 'rb') as f:
if f.read(4) == imp.get_magic():
t = struct.unpack('<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
# Do this one just in case?
importlib.invalidate_caches()
# 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
# Python.

View File

@ -4,11 +4,11 @@
from __future__ import unicode_literals
import keyword
import re
import sys
import unicodedata
from hy._compat import str_type, isidentifier, UCS4
from hy.lex.exceptions import PrematureEndOfInput, LexException # NOQA
from hy.models import HyExpression, HySymbol
@ -116,7 +116,7 @@ def mangle(s):
assert s
s = str_type(s)
s = str(s)
s = s.replace("-", "_")
s2 = s.lstrip('_')
leading_underscores = '_' * (len(s) - len(s2))
@ -135,7 +135,7 @@ def mangle(s):
else '{0}{1}{0}'.format(mangle_delim,
unicodedata.name(c, '').lower().replace('-', 'H').replace(' ', '_')
or 'U{}'.format(unicode_char_to_hex(c)))
for c in unicode_to_ucs4iter(s))
for c in s)
s = leading_underscores + s
assert isidentifier(s)
@ -147,7 +147,7 @@ def unmangle(s):
form. This may not round-trip, because different Hy symbol names can
mangle to the same Python identifier."""
s = str_type(s)
s = str(s)
s2 = s.lstrip('_')
leading_underscores = len(s) - len(s2)
@ -168,19 +168,6 @@ def unmangle(s):
return '-' * leading_underscores + s
def unicode_to_ucs4iter(ustr):
# Covert a unicode string to an iterable object,
# elements in the object are single USC-4 unicode characters
if UCS4:
return ustr
ucs4_list = list(ustr)
for i, u in enumerate(ucs4_list):
if 0xD7FF < ord(u) < 0xDC00:
ucs4_list[i] += ucs4_list[i + 1]
del ucs4_list[i + 1]
return ucs4_list
def read(from_file=sys.stdin, eof=""):
"""Read from input and returns a tokenized string.
@ -203,4 +190,12 @@ def read(from_file=sys.stdin, eof=""):
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 hy._compat import str_type
from hy.models import (HyBytes, HyComplex, HyDict, HyExpression, HyFloat,
HyInteger, HyKeyword, HyList, HySet, HyString, HySymbol)
from .lexer import lexer
@ -214,7 +213,7 @@ def t_string(state, p):
raise LexException.from_lexer("Can't convert {} to a HyString".format(p[0].value),
state, p[0])
return (HyString(s, is_format = is_format)
if isinstance(s, str_type)
if isinstance(s, str)
else HyBytes(s))

View File

@ -9,7 +9,7 @@ import traceback
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.lex import mangle
from hy.errors import (HyLanguageError, HyMacroExpansionError, HyTypeError,
@ -74,9 +74,6 @@ def tag(name):
def _(fn):
_name = mangle('#{}'.format(name))
if not PY3:
_name = _name.encode('UTF-8')
fn = rename_function(fn, _name)
module = inspect.getmodule(fn)
@ -156,7 +153,7 @@ def require(source_module, target_module, assignments, prefix=""):
parent_frame = inspect.stack()[1][0]
target_namespace = parent_frame.f_globals
target_module = target_namespace.get('__name__', None)
elif isinstance(target_module, string_types):
elif isinstance(target_module, str):
target_module = importlib.import_module(target_module)
target_namespace = target_module.__dict__
elif inspect.ismodule(target_module):
@ -384,3 +381,25 @@ def tag_macroexpand(tag, tree, module):
expr.module = inspect.getmodule(tag_macro)
return replace_hy_obj(expr, tree)
def rename_function(func, new_name):
"""Creates a copy of a function and [re]sets the name at the code-object
level.
"""
c = func.__code__
new_code = type(c)(*[getattr(c, 'co_{}'.format(a))
if a != 'name' else str(new_name)
for a in code_obj_args])
_fn = type(func)(new_code, func.__globals__, str(new_name),
func.__defaults__, func.__closure__)
_fn.__dict__.update(func.__dict__)
return _fn
code_obj_args = ['argcount', '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 math import isnan, isinf
from hy import _initialize_env_var
from hy._compat import PY3, str_type, bytes_type, long_type, string_types
from hy.errors import HyWrapperError
from fractions import Fraction
from clint.textui import colored
@ -88,7 +87,7 @@ def repr_indent(obj):
return repr(obj).replace("\n", "\n ")
class HyString(HyObject, str_type):
class HyString(HyObject, str):
"""
Generic Hy String object. Helpful to store string literals from Hy
scripts. It's either a ``str`` or a ``unicode``, depending on the
@ -100,20 +99,20 @@ class HyString(HyObject, str_type):
value.brackets = brackets
return value
_wrappers[str_type] = HyString
_wrappers[str] = HyString
class HyBytes(HyObject, bytes_type):
class HyBytes(HyObject, bytes):
"""
Generic Hy Bytes object. It's either a ``bytes`` or a ``str``, depending
on the Python version.
"""
pass
_wrappers[bytes_type] = HyBytes
_wrappers[bytes] = HyBytes
class HySymbol(HyObject, str_type):
class HySymbol(HyObject, str):
"""
Hy Symbol. Basically a string.
"""
@ -170,42 +169,39 @@ def strip_digit_separators(number):
# Don't strip a _ or , if it's the first character, as _42 and
# ,42 aren't valid numbers
return (number[0] + number[1:].replace("_", "").replace(",", "")
if isinstance(number, string_types) and len(number) > 1
if isinstance(number, str) and len(number) > 1
else number)
class HyInteger(HyObject, long_type):
class HyInteger(HyObject, int):
"""
Internal representation of a Hy Integer. May raise a ValueError as if
int(foo) was called, given HyInteger(foo). On python 2.x long will
be used instead
int(foo) was called, given HyInteger(foo).
"""
def __new__(cls, number, *args, **kwargs):
if isinstance(number, string_types):
if isinstance(number, str):
number = strip_digit_separators(number)
bases = {"0x": 16, "0o": 8, "0b": 2}
for leader, base in bases.items():
if number.startswith(leader):
# We've got a string, known leader, set base.
number = long_type(number, base=base)
number = int(number, base=base)
break
else:
# We've got a string, no known leader; base 10.
number = long_type(number, base=10)
number = int(number, base=10)
else:
# We've got a non-string; convert straight.
number = long_type(number)
number = int(number)
return super(HyInteger, cls).__new__(cls, number)
_wrappers[int] = HyInteger
if not PY3: # do not add long on python3
_wrappers[long_type] = HyInteger
def check_inf_nan_cap(arg, value):
if isinstance(arg, string_types):
if isinstance(arg, str):
if isinf(value) and "i" in arg.lower() and "Inf" not in arg:
raise ValueError('Inf must be capitalized as "Inf"')
if isnan(value) and "NaN" not in arg:
@ -233,7 +229,7 @@ class HyComplex(HyObject, complex):
"""
def __new__(cls, real, imag=0, *args, **kwargs):
if isinstance(real, string_types):
if isinstance(real, str):
value = super(HyComplex, cls).__new__(
cls, strip_digit_separators(real)
)

View File

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

View File

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

View File

@ -6,7 +6,6 @@ import ast
from hy import compiler
from hy.models import HyExpression, HyList, HySymbol, HyInteger
from hy._compat import PY3
def make_expression(*args):
@ -64,12 +63,5 @@ def test_compiler_yield_return():
assert len(body) == 2
assert isinstance(body[0], ast.Expr)
assert isinstance(body[0].value, ast.Yield)
if PY3:
# From 3.3+, the final statement becomes a return value
assert isinstance(body[1], ast.Return)
assert isinstance(body[1].value, ast.BinOp)
else:
# In earlier versions, the expression is not returned
assert isinstance(body[1], ast.Expr)
assert isinstance(body[1].value, ast.BinOp)
assert isinstance(body[1], ast.Return)
assert isinstance(body[1].value, ast.BinOp)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@
pytest)
(import sys)
(import [hy._compat [PY3 PY37 PY38]])
(import [hy._compat [PY38]])
(defn test-sys-argv []
"NATIVE: test sys.argv"
@ -71,13 +71,12 @@
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(try (eval '(defn None [] (print "hello")))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(when PY3
(try (eval '(setv False 1))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(try (eval '(setv True 0))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(try (eval '(defn True [] (print "hello")))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))))
(try (eval '(setv False 1))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(try (eval '(setv True 0))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e)))))
(try (eval '(defn True [] (print "hello")))
(except [e [SyntaxError]] (assert (in "Can't assign to" (str e))))))
(defn test-setv-pairs []
@ -513,9 +512,7 @@
(setv passed False)
(try
(raise)
;; Python 2 raises IndexError here (due to the previous test)
;; Python 3 raises RuntimeError
(except [[IndexError RuntimeError]]
(except [RuntimeError]
(setv passed True)))
(assert passed)
@ -747,16 +744,11 @@
(defn test-yield-with-return []
"NATIVE: test yield with return"
(defn gen [] (yield 3) "goodbye")
(if PY3
(do (setv gg (gen))
(assert (= 3 (next gg)))
(try (next gg)
(except [e StopIteration] (assert (hasattr e "value"))
(assert (= (getattr e "value") "goodbye")))))
(do (setv gg (gen))
(assert (= 3 (next gg)))
(try (next gg)
(except [e StopIteration] (assert (not (hasattr e "value"))))))))
(setv gg (gen))
(assert (= 3 (next gg)))
(try (next gg)
(except [e StopIteration] (assert (hasattr e "value"))
(assert (= (getattr e "value") "goodbye")))))
(defn test-yield-in-try []
@ -1242,19 +1234,14 @@ cee\"} dee" "ey bee\ncee dee"))
; Conversion characters and format specifiers
(setv p:9 "other")
(setv !r "bar")
(defn u [s]
; Add a "u" prefix for Python 2.
(if PY3
s
(.replace (.replace s "'" "u'" 1) " " " " 1)))
(assert (= f"a{p !r}" (u "a'xyzzy'")))
(assert (= f"a{p !r}" "a'xyzzy'"))
(assert (= f"a{p :9}" "axyzzy "))
(assert (= f"a{p:9}" "aother"))
(assert (= f"a{p !r :9}" (u "a'xyzzy' ")))
(assert (= f"a{p !r:9}" (u "a'xyzzy' ")))
(assert (= f"a{p !r :9}" "a'xyzzy' "))
(assert (= f"a{p !r:9}" "a'xyzzy' "))
(assert (= f"a{p:9 :9}" "aother "))
(assert (= f"a{!r}" "abar"))
(assert (= f"a{!r !r}" (u "a'bar'")))
(assert (= f"a{!r !r}" "a'bar'"))
; Fun with `r`
(assert (= f"hello {r\"\\n\"}" r"hello \n"))
@ -1278,7 +1265,7 @@ cee\"} dee" "ey bee\ncee dee"))
(assert (= f"{(C) : {(str (+ 1 1)) !r :x<5}}" "C[ '2'xx]"))
; Format bracket strings
(assert (= #[f[a{p !r :9}]f] (u "a'xyzzy' ")))
(assert (= #[f[a{p !r :9}]f] "a'xyzzy' "))
(assert (= #[f-string[result: {value :{width}.{precision}}]f-string]
"result: 12.34")))
@ -1482,11 +1469,6 @@ cee\"} dee" "ey bee\ncee dee"))
(assert (= y [5])))
(defn test-string []
(assert (string? (string "a")))
(assert (string? (string 1)))
(assert (= u"unicode" (string "unicode"))))
(defn test-del []
"NATIVE: Test the behavior of del"
(setv foo 42)
@ -1549,17 +1531,12 @@ cee\"} dee" "ey bee\ncee dee"))
(defn test-disassemble []
"NATIVE: Test the disassemble function"
(assert (= (disassemble '(do (leaky) (leaky) (macros))) (cond
[PY3 (.format "Module(
(assert (= (disassemble '(do (leaky) (leaky) (macros)))
(.format "Module(
body=[Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[])),
Expr(value=Call(func=Name(id='macros'), args=[], keywords=[]))]{})"
(if PY38 ",\n type_ignores=[]" ""))]
[True "Module(
body=[
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),
Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),
Expr(value=Call(func=Name(id='macros'), args=[], keywords=[], starargs=None, kwargs=None))])"])))
(if PY38 ",\n type_ignores=[]" ""))))
(assert (= (disassemble '(do (leaky) (leaky) (macros)) True)
"leaky()
leaky()
@ -1597,9 +1574,7 @@ macros()
(defn test-read []
"NATIVE: test that read takes something for stdin and reads"
(if-python2
(import [StringIO [StringIO]])
(import [io [StringIO]]))
(import [io [StringIO]])
(import [hy.models [HyExpression]])
(setv stdin-buffer (StringIO "(+ 2 2)\n(- 2 2)"))
@ -1716,3 +1691,204 @@ macros()
"Make sure relative imports work properly"
(import [..resources [tlib]])
(assert (= tlib.SECRET-MESSAGE "Hello World")))
(defn test-exception-cause []
(try (raise ValueError :from NameError)
(except [e [ValueError]]
(assert (= (type (. e __cause__)) NameError)))))
(defn test-kwonly []
"NATIVE: test keyword-only arguments"
;; keyword-only with default works
(defn kwonly-foo-default-false [&kwonly [foo False]] foo)
(assert (= (kwonly-foo-default-false) False))
(assert (= (kwonly-foo-default-false :foo True) True))
;; keyword-only without default ...
(defn kwonly-foo-no-default [&kwonly foo] foo)
(setv attempt-to-omit-default (try
(kwonly-foo-no-default)
(except [e [Exception]] e)))
;; works
(assert (= (kwonly-foo-no-default :foo "quux") "quux"))
;; raises TypeError with appropriate message if not supplied
(assert (isinstance attempt-to-omit-default TypeError))
(assert (in "missing 1 required keyword-only argument: 'foo'"
(. attempt-to-omit-default args [0])))
;; keyword-only with other arg types works
(defn function-of-various-args [a b &rest args &kwonly foo &kwargs kwargs]
(, a b args foo kwargs))
(assert (= (function-of-various-args 1 2 3 4 :foo 5 :bar 6 :quux 7)
(, 1 2 (, 3 4) 5 {"bar" 6 "quux" 7}))))
(defn test-extended-unpacking-1star-lvalues []
(setv [x #*y] [1 2 3 4])
(assert (= x 1))
(assert (= y [2 3 4]))
(setv [a #*b c] "ghijklmno")
(assert (= a "g"))
(assert (= b (list "hijklmn")))
(assert (= c "o")))
(defn test-yield-from []
"NATIVE: testing yield from"
(defn yield-from-test []
(for [i (range 3)]
(yield i))
(yield-from [1 2 3]))
(assert (= (list (yield-from-test)) [0 1 2 1 2 3])))
(defn test-yield-from-exception-handling []
"NATIVE: Ensure exception handling in yield from works right"
(defn yield-from-subgenerator-test []
(yield 1)
(yield 2)
(yield 3)
(assert 0))
(defn yield-from-test []
(for [i (range 3)]
(yield i))
(try
(yield-from (yield-from-subgenerator-test))
(except [e AssertionError]
(yield 4))))
(assert (= (list (yield-from-test)) [0 1 2 1 2 3 4])))
(require [hy.contrib.walk [let]])
(defn test-let-optional []
(let [a 1
b 6
d 2]
(defn foo [&kwonly [a a] b [c d]]
(, a b c))
(assert (= (foo :b "b")
(, 1 "b" 2)))
(assert (= (foo :b 20 :a 10 :c 30)
(, 10 20 30)))))
(defn test-pep-3115 []
(defclass member-table [dict]
[--init-- (fn [self] (setv self.member-names []))
--setitem-- (fn [self key value]
(if (not-in key self)
(.append self.member-names key))
(dict.--setitem-- self key value))])
(defclass OrderedClass [type]
[--prepare-- (classmethod (fn [metacls name bases] (member-table)))
--new-- (fn [cls name bases classdict]
(setv result (type.--new-- cls name bases (dict classdict)))
(setv result.member-names classdict.member-names)
result)])
(defclass MyClass [:metaclass OrderedClass]
[method1 (fn [self] (pass))
method2 (fn [self] (pass))])
(assert (= (. (MyClass) member-names)
["__module__" "__qualname__" "method1" "method2"])))
(import [asyncio [get-event-loop sleep]])
(defn test-unpacking-pep448-1star []
(setv l [1 2 3])
(setv p [4 5])
(assert (= ["a" #*l "b" #*p #*l] ["a" 1 2 3 "b" 4 5 1 2 3]))
(assert (= (, "a" #*l "b" #*p #*l) (, "a" 1 2 3 "b" 4 5 1 2 3)))
(assert (= #{"a" #*l "b" #*p #*l} #{"a" "b" 1 2 3 4 5}))
(defn f [&rest args] args)
(assert (= (f "a" #*l "b" #*p #*l) (, "a" 1 2 3 "b" 4 5 1 2 3)))
(assert (= (+ #*l #*p) 15))
(assert (= (and #*l) 3)))
(defn test-unpacking-pep448-2star []
(setv d1 {"a" 1 "b" 2})
(setv d2 {"c" 3 "d" 4})
(assert (= {1 "x" #**d1 #**d2 2 "y"} {"a" 1 "b" 2 "c" 3 "d" 4 1 "x" 2 "y"}))
(defn fun [&optional a b c d e f] [a b c d e f])
(assert (= (fun #**d1 :e "eee" #**d2) [1 2 3 4 "eee" None])))
(defn run-coroutine [coro]
"Run a coroutine until its done in the default event loop."""
(.run_until_complete (get-event-loop) (coro)))
(defn test-fn/a []
(assert (= (run-coroutine (fn/a [] (await (sleep 0)) [1 2 3]))
[1 2 3])))
(defn test-defn/a []
(defn/a coro-test []
(await (sleep 0))
[1 2 3])
(assert (= (run-coroutine coro-test) [1 2 3])))
(defn test-decorated-defn/a []
(defn decorator [func] (fn/a [] (/ (await (func)) 2)))
#@(decorator
(defn/a coro-test []
(await (sleep 0))
42))
(assert (= (run-coroutine coro-test) 21)))
(defclass AsyncWithTest []
(defn --init-- [self val]
(setv self.val val)
None)
(defn/a --aenter-- [self]
self.val)
(defn/a --aexit-- [self tyle value traceback]
(setv self.val None)))
(defn test-single-with/a []
(run-coroutine
(fn/a []
(with/a [t (AsyncWithTest 1)]
(assert (= t 1))))))
(defn test-two-with/a []
(run-coroutine
(fn/a []
(with/a [t1 (AsyncWithTest 1)
t2 (AsyncWithTest 2)]
(assert (= t1 1))
(assert (= t2 2))))))
(defn test-thrice-with/a []
(run-coroutine
(fn/a []
(with/a [t1 (AsyncWithTest 1)
t2 (AsyncWithTest 2)
t3 (AsyncWithTest 3)]
(assert (= t1 1))
(assert (= t2 2))
(assert (= t3 3))))))
(defn test-quince-with/a []
(run-coroutine
(fn/a []
(with/a [t1 (AsyncWithTest 1)
t2 (AsyncWithTest 2)
t3 (AsyncWithTest 3)
_ (AsyncWithTest 4)]
(assert (= t1 1))
(assert (= t2 2))
(assert (= t3 3))))))

View File

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

View File

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

View File

@ -140,11 +140,6 @@
(assert initialized)
(assert (test-initialized))
(defn test-if-python2 []
(import sys)
(assert (= (get sys.version_info 0)
(if-python2 2 3))))
(defn test-gensym-in-macros []
(import ast)
(import [astor.code-gen [to-source]])
@ -409,9 +404,9 @@ in expansions."
Additionally, we confirm that `require` statements are executed via loaded bytecode."
(import os sys marshal types)
(import [hy.importer [cache-from-source]])
(import importlib)
(setv pyc-file (cache-from-source
(setv pyc-file (importlib.util.cache-from-source
(os.path.realpath
(os.path.join
"tests" "resources" "macro_with_require.hy"))))

View File

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

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

View File

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