def test_recursive_model_detection(): """Check for self-references: https://github.com/hylang/hy/issues/2153 """ self_ref_list = [1, 2, 3] self_ref_dict = {1: 1, 2: 2} self_ref_list[1] = self_ref_list self_ref_dict[2] = self_ref_dict mutually_ref_list = [1, 2, 3] mutually_ref_dict = {1: 1, 2: 2} mutually_ref_list[1] = mutually_ref_dict mutually_ref_dict[2] = mutually_ref_list for structure in [ self_ref_list, self_ref_dict, mutually_ref_list, mutually_ref_dict, ]: with pytest.raises(HyWrapperError) as exc: as_model(structure) assert "Self-referential" in str(exc)
def test_wrap_nested_expr(): """ Test conversion of Expressions with embedded non-HyObjects.""" wrapped = as_model(Expression([0])) assert type(wrapped) == Expression assert type(wrapped[0]) == Integer assert wrapped == Expression([Integer(0)])
def test_wrap_tuple(): """ Test conversion of tuples.""" wrapped = as_model((Integer(0),)) assert type(wrapped) == List assert type(wrapped[0]) == Integer assert wrapped == List([Integer(0)])
def test_wrap_int(): """ Test conversion of integers.""" wrapped = as_model(0) assert type(wrapped) == Integer
def hy_compile( tree, module, root=ast.Module, get_expr=False, compiler=None, filename=None, source=None, import_stdlib=True, ): """Compile a hy.models.Object tree into a Python AST Module. Args: tree (Object): The Hy AST object to compile. module (Union[str, types.ModuleType]): Module, or name of the module, in which the Hy tree is evaluated. The module associated with `compiler` takes priority over this value. root (Type[ast.AST]): Root object for the Python AST tree. get_expr (bool): If true, return a tuple with `(root_obj, last_expression)`. compiler (Optional[HyASTCompiler]): An existing Hy compiler to use for compilation. Also serves as the `module` value when given. filename (Optional[str]): The filename corresponding to the source for `tree`. This will be overridden by the `filename` field of `tree`, if any; otherwise, it defaults to "<string>". When `compiler` is given, its `filename` field value is always used. source (Optional[str]): A string containing the source code for `tree`. This will be overridden by the `source` field of `tree`, if any; otherwise, if `None`, an attempt will be made to obtain it from the module given by `module`. When `compiler` is given, its `source` field value is always used. Returns: ast.AST: A Python AST tree """ module = get_compiler_module(module, compiler, False) if isinstance(module, str): if module.startswith("<") and module.endswith(">"): module = types.ModuleType(module) else: module = importlib.import_module(mangle(module)) if not inspect.ismodule(module): raise TypeError("Invalid module type: {}".format(type(module))) filename = getattr(tree, "filename", filename) source = getattr(tree, "source", source) tree = as_model(tree) if not isinstance(tree, Object): raise TypeError( "`tree` must be a hy.models.Object or capable of " "being promoted to one" ) compiler = compiler or HyASTCompiler(module, filename=filename, source=source) if import_stdlib: # Import hy for compile time, but save the compiled AST. stdlib_ast = compiler.compile( mkexpr("eval-and-compile", mkexpr("import", "hy")) ) with compiler.scope: result = compiler.compile(tree) expr = result.force_expr if not get_expr: result += result.expr_as_stmt() body = [] if issubclass(root, ast.Module): # Pull out a single docstring and prepend to the resulting body. if ( result.stmts and isinstance(result.stmts[0], ast.Expr) and isinstance(result.stmts[0].value, ast.Str) ): body += [result.stmts.pop(0)] # Pull out any __future__ imports, since they are required to be at the beginning. while ( result.stmts and isinstance(result.stmts[0], ast.ImportFrom) and result.stmts[0].module == "__future__" ): body += [result.stmts.pop(0)] # Import hy for runtime. if import_stdlib: body += stdlib_ast.stmts body += result.stmts ret = root(body=body, type_ignores=[]) if get_expr: expr = ast.Expression(body=expr) ret = (ret, expr) return ret
def macroexpand(tree, module, compiler=None, once=False, result_ok=True): """Expand the toplevel macros for the given Hy AST tree. Load the macros from the given `module`, then expand the (top-level) macros in `tree` until we no longer can. `Expression` resulting from macro expansions are assigned the module in which the macro function is defined (determined using `inspect.getmodule`). If the resulting `Expression` is itself macro expanded, then the namespace of the assigned module is checked first for a macro corresponding to the expression's head/car symbol. If the head/car symbol of such a `Expression` is not found among the macros of its assigned module's namespace, the outer-most namespace--e.g. the one given by the `module` parameter--is used as a fallback. Args: tree (Union[Object, list]): Hy AST tree. module (Union[str, ModuleType]): Module used to determine the local namespace for macros. compiler (Optional[HyASTCompiler] ): The compiler object passed to expanded macros. Defaults to None once (bool): Only expand the first macro in `tree`. Defaults to False result_ok (bool): Whether or not it's okay to return a compiler `Result` instance. Defaults to True. Returns: Union[Object, Result]: A mutated tree with macros expanded. """ if not inspect.ismodule(module): module = importlib.import_module(module) assert not compiler or compiler.module == module while isinstance(tree, Expression) and tree: fn = tree[0] if fn in ("quote", "quasiquote") or not isinstance(fn, Symbol): break fn = mangle(fn) expr_modules = ([] if not hasattr(tree, "module") else [tree.module ]) + [module] expr_modules.append(builtins) # Choose the first namespace with the macro. m = next( (mod.__macros__[fn] for mod in expr_modules if fn in getattr(mod, "__macros__", ())), None, ) if not m: break with MacroExceptions(module, tree, compiler): if compiler: compiler.this = tree obj = m(compiler, *tree[1:]) if isinstance(obj, (hy.compiler.Result, AST)): return obj if result_ok else tree if isinstance(obj, Expression): obj.module = inspect.getmodule(m) tree = replace_hy_obj(obj, tree) if once: break tree = as_model(tree) return tree
def test_wrap_tuple(): wrapped = as_model((Integer(0), )) assert type(wrapped) == List assert type(wrapped[0]) == Integer assert wrapped == List([Integer(0)])
def test_wrap_int(): wrapped = as_model(0) assert type(wrapped) == Integer