def wrapper(hy_compiler, *args): if shadow and any(is_unpack("iterable", x) for x in args): # Try a shadow function call with this name instead. return Expression([Symbol("hy.pyops." + name), *args]).replace(hy_compiler.this) expr = hy_compiler.this root = unmangle(expr[0]) if py_version_required and sys.version_info < py_version_required: raise hy_compiler._syntax_error( expr, "`{}` requires Python {} or later".format( root, ".".join(map(str, py_version_required))), ) try: parse_tree = pattern.parse(args) except NoParseError as e: raise hy_compiler._syntax_error( expr[min(e.state.pos + 1, len(expr) - 1)], "parse error for pattern macro '{}': {}".format( root, e.msg.replace("<EOF>", "end of form")), ) return fn(hy_compiler, expr, root, *parse_tree)
def require(source_module, target_module, assignments, prefix=""): """Load macros from one module into the namespace of another. This function is called from the `require` special form in the compiler. Parameters ---------- source_module: str or types.ModuleType The module from which macros are to be imported. target_module: str, types.ModuleType or None The module into which the macros will be loaded. If `None`, then the caller's namespace. The latter is useful during evaluation of generated AST/bytecode. assignments: str or list of tuples of strs The string "ALL" or a list of macro name and alias pairs. prefix: str, optional ("") If nonempty, its value is prepended to the name of each imported macro. This allows one to emulate namespaced macros, like "mymacromodule.mymacro", which looks like an attribute of a module. Returns ------- out: boolean Whether or not macros were actually transferred. """ if target_module is None: parent_frame = inspect.stack()[1][0] target_namespace = parent_frame.f_globals target_module = target_namespace.get('__name__', None) elif isinstance(target_module, str): target_module = importlib.import_module(target_module) target_namespace = target_module.__dict__ elif inspect.ismodule(target_module): target_namespace = target_module.__dict__ else: raise HyTypeError( '`target_module` is not a recognized type: {}'.format( type(target_module))) # Let's do a quick check to make sure the source module isn't actually # the module being compiled (e.g. when `runpy` executes a module's code # in `__main__`). # We use the module's underlying filename for this (when they exist), since # it's the most "fixed" attribute. if _same_modules(source_module, target_module): return False if not inspect.ismodule(source_module): try: if source_module.startswith("."): source_dirs = source_module.split(".") target_dirs = (getattr(target_module, "__name__", target_module).split(".")) while (len(source_dirs) > 1 and source_dirs[0] == "" and target_dirs): source_dirs.pop(0) target_dirs.pop() package = ".".join(target_dirs + source_dirs[:-1]) else: package = None source_module = importlib.import_module(source_module, package) except ImportError as e: reraise(HyRequireError, HyRequireError(e.args[0]), None) source_macros = source_module.__dict__.setdefault('__macros__', {}) if not source_module.__macros__: if assignments != "ALL": for name, alias in assignments: try: require(f"{source_module.__name__}.{mangle(name)}", target_module, "ALL", prefix=alias) except HyRequireError as e: raise HyRequireError(f"Cannot import name '{name}'" f" from '{source_module.__name__}'" f" ({source_module.__file__})") return True else: return False target_macros = target_namespace.setdefault('__macros__', {}) if prefix: prefix += "." if assignments == "ALL": name_assigns = [(k, k) for k in source_macros.keys()] else: name_assigns = assignments for name, alias in name_assigns: _name = mangle(name) alias = mangle('#' + prefix + unmangle(alias)[1:] if unmangle(alias). startswith('#') else prefix + alias) if _name in source_module.__macros__: target_macros[alias] = source_macros[_name] else: raise HyRequireError('Could not require name {} from {}'.format( _name, source_module)) return True