Repository: dbt-labs/dbt-core

Issue:
RecursionError: maximum recursion depth exceeded in comparison
## Issue
`RecursionError: maximum recursion depth exceeded in comparison`
### Issue description
Running `dbt run` when my project has 3 files in the model folder works, but adding one more errors out with the above.
### Results
Error above should not occur, must be able to complete `dbt run`

### System information
The output of `dbt --version`: 
```
installed version: 0.11.1
   latest version: 0.11.1

Up to date!
```
The operating system you're running on: MacOS 10.13.6

The python version you're using (probably the output of `python --version`)
`Python 3.6.6 :: Anaconda custom (64-bit)`
### Steps to reproduce
In as much detail as possible, please provide steps to reproduce the issue. Sample data that triggers the issue, example models, etc are all very helpful here.

Just keep adding new files with SQL into the models/afolder/

### The output of the `--debug` command:
```
(MainThread): maximum recursion depth exceeded in comparison
2018-10-22 16:36:31,926 (MainThread): Traceback (most recent call last):
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/main.py", line 72, in main
    results, succeeded = handle_and_check(args)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/main.py", line 117, in handle_and_check
    task, res = run_from_args(parsed)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/main.py", line 172, in run_from_args
    results = run_from_task(task, proj, parsed)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/main.py", line 180, in run_from_task
    result = task.run()
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/task/run.py", line 26, in run
    results = runner.run(query, ModelRunner)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/runner.py", line 244, in run
    return self.run_from_graph(Selector, Runner, query)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/runner.py", line 197, in run_from_graph
    manifest, linker = self.compile(self.project)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/runner.py", line 186, in compile
    return compiler.compile()
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/compilation.py", line 241, in compile
    manifest = dbt.loader.GraphLoader.load_all(self.project, all_projects)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/loader.py", line 21, in load_all
    nodes.update(loader.load_all(root_project, all_projects, macros))
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/loader.py", line 86, in load_all
    macros)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/loader.py", line 102, in load_project
    macros=macros)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/parser/base_sql.py", line 67, in load_and_parse
    macros)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/parser/base_sql.py", line 95, in parse_sql_nodes
    macros=macros)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/parser/base.py", line 125, in parse_node
    schema_override = config.config.get('schema')
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/model.py", line 82, in config
    self.in_model_config)
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/model.py", line 50, in _merge
    merged_config.copy(), config.copy()
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/utils.py", line 251, in deep_merge
    last = copy.deepcopy(lst.pop(len(lst)-1))
  File "/Users/azme/anaconda3/lib/python3.6/copy.py", line 150, in deepcopy
    y = copier(x, memo)
  File "/Users/azme/anaconda3/lib/python3.6/copy.py", line 240, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "/Users/azme/anaconda3/lib/python3.6/copy.py", line 180, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "/Users/azme/anaconda3/lib/python3.6/copy.py", line 281, in _reconstruct
    if hasattr(y, '__setstate__'):
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/clients/jinja.py", line 165, in __getattr__
    self.package_name = self.name
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/clients/jinja.py", line 165, in __getattr__
    self.package_name = self.name
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/clients/jinja.py", line 165, in __getattr__
    self.package_name = self.name
  [Previous line repeated 320 more times]
  File "/Users/azme/anaconda3/lib/python3.6/site-packages/dbt/clients/jinja.py", line 162, in __getattr__
    if name in ['unsafe_callable', 'alters_data']:
RecursionError: maximum recursion depth exceeded in comparison
```


Discussion:
Thanks for the report @ArthurZ! This one is definitely new to me. You indicated:
> Running dbt run when my project has 3 files in the model folder works, but adding one more errors out with the above.

I have to imagine that this has more to do with the context of the models, and not the number of models itself! Are you able to share some example code that you're using? That might help us track this down
Whenever I got they error before it had nothing to do with recursion or stack traces in Python, but was always typos or unquoted variables. For example, if you build a dependent model with the ref function, make sure you quote the model name 

E.g. 

```sql
SELECT *
FROM {{ ref(my_cool_model) }}
```

Could blow up dbt and return an error like that. Where fixing it to:

```sql
SELECT *
FROM {{ ref('my_cool_model') }}
```
Would run as expected
I am going to pick my jaw up from off of the floor, and then I am going to submit a bugfix for this. How am I just hearing about this issue???

Thanks for the test case @raybuhr - i can reproduce that locally

Resolving patch:
diff --git a/dbt/clients/jinja.py b/dbt/clients/jinja.py
--- a/dbt/clients/jinja.py
+++ b/dbt/clients/jinja.py
@@ -185,6 +185,10 @@ def parse(self, parser):
         return node
 
 
+def _is_dunder_name(name):
+    return name.startswith('__') and name.endswith('__')
+
+
 def create_macro_capture_env(node):
 
     class ParserMacroCapture(jinja2.Undefined):
@@ -194,17 +198,37 @@ class ParserMacroCapture(jinja2.Undefined):
         def __init__(self, hint=None, obj=None, name=None,
                      exc=None):
             super(jinja2.Undefined, self).__init__()
-
             self.node = node
             self.name = name
             self.package_name = node.get('package_name')
-
-        def __getattr__(self, name):
-
             # jinja uses these for safety, so we have to override them.
             # see https://github.com/pallets/jinja/blob/master/jinja2/sandbox.py#L332-L339 # noqa
-            if name in ['unsafe_callable', 'alters_data']:
-                return False
+            self.unsafe_callable = False
+            self.alters_data = False
+
+        def __deepcopy__(self, memo):
+            path = os.path.join(self.node.get('root_path'),
+                                self.node.get('original_file_path'))
+
+            logger.debug(
+                'A ParserMacroCapture has been deecopy()d, invalid reference '
+                'to "{}" in node {}.{} (source path: {})'
+                .format(self.name, self.node.get('package_name'),
+                        self.node.get('name'),
+                        path))
+
+            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'))
+            )
+
+        def __getattr__(self, name):
+            if name == 'name' or _is_dunder_name(name):
+                raise AttributeError(
+                    "'{}' object has no attribute '{}'"
+                    .format(type(self).__name__, name)
+                )
 
             self.package_name = self.name
             self.name = name


Test patch: