Merge pull request #1777 from Kodiologist/nopy2
Drop support for Python 2
This commit is contained in:
commit
c2cde0a821
@ -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
|
||||
|
7
NEWS.rst
7
NEWS.rst
@ -1,5 +1,12 @@
|
||||
.. default-role:: code
|
||||
|
||||
Unreleased
|
||||
==============================
|
||||
|
||||
Removals
|
||||
------------------------------
|
||||
* Python 2 is no longer supported.
|
||||
|
||||
0.17.0
|
||||
==============================
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
===================================
|
||||
|
103
hy/_compat.py
103
hy/_compat.py
@ -2,108 +2,15 @@
|
||||
# This file is part of Hy, which is free software licensed under the Expat
|
||||
# license. See the LICENSE.
|
||||
|
||||
try:
|
||||
import __builtin__ as builtins
|
||||
except ImportError:
|
||||
import builtins # NOQA
|
||||
import sys, keyword, textwrap
|
||||
import sys
|
||||
|
||||
PY3 = sys.version_info[0] >= 3
|
||||
PY36 = sys.version_info >= (3, 6)
|
||||
PY37 = sys.version_info >= (3, 7)
|
||||
PY38 = sys.version_info >= (3, 8)
|
||||
|
||||
# The value of UCS4 indicates whether Unicode strings are stored as UCS-4.
|
||||
# It is always true on Pythons >= 3.3, which use USC-4 on all systems.
|
||||
UCS4 = sys.maxunicode == 0x10FFFF
|
||||
|
||||
str_type = str if PY3 else unicode # NOQA
|
||||
bytes_type = bytes if PY3 else str # NOQA
|
||||
long_type = int if PY3 else long # NOQA
|
||||
string_types = str if PY3 else basestring # NOQA
|
||||
|
||||
#
|
||||
# Inspired by the same-named `six` functions.
|
||||
#
|
||||
if PY3:
|
||||
raise_src = textwrap.dedent('''
|
||||
def raise_from(value, from_value):
|
||||
raise value from from_value
|
||||
''')
|
||||
|
||||
def reraise(exc_type, value, traceback=None):
|
||||
try:
|
||||
raise value.with_traceback(traceback)
|
||||
finally:
|
||||
traceback = None
|
||||
|
||||
code_obj_args = ['argcount', 'kwonlyargcount', 'nlocals', 'stacksize',
|
||||
'flags', 'code', 'consts', 'names', 'varnames',
|
||||
'filename', 'name', 'firstlineno', 'lnotab', 'freevars',
|
||||
'cellvars']
|
||||
else:
|
||||
def raise_from(value, from_value=None):
|
||||
raise value
|
||||
|
||||
raise_src = textwrap.dedent('''
|
||||
def reraise(exc_type, value, traceback=None):
|
||||
try:
|
||||
raise exc_type, value, traceback
|
||||
finally:
|
||||
traceback = None
|
||||
''')
|
||||
|
||||
code_obj_args = ['argcount', 'nlocals', 'stacksize', 'flags', 'code',
|
||||
'consts', 'names', 'varnames', 'filename', 'name',
|
||||
'firstlineno', 'lnotab', 'freevars', 'cellvars']
|
||||
|
||||
raise_code = compile(raise_src, __file__, 'exec')
|
||||
exec(raise_code)
|
||||
|
||||
|
||||
def rename_function(func, new_name):
|
||||
"""Creates a copy of a function and [re]sets the name at the code-object
|
||||
level.
|
||||
"""
|
||||
c = func.__code__
|
||||
new_code = type(c)(*[getattr(c, 'co_{}'.format(a))
|
||||
if a != 'name' else str(new_name)
|
||||
for a in code_obj_args])
|
||||
|
||||
_fn = type(func)(new_code, func.__globals__, str(new_name),
|
||||
func.__defaults__, func.__closure__)
|
||||
_fn.__dict__.update(func.__dict__)
|
||||
|
||||
return _fn
|
||||
|
||||
|
||||
def isidentifier(x):
|
||||
if x in ('True', 'False', 'None', 'print'):
|
||||
# `print` is special-cased here because Python 2's
|
||||
# keyword.iskeyword will count it as a keyword, but we
|
||||
# use the __future__ feature print_function, which makes
|
||||
# it a non-keyword.
|
||||
return True
|
||||
if keyword.iskeyword(x):
|
||||
return False
|
||||
if PY3:
|
||||
return x.isidentifier()
|
||||
if x.rstrip() != x:
|
||||
return False
|
||||
import tokenize as T
|
||||
from io import StringIO
|
||||
def reraise(exc_type, value, traceback=None):
|
||||
try:
|
||||
tokens = list(T.generate_tokens(StringIO(x).readline))
|
||||
except (T.TokenError, IndentationError):
|
||||
return False
|
||||
# Some versions of Python 2.7 (including one that made it into
|
||||
# Ubuntu 18.10) have a Python 3 backport that adds a NEWLINE
|
||||
# token. Remove it if it's present.
|
||||
# https://bugs.python.org/issue33899
|
||||
tokens = [t for t in tokens if t[0] != T.NEWLINE]
|
||||
return len(tokens) == 2 and tokens[0][0] == T.NAME
|
||||
|
||||
try:
|
||||
FileNotFoundError = FileNotFoundError
|
||||
except NameError:
|
||||
FileNotFoundError = IOError
|
||||
raise value.with_traceback(traceback)
|
||||
finally:
|
||||
traceback = None
|
||||
|
@ -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))
|
||||
|
230
hy/compiler.py
230
hy/compiler.py
@ -14,8 +14,7 @@ from hy.errors import (HyCompileError, HyTypeError, HyLanguageError,
|
||||
|
||||
from hy.lex import mangle, unmangle, hy_parse, parse_one_thing, LexException
|
||||
|
||||
from hy._compat import (string_types, str_type, bytes_type, long_type, PY3,
|
||||
PY36, PY38, reraise)
|
||||
from hy._compat import (PY36, PY38, reraise)
|
||||
from hy.macros import require, load_macros, macroexpand, tag_macroexpand
|
||||
|
||||
import hy.core
|
||||
@ -29,15 +28,12 @@ import types
|
||||
import ast
|
||||
import sys
|
||||
import copy
|
||||
import builtins
|
||||
import __future__
|
||||
|
||||
from collections import defaultdict
|
||||
from functools import reduce
|
||||
|
||||
if PY3:
|
||||
import builtins
|
||||
else:
|
||||
import __builtin__ as builtins
|
||||
|
||||
Inf = float('inf')
|
||||
|
||||
@ -99,15 +95,12 @@ def calling_module(n=1):
|
||||
def ast_str(x, piecewise=False):
|
||||
if piecewise:
|
||||
return ".".join(ast_str(s) if s else "" for s in x.split("."))
|
||||
x = mangle(x)
|
||||
return x if PY3 else x.encode('UTF8')
|
||||
return mangle(x)
|
||||
|
||||
|
||||
_special_form_compilers = {}
|
||||
_model_compilers = {}
|
||||
_decoratables = (ast.FunctionDef, ast.ClassDef)
|
||||
if PY3:
|
||||
_decoratables += (ast.AsyncFunctionDef,)
|
||||
_decoratables = (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)
|
||||
# _bad_roots are fake special operators, which are used internally
|
||||
# by other special forms (e.g., `except` in `try`) but can't be
|
||||
# used to construct special forms themselves.
|
||||
@ -179,7 +172,7 @@ class Result(object):
|
||||
object gets added to a Result object, it gets converted on-the-fly.
|
||||
"""
|
||||
__slots__ = ("imports", "stmts", "temp_variables",
|
||||
"_expr", "__used_expr", "contains_yield")
|
||||
"_expr", "__used_expr")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if args:
|
||||
@ -190,14 +183,12 @@ class Result(object):
|
||||
self.stmts = []
|
||||
self.temp_variables = []
|
||||
self._expr = None
|
||||
self.contains_yield = False
|
||||
|
||||
self.__used_expr = False
|
||||
|
||||
# XXX: Make sure we only have AST where we should.
|
||||
for kwarg in kwargs:
|
||||
if kwarg not in ["imports", "contains_yield", "stmts", "expr",
|
||||
"temp_variables"]:
|
||||
if kwarg not in ["imports", "stmts", "expr", "temp_variables"]:
|
||||
raise TypeError(
|
||||
"%s() got an unexpected keyword argument '%s'" % (
|
||||
self.__class__.__name__, kwarg))
|
||||
@ -277,9 +268,7 @@ class Result(object):
|
||||
if isinstance(var, ast.Name):
|
||||
var.id = new_name
|
||||
var.arg = new_name
|
||||
elif isinstance(var, ast.FunctionDef):
|
||||
var.name = new_name
|
||||
elif PY3 and isinstance(var, ast.AsyncFunctionDef):
|
||||
elif isinstance(var, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
var.name = new_name
|
||||
else:
|
||||
raise TypeError("Don't know how to rename a %s!" % (
|
||||
@ -315,22 +304,17 @@ class Result(object):
|
||||
result.stmts = self.stmts + other.stmts
|
||||
result.expr = other.expr
|
||||
result.temp_variables = other.temp_variables
|
||||
result.contains_yield = False
|
||||
if self.contains_yield or other.contains_yield:
|
||||
result.contains_yield = True
|
||||
|
||||
return result
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
"Result(imports=[%s], stmts=[%s], "
|
||||
"expr=%s, contains_yield=%s)"
|
||||
) % (
|
||||
"Result(imports=[%s], stmts=[%s], expr=%s)"
|
||||
% (
|
||||
", ".join(ast.dump(x) for x in self.imports),
|
||||
", ".join(ast.dump(x) for x in self.stmts),
|
||||
ast.dump(self.expr) if self.expr else None,
|
||||
self.contains_yield
|
||||
)
|
||||
ast.dump(self.expr) if self.expr else None
|
||||
))
|
||||
|
||||
|
||||
def is_unpack(kind, x):
|
||||
@ -390,11 +374,7 @@ class HyASTCompiler(object):
|
||||
for stdlib_module in hy.core.STDLIB:
|
||||
mod = importlib.import_module(stdlib_module)
|
||||
for e in map(ast_str, getattr(mod, 'EXPORTS', [])):
|
||||
if getattr(mod, e) is not getattr(builtins, e, ''):
|
||||
# Don't bother putting a name in _stdlib if it
|
||||
# points to a builtin with the same name. This
|
||||
# prevents pointless imports.
|
||||
self._stdlib[e] = stdlib_module
|
||||
self._stdlib[e] = stdlib_module
|
||||
|
||||
def get_anon_var(self):
|
||||
self.anon_var_count += 1
|
||||
@ -458,8 +438,7 @@ class HyASTCompiler(object):
|
||||
def _syntax_error(self, expr, message):
|
||||
return HySyntaxError(message, expr, self.filename, self.source)
|
||||
|
||||
def _compile_collect(self, exprs, with_kwargs=False, dict_display=False,
|
||||
oldpy_unpack=False):
|
||||
def _compile_collect(self, exprs, with_kwargs=False, dict_display=False):
|
||||
"""Collect the expression contexts from a list of compiled expression.
|
||||
|
||||
This returns a list of the expression contexts, and the sum of the
|
||||
@ -469,34 +448,18 @@ class HyASTCompiler(object):
|
||||
compiled_exprs = []
|
||||
ret = Result()
|
||||
keywords = []
|
||||
oldpy_starargs = None
|
||||
oldpy_kwargs = None
|
||||
|
||||
exprs_iter = iter(exprs)
|
||||
for expr in exprs_iter:
|
||||
|
||||
if not PY3 and oldpy_unpack and is_unpack("iterable", expr):
|
||||
if oldpy_starargs:
|
||||
raise self._syntax_error(expr,
|
||||
"Pythons < 3.5 allow only one `unpack-iterable` per call")
|
||||
oldpy_starargs = self.compile(expr[1])
|
||||
ret += oldpy_starargs
|
||||
oldpy_starargs = oldpy_starargs.force_expr
|
||||
|
||||
elif is_unpack("mapping", expr):
|
||||
if is_unpack("mapping", expr):
|
||||
ret += self.compile(expr[1])
|
||||
if PY3:
|
||||
if dict_display:
|
||||
compiled_exprs.append(None)
|
||||
compiled_exprs.append(ret.force_expr)
|
||||
elif with_kwargs:
|
||||
keywords.append(asty.keyword(
|
||||
expr, arg=None, value=ret.force_expr))
|
||||
elif oldpy_unpack:
|
||||
if oldpy_kwargs:
|
||||
raise self._syntax_error(expr,
|
||||
"Pythons < 3.5 allow only one `unpack-mapping` per call")
|
||||
oldpy_kwargs = ret.force_expr
|
||||
if dict_display:
|
||||
compiled_exprs.append(None)
|
||||
compiled_exprs.append(ret.force_expr)
|
||||
elif with_kwargs:
|
||||
keywords.append(asty.keyword(
|
||||
expr, arg=None, value=ret.force_expr))
|
||||
|
||||
elif with_kwargs and isinstance(expr, HyKeyword):
|
||||
try:
|
||||
@ -512,7 +475,7 @@ class HyASTCompiler(object):
|
||||
compiled_value = self.compile(value)
|
||||
ret += compiled_value
|
||||
|
||||
arg = str_type(expr)[1:]
|
||||
arg = str(expr)[1:]
|
||||
keywords.append(asty.keyword(
|
||||
expr, arg=ast_str(arg), value=compiled_value.force_expr))
|
||||
|
||||
@ -520,10 +483,7 @@ class HyASTCompiler(object):
|
||||
ret += self.compile(expr)
|
||||
compiled_exprs.append(ret.force_expr)
|
||||
|
||||
if oldpy_unpack:
|
||||
return compiled_exprs, ret, keywords, oldpy_starargs, oldpy_kwargs
|
||||
else:
|
||||
return compiled_exprs, ret, keywords
|
||||
return compiled_exprs, ret, keywords
|
||||
|
||||
def _compile_branch(self, exprs):
|
||||
"""Make a branch out of an iterable of Result objects
|
||||
@ -564,7 +524,7 @@ class HyASTCompiler(object):
|
||||
new_name = ast.Subscript(value=name.value, slice=name.slice)
|
||||
elif isinstance(name, ast.Attribute):
|
||||
new_name = ast.Attribute(value=name.value, attr=name.attr)
|
||||
elif PY3 and isinstance(name, ast.Starred):
|
||||
elif isinstance(name, ast.Starred):
|
||||
new_name = ast.Starred(
|
||||
value=self._storeize(expr, name.value, func))
|
||||
else:
|
||||
@ -650,23 +610,10 @@ class HyASTCompiler(object):
|
||||
|
||||
@special("unpack-iterable", [FORM])
|
||||
def compile_unpack_iterable(self, expr, root, arg):
|
||||
if not PY3:
|
||||
raise self._syntax_error(expr,
|
||||
"`unpack-iterable` isn't allowed here")
|
||||
ret = self.compile(arg)
|
||||
ret += asty.Starred(expr, value=ret.force_expr, ctx=ast.Load())
|
||||
return ret
|
||||
|
||||
@special([(not PY3, "exec*")], [FORM, maybe(FORM), maybe(FORM)])
|
||||
# Under Python 3, `exec` is a function rather than a statement type, so Hy
|
||||
# doesn't need a special form for it.
|
||||
def compile_exec(self, expr, root, body, globals_, locals_):
|
||||
return asty.Exec(
|
||||
expr,
|
||||
body=self.compile(body).force_expr,
|
||||
globals=self.compile(globals_).force_expr if globals_ is not None else None,
|
||||
locals=self.compile(locals_).force_expr if locals_ is not None else None)
|
||||
|
||||
@special("do", [many(FORM)])
|
||||
def compile_do(self, expr, root, body):
|
||||
return self._compile_branch(body)
|
||||
@ -681,9 +628,6 @@ class HyASTCompiler(object):
|
||||
exc = exc.force_expr
|
||||
|
||||
if cause is not None:
|
||||
if not PY3:
|
||||
raise self._syntax_error(expr,
|
||||
"raise from only supported in python 3")
|
||||
cause = self.compile(cause)
|
||||
ret += cause
|
||||
cause = cause.force_expr
|
||||
@ -739,35 +683,17 @@ class HyASTCompiler(object):
|
||||
|
||||
returnable = Result(
|
||||
expr=asty.Name(expr, id=return_var.id, ctx=ast.Load()),
|
||||
temp_variables=[return_var],
|
||||
contains_yield=body.contains_yield)
|
||||
temp_variables=[return_var])
|
||||
body += body.expr_as_stmt() if orelse else asty.Assign(
|
||||
expr, targets=[return_var], value=body.force_expr)
|
||||
body = body.stmts or [asty.Pass(expr)]
|
||||
|
||||
if PY3:
|
||||
# Python 3.3 features a merge of TryExcept+TryFinally into Try.
|
||||
x = asty.Try(
|
||||
expr,
|
||||
body=body,
|
||||
handlers=handlers,
|
||||
orelse=orelse,
|
||||
finalbody=finalbody)
|
||||
elif finalbody and handlers:
|
||||
x = asty.TryFinally(
|
||||
expr,
|
||||
body=[asty.TryExcept(
|
||||
expr,
|
||||
body=body,
|
||||
handlers=handlers,
|
||||
orelse=orelse)],
|
||||
finalbody=finalbody)
|
||||
elif finalbody:
|
||||
x = asty.TryFinally(
|
||||
expr, body=body, finalbody=finalbody)
|
||||
else:
|
||||
x = asty.TryExcept(
|
||||
expr, body=body, handlers=handlers, orelse=orelse)
|
||||
x = asty.Try(
|
||||
expr,
|
||||
body=body,
|
||||
handlers=handlers,
|
||||
orelse=orelse,
|
||||
finalbody=finalbody)
|
||||
return handler_results + x + returnable
|
||||
|
||||
def _compile_catch_expression(self, expr, var, exceptions, body):
|
||||
@ -784,9 +710,7 @@ class HyASTCompiler(object):
|
||||
|
||||
name = None
|
||||
if len(exceptions) == 2:
|
||||
name = exceptions[0]
|
||||
name = (ast_str(name) if PY3
|
||||
else self._storeize(name, self.compile(name)))
|
||||
name = ast_str(exceptions[0])
|
||||
|
||||
exceptions_list = exceptions[-1] if exceptions else HyList()
|
||||
if isinstance(exceptions_list, HyList):
|
||||
@ -900,19 +824,19 @@ class HyASTCompiler(object):
|
||||
msg = self.compile(msg).force_expr
|
||||
return ret + asty.Assert(expr, test=e, msg=msg)
|
||||
|
||||
@special(["global", (PY3, "nonlocal")], [oneplus(SYM)])
|
||||
@special(["global", "nonlocal"], [oneplus(SYM)])
|
||||
def compile_global_or_nonlocal(self, expr, root, syms):
|
||||
node = asty.Global if root == "global" else asty.Nonlocal
|
||||
return node(expr, names=list(map(ast_str, syms)))
|
||||
|
||||
@special("yield", [maybe(FORM)])
|
||||
def compile_yield_expression(self, expr, root, arg):
|
||||
ret = Result(contains_yield=(not PY3))
|
||||
ret = Result()
|
||||
if arg is not None:
|
||||
ret += self.compile(arg)
|
||||
return ret + asty.Yield(expr, value=ret.force_expr)
|
||||
|
||||
@special([(PY3, "yield-from"), (PY3, "await")], [FORM])
|
||||
@special(["yield-from", "await"], [FORM])
|
||||
def compile_yield_from_or_await_expression(self, expr, root, arg):
|
||||
ret = Result() + self.compile(arg)
|
||||
node = asty.YieldFrom if root == "yield-from" else asty.Await
|
||||
@ -991,7 +915,7 @@ class HyASTCompiler(object):
|
||||
fn.stmts[-1].decorator_list = decs + fn.stmts[-1].decorator_list
|
||||
return ret + fn
|
||||
|
||||
@special(["with*", (PY3, "with/a*")],
|
||||
@special(["with*", "with/a*"],
|
||||
[brackets(FORM, maybe(FORM)), many(FORM)])
|
||||
def compile_with_expression(self, expr, root, args, body):
|
||||
thing, ctx = (None, args[0]) if args[1] is None else args
|
||||
@ -1015,14 +939,11 @@ class HyASTCompiler(object):
|
||||
the_with = node(expr,
|
||||
context_expr=ctx.force_expr,
|
||||
optional_vars=thing,
|
||||
body=body.stmts)
|
||||
|
||||
if PY3:
|
||||
the_with.items = [ast.withitem(context_expr=ctx.force_expr,
|
||||
optional_vars=thing)]
|
||||
body=body.stmts,
|
||||
items=[ast.withitem(context_expr=ctx.force_expr,
|
||||
optional_vars=thing)])
|
||||
|
||||
ret = Result(stmts=[initial_assign]) + ctx + the_with
|
||||
ret.contains_yield = ret.contains_yield or body.contains_yield
|
||||
# And make our expression context our temp variable
|
||||
expr_name = asty.Name(expr, id=ast_str(var), ctx=ast.Load())
|
||||
|
||||
@ -1096,7 +1017,6 @@ class HyASTCompiler(object):
|
||||
# The desired comprehension can't be expressed as a
|
||||
# real Python comprehension. We'll write it as a nested
|
||||
# loop in a function instead.
|
||||
contains_yield = []
|
||||
def f(parts):
|
||||
# This function is called recursively to construct
|
||||
# the nested loop.
|
||||
@ -1104,8 +1024,6 @@ class HyASTCompiler(object):
|
||||
if is_for:
|
||||
if body:
|
||||
bd = self._compile_branch(body)
|
||||
if bd.contains_yield:
|
||||
contains_yield.append(True)
|
||||
return bd + bd.expr_as_stmt()
|
||||
return Result(stmts=[asty.Pass(expr)])
|
||||
if node_class is asty.DictComp:
|
||||
@ -1136,16 +1054,14 @@ class HyASTCompiler(object):
|
||||
else:
|
||||
raise ValueError("can't happen")
|
||||
if is_for:
|
||||
ret = f(parts)
|
||||
ret.contains_yield = bool(contains_yield)
|
||||
return ret
|
||||
return f(parts)
|
||||
fname = self.get_anon_var()
|
||||
# Define the generator function.
|
||||
ret = Result() + asty.FunctionDef(
|
||||
expr,
|
||||
name=fname,
|
||||
args=ast.arguments(
|
||||
args=[], vararg=None, kwarg=None,
|
||||
args=[], vararg=None, kwarg=None, posonlyargs=[],
|
||||
kwonlyargs=[], kw_defaults=[], defaults=[]),
|
||||
body=f(parts).stmts,
|
||||
decorator_list=[])
|
||||
@ -1347,18 +1263,17 @@ class HyASTCompiler(object):
|
||||
">>": ast.RShift,
|
||||
"|": ast.BitOr,
|
||||
"^": ast.BitXor,
|
||||
"&": ast.BitAnd}
|
||||
if PY3:
|
||||
m_ops["@"] = ast.MatMult
|
||||
"&": ast.BitAnd,
|
||||
"@": ast.MatMult}
|
||||
|
||||
@special(["+", "*", "|"], [many(FORM)])
|
||||
@special(["-", "/", "&", (PY3, "@")], [oneplus(FORM)])
|
||||
@special(["-", "/", "&", "@"], [oneplus(FORM)])
|
||||
@special(["**", "//", "<<", ">>"], [times(2, Inf, FORM)])
|
||||
@special(["%", "^"], [times(2, 2, FORM)])
|
||||
def compile_maths_expression(self, expr, root, args):
|
||||
if len(args) == 0:
|
||||
# Return the identity element for this operator.
|
||||
return asty.Num(expr, n=long_type(
|
||||
return asty.Num(expr, n=(
|
||||
{"+": 0, "|": 0, "*": 1}[root]))
|
||||
|
||||
if len(args) == 1:
|
||||
@ -1411,9 +1326,7 @@ class HyASTCompiler(object):
|
||||
def _compile_assign(self, root, name, result):
|
||||
|
||||
str_name = "%s" % name
|
||||
if str_name in (["None"] + (["True", "False"] if PY3 else [])):
|
||||
# Python 2 allows assigning to True and False, although
|
||||
# this is rarely wise.
|
||||
if str_name in ("None", "True", "False"):
|
||||
raise self._syntax_error(name,
|
||||
"Can't assign to `%s'" % str_name)
|
||||
|
||||
@ -1477,13 +1390,12 @@ class HyASTCompiler(object):
|
||||
expr, test=cond_compiled.force_expr,
|
||||
body=body.stmts or [asty.Pass(expr)],
|
||||
orelse=orel.stmts)
|
||||
ret.contains_yield = body.contains_yield
|
||||
|
||||
return ret
|
||||
|
||||
NASYM = some(lambda x: isinstance(x, HySymbol) and x not in (
|
||||
"&optional", "&rest", "&kwonly", "&kwargs"))
|
||||
@special(["fn", "fn*", (PY3, "fn/a")], [
|
||||
@special(["fn", "fn*", "fn/a"], [
|
||||
# The starred version is for internal use (particularly, in the
|
||||
# definition of `defn`). It ensures that a FunctionDef is
|
||||
# produced rather than a Lambda.
|
||||
@ -1501,29 +1413,19 @@ class HyASTCompiler(object):
|
||||
|
||||
mandatory, optional, rest, kwonly, kwargs = params
|
||||
optional, defaults, ret = self._parse_optional_args(optional)
|
||||
if kwonly is not None and not PY3:
|
||||
raise self._syntax_error(params,
|
||||
"&kwonly parameters require Python 3")
|
||||
kwonly, kw_defaults, ret2 = self._parse_optional_args(kwonly, True)
|
||||
ret += ret2
|
||||
main_args = mandatory + optional
|
||||
|
||||
if PY3:
|
||||
# Python 3.4+ requires that args are an ast.arg object, rather
|
||||
# than an ast.Name or bare string.
|
||||
main_args, kwonly, [rest], [kwargs] = (
|
||||
[[x and asty.arg(x, arg=ast_str(x), annotation=None)
|
||||
for x in o]
|
||||
for o in (main_args or [], kwonly or [], [rest], [kwargs])])
|
||||
else:
|
||||
main_args = [asty.Name(x, id=ast_str(x), ctx=ast.Param())
|
||||
for x in main_args]
|
||||
rest = rest and ast_str(rest)
|
||||
kwargs = kwargs and ast_str(kwargs)
|
||||
main_args, kwonly, [rest], [kwargs] = (
|
||||
[[x and asty.arg(x, arg=ast_str(x), annotation=None)
|
||||
for x in o]
|
||||
for o in (main_args or [], kwonly or [], [rest], [kwargs])])
|
||||
|
||||
args = ast.arguments(
|
||||
args=main_args, defaults=defaults,
|
||||
vararg=rest,
|
||||
posonlyargs=[],
|
||||
kwonlyargs=kwonly, kw_defaults=kw_defaults,
|
||||
kwarg=kwargs)
|
||||
|
||||
@ -1533,13 +1435,7 @@ class HyASTCompiler(object):
|
||||
return ret + asty.Lambda(expr, args=args, body=body.force_expr)
|
||||
|
||||
if body.expr:
|
||||
if body.contains_yield and not PY3:
|
||||
# Prior to PEP 380 (introduced in Python 3.3)
|
||||
# generators may not have a value in a return
|
||||
# statement.
|
||||
body += body.expr_as_stmt()
|
||||
else:
|
||||
body += asty.Return(body.expr, value=body.expr)
|
||||
body += asty.Return(body.expr, value=body.expr)
|
||||
|
||||
name = self.get_anon_var()
|
||||
|
||||
@ -1585,7 +1481,7 @@ class HyASTCompiler(object):
|
||||
base_list, docstring, attrs, body = rest or ([[]], None, None, [])
|
||||
|
||||
bases_expr, bases, keywords = (
|
||||
self._compile_collect(base_list[0], with_kwargs=PY3))
|
||||
self._compile_collect(base_list[0], with_kwargs=True))
|
||||
|
||||
bodyr = Result()
|
||||
|
||||
@ -1754,16 +1650,14 @@ class HyASTCompiler(object):
|
||||
# a typecheck, eg (type :foo)
|
||||
with_kwargs = root not in (
|
||||
"type", "HyKeyword", "keyword", "name", "keyword?", "identity")
|
||||
args, ret, keywords, oldpy_star, oldpy_kw = self._compile_collect(
|
||||
args, with_kwargs, oldpy_unpack=True)
|
||||
args, ret, keywords = self._compile_collect(args, with_kwargs)
|
||||
|
||||
return func + ret + asty.Call(
|
||||
expr, func=func.expr, args=args, keywords=keywords,
|
||||
starargs=oldpy_star, kwargs=oldpy_kw)
|
||||
expr, func=func.expr, args=args, keywords=keywords)
|
||||
|
||||
@builds_model(HyInteger, HyFloat, HyComplex)
|
||||
def compile_numeric_literal(self, x):
|
||||
f = {HyInteger: long_type,
|
||||
f = {HyInteger: int,
|
||||
HyFloat: float,
|
||||
HyComplex: complex}[type(x)]
|
||||
return asty.Num(x, n=f(x))
|
||||
@ -1810,9 +1704,9 @@ class HyASTCompiler(object):
|
||||
def compile_string(self, string):
|
||||
if type(string) is HyString and string.is_format:
|
||||
# This is a format string (a.k.a. an f-string).
|
||||
return self._format_string(string, str_type(string))
|
||||
node = asty.Bytes if PY3 and type(string) is HyBytes else asty.Str
|
||||
f = bytes_type if type(string) is HyBytes else str_type
|
||||
return self._format_string(string, str(string))
|
||||
node = asty.Bytes if type(string) is HyBytes else asty.Str
|
||||
f = bytes if type(string) is HyBytes else str
|
||||
return node(string, s=f(string))
|
||||
|
||||
def _format_string(self, string, rest, allow_recursion=True):
|
||||
@ -1859,7 +1753,7 @@ class HyASTCompiler(object):
|
||||
try:
|
||||
model, item = parse_one_thing(item)
|
||||
except (ValueError, LexException) as e:
|
||||
raise self._syntax_error(string, "f-string: " + str_type(e))
|
||||
raise self._syntax_error(string, "f-string: " + str(e))
|
||||
|
||||
# Look for a conversion character.
|
||||
item = item.lstrip()
|
||||
@ -1933,7 +1827,7 @@ def get_compiler_module(module=None, compiler=None, calling_frame=False):
|
||||
|
||||
module = getattr(compiler, 'module', None) or module
|
||||
|
||||
if isinstance(module, string_types):
|
||||
if isinstance(module, str):
|
||||
if module.startswith('<') and module.endswith('>'):
|
||||
module = types.ModuleType(module)
|
||||
else:
|
||||
@ -2098,7 +1992,7 @@ def hy_compile(tree, module, root=ast.Module, get_expr=False,
|
||||
"""
|
||||
module = get_compiler_module(module, compiler, False)
|
||||
|
||||
if isinstance(module, string_types):
|
||||
if isinstance(module, str):
|
||||
if module.startswith('<') and module.endswith('>'):
|
||||
module = types.ModuleType(module)
|
||||
else:
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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])
|
||||
|
@ -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 '@))
|
||||
|
408
hy/importer.py
408
hy/importer.py
@ -19,33 +19,6 @@ from contextlib import contextmanager
|
||||
|
||||
from hy.compiler import hy_compile, hy_ast_compile_flags
|
||||
from hy.lex import hy_parse
|
||||
from hy._compat import PY3
|
||||
|
||||
|
||||
def cache_from_source(source_path):
|
||||
"""Get the cached bytecode file name for a given source file name.
|
||||
|
||||
This function's name is set to mirror Python 3.x's
|
||||
`importlib.util.cache_from_source`, which is also used when available.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
source_path : str
|
||||
Path of the source file
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : str
|
||||
Path of the corresponding bytecode file that may--or may
|
||||
not--actually exist.
|
||||
"""
|
||||
if PY3:
|
||||
return importlib.util.cache_from_source(source_path)
|
||||
else:
|
||||
# If source_path has a file extension, replace it with ".pyc".
|
||||
# Otherwise, just append ".pyc".
|
||||
d, f = os.path.split(source_path)
|
||||
return os.path.join(d, re.sub(r"(?:\.[^.]+)?\Z", ".pyc", f))
|
||||
|
||||
|
||||
@contextmanager
|
||||
@ -135,370 +108,41 @@ def _get_code_from_file(run_name, fname=None,
|
||||
source = f.read().decode('utf-8')
|
||||
code = compile(source, fname, 'exec')
|
||||
|
||||
return (code, fname) if PY3 else code
|
||||
return (code, fname)
|
||||
|
||||
|
||||
if PY3:
|
||||
importlib.machinery.SOURCE_SUFFIXES.insert(0, '.hy')
|
||||
_py_source_to_code = importlib.machinery.SourceFileLoader.source_to_code
|
||||
importlib.machinery.SOURCE_SUFFIXES.insert(0, '.hy')
|
||||
_py_source_to_code = importlib.machinery.SourceFileLoader.source_to_code
|
||||
|
||||
def _could_be_hy_src(filename):
|
||||
return (os.path.isfile(filename) and
|
||||
(filename.endswith('.hy') or
|
||||
not any(filename.endswith(ext)
|
||||
for ext in importlib.machinery.SOURCE_SUFFIXES[1:])))
|
||||
def _could_be_hy_src(filename):
|
||||
return (os.path.isfile(filename) and
|
||||
(filename.endswith('.hy') or
|
||||
not any(filename.endswith(ext)
|
||||
for ext in importlib.machinery.SOURCE_SUFFIXES[1:])))
|
||||
|
||||
def _hy_source_to_code(self, data, path, _optimize=-1):
|
||||
if _could_be_hy_src(path):
|
||||
source = data.decode("utf-8")
|
||||
hy_tree = hy_parse(source, filename=path)
|
||||
with loader_module_obj(self) as module:
|
||||
data = hy_compile(hy_tree, module)
|
||||
def _hy_source_to_code(self, data, path, _optimize=-1):
|
||||
if _could_be_hy_src(path):
|
||||
source = data.decode("utf-8")
|
||||
hy_tree = hy_parse(source, filename=path)
|
||||
with loader_module_obj(self) as module:
|
||||
data = hy_compile(hy_tree, module)
|
||||
|
||||
return _py_source_to_code(self, data, path, _optimize=_optimize)
|
||||
return _py_source_to_code(self, data, path, _optimize=_optimize)
|
||||
|
||||
importlib.machinery.SourceFileLoader.source_to_code = _hy_source_to_code
|
||||
importlib.machinery.SourceFileLoader.source_to_code = _hy_source_to_code
|
||||
|
||||
# This is actually needed; otherwise, pre-created finders assigned to the
|
||||
# current dir (i.e. `''`) in `sys.path` will not catch absolute imports of
|
||||
# directory-local modules!
|
||||
sys.path_importer_cache.clear()
|
||||
# This is actually needed; otherwise, pre-created finders assigned to the
|
||||
# current dir (i.e. `''`) in `sys.path` will not catch absolute imports of
|
||||
# directory-local modules!
|
||||
sys.path_importer_cache.clear()
|
||||
|
||||
# Do this one just in case?
|
||||
importlib.invalidate_caches()
|
||||
|
||||
# XXX: These and the 2.7 counterparts below aren't truly cross-compliant.
|
||||
# They're useful for testing, though.
|
||||
HyImporter = importlib.machinery.FileFinder
|
||||
HyLoader = importlib.machinery.SourceFileLoader
|
||||
|
||||
else:
|
||||
import imp
|
||||
import py_compile
|
||||
import marshal
|
||||
import struct
|
||||
import traceback
|
||||
|
||||
from pkgutil import ImpImporter, ImpLoader
|
||||
|
||||
def _could_be_hy_src(filename):
|
||||
return (filename.endswith('.hy') or
|
||||
(os.path.isfile(filename) and
|
||||
not any(filename.endswith(s[0]) for s in imp.get_suffixes())))
|
||||
|
||||
class HyLoader(ImpLoader, object):
|
||||
def __init__(self, fullname, filename, fileobj=None, etc=None):
|
||||
"""This constructor is designed for some compatibility with
|
||||
SourceFileLoader."""
|
||||
if etc is None and filename is not None:
|
||||
if _could_be_hy_src(filename):
|
||||
etc = ('.hy', 'U', imp.PY_SOURCE)
|
||||
if fileobj is None:
|
||||
fileobj = io.open(filename, 'rU', encoding='utf-8')
|
||||
|
||||
super(HyLoader, self).__init__(fullname, fileobj, filename, etc)
|
||||
|
||||
def __getattr__(self, item):
|
||||
# We add these for Python >= 3.4 Loader interface compatibility.
|
||||
if item == 'path':
|
||||
return self.filename
|
||||
elif item == 'name':
|
||||
return self.fullname
|
||||
else:
|
||||
return super(HyLoader, self).__getattr__(item)
|
||||
|
||||
def exec_module(self, module, fullname=None):
|
||||
fullname = self._fix_name(fullname)
|
||||
code = self.get_code(fullname)
|
||||
eval(code, module.__dict__)
|
||||
|
||||
def load_module(self, fullname=None):
|
||||
"""Same as `pkgutil.ImpLoader`, with an extra check for Hy
|
||||
source and the option to not run `self.exec_module`."""
|
||||
fullname = self._fix_name(fullname)
|
||||
ext_type = self.etc[0]
|
||||
mod_type = self.etc[2]
|
||||
mod = None
|
||||
pkg_path = os.path.join(self.filename, '__init__.hy')
|
||||
if ext_type == '.hy' or (
|
||||
mod_type == imp.PKG_DIRECTORY and
|
||||
os.path.isfile(pkg_path)):
|
||||
|
||||
was_in_sys = fullname in sys.modules
|
||||
if was_in_sys:
|
||||
mod = sys.modules[fullname]
|
||||
else:
|
||||
mod = sys.modules.setdefault(
|
||||
fullname, types.ModuleType(fullname))
|
||||
|
||||
# TODO: Should we set these only when not in `sys.modules`?
|
||||
if mod_type == imp.PKG_DIRECTORY:
|
||||
mod.__file__ = pkg_path
|
||||
mod.__path__ = [self.filename]
|
||||
mod.__package__ = fullname
|
||||
else:
|
||||
# mod.__path__ = self.filename
|
||||
mod.__file__ = self.get_filename(fullname)
|
||||
mod.__package__ = '.'.join(fullname.split('.')[:-1])
|
||||
|
||||
mod.__name__ = fullname
|
||||
|
||||
try:
|
||||
self.exec_module(mod, fullname=fullname)
|
||||
except Exception:
|
||||
# Follow Python 2.7 logic and only remove a new, bad
|
||||
# module; otherwise, leave the old--and presumably
|
||||
# good--module in there.
|
||||
if not was_in_sys:
|
||||
del sys.modules[fullname]
|
||||
raise
|
||||
|
||||
if mod is None:
|
||||
self._reopen()
|
||||
try:
|
||||
mod = imp.load_module(fullname, self.file, self.filename,
|
||||
self.etc)
|
||||
finally:
|
||||
if self.file:
|
||||
self.file.close()
|
||||
|
||||
mod.__loader__ = self
|
||||
return mod
|
||||
|
||||
def _reopen(self):
|
||||
"""Same as `pkgutil.ImpLoader`, with an extra check for Hy
|
||||
source"""
|
||||
if self.file and self.file.closed:
|
||||
ext_type = self.etc[0]
|
||||
if ext_type == '.hy':
|
||||
self.file = io.open(self.filename, 'rU', encoding='utf-8')
|
||||
else:
|
||||
super(HyLoader, self)._reopen()
|
||||
|
||||
def byte_compile_hy(self, fullname=None):
|
||||
fullname = self._fix_name(fullname)
|
||||
if fullname is None:
|
||||
fullname = self.fullname
|
||||
|
||||
hy_source = self.get_source(fullname)
|
||||
hy_tree = hy_parse(hy_source, filename=self.filename)
|
||||
|
||||
with loader_module_obj(self) as module:
|
||||
hy_ast = hy_compile(hy_tree, module)
|
||||
|
||||
code = compile(hy_ast, self.filename, 'exec',
|
||||
hy_ast_compile_flags)
|
||||
|
||||
if not sys.dont_write_bytecode:
|
||||
try:
|
||||
hyc_compile(code, module=fullname)
|
||||
except IOError:
|
||||
pass
|
||||
return code
|
||||
|
||||
def get_code(self, fullname=None):
|
||||
"""Same as `pkgutil.ImpLoader`, with an extra check for Hy
|
||||
source"""
|
||||
fullname = self._fix_name(fullname)
|
||||
ext_type = self.etc[0]
|
||||
if ext_type == '.hy':
|
||||
# Looks like we have to manually check for--and update--
|
||||
# the bytecode.
|
||||
t_py = long(os.stat(self.filename).st_mtime)
|
||||
pyc_file = cache_from_source(self.filename)
|
||||
if os.path.isfile(pyc_file):
|
||||
t_pyc = long(os.stat(pyc_file).st_mtime)
|
||||
|
||||
if t_pyc is not None and t_pyc >= t_py:
|
||||
with open(pyc_file, 'rb') as f:
|
||||
if f.read(4) == imp.get_magic():
|
||||
t = struct.unpack('<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.
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
|
||||
|
||||
|
29
hy/macros.py
29
hy/macros.py
@ -9,7 +9,7 @@ import traceback
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
from hy._compat import PY3, string_types, reraise, rename_function
|
||||
from hy._compat import 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")
|
||||
|
32
hy/models.py
32
hy/models.py
@ -6,7 +6,6 @@ from __future__ import unicode_literals
|
||||
from contextlib import contextmanager
|
||||
from math import isnan, isinf
|
||||
from hy import _initialize_env_var
|
||||
from hy._compat import PY3, str_type, bytes_type, long_type, string_types
|
||||
from hy.errors import HyWrapperError
|
||||
from fractions import Fraction
|
||||
from clint.textui import colored
|
||||
@ -88,7 +87,7 @@ def repr_indent(obj):
|
||||
return repr(obj).replace("\n", "\n ")
|
||||
|
||||
|
||||
class HyString(HyObject, str_type):
|
||||
class HyString(HyObject, str):
|
||||
"""
|
||||
Generic Hy String object. Helpful to store string literals from Hy
|
||||
scripts. It's either a ``str`` or a ``unicode``, depending on the
|
||||
@ -100,20 +99,20 @@ class HyString(HyObject, str_type):
|
||||
value.brackets = brackets
|
||||
return value
|
||||
|
||||
_wrappers[str_type] = HyString
|
||||
_wrappers[str] = HyString
|
||||
|
||||
|
||||
class HyBytes(HyObject, bytes_type):
|
||||
class HyBytes(HyObject, bytes):
|
||||
"""
|
||||
Generic Hy Bytes object. It's either a ``bytes`` or a ``str``, depending
|
||||
on the Python version.
|
||||
"""
|
||||
pass
|
||||
|
||||
_wrappers[bytes_type] = HyBytes
|
||||
_wrappers[bytes] = HyBytes
|
||||
|
||||
|
||||
class HySymbol(HyObject, str_type):
|
||||
class HySymbol(HyObject, str):
|
||||
"""
|
||||
Hy Symbol. Basically a string.
|
||||
"""
|
||||
@ -170,42 +169,39 @@ def strip_digit_separators(number):
|
||||
# Don't strip a _ or , if it's the first character, as _42 and
|
||||
# ,42 aren't valid numbers
|
||||
return (number[0] + number[1:].replace("_", "").replace(",", "")
|
||||
if isinstance(number, string_types) and len(number) > 1
|
||||
if isinstance(number, str) and len(number) > 1
|
||||
else number)
|
||||
|
||||
|
||||
class HyInteger(HyObject, long_type):
|
||||
class HyInteger(HyObject, int):
|
||||
"""
|
||||
Internal representation of a Hy Integer. May raise a ValueError as if
|
||||
int(foo) was called, given HyInteger(foo). On python 2.x long will
|
||||
be used instead
|
||||
int(foo) was called, given HyInteger(foo).
|
||||
"""
|
||||
|
||||
def __new__(cls, number, *args, **kwargs):
|
||||
if isinstance(number, string_types):
|
||||
if isinstance(number, str):
|
||||
number = strip_digit_separators(number)
|
||||
bases = {"0x": 16, "0o": 8, "0b": 2}
|
||||
for leader, base in bases.items():
|
||||
if number.startswith(leader):
|
||||
# We've got a string, known leader, set base.
|
||||
number = long_type(number, base=base)
|
||||
number = int(number, base=base)
|
||||
break
|
||||
else:
|
||||
# We've got a string, no known leader; base 10.
|
||||
number = long_type(number, base=10)
|
||||
number = int(number, base=10)
|
||||
else:
|
||||
# We've got a non-string; convert straight.
|
||||
number = long_type(number)
|
||||
number = int(number)
|
||||
return super(HyInteger, cls).__new__(cls, number)
|
||||
|
||||
|
||||
_wrappers[int] = HyInteger
|
||||
if not PY3: # do not add long on python3
|
||||
_wrappers[long_type] = HyInteger
|
||||
|
||||
|
||||
def check_inf_nan_cap(arg, value):
|
||||
if isinstance(arg, string_types):
|
||||
if isinstance(arg, str):
|
||||
if isinf(value) and "i" in arg.lower() and "Inf" not in arg:
|
||||
raise ValueError('Inf must be capitalized as "Inf"')
|
||||
if isnan(value) and "NaN" not in arg:
|
||||
@ -233,7 +229,7 @@ class HyComplex(HyObject, complex):
|
||||
"""
|
||||
|
||||
def __new__(cls, real, imag=0, *args, **kwargs):
|
||||
if isinstance(real, string_types):
|
||||
if isinstance(real, str):
|
||||
value = super(HyComplex, cls).__new__(
|
||||
cls, strip_digit_separators(real)
|
||||
)
|
||||
|
2
setup.py
2
setup.py
@ -80,8 +80,6 @@ setup(
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Lisp",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
|
@ -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)")
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 [])
|
||||
|
@ -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 []
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)))
|
||||
|
@ -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))))))
|
||||
|
@ -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 []
|
||||
|
@ -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)))
|
||||
|
@ -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"))))
|
||||
|
@ -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 <<
|
||||
|
@ -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))))))
|
@ -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
|
||||
|
@ -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)])
|
||||
|
Loading…
Reference in New Issue
Block a user