-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/more helpful invalid refs (#1080) #1085
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dbt/clients/jinja.py
Outdated
) | ||
|
||
def __getattr__(self, name): | ||
if name == 'name' or name.startswith('__') and name.endswith('__'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you put some parens in here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did one better and just made it a function
|
||
def __getattr__(self, name): | ||
if name == 'name' or name.startswith('__') and name.endswith('__'): | ||
raise AttributeError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why would we raise this error for __*__
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because copy.deepcopy
does a series of successive hasattr(x, '__*__')
checks to decide how to deepcopy things, and hasattr(obj, name)
is basically implemented as:
try:
getattr(obj, name)
except AttributeError:
return False
else:
return True
Since __getattr__
is only accessed when an attribute is not already defined, we want to signal that we don't implement those methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
roger!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just tested this out locally -- works great! Better error messages, better ingredients, papa johns
🚢 when the tests pass!
Fixes #1080 - in particular the issue described by @raybuhr
There are some neat things going on here:
ParserMacroCapture.__getattr__
accessesself.name
without ensuring that it is not doing a lookup forself.name
-> recursion errorParserMacroCapture
is deepcopied. deepcopy sort of calls new(), and populates the created object, bypassing init through a convoluted set ofhasattr
checks that we pass due to the__getattr__
implementation.ref
'dThe ways you might think would help (raising AttributeError in getattr, etc) all end up being pretty unhelpful since jinja does propagation of Undefined values, so you get odd TypeErrors down the line. But implementing a
__deepcopy__
that raises a compiler error works: jinja won't catch it and the error can be pretty precise/helpful. Any time you find yourself calling copy.deepcopy() with an undefined value, things have gone horribly wrong, so I think it's a valid solution.