Merge pull request #1500 from vodik/deprecations

Deal with some deprecation and resource warnings / fix (return) semantics
This commit is contained in:
Tuukka Turto 2018-02-27 23:20:49 +02:00 committed by GitHub
commit c1e5c3e48c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 87 additions and 37 deletions

View File

@ -64,7 +64,7 @@ ifeq (Python 2.6,$(findstring Python 2.6,$(shell python -V 2>&1)))
endif
$(pip) install -r requirements-travis.txt
$(pip) install coveralls
$(pip) install --allow-all-external -e .
$(pip) install -e .
coveralls:
$(coveralls)

View File

@ -1,5 +1,12 @@
.. default-role:: code
Unreleased
==============================
Bug Fixes
------------------------------
* Fix `(return)` so it works correctly to exit a Python 2 generator
0.14.0
==============================

View File

@ -16,6 +16,7 @@ from hy._compat import (
raise_empty)
from hy.macros import require, macroexpand, tag_macroexpand
import hy.importer
import hy.inspect
import traceback
import importlib
@ -24,7 +25,6 @@ import ast
import sys
import keyword
import copy
import inspect
from collections import defaultdict
from cmath import isnan
@ -441,10 +441,11 @@ class HyASTCompiler(object):
# _compile_table[atom_type] is a method for compiling this
# type of atom, so call it. If it has an extra parameter,
# pass in `atom_type`.
arity = len(inspect.getargspec(_compile_table[atom_type])[0])
ret = (_compile_table[atom_type](self, atom, atom_type)
atom_compiler = _compile_table[atom_type]
arity = hy.inspect.get_arity(atom_compiler)
ret = (atom_compiler(self, atom, atom_type)
if arity == 3
else _compile_table[atom_type](self, atom))
else atom_compiler(self, atom))
if not isinstance(ret, Result):
ret = Result() + ret
return ret
@ -2040,8 +2041,10 @@ class HyASTCompiler(object):
@checkargs(max=1)
def compile_return(self, expr):
ret = Result()
if len(expr) > 1:
ret += self.compile(expr[1])
if len(expr) == 1:
return asty.Return(expr, value=None)
ret += self.compile(expr[1])
return ret + asty.Return(expr, value=ret.force_expr)
@builds("defclass")

View File

