Skip to content

Commit

Permalink
Fix AST handling of docstrings and __future__ ordering
Browse files Browse the repository at this point in the history
This closes hylang#1367 and closes hylang#1540
  • Loading branch information
brandonwillard committed Aug 27, 2018
1 parent c92fb3c commit dac510d
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 1 deletion.
26 changes: 25 additions & 1 deletion hy/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from hy.macros import require, macroexpand, tag_macroexpand
import hy.importer

import functools
import traceback
import importlib
import ast
Expand Down Expand Up @@ -279,6 +280,17 @@ def is_unpack(kind, x):
and x[0] == "unpack-" + kind)


def import_order(a, b):
"""Put __future__ imports ahead of others"""
if (isinstance(a, ast.ImportFrom) and
isinstance(b, (ast.Import, ast.ImportFrom)) and
a.module == '__future__'):
return -1
else:
return 0

import_order_key = functools.cmp_to_key(import_order)

_stdlib = {}


Expand Down Expand Up @@ -318,6 +330,7 @@ def update_imports(self, result):
def imports_as_stmts(self, expr):
"""Convert the Result's imports to statements"""
ret = Result()

for module, names in self.imports.items():
if None in names:
e = HyExpression([
Expand Down Expand Up @@ -1719,7 +1732,18 @@ def hy_compile(tree, module_name, root=ast.Module, get_expr=False):
if not get_expr:
result += result.expr_as_stmt()

body = compiler.imports_as_stmts(tree) + result.stmts
body = []

# Pull out a single docstring and prepend to the resulting body.
if (len(result.stmts) > 0 and
issubclass(root, ast.Module) and
isinstance(result.stmts[0], ast.Expr) and
isinstance(result.stmts[0].value, ast.Str)):

body += [result.stmts.pop(0)]

body += sorted(compiler.imports_as_stmts(tree) + result.stmts,
key=import_order_key)

ret = root(body=body)

Expand Down
28 changes: 28 additions & 0 deletions tests/compilers/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,3 +664,31 @@ def test_ast_bad_yield_from():
def test_eval_generator_with_return():
"""Ensure generators with a return statement works."""
can_eval("(fn [] (yield 1) (yield 2) (return))")


def test_docsting_imports():
"""Confirm that docstrings come before import statements in AST output."""
hy_tree = import_buffer_to_hst('"hello world"\n(print (first [1 2]))')
hy_ast = hy_compile(hy_tree, 'test')
assert hy_ast.body[0].value.s == 'hello world'


def test_futures_imports():
"""Make sure __future__ imports go first."""
hy_tree = import_buffer_to_hst((
'(import [__future__ [print_function]])\n'
'(import sys)\n'
'(setv name 10)'))

hy_ast = hy_compile(hy_tree, 'test')
assert hy_ast.body[0].module == '__future__'
assert hy_ast.body[1].module == 'hy.core.language'

hy_tree = import_buffer_to_hst((
'(import sys)\n'
'(import [__future__ [print_function]])\n'
'(setv name 10)'))

hy_ast = hy_compile(hy_tree, 'test')
assert hy_ast.body[0].module == '__future__'
assert hy_ast.body[1].module == 'hy.core.language'

0 comments on commit dac510d

Please sign in to comment.