From 5d895c2005e71f4ee3fcef021edd1727acd71bd9 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sat, 13 Apr 2013 12:50:25 +0200 Subject: [PATCH] Making (import) a lot smarter With these changes, the import function will become a lot smarter, and will combine all of import, import-from and import-as in a hyly lispy syntax: (import sys os whatever_else) (import [sys [exit argv]] [os :as real_os] [whatever_else [some_function :as sf]]) That is, each argument of import can be: - A plain symbol, which will be imported - A list, which will be handled specially If the argument is a list, the first element will always be the module name to import, the second member can be either of these: - A list of symbols to import - The ':as' keyword - Nothing If it is the ':as' keyword, the third argument must be an alias. If it is a list of symbols to import, we'll iterate through that list too. If any symbol is followed by an ':as' keyword, we'll pick all three, and treat the third member as an alias. If there is nothing else in the list, we'll import the module as-is. All this combined fixes #113. Signed-off-by: Gergely Nagy --- hy/compiler.py | 44 ++++++++++++++++++++++++++++++---- tests/native_tests/language.hy | 28 ++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 318174b..2342c7f 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -466,11 +466,47 @@ class HyASTCompiler(object): @builds("import") def compile_import_expression(self, expr): + def _compile_import(expr, module, names = None, importer = ast.Import): + return [ + importer( + lineno=expr.start_line, + col_offset=expr.start_column, + module=ast_str(module), + names=names or [ast.alias(name=ast_str(module), asname=None)], + level=0) + ] + expr.pop(0) # index - return ast.Import( - lineno=expr.start_line, - col_offset=expr.start_column, - names=[ast.alias(name=ast_str(x), asname=None) for x in expr]) + rimports = [] + while len(expr) > 0: + iexpr = expr.pop(0) + + if type(iexpr) == HySymbol: + rimports += _compile_import(expr, iexpr) + elif type(iexpr) == HyList and len(iexpr) == 1: + rimports += _compile_import(expr, iexpr.pop(0)) + elif type(iexpr) == HyList: + module = iexpr.pop(0) + if type(iexpr[0]) == HyKeyword and iexpr[0] == HyKeyword(":as"): + assert len(iexpr) == 2, "garbage after aliased import" + iexpr.pop(0) # :as + alias=iexpr.pop(0) + rimports += _compile_import(expr, ast_str(module), + [ast.alias(name=ast_str(module), + asname=ast_str(alias))]) + elif type(iexpr[0] == HyList): + symbol_list = iexpr.pop(0) + names = [] + while len(symbol_list) > 0: + sym = symbol_list.pop(0) + if len(symbol_list) > 0 and type(symbol_list[0]) == HyKeyword: + symbol_list.pop(0) + alias = ast_str(symbol_list.pop(0)) + else: + alias = None + names += [ast.alias(name=ast_str(sym), asname=alias)] + rimports += _compile_import(expr, module, names, ast.ImportFrom) + return rimports @builds("import_as") def compile_import_as_expression(self, expr): diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 45045c6..777b37b 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -546,3 +546,31 @@ (assert (= "foobar" (eval (quote "foobar")))) (setv x (quote 42)) (assert (= x (eval x)))) + +(defn test-import-syntax [] + "NATIVE: test the import syntax." + + ; Simple import + (import sys os) + + ; from os.path import basename + (import [os.path [basename]]) + (assert (= (basename "/some/path") "path")) + + ; import os.path as p + (import [os.path :as p]) + (assert (= p.basename basename)) + + ; from os.path import basename as bn + (import [os.path [basename :as bn]]) + (assert (= bn basename)) + + (import [sys]) + + ;; Multiple stuff to import + (import sys [os.path [dirname]] + [os.path :as op] + [os.path [dirname :as dn]]) + (assert (= (dirname "/some/path") "/some")) + (assert (= op.dirname dirname)) + (assert (= dn dirname)))