We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support jinja expression statements
## Feature

### Feature description
http://jinja.pocoo.org/docs/2.10/extensions/#expression-statement

Presently, we hack expressions with:
```
{% set _ = my_dict.update({"a": 1, "b": 2}) %}
```

Instead, the jinja expression statement will make it possible to write:

```
{% do my_dict.update({"a": 1, "b": 2}) %}
```

This is a minor difference, but it will make jinja sql more readable and idiomatic.

### Who will this benefit?
jinja writers

--- 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, exc=None):
199             super(ParserMacroCapture, self).__init__(hint=hint, name=name)
200             self.node = node
201             self.name = name
202             self.package_name = node.get('package_name')
203             # jinja uses these for safety, so we have to override them.
204             # see https://github.com/pallets/jinja/blob/master/jinja2/sandbox.py#L332-L339 # noqa
205             self.unsafe_callable = False
206             self.alters_data = False
207 
208         def __deepcopy__(self, memo):
209             path = os.path.join(self.node.get('root_path'),
210                                 self.node.get('original_file_path'))
211 
212             logger.debug(
213                 'dbt encountered an undefined variable, "{}" in node {}.{} '
214                 '(source path: {})'
215                 .format(self.name, self.node.get('package_name'),
216                         self.node.get('name'), path))
217 
218             # match jinja's message
219             dbt.exceptions.raise_compiler_error(
220                 "{!r} is undefined".format(self.name),
221                 node=self.node
222             )
223 
224         def __getitem__(self, name):
225             # Propagate the undefined value if a caller accesses this as if it
226             # were a dictionary
227             return self
228 
229         def __getattr__(self, name):
230             if name == 'name' or _is_dunder_name(name):
231                 raise AttributeError(
232                     "'{}' object has no attribute '{}'"
233                     .format(type(self).__name__, name)
234                 )
235 
236             self.package_name = self.name
237             self.name = name
238 
239             return self
240 
241         def __call__(self, *args, **kwargs):
242             return True
243 
244     return ParserMacroCapture
245 
246 
247 def get_environment(node=None, capture_macros=False):
248     args = {
249         'extensions': []
250     }
251 
252     if capture_macros:
253         args['undefined'] = create_macro_capture_env(node)
254 
255     args['extensions'].append(MaterializationExtension)
256     args['extensions'].append(OperationExtension)
257     args['extensions'].append(DocumentationExtension)
258 
259     return MacroFuzzEnvironment(**args)
260 
261 
262 def parse(string):
263     try:
264         return get_environment().parse(dbt.compat.to_string(string))
265 
266     except (jinja2.exceptions.TemplateSyntaxError,
267             jinja2.exceptions.UndefinedError) as e:
268         e.translated = False
269         dbt.exceptions.raise_compiler_error(str(e))
270 
271 
272 def get_template(string, ctx, node=None, capture_macros=False):
273     try:
274         env = get_environment(node, capture_macros)
275 
276         template_source = dbt.compat.to_string(string)
277         return env.from_string(template_source, globals=ctx)
278 
279     except (jinja2.exceptions.TemplateSyntaxError,
280             jinja2.exceptions.UndefinedError) as e:
281         e.translated = False
282         dbt.exceptions.raise_compiler_error(str(e), node)
283 
284 
285 def render_template(template, ctx, node=None):
286     try:
287         return template.render(ctx)
288 
289     except (jinja2.exceptions.TemplateSyntaxError,
290             jinja2.exceptions.UndefinedError) as e:
291         e.translated = False
292         dbt.exceptions.raise_compiler_error(str(e), node)
293 
294 
295 def get_rendered(string, ctx, node=None,
296                  capture_macros=False):
297     template = get_template(string, ctx, node,
298                             capture_macros=capture_macros)
299 
300     return render_template(template, ctx, node)
301 
302 
303 def undefined_error(msg):
304     raise jinja2.exceptions.UndefinedError(msg)
305
```
--- 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
@@ -246,7 +246,7 @@
 
 def get_environment(node=None, capture_macros=False):
     args = {
-        'extensions': []
+        'extensions': ['jinja2.ext.do']
     }
 
     if capture_macros:

{"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@@ -246,7 +246,7 @@\n \n def get_environment(node=None, capture_macros=False):\n     args = {\n-        'extensions': []\n+        'extensions': ['jinja2.ext.do']\n     }\n \n     if capture_macros:\n", "issue": "Support jinja expression statements\n## Feature\r\n\r\n### Feature description\r\nhttp://jinja.pocoo.org/docs/2.10/extensions/#expression-statement\r\n\r\nPresently, we hack expressions with:\r\n```\r\n{% set _ = my_dict.update({\"a\": 1, \"b\": 2}) %}\r\n```\r\n\r\nInstead, the jinja expression statement will make it possible to write:\r\n\r\n```\r\n{% do my_dict.update({\"a\": 1, \"b\": 2}) %}\r\n```\r\n\r\nThis is a minor difference, but it will make jinja sql more readable and idiomatic.\r\n\r\n### Who will this benefit?\r\njinja writers\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, 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"}], "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': ['jinja2.ext.do']\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"}]}