Repository: dbt-labs/dbt-core

Issue:
[CT-1189] [Bug] Missing configs when multiple dbt.config.get in Python model
### Is this a new bug in dbt-core?

- [X] I believe this is a new bug in dbt-core
- [X] I have searched the existing issues, and I could not find an existing issue for this bug

### Current Behavior

Not all configs show up in generated code (`config_dict`) for Python model with multiple `dbt.config.get()` calls

### Expected Behavior

All configs are included in `config_dict`

### Steps To Reproduce

Define a model like:
```python
def model(dbt, session):

    PROJECT_ID = dbt.config.get("POSTHOG_PROJECT_ID")
    POSTHOG_TOKEN = dbt.config.get("POSTHOG_TOKEN")

    df = dbt.ref("orders")

    return df
```
With configs like:
```yml
version: 2
models:
  - name: something
    config:
      PROJECT_ID: something
      POSTHOG_TOKEN: something_else
```

Run `dbt compile`. See compiled output like:
```
def model(dbt, session):

    PROJECT_ID = dbt.config.get("POSTHOG_PROJECT_ID")
    POSTHOG_TOKEN = dbt.config.get("POSTHOG_TOKEN")

    df = dbt.ref("orders")

    return df


# This part is user provided model code
# you will need to copy the next section to run the code
# COMMAND ----------
# this part is dbt logic for get ref work, do not modify

def ref(*args,dbt_load_df_function):
    refs = {"orders": "ad_hoc.dbt_jcohen.orders"}
    key = ".".join(args)
    return dbt_load_df_function(refs[key])


def source(*args, dbt_load_df_function):
    sources = {}
    key = ".".join(args)
    return dbt_load_df_function(sources[key])


config_dict = {'POSTHOG_TOKEN': 'something_else'}


class config:
    def __init__(self, *args, **kwargs):
        pass

    @staticmethod
    def get(key):
        return config_dict.get(key)

class this:
    """dbt.this() or dbt.this.identifier"""
    database = 'ad_hoc'
    schema = 'dbt_jcohen'
    identifier = 'something'
    def __repr__(self):
        return 'ad_hoc.dbt_jcohen.something'


class dbtObj:
    def __init__(self, load_df_function) -> None:
        self.source = lambda *args: source(*args, dbt_load_df_function=load_df_function)
        self.ref = lambda *args: ref(*args, dbt_load_df_function=load_df_function)
        self.config = config
        self.this = this()
        self.is_incremental = False

# COMMAND ----------

# To run this in snowsight, you need to select entry point to be main
# And you may have to modify the return type to text to get the result back
# def main(session):
#     dbt = dbtObj(session.table)
#     df = model(dbt, session)
#     return df.collect()

# to run this in local notebook, you need to create a session following examples https://github.com/Snowflake-Labs/sfguide-getting-started-snowpark-python
# then you can do the following to run model
# dbt = dbtObj(session.table)
# df = model(dbt, session)
```

Namely:
```
config_dict = {'POSTHOG_TOKEN': 'something_else'}
```

### Relevant log output

_No response_

### Environment

```markdown
- OS:
- Python:
- dbt: 1.3.0-b2 (I think latest `main`)
```


### Which database adapter are you using with dbt?

snowflake

### Additional Context

_No response_


Discussion:

Resolving patch:
diff --git a/core/dbt/parser/models.py b/core/dbt/parser/models.py
--- a/core/dbt/parser/models.py
+++ b/core/dbt/parser/models.py
@@ -205,16 +205,16 @@ def parse_python_model(self, node, config, context):
 
         dbtParser = PythonParseVisitor(node)
         dbtParser.visit(tree)
-
+        config_keys_used = []
         for (func, args, kwargs) in dbtParser.dbt_function_calls:
-            # TODO decide what we want to do with detected packages
-            # if func == "config":
-            #     kwargs["detected_packages"] = dbtParser.packages
             if func == "get":
-                context["config"](utilized=args)
+                config_keys_used.append(args[0])
                 continue
 
             context[func](*args, **kwargs)
+        if config_keys_used:
+            # this is being used in macro build_config_dict
+            context["config"](config_keys_used=config_keys_used)
 
     def render_update(self, node: ParsedModelNode, config: ContextConfig) -> None:
         self.manifest._parsing_info.static_analysis_path_count += 1


Test patch:
diff --git a/test/unit/test_parser.py b/test/unit/test_parser.py
--- a/test/unit/test_parser.py
+++ b/test/unit/test_parser.py
@@ -597,6 +597,20 @@ def model(dbt, session):
         self.assertIn(file_id, self.parser.manifest.files)
         self.assertEqual(self.parser.manifest.files[file_id].nodes, ['model.snowplow.py_model'])
 
+    def test_python_model_config_get(self):
+        py_code = """
+def model(dbt, session):
+    dbt.config.get("param_1")
+    dbt.config.get("param_2")
+    return df
+        """
+        block = self.file_block_for(py_code, 'nested/py_model.py')
+        self.parser.manifest.files[block.file.file_id] = block.file
+        
+        self.parser.parse_file(block)
+        node = list(self.parser.manifest.nodes.values())[0]
+        self.assertEqual(node.config.to_dict()["config_keys_used"], ["param_1", "param_2"])
+
     def test_wrong_python_model_def_miss_session(self):
         py_code = """
 def model(dbt):