package_hash: rework RemoveDirectives
and add a test
Previously we used `directives.__all__` to get directive names, but it wasn't quite right -- it included `DirectiveMeta`, etc. It's not wrong, but it's also not the clearest way to do this. - [x] Refactor `@directive` to track names in `directive_names` global - [x] Rename `_directive_names` to `_directive_dict_names` in `DirectiveMeta` - [x] Add a test for `RemoveDirectives` Co-authored-by: Danny McClanahan <1305167+cosmicexplorer@users.noreply.github.com>
This commit is contained in:

committed by
Greg Becker

parent
8880a00862
commit
93a6c51d88
@@ -59,6 +59,10 @@ class OpenMpi(Package):
|
|||||||
#: These are variant names used by Spack internally; packages can't use them
|
#: These are variant names used by Spack internally; packages can't use them
|
||||||
reserved_names = ['patches', 'dev_path']
|
reserved_names = ['patches', 'dev_path']
|
||||||
|
|
||||||
|
#: Names of possible directives. This list is populated elsewhere in the file and then
|
||||||
|
#: added to `__all__` at the bottom.
|
||||||
|
directive_names = []
|
||||||
|
|
||||||
_patch_order_index = 0
|
_patch_order_index = 0
|
||||||
|
|
||||||
|
|
||||||
@@ -113,7 +117,7 @@ class DirectiveMeta(type):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Set of all known directives
|
# Set of all known directives
|
||||||
_directive_names = set() # type: Set[str]
|
_directive_dict_names = set() # type: Set[str]
|
||||||
_directives_to_be_executed = [] # type: List[str]
|
_directives_to_be_executed = [] # type: List[str]
|
||||||
_when_constraints_from_context = [] # type: List[str]
|
_when_constraints_from_context = [] # type: List[str]
|
||||||
|
|
||||||
@@ -156,7 +160,7 @@ def __init__(cls, name, bases, attr_dict):
|
|||||||
if 'spack.pkg' in cls.__module__:
|
if 'spack.pkg' in cls.__module__:
|
||||||
# Ensure the presence of the dictionaries associated
|
# Ensure the presence of the dictionaries associated
|
||||||
# with the directives
|
# with the directives
|
||||||
for d in DirectiveMeta._directive_names:
|
for d in DirectiveMeta._directive_dict_names:
|
||||||
setattr(cls, d, {})
|
setattr(cls, d, {})
|
||||||
|
|
||||||
# Lazily execute directives
|
# Lazily execute directives
|
||||||
@@ -222,7 +226,7 @@ class Foo(Package):
|
|||||||
Package class, and it's how Spack gets information from the
|
Package class, and it's how Spack gets information from the
|
||||||
packages to the core.
|
packages to the core.
|
||||||
"""
|
"""
|
||||||
global __all__
|
global directive_names
|
||||||
|
|
||||||
if isinstance(dicts, six.string_types):
|
if isinstance(dicts, six.string_types):
|
||||||
dicts = (dicts, )
|
dicts = (dicts, )
|
||||||
@@ -232,11 +236,11 @@ class Foo(Package):
|
|||||||
raise TypeError(message.format(type(dicts)))
|
raise TypeError(message.format(type(dicts)))
|
||||||
|
|
||||||
# Add the dictionary names if not already there
|
# Add the dictionary names if not already there
|
||||||
DirectiveMeta._directive_names |= set(dicts)
|
DirectiveMeta._directive_dict_names |= set(dicts)
|
||||||
|
|
||||||
# This decorator just returns the directive functions
|
# This decorator just returns the directive functions
|
||||||
def _decorator(decorated_function):
|
def _decorator(decorated_function):
|
||||||
__all__.append(decorated_function.__name__)
|
directive_names.append(decorated_function.__name__)
|
||||||
|
|
||||||
@functools.wraps(decorated_function)
|
@functools.wraps(decorated_function)
|
||||||
def _wrapper(*args, **kwargs):
|
def _wrapper(*args, **kwargs):
|
||||||
@@ -730,3 +734,7 @@ class DependencyPatchError(DirectiveError):
|
|||||||
|
|
||||||
class UnsupportedPackageDirective(DirectiveError):
|
class UnsupportedPackageDirective(DirectiveError):
|
||||||
"""Raised when an invalid or unsupported package directive is specified."""
|
"""Raised when an invalid or unsupported package directive is specified."""
|
||||||
|
|
||||||
|
|
||||||
|
#: add all directive names to __all__
|
||||||
|
__all__.extend(directive_names)
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import ast
|
import ast
|
||||||
|
|
||||||
|
import spack.directives
|
||||||
import spack.paths
|
import spack.paths
|
||||||
import spack.util.package_hash as ph
|
import spack.util.package_hash as ph
|
||||||
from spack.spec import Spec
|
from spack.spec import Spec
|
||||||
@@ -123,3 +124,29 @@ def test_remove_docstrings():
|
|||||||
assert "SEVEN" in unparsed
|
assert "SEVEN" in unparsed
|
||||||
assert "NINE" in unparsed
|
assert "NINE" in unparsed
|
||||||
assert "TWELVE" in unparsed
|
assert "TWELVE" in unparsed
|
||||||
|
|
||||||
|
|
||||||
|
many_directives = """\
|
||||||
|
|
||||||
|
class HasManyDirectives:
|
||||||
|
{directives}
|
||||||
|
|
||||||
|
def foo():
|
||||||
|
# just a method to get in the way
|
||||||
|
pass
|
||||||
|
|
||||||
|
{directives}
|
||||||
|
""".format(directives="\n".join(
|
||||||
|
" %s()" % name for name in spack.directives.directive_names
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_directives():
|
||||||
|
"""Ensure all directives are removed from packages before hashing."""
|
||||||
|
tree = ast.parse(many_directives)
|
||||||
|
spec = Spec("has-many-directives")
|
||||||
|
tree = ph.RemoveDirectives(spec).visit(tree)
|
||||||
|
unparsed = unparse(tree)
|
||||||
|
|
||||||
|
for name in spack.directives.directive_names:
|
||||||
|
assert name not in unparsed
|
||||||
|
@@ -66,7 +66,7 @@ def is_directive(self, node):
|
|||||||
return (isinstance(node, ast.Expr) and
|
return (isinstance(node, ast.Expr) and
|
||||||
node.value and isinstance(node.value, ast.Call) and
|
node.value and isinstance(node.value, ast.Call) and
|
||||||
isinstance(node.value.func, ast.Name) and
|
isinstance(node.value.func, ast.Name) and
|
||||||
node.value.func.id in spack.directives.__all__)
|
node.value.func.id in spack.directives.directive_names)
|
||||||
|
|
||||||
def is_spack_attr(self, node):
|
def is_spack_attr(self, node):
|
||||||
return (isinstance(node, ast.Assign) and
|
return (isinstance(node, ast.Assign) and
|
||||||
|
Reference in New Issue
Block a user