Repository: dbt-labs/dbt-core

Issue:
[CT-2594] [Regression] WarnErrorOptions in invocation_args_dict is not JSON serialisable 
### 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

- In dbt 1.5.0 the value for the key `warn_error_options`  in the `invocation_arg_dict` object is a Python object which looks like `"warn_error_options": WarnErrorOptions(include=[], exclude=[]),`

### Expected Behavior

- In dbt 1.4.x and earlier the value for the key `warn_error_options` in the `invocation_arg_dict` object was a string of a dict object which looks like `"warn_error_options\": {\"exclude\":[],\"include\":[]}`

### Steps To Reproduce

1. Invoke dbt in 1.4.x with [the warn options flag ](https://github.com/dbt-labs/docs.getdbt.com/pull/2691/files?short_path=5282227#diff-52822271cc0a418238ec087115df4ef8b81bf2c5d5b2eb520efad49e1c62cb95)
2. Do the same in 1.5.0
3. Observe the difference `invocation_arg_dict` object

### Relevant log output

_No response_

### Environment

```markdown
- OS: Mac
- Python: 3.8
- dbt: 1.5.0
```


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

other (mention it in "Additional Context")

### Additional Context

All of them


Discussion:
Thanks @jared-rimmer! This is similar to:
- https://github.com/dbt-labs/dbt-core/issues/7495.

I agree that we should serialize `WarnErrorOptions` somehow—whether to a real Python dictionary, or a JSON string—before exposing it to the Jinja context. The right spot to do that is probably in `args_to_dict`:

https://github.com/dbt-labs/dbt-core/blob/45d614533f0c7de81e20488c75c707262c9312e1/core/dbt/utils.py#L614-L656

Is that something you'd be interested in poking around with? Otherwise, we'll try to include this for a v1.5 patch, but I view it as lower priority relative to other bugs/regressions.
May be able to just pass the dbtclass mixin to the warnerroroptions.
Interesting the `WarnErrorOptions` class already uses the `dbtClassMixin` 🤔 Specifically [WarnErrorOptions inherits IncludeExclude](https://github.com/dbt-labs/dbt-core/blob/fb6c349677cd924d8013f46261620f89a13a7bfd/core/dbt/helper_types.py#L125) and [IncludeExclude inherits dbtClassMixin](https://github.com/dbt-labs/dbt-core/blob/fb6c349677cd924d8013f46261620f89a13a7bfd/core/dbt/helper_types.py#L93). So having `dbtClassMixin` as part of `WarnErrorOptions` doesn't resolve the issue. It appears to be more of an issue of dictifying nested objects.
@jtcohen6 you said
> I agree that we should serialize WarnErrorOptions somehow
Does that mean we're not specifically aiming to give the exact previous output, but just that it is serialized in a more friendly way. I.e. it doesn't need to be `"warn_error_options\": {\"exclude\":[],\"include\":[]}` but could be `"warn_error_options": {"exclude": [], "include": []}`

@jared-rimmer how exactly are you going about inspecting the value of `invocation_arg_dict`? I'm trying to inspect the output of 1.4 to write a test for 1.5 and 1.6, but am finding it difficult to actually ensure the proper initial state is setup 🙃 

Resolving patch:
diff --git a/core/dbt/utils.py b/core/dbt/utils.py
--- a/core/dbt/utils.py
+++ b/core/dbt/utils.py
@@ -17,6 +17,7 @@
 
 from contextlib import contextmanager
 from dbt.events.types import RetryExternalCall, RecordRetryException
+from dbt.helper_types import WarnErrorOptions
 from dbt import flags
 from enum import Enum
 from typing_extensions import Protocol
@@ -654,6 +655,9 @@ def args_to_dict(args):
         # this was required for a test case
         if isinstance(var_args[key], PosixPath) or isinstance(var_args[key], WindowsPath):
             var_args[key] = str(var_args[key])
+        if isinstance(var_args[key], WarnErrorOptions):
+            var_args[key] = var_args[key].to_dict()
+
         dict_args[key] = var_args[key]
     return dict_args
 


Test patch:
diff --git a/tests/unit/test_context.py b/tests/unit/test_context.py
--- a/tests/unit/test_context.py
+++ b/tests/unit/test_context.py
@@ -424,6 +424,9 @@ def test_invocation_args_to_dict_in_macro_runtime_context(
     # Comes from unit/utils.py config_from_parts_or_dicts method
     assert ctx["invocation_args_dict"]["profile_dir"] == "/dev/null"
 
+    assert isinstance(ctx["invocation_args_dict"]["warn_error_options"], Dict)
+    assert ctx["invocation_args_dict"]["warn_error_options"] == {"include": [], "exclude": []}
+
 
 def test_model_parse_context(config_postgres, manifest_fx, get_adapter, get_include_paths):
     ctx = providers.generate_parser_model_context(