@ -29,15 +29,15 @@
(try (while True
(yield (get self index))
(setv index (inc index)))
(except [_ IndexError]
(raise StopIteration))))
(except [IndexError]
(return))))
--len-- (fn [self]
"length of the sequence, dangerous for infinite sequences"
(setv index (. self high-water))
(try (while True
(get self index)
(setv index (inc index)))
(except [_ IndexError]
(except [IndexError]
(len (. self cache)))))
max-items-in-repr 10
--str-- (fn [self]

View File

@ -422,7 +422,10 @@ Raises ValueError for (not (pos? n))."
(for* [val citer]
(yield val)
(for* [_ (range skip)]
(next citer))))
(try
(next citer)
(except [StopIteration]
(return))))))
(defn zero? [n]
"Check if `n` equals 0."

View File

@ -2,6 +2,8 @@
# This file is part of Hy, which is free software licensed under the Expat
# license. See the LICENSE.
from __future__ import absolute_import
from hy.compiler import hy_compile, HyTypeError
from hy.models import HyObject, HyExpression, HySymbol, replace_hy_obj
from hy.lex import tokenize, LexException

37
hy/inspect.py Normal file
View File

@ -0,0 +1,37 @@
# Copyright 2018 the authors.
# This file is part of Hy, which is free software licensed under the Expat
# license. See the LICENSE.
from __future__ import absolute_import
import inspect
try:
# Check if we have the newer inspect.signature available.
# Otherwise fallback to the legacy getargspec.
inspect.signature # noqa
except AttributeError:
def get_arity(fn):
return len(inspect.getargspec(fn)[0])
def has_kwargs(fn):
argspec = inspect.getargspec(fn)
return argspec.keywords is not None
def format_args(fn):
argspec = inspect.getargspec(fn)
return inspect.formatargspec(*argspec)
else:
def get_arity(fn):
parameters = inspect.signature(fn).parameters
return sum(1 for param in parameters.values()
if param.kind == param.POSITIONAL_OR_KEYWORD)
def has_kwargs(fn):
parameters = inspect.signature(fn).parameters
return any(param.kind == param.VAR_KEYWORD
for param in parameters.values())
def format_args(fn):
return str(inspect.signature(fn))

View File

@ -2,9 +2,8 @@
# This file is part of Hy, which is free software licensed under the Expat
# license. See the LICENSE.
from inspect import getargspec, formatargspec
import hy.inspect
from hy.models import replace_hy_obj, HyExpression, HySymbol
from hy.errors import HyTypeError, HyMacroExpansionError
from collections import defaultdict
@ -36,8 +35,7 @@ def macro(name):
def _(fn):
fn.__name__ = '({})'.format(name)
try:
argspec = getargspec(fn)
fn._hy_macro_pass_compiler = argspec.keywords is not None
fn._hy_macro_pass_compiler = hy.inspect.has_kwargs(fn)
except Exception:
# An exception might be raised if fn has arguments with
# names that are invalid in Python.
@ -136,9 +134,7 @@ def make_empty_fn_copy(fn):
# can continue running. Unfortunately, the error message that might get
# raised later on while expanding a macro might not make sense at all.
argspec = getargspec(fn)
formatted_args = formatargspec(*argspec)
formatted_args = hy.inspect.format_args(fn)
fn_str = 'lambda {}: None'.format(
formatted_args.lstrip('(').rstrip(')'))
empty_fn = eval(fn_str)

View File

@ -20,3 +20,6 @@ ignore_errors = True
# Be sure to include Hy test functions whose names end with "?",
# which will be mangled to begin with "is_".
python_functions=test_* is_test_*
filterwarnings =
once::DeprecationWarning
once::PendingDeprecationWarning

View File

@ -8,7 +8,7 @@ from __future__ import unicode_literals
from hy import HyString
from hy.models import HyObject
from hy.compiler import hy_compile
from hy.importer import import_buffer_to_hst
from hy.importer import hy_eval, import_buffer_to_hst
from hy.errors import HyCompileError, HyTypeError
from hy.lex.exceptions import LexException
from hy._compat import PY3
@ -30,6 +30,10 @@ def can_compile(expr):
return hy_compile(import_buffer_to_hst(expr), "__main__")
def can_eval(expr):
return hy_eval(import_buffer_to_hst(expr))
def cant_compile(expr):
try:
hy_compile(import_buffer_to_hst(expr), "__main__")
@ -664,3 +668,8 @@ def test_ast_good_yield_from():
def test_ast_bad_yield_from():
"Make sure AST can't compile invalid yield-from"
cant_compile("(yield-from)")
def test_eval_generator_with_return():
"""Ensure generators with a return statement works."""
can_eval("(fn [] (yield 1) (yield 2) (return))")

View File

@ -37,19 +37,9 @@ def run_cmd(cmd, stdin_data=None, expect=0, dontwritebytecode=False):
universal_newlines=True,
shell=False,
env=env)
if stdin_data is not None:
p.stdin.write(stdin_data)
p.stdin.flush()
p.stdin.close()
# Read stdout and stderr otherwise if the PIPE buffer is full, we might
# wait for ever…
stdout = ""
stderr = ""
while p.poll() is None:
stdout += p.stdout.read()
stderr += p.stderr.read()
assert p.returncode == expect
return stdout, stderr
output = p.communicate(input=stdin_data)
assert p.wait() == expect
return output
def rm(fpath):

View File

@ -304,11 +304,11 @@ def test_nospace():
def test_escapes():
""" Ensure we can escape things """
entry = tokenize("(foo \"foo\\n\")")[0]
entry = tokenize(r"""(foo "foo\n")""")[0]
assert entry[1] == "foo\n"
entry = tokenize("(foo \"foo\\s\")")[0]
assert entry[1] == "foo\\s"
entry = tokenize(r"""(foo r"foo\s")""")[0]
assert entry[1] == r"foo\s"
def test_unicode_escapes():

View File

@ -4,8 +4,8 @@ skipsdist = True
[testenv]
commands =
pip install --allow-all-external -e .
pytest
pip install -e .
pytest {posargs}
passenv =
TERM
deps =