2020-01-03 19:47:51 +01:00
|
|
|
# Copyright 2020 the authors.
|
2017-04-27 23:16:57 +02:00
|
|
|
# This file is part of Hy, which is free software licensed under the Expat
|
|
|
|
# license. See the LICENSE.
|
2018-10-29 02:43:17 +01:00
|
|
|
import sys
|
2018-08-20 06:29:29 +02:00
|
|
|
import importlib
|
2018-10-23 20:41:20 +02:00
|
|
|
import inspect
|
|
|
|
import pkgutil
|
2018-10-13 06:25:43 +02:00
|
|
|
import traceback
|
2018-08-20 06:29:29 +02:00
|
|
|
|
2018-10-29 02:43:17 +01:00
|
|
|
from contextlib import contextmanager
|
|
|
|
|
2019-06-04 22:03:52 +02:00
|
|
|
from hy._compat import reraise, PY38
|
2018-05-21 20:37:46 +02:00
|
|
|
from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value
|
2018-08-03 02:20:42 +02:00
|
|
|
from hy.lex import mangle
|
2018-10-13 06:25:43 +02:00
|
|
|
from hy.errors import (HyLanguageError, HyMacroExpansionError, HyTypeError,
|
|
|
|
HyRequireError)
|
2013-12-26 04:02:20 +01:00
|
|
|
|
2018-10-20 21:25:58 +02:00
|
|
|
try:
|
|
|
|
# Check if we have the newer inspect.signature available.
|
|
|
|
# Otherwise fallback to the legacy getargspec.
|
|
|
|
inspect.signature # noqa
|
|
|
|
except AttributeError:
|
|
|
|
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 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))
|
|
|
|
|
|
|
|
|
2013-08-17 17:37:48 +02:00
|
|
|
CORE_MACROS = [
|
2013-07-06 20:00:11 +02:00
|
|
|
"hy.core.bootstrap",
|
|
|
|
]
|
|
|
|
|
2013-08-17 17:37:48 +02:00
|
|
|
EXTRA_MACROS = [
|
|
|
|
"hy.core.macros",
|
|
|
|
]
|
|
|
|
|
2013-03-08 04:52:47 +01:00
|
|
|
|
|
|
|
def macro(name):
|
2013-09-29 17:55:15 +02:00
|
|
|
"""Decorator to define a macro called `name`.
|
|
|
|
"""
|
2018-03-04 23:20:46 +01:00
|
|
|
name = mangle(name)
|
2013-03-08 04:52:47 +01:00
|
|
|
def _(fn):
|
2018-10-13 06:25:43 +02:00
|
|
|
fn = rename_function(fn, name)
|
2016-12-15 01:10:46 +01:00
|
|
|
try:
|
2018-10-20 21:25:58 +02:00
|
|
|
fn._hy_macro_pass_compiler = has_kwargs(fn)
|
2016-12-15 01:10:46 +01:00
|
|
|
except Exception:
|
|
|
|
# An exception might be raised if fn has arguments with
|
|
|
|
# names that are invalid in Python.
|
|
|
|
fn._hy_macro_pass_compiler = False
|
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
module = inspect.getmodule(fn)
|
|
|
|
module_macros = module.__dict__.setdefault('__macros__', {})
|
|
|
|
module_macros[name] = fn
|
|
|
|
|
2013-03-08 05:04:20 +01:00
|
|
|
return fn
|
2013-03-08 04:52:47 +01:00
|
|
|
return _
|
|
|
|
|
|
|
|
|
2017-06-21 05:48:54 +02:00
|
|
|
def tag(name):
|
|
|
|
"""Decorator to define a tag macro called `name`.
|
2013-12-15 17:47:24 +01:00
|
|
|
"""
|
|
|
|
def _(fn):
|
2018-03-04 23:20:46 +01:00
|
|
|
_name = mangle('#{}'.format(name))
|
2018-10-23 20:41:20 +02:00
|
|
|
|
2018-10-13 06:25:43 +02:00
|
|
|
fn = rename_function(fn, _name)
|
2018-10-23 20:41:20 +02:00
|
|
|
|
|
|
|
module = inspect.getmodule(fn)
|
|
|
|
|
|
|
|
module_name = module.__name__
|
2013-12-15 17:47:24 +01:00
|
|
|
if module_name.startswith("hy.core"):
|
|
|
|
module_name = None
|
2018-10-23 20:41:20 +02:00
|
|
|
|
|
|
|
module_tags = module.__dict__.setdefault('__tags__', {})
|
|
|
|
module_tags[mangle(name)] = fn
|
2013-12-15 17:47:24 +01:00
|
|
|
|
|
|
|
return fn
|
|
|
|
return _
|
|
|
|
|
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
def _same_modules(source_module, target_module):
|
|
|
|
"""Compare the filenames associated with the given modules names.
|
|
|
|
|
|
|
|
This tries to not actually load the modules.
|
|
|
|
"""
|
|
|
|
if not (source_module or target_module):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if target_module == source_module:
|
|
|
|
return True
|
|
|
|
|
|
|
|
def _get_filename(module):
|
|
|
|
filename = None
|
|
|
|
try:
|
|
|
|
if not inspect.ismodule(module):
|
|
|
|
loader = pkgutil.get_loader(module)
|
|
|
|
if loader:
|
|
|
|
filename = loader.get_filename()
|
|
|
|
else:
|
|
|
|
filename = inspect.getfile(module)
|
|
|
|
except (TypeError, ImportError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
return filename
|
|
|
|
|
|
|
|
source_filename = _get_filename(source_module)
|
|
|
|
target_filename = _get_filename(target_module)
|
|
|
|
|
|
|
|
return (source_filename and target_filename and
|
|
|
|
source_filename == target_filename)
|
|
|
|
|
|
|
|
|
2018-04-18 20:59:01 +02:00
|
|
|
def require(source_module, target_module, assignments, prefix=""):
|
2018-10-23 20:41:20 +02:00
|
|
|
"""Load macros from one module into the namespace of another.
|
2013-09-29 17:55:15 +02:00
|
|
|
|
|
|
|
This function is called from the `require` special form in the compiler.
|
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
source_module: str or types.ModuleType
|
|
|
|
The module from which macros are to be imported.
|
Give `require` the same features as `import` (#1142)
Give `require` the same features as `import`
You can now do (require foo), (require [foo [a b c]]), (require [foo [*]]), and (require [foo :as bar]). The first and last forms get you macros named foo.a, foo.b, etc. or bar.a, bar.b, etc., respectively. The second form only gets the macros in the list.
Implements #1118 and perhaps partly addresses #277.
N.B. The new meaning of (require foo) will cause all existing code that uses macros to break. Simply replace these forms with (require [foo [*]]) to get your code working again.
There's a bit of a hack involved in the forms (require foo) or (require [foo :as bar]). When you call (foo.a ...) or (bar.a ...), Hy doesn't actually look inside modules. Instead, these (require ...) forms give the macros names that have periods in them, which happens to work fine with the way Hy finds and interprets macro calls.
* Make `require` syntax stricter and add tests
* Update documentation for `require`
* Documentation wording improvements
* Allow :as in `require` name lists
2016-11-03 08:35:58 +01:00
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
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.
|
Give `require` the same features as `import` (#1142)
Give `require` the same features as `import`
You can now do (require foo), (require [foo [a b c]]), (require [foo [*]]), and (require [foo :as bar]). The first and last forms get you macros named foo.a, foo.b, etc. or bar.a, bar.b, etc., respectively. The second form only gets the macros in the list.
Implements #1118 and perhaps partly addresses #277.
N.B. The new meaning of (require foo) will cause all existing code that uses macros to break. Simply replace these forms with (require [foo [*]]) to get your code working again.
There's a bit of a hack involved in the forms (require foo) or (require [foo :as bar]). When you call (foo.a ...) or (bar.a ...), Hy doesn't actually look inside modules. Instead, these (require ...) forms give the macros names that have periods in them, which happens to work fine with the way Hy finds and interprets macro calls.
* Make `require` syntax stricter and add tests
* Update documentation for `require`
* Documentation wording improvements
* Allow :as in `require` name lists
2016-11-03 08:35:58 +01:00
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
assignments: str or list of tuples of strs
|
|
|
|
The string "ALL" or a list of macro name and alias pairs.
|
Give `require` the same features as `import` (#1142)
Give `require` the same features as `import`
You can now do (require foo), (require [foo [a b c]]), (require [foo [*]]), and (require [foo :as bar]). The first and last forms get you macros named foo.a, foo.b, etc. or bar.a, bar.b, etc., respectively. The second form only gets the macros in the list.
Implements #1118 and perhaps partly addresses #277.
N.B. The new meaning of (require foo) will cause all existing code that uses macros to break. Simply replace these forms with (require [foo [*]]) to get your code working again.
There's a bit of a hack involved in the forms (require foo) or (require [foo :as bar]). When you call (foo.a ...) or (bar.a ...), Hy doesn't actually look inside modules. Instead, these (require ...) forms give the macros names that have periods in them, which happens to work fine with the way Hy finds and interprets macro calls.
* Make `require` syntax stricter and add tests
* Update documentation for `require`
* Documentation wording improvements
* Allow :as in `require` name lists
2016-11-03 08:35:58 +01:00
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
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.
|
2013-12-15 17:47:24 +01:00
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
out: boolean
|
|
|
|
Whether or not macros and tags 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)
|
2019-05-20 21:18:12 +02:00
|
|
|
elif isinstance(target_module, str):
|
2018-10-23 20:41:20 +02:00
|
|
|
target_module = importlib.import_module(target_module)
|
|
|
|
target_namespace = target_module.__dict__
|
|
|
|
elif inspect.ismodule(target_module):
|
|
|
|
target_namespace = target_module.__dict__
|
|
|
|
else:
|
2018-10-13 06:25:43 +02:00
|
|
|
raise HyTypeError('`target_module` is not a recognized type: {}'.format(
|
2018-10-23 20:41:20 +02:00
|
|
|
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):
|
2018-10-13 06:25:43 +02:00
|
|
|
try:
|
|
|
|
source_module = importlib.import_module(source_module)
|
|
|
|
except ImportError as e:
|
|
|
|
reraise(HyRequireError, HyRequireError(e.args[0]), None)
|
2018-10-23 20:41:20 +02:00
|
|
|
|
|
|
|
source_macros = source_module.__dict__.setdefault('__macros__', {})
|
|
|
|
source_tags = source_module.__dict__.setdefault('__tags__', {})
|
|
|
|
|
|
|
|
if len(source_module.__macros__) + len(source_module.__tags__) == 0:
|
|
|
|
if assignments != "ALL":
|
2018-10-13 06:25:43 +02:00
|
|
|
raise HyRequireError('The module {} has no macros or tags'.format(
|
2018-10-23 20:41:20 +02:00
|
|
|
source_module))
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
target_macros = target_namespace.setdefault('__macros__', {})
|
|
|
|
target_tags = target_namespace.setdefault('__tags__', {})
|
2013-05-16 15:34:14 +02:00
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
if prefix:
|
|
|
|
prefix += "."
|
|
|
|
|
|
|
|
if assignments == "ALL":
|
2019-01-16 14:02:01 +01:00
|
|
|
name_assigns = [(k, k) for k in
|
|
|
|
tuple(source_macros.keys()) + tuple(source_tags.keys())]
|
2018-10-23 20:41:20 +02:00
|
|
|
else:
|
|
|
|
name_assigns = assignments
|
|
|
|
|
|
|
|
for name, alias in name_assigns:
|
|
|
|
_name = mangle(name)
|
|
|
|
alias = mangle(prefix + alias)
|
|
|
|
if _name in source_module.__macros__:
|
|
|
|
target_macros[alias] = source_macros[_name]
|
|
|
|
elif _name in source_module.__tags__:
|
|
|
|
target_tags[alias] = source_tags[_name]
|
|
|
|
else:
|
2018-10-13 06:25:43 +02:00
|
|
|
raise HyRequireError('Could not require name {} from {}'.format(
|
2018-10-23 20:41:20 +02:00
|
|
|
_name, source_module))
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def load_macros(module):
|
2013-09-29 17:55:15 +02:00
|
|
|
"""Load the hy builtin macros for module `module_name`.
|
|
|
|
|
|
|
|
Modules from `hy.core` can only use the macros from CORE_MACROS.
|
|
|
|
Other modules get the macros from CORE_MACROS and EXTRA_MACROS.
|
|
|
|
"""
|
2018-10-23 20:41:20 +02:00
|
|
|
builtin_macros = CORE_MACROS
|
2013-08-17 17:37:48 +02:00
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
if not module.__name__.startswith("hy.core"):
|
|
|
|
builtin_macros += EXTRA_MACROS
|
2013-08-17 17:37:48 +02:00
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
module_macros = module.__dict__.setdefault('__macros__', {})
|
|
|
|
module_tags = module.__dict__.setdefault('__tags__', {})
|
|
|
|
|
|
|
|
for builtin_mod_name in builtin_macros:
|
|
|
|
builtin_mod = importlib.import_module(builtin_mod_name)
|
|
|
|
|
|
|
|
# Make sure we don't overwrite macros in the module.
|
|
|
|
if hasattr(builtin_mod, '__macros__'):
|
|
|
|
module_macros.update({k: v
|
|
|
|
for k, v in builtin_mod.__macros__.items()
|
|
|
|
if k not in module_macros})
|
|
|
|
if hasattr(builtin_mod, '__tags__'):
|
|
|
|
module_tags.update({k: v
|
|
|
|
for k, v in builtin_mod.__tags__.items()
|
|
|
|
if k not in module_tags})
|
2013-07-06 20:00:11 +02:00
|
|
|
|
|
|
|
|
2018-10-29 02:43:17 +01:00
|
|
|
@contextmanager
|
|
|
|
def macro_exceptions(module, macro_tree, compiler=None):
|
|
|
|
try:
|
|
|
|
yield
|
2018-10-13 06:25:43 +02:00
|
|
|
except HyLanguageError as e:
|
|
|
|
# These are user-level Hy errors occurring in the macro.
|
|
|
|
# We want to pass them up to the user.
|
|
|
|
reraise(type(e), e, sys.exc_info()[2])
|
2018-10-29 02:43:17 +01:00
|
|
|
except Exception as e:
|
2018-10-13 06:25:43 +02:00
|
|
|
|
|
|
|
if compiler:
|
|
|
|
filename = compiler.filename
|
|
|
|
source = compiler.source
|
2018-10-29 02:43:17 +01:00
|
|
|
else:
|
2018-10-13 06:25:43 +02:00
|
|
|
filename = None
|
|
|
|
source = None
|
|
|
|
|
|
|
|
exc_msg = ' '.join(traceback.format_exception_only(
|
|
|
|
sys.exc_info()[0], sys.exc_info()[1]))
|
|
|
|
|
|
|
|
msg = "expanding macro {}\n ".format(str(macro_tree[0]))
|
|
|
|
msg += exc_msg
|
2018-10-29 02:43:17 +01:00
|
|
|
|
2018-10-13 06:25:43 +02:00
|
|
|
reraise(HyMacroExpansionError,
|
|
|
|
HyMacroExpansionError(
|
|
|
|
msg, macro_tree, filename, source),
|
|
|
|
sys.exc_info()[2])
|
2018-10-29 02:43:17 +01:00
|
|
|
|
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
def macroexpand(tree, module, compiler=None, once=False):
|
2018-10-23 20:06:22 +02:00
|
|
|
"""Expand the toplevel macros for the given Hy AST tree.
|
2013-09-29 17:55:15 +02:00
|
|
|
|
2018-10-23 20:06:22 +02:00
|
|
|
Load the macros from the given `module`, then expand the (top-level) macros
|
|
|
|
in `tree` until we no longer can.
|
|
|
|
|
|
|
|
`HyExpression` resulting from macro expansions are assigned the module in
|
|
|
|
which the macro function is defined (determined using `inspect.getmodule`).
|
|
|
|
If the resulting `HyExpression` 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
|
|
|
|
`HyExpression` 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.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
tree: HyObject or list
|
|
|
|
Hy AST tree.
|
|
|
|
|
|
|
|
module: str or types.ModuleType
|
|
|
|
Module used to determine the local namespace for macros.
|
|
|
|
|
|
|
|
compiler: HyASTCompiler, optional
|
|
|
|
The compiler object passed to expanded macros.
|
|
|
|
|
|
|
|
once: boolean, optional
|
|
|
|
Only expand the first macro in `tree`.
|
|
|
|
|
|
|
|
Returns
|
|
|
|
------
|
|
|
|
out: HyObject
|
|
|
|
Returns a mutated tree with macros expanded.
|
2013-09-29 17:55:15 +02:00
|
|
|
"""
|
2018-10-23 20:41:20 +02:00
|
|
|
if not inspect.ismodule(module):
|
|
|
|
module = importlib.import_module(module)
|
|
|
|
|
|
|
|
assert not compiler or compiler.module == module
|
|
|
|
|
2020-03-20 17:56:49 +01:00
|
|
|
while isinstance(tree, HyExpression) and tree:
|
2013-09-29 17:55:15 +02:00
|
|
|
|
2018-04-09 21:01:12 +02:00
|
|
|
fn = tree[0]
|
|
|
|
if fn in ("quote", "quasiquote") or not isinstance(fn, HySymbol):
|
2018-05-21 20:37:46 +02:00
|
|
|
break
|
2018-04-08 02:17:40 +02:00
|
|
|
|
2018-04-09 21:01:12 +02:00
|
|
|
fn = mangle(fn)
|
2018-10-23 20:06:22 +02:00
|
|
|
expr_modules = (([] if not hasattr(tree, 'module') else [tree.module])
|
|
|
|
+ [module])
|
|
|
|
|
|
|
|
# Choose the first namespace with the macro.
|
|
|
|
m = next((mod.__macros__[fn]
|
|
|
|
for mod in expr_modules
|
|
|
|
if fn in mod.__macros__),
|
|
|
|
None)
|
2018-04-09 21:01:12 +02:00
|
|
|
if not m:
|
2018-05-21 20:37:46 +02:00
|
|
|
break
|
2018-04-08 02:17:40 +02:00
|
|
|
|
2018-04-09 21:01:12 +02:00
|
|
|
opts = {}
|
|
|
|
if m._hy_macro_pass_compiler:
|
2018-10-23 20:41:20 +02:00
|
|
|
if compiler is None:
|
|
|
|
from hy.compiler import HyASTCompiler
|
|
|
|
compiler = HyASTCompiler(module)
|
2018-04-09 21:01:12 +02:00
|
|
|
opts['compiler'] = compiler
|
2018-04-08 02:17:40 +02:00
|
|
|
|
2018-10-29 02:43:17 +01:00
|
|
|
with macro_exceptions(module, tree, compiler):
|
2018-10-23 20:41:20 +02:00
|
|
|
obj = m(module.__name__, *tree[1:], **opts)
|
2018-10-23 20:06:22 +02:00
|
|
|
|
2018-11-13 19:11:51 +01:00
|
|
|
if isinstance(obj, HyExpression):
|
|
|
|
obj.module = inspect.getmodule(m)
|
2018-10-23 20:06:22 +02:00
|
|
|
|
2018-11-13 19:11:51 +01:00
|
|
|
tree = replace_hy_obj(obj, tree)
|
2018-04-08 02:17:40 +02:00
|
|
|
|
2018-04-09 21:01:12 +02:00
|
|
|
if once:
|
2018-05-21 20:37:46 +02:00
|
|
|
break
|
2018-04-09 21:01:12 +02:00
|
|
|
|
2018-05-21 20:37:46 +02:00
|
|
|
tree = wrap_value(tree)
|
|
|
|
return tree
|
2018-04-09 21:01:12 +02:00
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
|
|
|
|
def macroexpand_1(tree, module, compiler=None):
|
2018-04-09 21:01:12 +02:00
|
|
|
"""Expand the toplevel macro from `tree` once, in the context of
|
|
|
|
`compiler`."""
|
2018-10-23 20:41:20 +02:00
|
|
|
return macroexpand(tree, module, compiler, once=True)
|
2014-01-14 02:38:16 +01:00
|
|
|
|
|
|
|
|
2018-10-23 20:41:20 +02:00
|
|
|
def tag_macroexpand(tag, tree, module):
|
|
|
|
"""Expand the tag macro `tag` with argument `tree`."""
|
|
|
|
if not inspect.ismodule(module):
|
|
|
|
module = importlib.import_module(module)
|
|
|
|
|
2018-10-23 20:06:22 +02:00
|
|
|
expr_modules = (([] if not hasattr(tree, 'module') else [tree.module])
|
|
|
|
+ [module])
|
|
|
|
|
|
|
|
# Choose the first namespace with the macro.
|
|
|
|
tag_macro = next((mod.__tags__[tag]
|
|
|
|
for mod in expr_modules
|
|
|
|
if tag in mod.__tags__),
|
|
|
|
None)
|
2014-01-14 02:38:16 +01:00
|
|
|
|
2017-06-21 05:48:54 +02:00
|
|
|
if tag_macro is None:
|
2018-10-29 02:43:17 +01:00
|
|
|
raise HyTypeError("`{0}' is not a defined tag macro.".format(tag),
|
|
|
|
None, tag, None)
|
2014-01-14 02:38:16 +01:00
|
|
|
|
2017-06-21 05:48:54 +02:00
|
|
|
expr = tag_macro(tree)
|
2018-10-23 20:06:22 +02:00
|
|
|
|
|
|
|
if isinstance(expr, HyExpression):
|
|
|
|
expr.module = inspect.getmodule(tag_macro)
|
|
|
|
|
2017-06-23 01:07:50 +02:00
|
|
|
return replace_hy_obj(expr, tree)
|
2019-05-20 21:55:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
def rename_function(func, new_name):
|
|
|
|
"""Creates a copy of a function and [re]sets the name at the code-object
|
|
|
|
level.
|
|
|
|
"""
|
|
|
|
c = func.__code__
|
|
|
|
new_code = type(c)(*[getattr(c, 'co_{}'.format(a))
|
|
|
|
if a != 'name' else str(new_name)
|
|
|
|
for a in code_obj_args])
|
|
|
|
|
|
|
|
_fn = type(func)(new_code, func.__globals__, str(new_name),
|
|
|
|
func.__defaults__, func.__closure__)
|
|
|
|
_fn.__dict__.update(func.__dict__)
|
|
|
|
|
|
|
|
return _fn
|
|
|
|
|
2019-06-04 22:03:52 +02:00
|
|
|
code_obj_args = ['argcount', 'posonlyargcount', 'kwonlyargcount', 'nlocals', 'stacksize',
|
|
|
|
'flags', 'code', 'consts', 'names', 'varnames', 'filename', 'name',
|
|
|
|
'firstlineno', 'lnotab', 'freevars', 'cellvars']
|
|
|
|
if not PY38:
|
|
|
|
code_obj_args.remove("posonlyargcount")
|