Repository: spack/spack

Issue:
Visit + modern Python: maximum recursion depth exceeded
### Steps to reproduce

```console
> spack spec visit+python ^python@3.9:
==> Error: maximum recursion depth exceeded in comparison
```

### Error message

```console
> spack -d spec visit+python ^python@3.9:
...
Traceback (most recent call last):
  File "/Users/Adam/spack/bin/spack", line 52, in <module>
    sys.exit(main())
             ^^^^^^
  File "/Users/Adam/spack/lib/spack/spack_installable/main.py", line 37, in main
    sys.exit(spack.main.main(argv))
             ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/main.py", line 1064, in main
    return _main(argv)
           ^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/main.py", line 1019, in _main
    return finish_parse_and_run(parser, cmd_name, args.command, env_format_error)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/main.py", line 1047, in finish_parse_and_run
    return _invoke_command(command, parser, args, unknown)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/main.py", line 648, in _invoke_command
    return_val = command(parser, args)
                 ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/cmd/spec.py", line 101, in spec
    concretized_specs = spack.cmd.parse_specs(args.specs, concretize=True)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/cmd/__init__.py", line 218, in parse_specs
    spec.concretize(tests=tests)  # implies normalize
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/spec.py", line 2987, in concretize
    self._new_concretize(tests)
  File "/Users/Adam/spack/lib/spack/spack/spec.py", line 2959, in _new_concretize
    result = solver.solve([self], tests=tests, allow_deprecated=allow_deprecated)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 3212, in solve
    result, _, _ = self.driver.solve(
                   ^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 990, in solve
    error_handler.raise_if_errors()
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 815, in raise_if_errors
    msg = self.message(errors)
          ^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 781, in message
    messages = [
               ^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 782, in <listcomp>
    f"  {idx+1: 2}. {self.handle_error(msg, *args)}"
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 775, in handle_error
    for c in self.get_cause_tree(cause):
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 743, in get_cause_tree
    return self._get_cause_tree(cause, conditions, condition_causes, set())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 720, in _get_cause_tree
    return [indent + local] + [
                              ^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 723, in <listcomp>
    for c in self._get_cause_tree(
             ^^^^^^^^^^^^^^^^^^^^^
...
# repeated many times
...
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 720, in _get_cause_tree
    return [indent + local] + [
                              ^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 723, in <listcomp>
    for c in self._get_cause_tree(
             ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 717, in _get_cause_tree
    parents = [c for e, c in condition_causes if e == cause and c not in seen]
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Adam/spack/lib/spack/spack/solver/asp.py", line 717, in <listcomp>
    parents = [c for e, c in condition_causes if e == cause and c not in seen]
                                                 ^^^^^^^^^^
RecursionError: maximum recursion depth exceeded in comparison
```


### Information on your system

* **Spack:** 0.22.0.dev0 (15dcd3c65c6bbf36127c4cc4d0ed767c14d396b2)
* **Python:** 3.11.6
* **Platform:** darwin-sonoma-m2
* **Concretizer:** clingo

@tgamblin this is the issue I mentioned.

@alalazo this seems similar to #40553 and #37947.

@cyrush when will VisIt support modern VTK? Python 3.8 reaches EOL next year.

### General information

- [X] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [X] I have searched the issues of this repo and believe this is not a duplicate
- [X] I have run the failing commands in debug mode and reported the output


Discussion:
I think it relates to this https://github.com/spack/spack/pull/40173#discussion_r1378987279
@becker33 
Confirmed:

* **Spack:** 0.22.0.dev0 (ab60bfe36aa16128182d3fc240f9466740568d9c)
* **Python:** 3.11.5
* **Platform:** linux-ubuntu20.04-icelake
* **Concretizer:** clingo


Resolving patch:
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -713,7 +713,7 @@ def _get_cause_tree(
         (condition_id, set_id) in which the latter idea means that the condition represented by
         the former held in the condition set represented by the latter.
         """
-        seen = set(seen) | set(cause)
+        seen.add(cause)
         parents = [c for e, c in condition_causes if e == cause and c not in seen]
         local = "required because %s " % conditions[cause[0]]
 
@@ -812,7 +812,14 @@ def on_model(model):
         errors = sorted(
             [(int(priority), msg, args) for priority, msg, *args in error_args], reverse=True
         )
-        msg = self.message(errors)
+        try:
+            msg = self.message(errors)
+        except Exception as e:
+            msg = (
+                f"unexpected error during concretization [{str(e)}]. "
+                f"Please report a bug at https://github.com/spack/spack/issues"
+            )
+            raise spack.error.SpackError(msg)
         raise UnsatisfiableSpecError(msg)
 
 


Test patch: