[spack/spec.py] raise a query failure error if a property query returns None (#7277)

* [SPACK/spec.py] When a query through ForwardQueryToPackage returns
'None', treat that as query failure and raise RuntimeError with
suitable message. This overrides the current behavior to raise an
AttributeError which is now triggered only when no suitable query
property is found and there is no default handler.

* [spack/spec.py] Fix style.

* [SPACK/spec.py] In case of query failure, i.e. property returning
'None', raise AttributeError instead of RuntimeError in order to
pass the unit test. Also, small update in the logic distinguishing
query failure and lack of relevant property/attribute handling.
This commit is contained in:
Veselin Dobrev 2018-02-28 14:25:29 -08:00 committed by Christoph Junghans
parent 98949bd79b
commit d3ed46e958

View File

@ -770,10 +770,6 @@ def __init__(self, attribute_name, default_handler=None):
instance instance
""" """
self.attribute_name = attribute_name self.attribute_name = attribute_name
# Turn the default handler into a function with the right
# signature that always returns None
if default_handler is None:
default_handler = lambda descriptor, spec, cls: None
self.default = default_handler self.default = default_handler
def __get__(self, instance, cls): def __get__(self, instance, cls):
@ -792,8 +788,11 @@ def __get__(self, instance, cls):
The first call that produces a value will stop the chain. The first call that produces a value will stop the chain.
If no call can handle the request or a None value is produced, If no call can handle the request then AttributeError is raised with a
then AttributeError is raised. message indicating that no relevant attribute exists.
If a call returns None, an AttributeError is raised with a message
indicating a query failure, e.g. that library files were not found in a
'libs' query.
""" """
pkg = instance.package pkg = instance.package
try: try:
@ -814,34 +813,53 @@ def __get__(self, instance, cls):
# Try to get the generic method from Package # Try to get the generic method from Package
callbacks_chain.append(lambda: getattr(pkg, self.attribute_name)) callbacks_chain.append(lambda: getattr(pkg, self.attribute_name))
# Final resort : default callback # Final resort : default callback
callbacks_chain.append(lambda: self.default(self, instance, cls)) if self.default is not None:
callbacks_chain.append(lambda: self.default(self, instance, cls))
# Trigger the callbacks in order, the first one producing a # Trigger the callbacks in order, the first one producing a
# value wins # value wins
value = None value = None
message = None
for f in callbacks_chain: for f in callbacks_chain:
try: try:
value = f() value = f()
# A callback can return None to trigger an error indicating
# that the query failed.
if value is None:
msg = "Query of package '{name}' for '{attrib}' failed\n"
msg += "\tprefix : {spec.prefix}\n"
msg += "\tspec : {spec}\n"
msg += "\tqueried as : {query.name}\n"
msg += "\textra parameters : {query.extra_parameters}"
message = msg.format(
name=pkg.name, attrib=self.attribute_name,
spec=instance, query=instance.last_query)
else:
return value
break break
except AttributeError: except AttributeError:
pass pass
# 'None' value raises AttributeError : this permits to 'disable' # value is 'None'
# the call in a particular package by returning None from the if message is not None:
# queried attribute, or will trigger an exception if things # Here we can use another type of exception. If we do that, the
# searched for were not found # unit test 'test_getitem_exceptional_paths' in the file
if value is None: # lib/spack/spack/test/spec_dag.py will need to be updated to match
fmt = '\'{name}\' package has no relevant attribute \'{query}\'\n' # NOQA: ignore=E501 # the type.
fmt += '\tspec : \'{spec}\'\n'
fmt += '\tqueried as : \'{spec.last_query.name}\'\n'
fmt += '\textra parameters : \'{spec.last_query.extra_parameters}\'\n' # NOQA: ignore=E501
message = fmt.format(
name=pkg.name,
query=self.attribute_name,
spec=instance
)
raise AttributeError(message) raise AttributeError(message)
# 'None' value at this point means that there are no appropriate
return value # properties defined and no default handler, or that all callbacks
# raised AttributeError. In this case, we raise AttributeError with an
# appropriate message.
fmt = '\'{name}\' package has no relevant attribute \'{query}\'\n' # NOQA: ignore=E501
fmt += '\tspec : \'{spec}\'\n'
fmt += '\tqueried as : \'{spec.last_query.name}\'\n'
fmt += '\textra parameters : \'{spec.last_query.extra_parameters}\'\n' # NOQA: ignore=E501
message = fmt.format(
name=pkg.name,
query=self.attribute_name,
spec=instance
)
raise AttributeError(message)
def __set__(self, instance, value): def __set__(self, instance, value):
cls_name = type(instance).__name__ cls_name = type(instance).__name__