We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
handle exceptions that occur inside of jinja
## Issue

### Issue description
Given sql with invalid jinja like:
```
{{ config(
    materialized = "table",
    schema = 'custom_schema' -- render this in a custom schema
) }}
```

dbt will throw a very unhelpful error that looks like:
```
  File "/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/dbt/parser/base_sql.py", line 95, in parse_sql_nodes
    macros=macros)
  File "/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/dbt/parser/base.py", line 105, in parse_node
    capture_macros=True)
  File "/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/dbt/clients/jinja.py", line 198, in get_rendered
    return render_template(template, ctx, node)
  File "/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/dbt/clients/jinja.py", line 185, in render_template
    return template.render(ctx)
  File "/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/jinja2/asyncsupport.py", line 76, in render
    return original_render(self, *args, **kwargs)
  File "/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 1, in top-level template code
TypeError: exceptions must derive from BaseException
```

It will likely be difficult to pinpoint the exact error, but it should ideally be able to throw some sort of human-readable parsing error, indicating the model where the syntax error occurred.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `dbt/clients/jinja.py`
Content:
```
1 import codecs
2 import linecache
3 import os
4 
5 import jinja2
6 import jinja2._compat
7 import jinja2.ext
8 import jinja2.nodes
9 import jinja2.parser
10 import jinja2.sandbox
11 
12 import dbt.compat
13 import dbt.exceptions
14 
15 from dbt.node_types import NodeType
16 from dbt.utils import AttrDict
17 
18 from dbt.logger import GLOBAL_LOGGER as logger  # noqa
19 
20 
21 class MacroFuzzParser(jinja2.parser.Parser):
22     def parse_macro(self):
23         node = jinja2.nodes.Macro(lineno=next(self.stream).lineno)
24 
25         # modified to fuzz macros defined in the same file. this way
26         # dbt can understand the stack of macros being called.
27         #  - @cmcarthur
28         node.name = dbt.utils.get_dbt_macro_name(
29             self.parse_assign_target(name_only=True).name)
30 
31         self.parse_signature(node)
32         node.body = self.parse_statements(('name:endmacro',),
33                                           drop_needle=True)
34         return node
35 
36 
37 class MacroFuzzEnvironment(jinja2.sandbox.SandboxedEnvironment):
38     def _parse(self, source, name, filename):
39         return MacroFuzzParser(
40             self, source, name,
41             jinja2._compat.encode_filename(filename)
42         ).parse()
43 
44     def _compile(self, source, filename):
45         """Override jinja's compilation to stash the rendered source inside
46         the python linecache for debugging.
47         """
48         if filename == '<template>':
49             # make a better filename
50             filename = 'dbt-{}'.format(
51                 codecs.encode(os.urandom(12), 'hex').decode('ascii')
52             )
53             # encode, though I don't think this matters
54             filename = jinja2._compat.encode_filename(filename)
55             # put ourselves in the cache using the 'lazycache' method
56             linecache.cache[filename] = (lambda: source,)
57 
58         return super(MacroFuzzEnvironment, self)._compile(source, filename)
59 
60 
61 class TemplateCache(object):
62 
63     def __init__(self):
64         self.file_cache = {}
65 
66     def get_node_template(self, node):
67         key = (node['package_name'], node['original_file_path'])
68 
69         if key in self.file_cache:
70             return self.file_cache[key]
71 
72         template = get_template(
73             string=node.get('raw_sql'),
74             ctx={},
75             node=node
76         )
77         self.file_cache[key] = template
78 
79         return template
80 
81     def clear(self):
82         self.file_cache.clear()
83 
84 
85 template_cache = TemplateCache()
86 
87 
88 def macro_generator(node):
89     def apply_context(context):
90         def call(*args, **kwargs):
91             name = node.get('name')
92             template = template_cache.get_node_template(node)
93             module = template.make_module(context, False, context)
94 
95             if node['resource_type'] == NodeType.Operation:
96                 macro = module.__dict__[dbt.utils.get_dbt_operation_name(name)]
97             else:
98                 macro = module.__dict__[dbt.utils.get_dbt_macro_name(name)]
99             module.__dict__.update(context)
100 
101             try:
102                 return macro(*args, **kwargs)
103             except dbt.exceptions.MacroReturn as e:
104                 return e.value
105             except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:
106                 dbt.exceptions.raise_compiler_error(str(e), node)
107             except dbt.exceptions.CompilationException as e:
108                 e.stack.append(node)
109                 raise e
110 
111         return call
112     return apply_context
113 
114 
115 class MaterializationExtension(jinja2.ext.Extension):
116     tags = ['materialization']
117 
118     def parse(self, parser):
119         node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)
120         materialization_name = \
121             parser.parse_assign_target(name_only=True).name
122 
123         adapter_name = 'default'
124         node.args = []
125         node.defaults = []
126 
127         while parser.stream.skip_if('comma'):
128             target = parser.parse_assign_target(name_only=True)
129 
130             if target.name == 'default':
131                 pass
132 
133             elif target.name == 'adapter':
134                 parser.stream.expect('assign')
135                 value = parser.parse_expression()
136                 adapter_name = value.value
137 
138             else:
139                 dbt.exceptions.invalid_materialization_argument(
140                     materialization_name, target.name)
141 
142         node.name = dbt.utils.get_materialization_macro_name(
143             materialization_name, adapter_name)
144 
145         node.body = parser.parse_statements(('name:endmaterialization',),
146                                             drop_needle=True)
147 
148         return node
149 
150 
151 class OperationExtension(jinja2.ext.Extension):
152     tags = ['operation']
153 
154     def parse(self, parser):
155         node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)
156         operation_name = \
157             parser.parse_assign_target(name_only=True).name
158 
159         node.args = []
160         node.defaults = []
161 
162         while parser.stream.skip_if('comma'):
163             target = parser.parse_assign_target(name_only=True)
164 
165         node.name = dbt.utils.get_operation_macro_name(operation_name)
166 
167         node.body = parser.parse_statements(('name:endoperation',),
168                                             drop_needle=True)
169 
170         return node
171 
172 
173 class DocumentationExtension(jinja2.ext.Extension):
174     tags = ['docs']
175 
176     def parse(self, parser):
177         node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)
178         docs_name = parser.parse_assign_target(name_only=True).name
179 
180         node.args = []
181         node.defaults = []
182         node.name = dbt.utils.get_docs_macro_name(docs_name)
183         node.body = parser.parse_statements(('name:enddocs',),
184                                             drop_needle=True)
185         return node
186 
187 
188 def _is_dunder_name(name):
189     return name.startswith('__') and name.endswith('__')
190 
191 
192 def create_macro_capture_env(node):
193 
194     class ParserMacroCapture(jinja2.Undefined):
195         """
196         This class sets up the parser to capture macros.
197         """
198         def __init__(self, hint=None, obj=None, name=None,
199                      exc=None):
200             super(jinja2.Undefined, self).__init__()
201             self.node = node
202             self.name = name
203             self.package_name = node.get('package_name')
204             # jinja uses these for safety, so we have to override them.
205             # see https://github.com/pallets/jinja/blob/master/jinja2/sandbox.py#L332-L339 # noqa
206             self.unsafe_callable = False
207             self.alters_data = False
208 
209         def __deepcopy__(self, memo):
210             path = os.path.join(self.node.get('root_path'),
211                                 self.node.get('original_file_path'))
212 
213             logger.debug(
214                 'A ParserMacroCapture has been deecopy()d, invalid reference '
215                 'to "{}" in node {}.{} (source path: {})'
216                 .format(self.name, self.node.get('package_name'),
217                         self.node.get('name'),
218                         path))
219 
220             dbt.exceptions.raise_compiler_error(
221                 'dbt has detected at least one invalid reference in {}.{}. '
222                 'Check logs for more information'
223                 .format(self.node.get('package_name'), self.node.get('name'))
224             )
225 
226         def __getattr__(self, name):
227             if name == 'name' or _is_dunder_name(name):
228                 raise AttributeError(
229                     "'{}' object has no attribute '{}'"
230                     .format(type(self).__name__, name)
231                 )
232 
233             self.package_name = self.name
234             self.name = name
235 
236             return self
237 
238         def __call__(self, *args, **kwargs):
239             return True
240 
241     return ParserMacroCapture
242 
243 
244 def get_environment(node=None, capture_macros=False):
245     args = {
246         'extensions': []
247     }
248 
249     if capture_macros:
250         args['undefined'] = create_macro_capture_env(node)
251 
252     args['extensions'].append(MaterializationExtension)
253     args['extensions'].append(OperationExtension)
254     args['extensions'].append(DocumentationExtension)
255 
256     return MacroFuzzEnvironment(**args)
257 
258 
259 def parse(string):
260     try:
261         return get_environment().parse(dbt.compat.to_string(string))
262 
263     except (jinja2.exceptions.TemplateSyntaxError,
264             jinja2.exceptions.UndefinedError) as e:
265         e.translated = False
266         dbt.exceptions.raise_compiler_error(str(e))
267 
268 
269 def get_template(string, ctx, node=None, capture_macros=False):
270     try:
271         env = get_environment(node, capture_macros)
272 
273         template_source = dbt.compat.to_string(string)
274         return env.from_string(template_source, globals=ctx)
275 
276     except (jinja2.exceptions.TemplateSyntaxError,
277             jinja2.exceptions.UndefinedError) as e:
278         e.translated = False
279         dbt.exceptions.raise_compiler_error(str(e), node)
280 
281 
282 def render_template(template, ctx, node=None):
283     try:
284         return template.render(ctx)
285 
286     except (jinja2.exceptions.TemplateSyntaxError,
287             jinja2.exceptions.UndefinedError) as e:
288         e.translated = False
289         dbt.exceptions.raise_compiler_error(str(e), node)
290 
291 
292 def get_rendered(string, ctx, node=None,
293                  capture_macros=False):
294     template = get_template(string, ctx, node,
295                             capture_macros=capture_macros)
296 
297     return render_template(template, ctx, node)
298 
299 
300 def undefined_error(msg):
301     raise jinja2.exceptions.UndefinedError(msg)
302
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/dbt/clients/jinja.py b/dbt/clients/jinja.py
--- a/dbt/clients/jinja.py
+++ b/dbt/clients/jinja.py
@@ -195,9 +195,8 @@
         """
         This class sets up the parser to capture macros.
         """
-        def __init__(self, hint=None, obj=None, name=None,
-                     exc=None):
-            super(jinja2.Undefined, self).__init__()
+        def __init__(self, hint=None, obj=None, name=None, exc=None):
+            super(ParserMacroCapture, self).__init__(hint=hint, name=name)
             self.node = node
             self.name = name
             self.package_name = node.get('package_name')
@@ -211,18 +210,22 @@
                                 self.node.get('original_file_path'))
 
             logger.debug(
-                'A ParserMacroCapture has been deecopy()d, invalid reference '
-                'to "{}" in node {}.{} (source path: {})'
+                'dbt encountered an undefined variable, "{}" in node {}.{} '
+                '(source path: {})'
                 .format(self.name, self.node.get('package_name'),
-                        self.node.get('name'),
-                        path))
+                        self.node.get('name'), path))
 
+            # match jinja's message
             dbt.exceptions.raise_compiler_error(
-                'dbt has detected at least one invalid reference in {}.{}. '
-                'Check logs for more information'
-                .format(self.node.get('package_name'), self.node.get('name'))
+                "{!r} is undefined".format(self.name),
+                node=self.node
             )
 
+        def __getitem__(self, name):
+            # Propagate the undefined value if a caller accesses this as if it
+            # were a dictionary
+            return self
+
         def __getattr__(self, name):
             if name == 'name' or _is_dunder_name(name):
                 raise AttributeError(

{"golden_diff": "diff --git a/dbt/clients/jinja.py b/dbt/clients/jinja.py\n--- a/dbt/clients/jinja.py\n+++ b/dbt/clients/jinja.py\n@@ -195,9 +195,8 @@\n         \"\"\"\n         This class sets up the parser to capture macros.\n         \"\"\"\n-        def __init__(self, hint=None, obj=None, name=None,\n-                     exc=None):\n-            super(jinja2.Undefined, self).__init__()\n+        def __init__(self, hint=None, obj=None, name=None, exc=None):\n+            super(ParserMacroCapture, self).__init__(hint=hint, name=name)\n             self.node = node\n             self.name = name\n             self.package_name = node.get('package_name')\n@@ -211,18 +210,22 @@\n                                 self.node.get('original_file_path'))\n \n             logger.debug(\n-                'A ParserMacroCapture has been deecopy()d, invalid reference '\n-                'to \"{}\" in node {}.{} (source path: {})'\n+                'dbt encountered an undefined variable, \"{}\" in node {}.{} '\n+                '(source path: {})'\n                 .format(self.name, self.node.get('package_name'),\n-                        self.node.get('name'),\n-                        path))\n+                        self.node.get('name'), path))\n \n+            # match jinja's message\n             dbt.exceptions.raise_compiler_error(\n-                'dbt has detected at least one invalid reference in {}.{}. '\n-                'Check logs for more information'\n-                .format(self.node.get('package_name'), self.node.get('name'))\n+                \"{!r} is undefined\".format(self.name),\n+                node=self.node\n             )\n \n+        def __getitem__(self, name):\n+            # Propagate the undefined value if a caller accesses this as if it\n+            # were a dictionary\n+            return self\n+\n         def __getattr__(self, name):\n             if name == 'name' or _is_dunder_name(name):\n                 raise AttributeError(\n", "issue": "handle exceptions that occur inside of jinja\n## Issue\r\n\r\n### Issue description\r\nGiven sql with invalid jinja like:\r\n```\r\n{{ config(\r\n    materialized = \"table\",\r\n    schema = 'custom_schema' -- render this in a custom schema\r\n) }}\r\n```\r\n\r\ndbt will throw a very unhelpful error that looks like:\r\n```\r\n  File \"/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/dbt/parser/base_sql.py\", line 95, in parse_sql_nodes\r\n    macros=macros)\r\n  File \"/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/dbt/parser/base.py\", line 105, in parse_node\r\n    capture_macros=True)\r\n  File \"/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/dbt/clients/jinja.py\", line 198, in get_rendered\r\n    return render_template(template, ctx, node)\r\n  File \"/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/dbt/clients/jinja.py\", line 185, in render_template\r\n    return template.render(ctx)\r\n  File \"/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/jinja2/asyncsupport.py\", line 76, in render\r\n    return original_render(self, *args, **kwargs)\r\n  File \"/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/jinja2/environment.py\", line 1008, in render\r\n    return self.environment.handle_exception(exc_info, True)\r\n  File \"/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/jinja2/environment.py\", line 780, in handle_exception\r\n    reraise(exc_type, exc_value, tb)\r\n  File \"/usr/local/Cellar/dbt/0.10.2/libexec/lib/python3.7/site-packages/jinja2/_compat.py\", line 37, in reraise\r\n    raise value.with_traceback(tb)\r\n  File \"<template>\", line 1, in top-level template code\r\nTypeError: exceptions must derive from BaseException\r\n```\r\n\r\nIt will likely be difficult to pinpoint the exact error, but it should ideally be able to throw some sort of human-readable parsing error, indicating the model where the syntax error occurred.\n", "before_files": [{"content": "import codecs\nimport linecache\nimport os\n\nimport jinja2\nimport jinja2._compat\nimport jinja2.ext\nimport jinja2.nodes\nimport jinja2.parser\nimport jinja2.sandbox\n\nimport dbt.compat\nimport dbt.exceptions\n\nfrom dbt.node_types import NodeType\nfrom dbt.utils import AttrDict\n\nfrom dbt.logger import GLOBAL_LOGGER as logger  # noqa\n\n\nclass MacroFuzzParser(jinja2.parser.Parser):\n    def parse_macro(self):\n        node = jinja2.nodes.Macro(lineno=next(self.stream).lineno)\n\n        # modified to fuzz macros defined in the same file. this way\n        # dbt can understand the stack of macros being called.\n        #  - @cmcarthur\n        node.name = dbt.utils.get_dbt_macro_name(\n            self.parse_assign_target(name_only=True).name)\n\n        self.parse_signature(node)\n        node.body = self.parse_statements(('name:endmacro',),\n                                          drop_needle=True)\n        return node\n\n\nclass MacroFuzzEnvironment(jinja2.sandbox.SandboxedEnvironment):\n    def _parse(self, source, name, filename):\n        return MacroFuzzParser(\n            self, source, name,\n            jinja2._compat.encode_filename(filename)\n        ).parse()\n\n    def _compile(self, source, filename):\n        \"\"\"Override jinja's compilation to stash the rendered source inside\n        the python linecache for debugging.\n        \"\"\"\n        if filename == '<template>':\n            # make a better filename\n            filename = 'dbt-{}'.format(\n                codecs.encode(os.urandom(12), 'hex').decode('ascii')\n            )\n            # encode, though I don't think this matters\n            filename = jinja2._compat.encode_filename(filename)\n            # put ourselves in the cache using the 'lazycache' method\n            linecache.cache[filename] = (lambda: source,)\n\n        return super(MacroFuzzEnvironment, self)._compile(source, filename)\n\n\nclass TemplateCache(object):\n\n    def __init__(self):\n        self.file_cache = {}\n\n    def get_node_template(self, node):\n        key = (node['package_name'], node['original_file_path'])\n\n        if key in self.file_cache:\n            return self.file_cache[key]\n\n        template = get_template(\n            string=node.get('raw_sql'),\n            ctx={},\n            node=node\n        )\n        self.file_cache[key] = template\n\n        return template\n\n    def clear(self):\n        self.file_cache.clear()\n\n\ntemplate_cache = TemplateCache()\n\n\ndef macro_generator(node):\n    def apply_context(context):\n        def call(*args, **kwargs):\n            name = node.get('name')\n            template = template_cache.get_node_template(node)\n            module = template.make_module(context, False, context)\n\n            if node['resource_type'] == NodeType.Operation:\n                macro = module.__dict__[dbt.utils.get_dbt_operation_name(name)]\n            else:\n                macro = module.__dict__[dbt.utils.get_dbt_macro_name(name)]\n            module.__dict__.update(context)\n\n            try:\n                return macro(*args, **kwargs)\n            except dbt.exceptions.MacroReturn as e:\n                return e.value\n            except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:\n                dbt.exceptions.raise_compiler_error(str(e), node)\n            except dbt.exceptions.CompilationException as e:\n                e.stack.append(node)\n                raise e\n\n        return call\n    return apply_context\n\n\nclass MaterializationExtension(jinja2.ext.Extension):\n    tags = ['materialization']\n\n    def parse(self, parser):\n        node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)\n        materialization_name = \\\n            parser.parse_assign_target(name_only=True).name\n\n        adapter_name = 'default'\n        node.args = []\n        node.defaults = []\n\n        while parser.stream.skip_if('comma'):\n            target = parser.parse_assign_target(name_only=True)\n\n            if target.name == 'default':\n                pass\n\n            elif target.name == 'adapter':\n                parser.stream.expect('assign')\n                value = parser.parse_expression()\n                adapter_name = value.value\n\n            else:\n                dbt.exceptions.invalid_materialization_argument(\n                    materialization_name, target.name)\n\n        node.name = dbt.utils.get_materialization_macro_name(\n            materialization_name, adapter_name)\n\n        node.body = parser.parse_statements(('name:endmaterialization',),\n                                            drop_needle=True)\n\n        return node\n\n\nclass OperationExtension(jinja2.ext.Extension):\n    tags = ['operation']\n\n    def parse(self, parser):\n        node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)\n        operation_name = \\\n            parser.parse_assign_target(name_only=True).name\n\n        node.args = []\n        node.defaults = []\n\n        while parser.stream.skip_if('comma'):\n            target = parser.parse_assign_target(name_only=True)\n\n        node.name = dbt.utils.get_operation_macro_name(operation_name)\n\n        node.body = parser.parse_statements(('name:endoperation',),\n                                            drop_needle=True)\n\n        return node\n\n\nclass DocumentationExtension(jinja2.ext.Extension):\n    tags = ['docs']\n\n    def parse(self, parser):\n        node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)\n        docs_name = parser.parse_assign_target(name_only=True).name\n\n        node.args = []\n        node.defaults = []\n        node.name = dbt.utils.get_docs_macro_name(docs_name)\n        node.body = parser.parse_statements(('name:enddocs',),\n                                            drop_needle=True)\n        return node\n\n\ndef _is_dunder_name(name):\n    return name.startswith('__') and name.endswith('__')\n\n\ndef create_macro_capture_env(node):\n\n    class ParserMacroCapture(jinja2.Undefined):\n        \"\"\"\n        This class sets up the parser to capture macros.\n        \"\"\"\n        def __init__(self, hint=None, obj=None, name=None,\n                     exc=None):\n            super(jinja2.Undefined, self).__init__()\n            self.node = node\n            self.name = name\n            self.package_name = node.get('package_name')\n            # jinja uses these for safety, so we have to override them.\n            # see https://github.com/pallets/jinja/blob/master/jinja2/sandbox.py#L332-L339 # noqa\n            self.unsafe_callable = False\n            self.alters_data = False\n\n        def __deepcopy__(self, memo):\n            path = os.path.join(self.node.get('root_path'),\n                                self.node.get('original_file_path'))\n\n            logger.debug(\n                'A ParserMacroCapture has been deecopy()d, invalid reference '\n                'to \"{}\" in node {}.{} (source path: {})'\n                .format(self.name, self.node.get('package_name'),\n                        self.node.get('name'),\n                        path))\n\n            dbt.exceptions.raise_compiler_error(\n                'dbt has detected at least one invalid reference in {}.{}. '\n                'Check logs for more information'\n                .format(self.node.get('package_name'), self.node.get('name'))\n            )\n\n        def __getattr__(self, name):\n            if name == 'name' or _is_dunder_name(name):\n                raise AttributeError(\n                    \"'{}' object has no attribute '{}'\"\n                    .format(type(self).__name__, name)\n                )\n\n            self.package_name = self.name\n            self.name = name\n\n            return self\n\n        def __call__(self, *args, **kwargs):\n            return True\n\n    return ParserMacroCapture\n\n\ndef get_environment(node=None, capture_macros=False):\n    args = {\n        'extensions': []\n    }\n\n    if capture_macros:\n        args['undefined'] = create_macro_capture_env(node)\n\n    args['extensions'].append(MaterializationExtension)\n    args['extensions'].append(OperationExtension)\n    args['extensions'].append(DocumentationExtension)\n\n    return MacroFuzzEnvironment(**args)\n\n\ndef parse(string):\n    try:\n        return get_environment().parse(dbt.compat.to_string(string))\n\n    except (jinja2.exceptions.TemplateSyntaxError,\n            jinja2.exceptions.UndefinedError) as e:\n        e.translated = False\n        dbt.exceptions.raise_compiler_error(str(e))\n\n\ndef get_template(string, ctx, node=None, capture_macros=False):\n    try:\n        env = get_environment(node, capture_macros)\n\n        template_source = dbt.compat.to_string(string)\n        return env.from_string(template_source, globals=ctx)\n\n    except (jinja2.exceptions.TemplateSyntaxError,\n            jinja2.exceptions.UndefinedError) as e:\n        e.translated = False\n        dbt.exceptions.raise_compiler_error(str(e), node)\n\n\ndef render_template(template, ctx, node=None):\n    try:\n        return template.render(ctx)\n\n    except (jinja2.exceptions.TemplateSyntaxError,\n            jinja2.exceptions.UndefinedError) as e:\n        e.translated = False\n        dbt.exceptions.raise_compiler_error(str(e), node)\n\n\ndef get_rendered(string, ctx, node=None,\n                 capture_macros=False):\n    template = get_template(string, ctx, node,\n                            capture_macros=capture_macros)\n\n    return render_template(template, ctx, node)\n\n\ndef undefined_error(msg):\n    raise jinja2.exceptions.UndefinedError(msg)\n", "path": "dbt/clients/jinja.py"}], "after_files": [{"content": "import codecs\nimport linecache\nimport os\n\nimport jinja2\nimport jinja2._compat\nimport jinja2.ext\nimport jinja2.nodes\nimport jinja2.parser\nimport jinja2.sandbox\n\nimport dbt.compat\nimport dbt.exceptions\n\nfrom dbt.node_types import NodeType\nfrom dbt.utils import AttrDict\n\nfrom dbt.logger import GLOBAL_LOGGER as logger  # noqa\n\n\nclass MacroFuzzParser(jinja2.parser.Parser):\n    def parse_macro(self):\n        node = jinja2.nodes.Macro(lineno=next(self.stream).lineno)\n\n        # modified to fuzz macros defined in the same file. this way\n        # dbt can understand the stack of macros being called.\n        #  - @cmcarthur\n        node.name = dbt.utils.get_dbt_macro_name(\n            self.parse_assign_target(name_only=True).name)\n\n        self.parse_signature(node)\n        node.body = self.parse_statements(('name:endmacro',),\n                                          drop_needle=True)\n        return node\n\n\nclass MacroFuzzEnvironment(jinja2.sandbox.SandboxedEnvironment):\n    def _parse(self, source, name, filename):\n        return MacroFuzzParser(\n            self, source, name,\n            jinja2._compat.encode_filename(filename)\n        ).parse()\n\n    def _compile(self, source, filename):\n        \"\"\"Override jinja's compilation to stash the rendered source inside\n        the python linecache for debugging.\n        \"\"\"\n        if filename == '<template>':\n            # make a better filename\n            filename = 'dbt-{}'.format(\n                codecs.encode(os.urandom(12), 'hex').decode('ascii')\n            )\n            # encode, though I don't think this matters\n            filename = jinja2._compat.encode_filename(filename)\n            # put ourselves in the cache using the 'lazycache' method\n            linecache.cache[filename] = (lambda: source,)\n\n        return super(MacroFuzzEnvironment, self)._compile(source, filename)\n\n\nclass TemplateCache(object):\n\n    def __init__(self):\n        self.file_cache = {}\n\n    def get_node_template(self, node):\n        key = (node['package_name'], node['original_file_path'])\n\n        if key in self.file_cache:\n            return self.file_cache[key]\n\n        template = get_template(\n            string=node.get('raw_sql'),\n            ctx={},\n            node=node\n        )\n        self.file_cache[key] = template\n\n        return template\n\n    def clear(self):\n        self.file_cache.clear()\n\n\ntemplate_cache = TemplateCache()\n\n\ndef macro_generator(node):\n    def apply_context(context):\n        def call(*args, **kwargs):\n            name = node.get('name')\n            template = template_cache.get_node_template(node)\n            module = template.make_module(context, False, context)\n\n            if node['resource_type'] == NodeType.Operation:\n                macro = module.__dict__[dbt.utils.get_dbt_operation_name(name)]\n            else:\n                macro = module.__dict__[dbt.utils.get_dbt_macro_name(name)]\n            module.__dict__.update(context)\n\n            try:\n                return macro(*args, **kwargs)\n            except dbt.exceptions.MacroReturn as e:\n                return e.value\n            except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:\n                dbt.exceptions.raise_compiler_error(str(e), node)\n            except dbt.exceptions.CompilationException as e:\n                e.stack.append(node)\n                raise e\n\n        return call\n    return apply_context\n\n\nclass MaterializationExtension(jinja2.ext.Extension):\n    tags = ['materialization']\n\n    def parse(self, parser):\n        node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)\n        materialization_name = \\\n            parser.parse_assign_target(name_only=True).name\n\n        adapter_name = 'default'\n        node.args = []\n        node.defaults = []\n\n        while parser.stream.skip_if('comma'):\n            target = parser.parse_assign_target(name_only=True)\n\n            if target.name == 'default':\n                pass\n\n            elif target.name == 'adapter':\n                parser.stream.expect('assign')\n                value = parser.parse_expression()\n                adapter_name = value.value\n\n            else:\n                dbt.exceptions.invalid_materialization_argument(\n                    materialization_name, target.name)\n\n        node.name = dbt.utils.get_materialization_macro_name(\n            materialization_name, adapter_name)\n\n        node.body = parser.parse_statements(('name:endmaterialization',),\n                                            drop_needle=True)\n\n        return node\n\n\nclass OperationExtension(jinja2.ext.Extension):\n    tags = ['operation']\n\n    def parse(self, parser):\n        node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)\n        operation_name = \\\n            parser.parse_assign_target(name_only=True).name\n\n        node.args = []\n        node.defaults = []\n\n        while parser.stream.skip_if('comma'):\n            target = parser.parse_assign_target(name_only=True)\n\n        node.name = dbt.utils.get_operation_macro_name(operation_name)\n\n        node.body = parser.parse_statements(('name:endoperation',),\n                                            drop_needle=True)\n\n        return node\n\n\nclass DocumentationExtension(jinja2.ext.Extension):\n    tags = ['docs']\n\n    def parse(self, parser):\n        node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)\n        docs_name = parser.parse_assign_target(name_only=True).name\n\n        node.args = []\n        node.defaults = []\n        node.name = dbt.utils.get_docs_macro_name(docs_name)\n        node.body = parser.parse_statements(('name:enddocs',),\n                                            drop_needle=True)\n        return node\n\n\ndef _is_dunder_name(name):\n    return name.startswith('__') and name.endswith('__')\n\n\ndef create_macro_capture_env(node):\n\n    class ParserMacroCapture(jinja2.Undefined):\n        \"\"\"\n        This class sets up the parser to capture macros.\n        \"\"\"\n        def __init__(self, hint=None, obj=None, name=None, exc=None):\n            super(ParserMacroCapture, self).__init__(hint=hint, name=name)\n            self.node = node\n            self.name = name\n            self.package_name = node.get('package_name')\n            # jinja uses these for safety, so we have to override them.\n            # see https://github.com/pallets/jinja/blob/master/jinja2/sandbox.py#L332-L339 # noqa\n            self.unsafe_callable = False\n            self.alters_data = False\n\n        def __deepcopy__(self, memo):\n            path = os.path.join(self.node.get('root_path'),\n                                self.node.get('original_file_path'))\n\n            logger.debug(\n                'dbt encountered an undefined variable, \"{}\" in node {}.{} '\n                '(source path: {})'\n                .format(self.name, self.node.get('package_name'),\n                        self.node.get('name'), path))\n\n            # match jinja's message\n            dbt.exceptions.raise_compiler_error(\n                \"{!r} is undefined\".format(self.name),\n                node=self.node\n            )\n\n        def __getitem__(self, name):\n            # Propagate the undefined value if a caller accesses this as if it\n            # were a dictionary\n            return self\n\n        def __getattr__(self, name):\n            if name == 'name' or _is_dunder_name(name):\n                raise AttributeError(\n                    \"'{}' object has no attribute '{}'\"\n                    .format(type(self).__name__, name)\n                )\n\n            self.package_name = self.name\n            self.name = name\n\n            return self\n\n        def __call__(self, *args, **kwargs):\n            return True\n\n    return ParserMacroCapture\n\n\ndef get_environment(node=None, capture_macros=False):\n    args = {\n        'extensions': []\n    }\n\n    if capture_macros:\n        args['undefined'] = create_macro_capture_env(node)\n\n    args['extensions'].append(MaterializationExtension)\n    args['extensions'].append(OperationExtension)\n    args['extensions'].append(DocumentationExtension)\n\n    return MacroFuzzEnvironment(**args)\n\n\ndef parse(string):\n    try:\n        return get_environment().parse(dbt.compat.to_string(string))\n\n    except (jinja2.exceptions.TemplateSyntaxError,\n            jinja2.exceptions.UndefinedError) as e:\n        e.translated = False\n        dbt.exceptions.raise_compiler_error(str(e))\n\n\ndef get_template(string, ctx, node=None, capture_macros=False):\n    try:\n        env = get_environment(node, capture_macros)\n\n        template_source = dbt.compat.to_string(string)\n        return env.from_string(template_source, globals=ctx)\n\n    except (jinja2.exceptions.TemplateSyntaxError,\n            jinja2.exceptions.UndefinedError) as e:\n        e.translated = False\n        dbt.exceptions.raise_compiler_error(str(e), node)\n\n\ndef render_template(template, ctx, node=None):\n    try:\n        return template.render(ctx)\n\n    except (jinja2.exceptions.TemplateSyntaxError,\n            jinja2.exceptions.UndefinedError) as e:\n        e.translated = False\n        dbt.exceptions.raise_compiler_error(str(e), node)\n\n\ndef get_rendered(string, ctx, node=None,\n                 capture_macros=False):\n    template = get_template(string, ctx, node,\n                            capture_macros=capture_macros)\n\n    return render_template(template, ctx, node)\n\n\ndef undefined_error(msg):\n    raise jinja2.exceptions.UndefinedError(msg)\n", "path": "dbt/clients/jinja.py"}]}