Skip to content

Conversation

@dependabot
Copy link
Contributor

@dependabot dependabot bot commented on behalf of github Jan 16, 2026

Bumps kubernetes from 33.1.0 to 35.0.0.

Release notes

Sourced from kubernetes's releases.

Kubernetes Python Client v35.0.0 Stable Release

Getting started:

pip install --pre --upgrade kubernetes

Or from source, download attached zip file, then

unzip client-python-v35.0.0.zip
cd client-python-v35.0.0
python setup.py install

Then follow examples in https://github.com/kubernetes-client/python/tree/release-35.0/examples

Changelog: https://github.com/kubernetes-client/python/blob/release-35.0/CHANGELOG.md

Kubernetes Python Client v35.0.0 Beta 1 Release

Getting started:

pip install --pre --upgrade kubernetes

Or from source, download attached zip file, then

unzip client-python-v35.0.0b1.zip
cd client-python-v35.0.0b1
python setup.py install

Then follow examples in https://github.com/kubernetes-client/python/tree/release-35.0/examples

Changelog: https://github.com/kubernetes-client/python/blob/release-35.0/CHANGELOG.md

Kubernetes Python Client v35.0.0 Alpha 1 Release

Getting started:

pip install --pre --upgrade kubernetes

Or from source, download attached zip file, then

unzip client-python-v35.0.0a1.zip
cd client-python-v35.0.0a1
</tr></table> 

... (truncated)

Changelog

Sourced from kubernetes's changelog.

v35.0.0+snapshot

Kubernetes API Version: v1.35.0

API Change

  • Added ObservedGeneration to CustomResourceDefinition conditions. (kubernetes/kubernetes#134984, @​michaelasp)

  • Added WithOrigin within apis/core/validation with adjusted tests. (kubernetes/kubernetes#132825, @​PatrickLaabs)

  • Added scoring for the prioritized list feature so nodes that best satisfy the highest-ranked subrequests were chosen. (kubernetes/kubernetes#134711, @​mortent) [SIG Node, Scheduling and Testing]

  • Added the --min-compatibility-version flag to kube-apiserver, kube-controller-manager, and kube-scheduler. (kubernetes/kubernetes#133980, @​siyuanfoundation) [SIG API Machinery, Architecture, Cluster Lifecycle, Etcd, Scheduling and Testing]

  • Added the StorageVersionMigration v1beta1 API and removed the v1alpha1 API.

    ACTION REQUIRED: The v1alpha1 API is no longer supported. Users must remove any v1alpha1 resources before upgrading. (kubernetes/kubernetes#134784, @​michaelasp) [SIG API Machinery, Apps, Auth, Etcd and Testing]

  • Added validation to ensure log-flush-frequency is a positive value, returning an error instead of causing a panic. (kubernetes/kubernetes#133540, @​BenTheElder) [SIG Architecture, Instrumentation, Network and Node]

  • All containers are restarted when a source container in a restart policy rule exits. This alpha feature is gated behind RestartAllContainersOnContainerExit. (kubernetes/kubernetes#134345, @​yuanwang04) [SIG Apps, Node and Testing]

  • CSI drivers can now opt in to receive service account tokens via the secrets field instead of volume context by setting spec.serviceAccountTokenInSecrets: true in the CSIDriver object. This prevents tokens from being exposed in logs and other outputs. The feature is gated by the CSIServiceAccountTokenSecrets feature gate (beta in v1.35). (kubernetes/kubernetes#134826, @​aramase) [SIG API Machinery, Auth, Storage and Testing]

  • Changed kuberc configuration schema. Two new optional fields added to kuberc configuration, credPluginPolicy and credPluginAllowlist. This is documented in KEP-3104 and documentation is added to the website by kubernetes/website#52877 (kubernetes/kubernetes#134870, @​pmengelbert) [SIG API Machinery, Architecture, Auth, CLI, Instrumentation and Testing]

  • DRA device taints: DeviceTaintRule status provides information about the rule, including whether Pods still need to be evicted (EvictionInProgress condition). The newly added None effect can be used to preview what a DeviceTaintRule would do if it used the NoExecute effect and to taint devices (device health) without immediately affecting scheduling or running Pods. (kubernetes/kubernetes#134152, @​pohly) [SIG API Machinery, Apps, Auth, Node, Release, Scheduling and Testing]

  • DRA: The DynamicResourceAllocation feature gate for the core functionality (GA in v1.34) has now been locked to enabled-by-default and cannot be disabled anymore. (kubernetes/kubernetes#134452, @​pohly) [SIG Auth, Node, Scheduling and Testing]

  • Enabled kubectl get -o kyaml by default. To disable it, set KUBECTL_KYAML=false. (kubernetes/kubernetes#133327, @​thockin)

  • Enabled in-place resizing of pod-level resources.

    • Added Resources in PodStatus to capture resources set in the pod-level cgroup.
    • Added AllocatedResources in PodStatus to capture resources requested in the PodSpec. (kubernetes/kubernetes#132919, @​ndixita) [SIG API Machinery, Apps, Architecture, Auth, CLI, Instrumentation, Node, Scheduling and Testing]
  • Enabled the NominatedNodeNameForExpectation feature in kube-scheduler by default.

    • Enabled the ClearingNominatedNodeNameAfterBinding feature in kube-apiserver by default. (kubernetes/kubernetes#135103, @​ania-borowiec) [SIG API Machinery, Apps, Architecture, Auth, Autoscaling, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Scheduling, Storage and Testing]
  • Enhanced discovery responses to merge API groups and resources from all peer apiservers when the UnknownVersionInteroperabilityProxy feature is enabled. (kubernetes/kubernetes#133648, @​richabanker) [SIG API Machinery, Auth, Cloud Provider, Node, Scheduling and Testing]

  • Extended core/v1 Toleration to support numeric comparison operators (Gt,Lt). (kubernetes/kubernetes#134665, @​helayoty) [SIG API Machinery, Apps, Node, Scheduling, Testing and Windows]

  • Feature gate dependencies are now explicit, and validated at startup. A feature can no longer be enabled if it depends on a disabled feature. In particular, this means that AllAlpha=true will no longer work without enabling disabled-by-default beta features that are depended on (either with AllBeta=true or explicitly enumerating the disabled dependencies). (kubernetes/kubernetes#133697, @​tallclair) [SIG API Machinery, Architecture, Cluster Lifecycle and Node]

  • Generated OpenAPI model packages for API types into zz_generated.model_name.go files, accessible via the OpenAPIModelName() function. This allows API authors to declare desired OpenAPI model packages instead of relying on the Go package path of API types. (kubernetes/kubernetes#131755, @​jpbetz) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling, Storage and Testing]

  • Implemented constrained impersonation as described in KEP-5284. (kubernetes/kubernetes#134803, @​enj) [SIG API Machinery, Auth and Testing]

  • Introduced a new declarative validation tag +k8s:customUnique to control listmap uniqueness. (kubernetes/kubernetes#134279, @​yongruilin) [SIG API Machinery and Auth]

  • Introduced a structured and versioned v1alpha1 response for the statusz endpoint. (kubernetes/kubernetes#134313, @​richabanker) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing]

  • Introduced a structured and versioned v1alpha1 response format for the flagz endpoint. (kubernetes/kubernetes#134995, @​yongruilin) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing]

  • Introduced the GangScheduling kube-scheduler plugin to support "all-or-nothing" scheduling using the scheduling.k8s.io/v1alpha1 Workload API. (kubernetes/kubernetes#134722, @​macsko) [SIG API Machinery, Apps, Auth, CLI, Etcd, Scheduling and Testing]

  • Introduced the Node Declared Features capability (alpha), which includes:

    • A new Node.Status.DeclaredFeatures field for publishing node-specific features.
    • A component-helpers library for feature registration and inference.
    • A NodeDeclaredFeatures scheduler plugin to match pods with nodes that provide required features.
    • A NodeDeclaredFeatureValidator admission plugin to validate pod updates against a node's declared features. (kubernetes/kubernetes#133389, @​pravk03) [SIG API Machinery, Apps, Node, Release, Scheduling and Testing]
  • Introduced the scheduling.k8s.io/v1alpha1 Workload API to express workload-level scheduling requirements and allow the kube-scheduler to act on them. (kubernetes/kubernetes#134564, @​macsko) [SIG API Machinery, Apps, CLI, Etcd, Scheduling and Testing]

  • Introduced the alpha MutableSchedulingDirectivesForSuspendedJobs feature gate (disabled by default), which allows mutating a Job's scheduling directives while the Job is suspended. It also updates the Job controller to clears the status.startTime field for suspended Jobs. (kubernetes/kubernetes#135104, @​mimowo) [SIG Apps and Testing]

  • Kube-apiserver: Fixed a v1.34 regression in CustomResourceDefinition handling that incorrectly warned about unrecognized formats on number and integer properties. (kubernetes/kubernetes#133896, @​yongruilin) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cloud Provider, Contributor Experience, Network, Node and Scheduling]

  • Kube-apiserver: Fixed a possible panic validating a custom resource whose CustomResourceDefinition indicates a status subresource exists, but which does not define a status property in the openAPIV3Schema. (kubernetes/kubernetes#133721, @​fusida) [SIG API Machinery, Apps, Architecture, Auth, Autoscaling, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Release, Scheduling, Storage and Testing]

  • Kubernetes API Go types removed runtime use of the github.com/gogo/protobuf library, and are no longer registered into the global gogo type registry. Kubernetes API Go types were not suitable for use with the google.golang.org/protobuf library, and no longer implement ProtoMessage() by default to avoid accidental incompatible use. If removal of these marker methods impacts your use, it can be re-enabled for one more release with a kubernetes_protomessage_one_more_release build tag, but will be removed in v1.36. (kubernetes/kubernetes#134256, @​liggitt) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling and Storage]

  • Made node affinity in Persistent Volume mutable. (kubernetes/kubernetes#134339, @​huww98) [SIG API Machinery, Apps and Node]

  • Moved the ImagePullIntent and ImagePulledRecord objects used by the kubelet to track image pulls to the v1beta1 API version. (kubernetes/kubernetes#132579, @​stlaz) [SIG Auth and Node]

  • Pod resize now only allows CPU and memory resources; other resource types are forbidden. (kubernetes/kubernetes#135084, @​tallclair) [SIG Apps, Node and Testing]

  • Prevented Pods from being scheduled onto nodes that lack the required CSI driver. (kubernetes/kubernetes#135012, @​gnufied) [SIG API Machinery, Scheduling, Storage and Testing]

  • Promoted HPA configurable tolerance to beta. The HPAConfigurableTolerance feature gate has now been enabled by default. (kubernetes/kubernetes#133128, @​jm-franc) [SIG API Machinery and Autoscaling]

  • Promoted ReplicaSet and Deployment .status.terminatingReplicas tracking to beta. The DeploymentReplicaSetTerminatingReplicas feature gate is now enabled by default. (kubernetes/kubernetes#133087, @​atiratree) [SIG API Machinery, Apps and Testing]

... (truncated)

Commits
  • ce36f0b Merge pull request #2503 from yliaog/automated-release-of-35.0.0-upstream-rel...
  • b2dac4f updated compatibility matrix and maintenance status
  • a12b59b generated client change
  • 74a1b50 update changelog
  • c01bc6e update version constants for 35.0.0 release
  • 7b0378c examples: add README and improve error handling in apply_from_dict
  • 38dfa82 Merge pull request #2500 from yliaog/automated-release-of-35.0.0b1-upstream-r...
  • 2157448 updated the compatibility matrix
  • ad157bb generated client change
  • 042ab9e update changelog
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [kubernetes](https://github.com/kubernetes-client/python) from 33.1.0 to 35.0.0.
- [Release notes](https://github.com/kubernetes-client/python/releases)
- [Changelog](https://github.com/kubernetes-client/python/blob/master/CHANGELOG.md)
- [Commits](kubernetes-client/python@v33.1.0...v35.0.0)

---
updated-dependencies:
- dependency-name: kubernetes
  dependency-version: 35.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot bot added the dependencies Pull requests that update a dependency file label Jan 16, 2026
@dependabot dependabot bot requested a review from a team as a code owner January 16, 2026 04:10
@codecov
Copy link

codecov bot commented Jan 16, 2026

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
2876 2 2874 2
View the top 3 failed test(s) by shortest run time
tests.integration.test_proxy_docker.TestProxyDocker::test_docker_controller
Stack Traces | 0.001s run time
cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x7f56006e8250>
when = 'setup'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: Callable[[], TResult],
        when: Literal["collect", "setup", "call", "teardown"],
        reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
    ) -> CallInfo[TResult]:
        """Call func, wrapping the result in a CallInfo.
    
        :param func:
            The function to call. Called without arguments.
        :type func: Callable[[], _pytest.runner.TResult]
        :param when:
            The phase in which the function is called.
        :param reraise:
            Exception or exceptions that shall propagate if raised by the
            function, instead of being wrapped in the CallInfo.
        """
        excinfo = None
        instant = timing.Instant()
        try:
>           result: TResult | None = func()
                                     ^^^^^^

.venv/lib/python3.14............/site-packages/_pytest/runner.py:353: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

>       lambda: runtest_hook(item=item, **kwds),
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        when=when,
        reraise=get_reraise_exceptions(item.config),
    )

.venv/lib/python3.14............/site-packages/_pytest/runner.py:245: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <HookCaller 'pytest_runtest_setup'>
kwargs = {'item': <TestCaseFunction test_docker_controller>}
firstresult = False

    def __call__(self, **kwargs: object) -> Any:
        """Call the hook.
    
        Only accepts keyword arguments, which should match the hook
        specification.
    
        Returns the result(s) of calling all registered plugins, see
        :ref:`calling`.
        """
        assert not self.is_historic(), (
            "Cannot directly call a historic hook - use call_historic instead."
        )
        self._verify_all_args_are_provided(kwargs)
        firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
        # Copy because plugins may register other plugins during iteration (#438).
>       return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../site-packages/pluggy/_hooks.py:512: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.config.PytestPluginManager object at 0x7f56064a81a0>
hook_name = 'pytest_runtest_setup'
methods = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
kwargs = {'item': <TestCaseFunction test_docker_controller>}
firstresult = False

    def _hookexec(
        self,
        hook_name: str,
        methods: Sequence[HookImpl],
        kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        # called from all hookcaller instances.
        # enable_tracing will set its own wrapping function at self._inner_hookexec
>       return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../site-packages/pluggy/_manager.py:120: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_runtest_setup'
hook_impls = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
caller_kwargs = {'item': <TestCaseFunction test_docker_controller>}
firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            if firstresult:  # first result hooks return a single value
                result = results[0] if results else None
            else:
                result = results
    
            # run all wrapper post-yield blocks
            for teardown in reversed(teardowns):
                try:
                    if exception is not None:
                        try:
                            teardown.throw(exception)
                        except RuntimeError as re:
                            # StopIteration from generator causes RuntimeError
                            # even for coroutine usage - see #544
                            if (
                                isinstance(exception, StopIteration)
                                and re.__cause__ is exception
                            ):
                                teardown.close()
                                continue
                            else:
                                raise
                    else:
                        teardown.send(result)
                    # Following is unreachable for a well behaved hook wrapper.
                    # Try to force finalizers otherwise postponed till GC action.
                    # Note: close() may raise if generator handles GeneratorExit.
                    teardown.close()
                except StopIteration as si:
                    result = si.value
                    exception = None
                    continue
                except BaseException as e:
                    exception = e
                    continue
                _raise_wrapfail(teardown, "has second yield")
    
        if exception is not None:
>           raise exception

.venv/lib/python3.14............/site-packages/pluggy/_callers.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_runtest_setup'
hook_impls = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
caller_kwargs = {'item': <TestCaseFunction test_docker_controller>}
firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            if firstresult:  # first result hooks return a single value
                result = results[0] if results else None
            else:
                result = results
    
            # run all wrapper post-yield blocks
            for teardown in reversed(teardowns):
                try:
                    if exception is not None:
                        try:
>                           teardown.throw(exception)

.venv/lib/python3.14............/site-packages/pluggy/_callers.py:139: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.logging.LoggingPlugin object at 0x7f5604817230>
item = <TestCaseFunction test_docker_controller>

    @hookimpl(wrapper=True)
    def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None]:
        self.log_cli_handler.set_when("setup")
    
        empty: dict[str, list[logging.LogRecord]] = {}
        item.stash[caplog_records_key] = empty
        with self._runtest_for(item, "setup"):
>           yield

.venv/lib/python3.14.../site-packages/_pytest/logging.py:843: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_runtest_setup'
hook_impls = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
caller_kwargs = {'item': <TestCaseFunction test_docker_controller>}
firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            if firstresult:  # first result hooks return a single value
                result = results[0] if results else None
            else:
                result = results
    
            # run all wrapper post-yield blocks
            for teardown in reversed(teardowns):
                try:
                    if exception is not None:
                        try:
>                           teardown.throw(exception)

.venv/lib/python3.14............/site-packages/pluggy/_callers.py:139: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <CaptureManager _method='fd' _global_capturing=<MultiCapture out=<FDCapture 1 oldfd=6 _state='suspended' tmpfile=<Enco...xtIOWrapper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>
item = <TestCaseFunction test_docker_controller>

    @hookimpl(wrapper=True)
    def pytest_runtest_setup(self, item: Item) -> Generator[None]:
        with self.item_capture("setup", item):
>           return (yield)
                    ^^^^^

.venv/lib/python3.14.../site-packages/_pytest/capture.py:895: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_runtest_setup'
hook_impls = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
caller_kwargs = {'item': <TestCaseFunction test_docker_controller>}
firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
>                       res = hook_impl.function(*args)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14............/site-packages/pluggy/_callers.py:121: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

item = <TestCaseFunction test_docker_controller>

    def pytest_runtest_setup(item: Item) -> None:
        _update_current_test_var(item, "setup")
>       item.session._setupstate.setup(item)

.venv/lib/python3.14............/site-packages/_pytest/runner.py:165: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.runner.SetupState object at 0x7f5604817e00>
item = <TestCaseFunction test_docker_controller>

    def setup(self, item: Item) -> None:
        """Setup objects along the collector chain to the item."""
        needed_collectors = item.listchain()
    
        # If a collector fails its setup, fail its entire subtree of items.
        # The setup is not retried for each item - the same exception is used.
        for col, (finalizers, exc) in self.stack.items():
            assert col in needed_collectors, "previous item was not torn down properly"
            if exc:
                raise exc[0].with_traceback(exc[1])
    
        for col in needed_collectors[len(self.stack) :]:
            assert col not in self.stack
            # Push onto the stack.
            self.stack[col] = ([col.teardown], None)
            try:
>               col.setup()

.venv/lib/python3.14............/site-packages/_pytest/runner.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <TestCaseFunction test_docker_controller>

    def setup(self) -> None:
        # A bound method to be called during teardown() if set (see 'runtest()').
        self._explicit_tearDown: Callable[[], None] | None = None
>       super().setup()

.venv/lib/python3.14....../site-packages/_pytest/unittest.py:227: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <TestCaseFunction test_docker_controller>

    def setup(self) -> None:
>       self._request._fillfixtures()

.venv/lib/python3.14.../site-packages/_pytest/python.py:1723: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FixtureRequest for <TestCaseFunction test_docker_controller>>

    def _fillfixtures(self) -> None:
        item = self._pyfuncitem
        for argname in item.fixturenames:
            if argname not in item.funcargs:
>               item.funcargs[argname] = self.getfixturevalue(argname)
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:707: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FixtureRequest for <TestCaseFunction test_docker_controller>>
argname = '_unittest_setUpClass_fixture_TestProxyDocker'

    def getfixturevalue(self, argname: str) -> Any:
        """Dynamically run a named fixture function.
    
        Declaring fixtures via function argument is recommended where possible.
        But if you can only decide whether to use another fixture at test
        setup time, you may use this function to retrieve it inside a fixture
        or test function body.
    
        This method can be used during the test setup phase or the test run
        phase, but during the test teardown phase a fixture's value may not
        be available.
    
        :param argname:
            The fixture name.
        :raises pytest.FixtureLookupError:
            If the given fixture could not be found.
        """
        # Note that in addition to the use case described in the docstring,
        # getfixturevalue() is also called by pytest itself during item and fixture
        # setup to evaluate the fixtures that are requested statically
        # (using function parameters, autouse, etc).
    
>       fixturedef = self._get_active_fixturedef(argname)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:539: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FixtureRequest for <TestCaseFunction test_docker_controller>>
argname = '_unittest_setUpClass_fixture_TestProxyDocker'

    def _get_active_fixturedef(self, argname: str) -> FixtureDef[object]:
        if argname == "request":
            return RequestFixtureDef(self)
    
        # If we already finished computing a fixture by this name in this item,
        # return it.
        fixturedef = self._fixture_defs.get(argname)
        if fixturedef is not None:
            self._check_scope(fixturedef, fixturedef._scope)
            return fixturedef
    
        # Find the appropriate fixturedef.
        fixturedefs = self._arg2fixturedefs.get(argname, None)
        if fixturedefs is None:
            # We arrive here because of a dynamic call to
            # getfixturevalue(argname) which was naturally
            # not known at parsing/collection time.
            fixturedefs = self._fixturemanager.getfixturedefs(argname, self._pyfuncitem)
            if fixturedefs is not None:
                self._arg2fixturedefs[argname] = fixturedefs
        # No fixtures defined with this name.
        if fixturedefs is None:
            raise FixtureLookupError(argname, self)
        # The are no fixtures with this name applicable for the function.
        if not fixturedefs:
            raise FixtureLookupError(argname, self)
    
        # A fixture may override another fixture with the same name, e.g. a
        # fixture in a module can override a fixture in a conftest, a fixture in
        # a class can override a fixture in the module, and so on.
        # An overriding fixture can request its own name (possibly indirectly);
        # in this case it gets the value of the fixture it overrides, one level
        # up.
        # Check how many `argname`s deep we are, and take the next one.
        # `fixturedefs` is sorted from furthest to closest, so use negative
        # indexing to go in reverse.
        index = -1
        for request in self._iter_chain():
            if request.fixturename == argname:
                index -= 1
        # If already consumed all of the available levels, fail.
        if -index > len(fixturedefs):
            raise FixtureLookupError(argname, self)
        fixturedef = fixturedefs[index]
    
        # Prepare a SubRequest object for calling the fixture.
        try:
            callspec = self._pyfuncitem.callspec
        except AttributeError:
            callspec = None
        if callspec is not None and argname in callspec.params:
            param = callspec.params[argname]
            param_index = callspec.indices[argname]
            # The parametrize invocation scope overrides the fixture's scope.
            scope = callspec._arg2scope[argname]
        else:
            param = NOTSET
            param_index = 0
            scope = fixturedef._scope
            self._check_fixturedef_without_param(fixturedef)
        # The parametrize invocation scope only controls caching behavior while
        # allowing wider-scoped fixtures to keep depending on the parametrized
        # fixture. Scope control is enforced for parametrized fixtures
        # by recreating the whole fixture tree on parameter change.
        # Hence `fixturedef._scope`, not `scope`.
        self._check_scope(fixturedef, fixturedef._scope)
        subrequest = SubRequest(
            self, scope, param, param_index, fixturedef, _ispytest=True
        )
    
        # Make sure the fixture value is cached, running it if it isn't
>       fixturedef.execute(request=subrequest)

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:627: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integration/test_proxy_docker.py::TestProxyDocker'>
request = <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_controller>>

    def execute(self, request: SubRequest) -> FixtureValue:
        """Return the value of this fixture, executing it if not cached."""
        # Ensure that the dependent fixtures requested by this fixture are loaded.
        # This needs to be done before checking if we have a cached value, since
        # if a dependent fixture has their cache invalidated, e.g. due to
        # parametrization, they finalize themselves and fixtures depending on it
        # (which will likely include this fixture) setting `self.cached_result = None`.
        # See #4871
        requested_fixtures_that_should_finalize_us = []
        for argname in self.argnames:
            fixturedef = request._get_active_fixturedef(argname)
            # Saves requested fixtures in a list so we later can add our finalizer
            # to them, ensuring that if a requested fixture gets torn down we get torn
            # down first. This is generally handled by SetupState, but still currently
            # needed when this fixture is not parametrized but depends on a parametrized
            # fixture.
            requested_fixtures_that_should_finalize_us.append(fixturedef)
    
        # Check for (and return) cached value/exception.
        if self.cached_result is not None:
            request_cache_key = self.cache_key(request)
            cache_key = self.cached_result[1]
            try:
                # Attempt to make a normal == check: this might fail for objects
                # which do not implement the standard comparison (like numpy arrays -- #6497).
                cache_hit = bool(request_cache_key == cache_key)
            except (ValueError, RuntimeError):
                # If the comparison raises, use 'is' as fallback.
                cache_hit = request_cache_key is cache_key
    
            if cache_hit:
                if self.cached_result[2] is not None:
                    exc, exc_tb = self.cached_result[2]
>                   raise exc.with_traceback(exc_tb)

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:1091: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

fixturedef = <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integration/test_proxy_docker.py::TestProxyDocker'>
request = <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>

    def pytest_fixture_setup(
        fixturedef: FixtureDef[FixtureValue], request: SubRequest
    ) -> FixtureValue:
        """Execution of fixture setup."""
        kwargs = {}
        for argname in fixturedef.argnames:
            kwargs[argname] = request.getfixturevalue(argname)
    
        fixturefunc = resolve_fixture_function(fixturedef, request)
        my_cache_key = fixturedef.cache_key(request)
    
        if inspect.isasyncgenfunction(fixturefunc) or inspect.iscoroutinefunction(
            fixturefunc
        ):
            auto_str = " with autouse=True" if fixturedef._autouse else ""
    
            warnings.warn(
                PytestRemovedIn9Warning(
                    f"{request.node.name!r} requested an async fixture "
                    f"{request.fixturename!r}{auto_str}, with no plugin or hook that "
                    "handled it. This is usually an error, as pytest does not natively "
                    "support it. "
                    "This will turn into an error in pytest 9.\n"
                    "See: https://docs.pytest..../en/stable/deprecations.html#sync-test-depending-on-async-fixture"
                ),
                # no stacklevel will point at users code, so we just point here
                stacklevel=1,
            )
    
        try:
>           result = call_fixture_func(fixturefunc, request, kwargs)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:1202: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

fixturefunc = <function UnitTestCase._register_unittest_setup_class_fixture.<locals>.unittest_setup_class_fixture at 0x7f56049ff110>
request = <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>
kwargs = {'request': <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>}

    def call_fixture_func(
        fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs
    ) -> FixtureValue:
        if inspect.isgeneratorfunction(fixturefunc):
            fixturefunc = cast(Callable[..., Generator[FixtureValue]], fixturefunc)
            generator = fixturefunc(**kwargs)
            try:
>               fixture_result = next(generator)
                                 ^^^^^^^^^^^^^^^

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:908: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>

    def unittest_setup_class_fixture(
        request: FixtureRequest,
    ) -> Generator[None]:
        cls = request.cls
        if _is_skipped(cls):
            reason = cls.__unittest_skip_why__
            raise skip.Exception(reason, _use_item_location=True)
        if setup is not None:
            try:
>               setup()

.venv/lib/python3.14....../site-packages/_pytest/unittest.py:154: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'tests.integration.test_proxy_docker.TestProxyDocker'>

    @classmethod
    def setUpClass(cls):
        for connection in connections.all():
            if cls._is_in_memory_db(connection):
                raise ImproperlyConfigured(
                    "ChannelLiveServerTestCase can not be used with in memory databases"
                )
    
        super().setUpClass()
    
        cls._live_server_modified_settings = modify_settings(
            ALLOWED_HOSTS={"append": cls.host}
        )
        cls._live_server_modified_settings.enable()
    
        get_application = partial(
            make_application,
            static_wrapper=cls.static_wrapper if cls.serve_static else None,
        )
        cls._server_process = cls.ProtocolServerProcess(
            cls.host,
            get_application,
            setup=set_database_connection,
        )
        cls._server_process.start()
        while True:
>           if not cls._server_process.ready.wait(timeout=1):
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../channels/testing/live.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Event at 0x7f56042ce3c0 set>, timeout = 1

    def wait(self, timeout=None):
        with self._cond:
            if self._flag.acquire(False):
                self._flag.release()
            else:
>               self._cond.wait(timeout)

.../hostedtoolcache/Python/3.14.2........./x64/lib/python3.14/multiprocessing/synchronize.py:363: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Condition(<Lock(owner=None)>, 0)>, timeout = 1

    def wait(self, timeout=None):
        assert self._lock._semlock._is_mine(), \
               'must acquire() condition before using wait()'
    
        # indicate that this thread is going to sleep
        self._sleeping_count.release()
    
        # release lock
        count = self._lock._semlock._count()
        for i in range(count):
            self._lock.release()
    
        try:
            # wait for notification or timeout
>           return self._wait_semaphore.acquire(True, timeout)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.../hostedtoolcache/Python/3.14.2........./x64/lib/python3.14/multiprocessing/synchronize.py:275: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signum = 14
frame = <frame at 0x7f55ffbb9990, file '.../hostedtoolcache/Python/3.14.2........./x64/lib/python3.14/multiprocessing/synchronize.py', line 281, code wait>

    def handler(signum, frame):
        __tracebackhide__ = True
>       timeout_sigalrm(item, settings)

.venv/lib/python3.14/site-packages/pytest_timeout.py:317: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

item = <TestCaseFunction test_docker_static>
settings = Settings(timeout=120.0, method='signal', func_only=False, disable_debugger_detection=False)

    def timeout_sigalrm(item, settings):
        """Dump stack of threads and raise an exception.
    
        This will output the stacks of any threads other then the
        current to stderr and then raise an AssertionError, thus
        terminating the test.
        """
        if not settings.disable_debugger_detection and is_debugging():
            return
        __tracebackhide__ = True
        nthreads = len(threading.enumerate())
        terminal = item.config.get_terminal_writer()
        if nthreads > 1:
            terminal.sep("+", title="Timeout")
        dump_stacks(terminal)
        if nthreads > 1:
            terminal.sep("+", title="Timeout")
>       pytest.fail(PYTEST_FAILURE_MESSAGE % settings.timeout)

.venv/lib/python3.14/site-packages/pytest_timeout.py:502: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.outcomes._Fail object at 0x7f5605ce3230>
reason = 'Timeout (>120.0s) from pytest-timeout.', pytrace = True

    def __call__(self, reason: str = "", pytrace: bool = True) -> NoReturn:
        __tracebackhide__ = True
>       raise Failed(msg=reason, pytrace=pytrace)
E       Failed: Timeout (>120.0s) from pytest-timeout.

.venv/lib/python3.14.../site-packages/_pytest/outcomes.py:163: Failed
tests.integration.test_proxy_docker.TestProxyDocker::test_docker_static
Stack Traces | 120s run time
cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x7f5604a54460>
when = 'setup'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: Callable[[], TResult],
        when: Literal["collect", "setup", "call", "teardown"],
        reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
    ) -> CallInfo[TResult]:
        """Call func, wrapping the result in a CallInfo.
    
        :param func:
            The function to call. Called without arguments.
        :type func: Callable[[], _pytest.runner.TResult]
        :param when:
            The phase in which the function is called.
        :param reraise:
            Exception or exceptions that shall propagate if raised by the
            function, instead of being wrapped in the CallInfo.
        """
        excinfo = None
        instant = timing.Instant()
        try:
>           result: TResult | None = func()
                                     ^^^^^^

.venv/lib/python3.14............/site-packages/_pytest/runner.py:353: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

>       lambda: runtest_hook(item=item, **kwds),
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        when=when,
        reraise=get_reraise_exceptions(item.config),
    )

.venv/lib/python3.14............/site-packages/_pytest/runner.py:245: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <HookCaller 'pytest_runtest_setup'>
kwargs = {'item': <TestCaseFunction test_docker_static>}, firstresult = False

    def __call__(self, **kwargs: object) -> Any:
        """Call the hook.
    
        Only accepts keyword arguments, which should match the hook
        specification.
    
        Returns the result(s) of calling all registered plugins, see
        :ref:`calling`.
        """
        assert not self.is_historic(), (
            "Cannot directly call a historic hook - use call_historic instead."
        )
        self._verify_all_args_are_provided(kwargs)
        firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
        # Copy because plugins may register other plugins during iteration (#438).
>       return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14....../site-packages/pluggy/_hooks.py:512: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.config.PytestPluginManager object at 0x7f56064a81a0>
hook_name = 'pytest_runtest_setup'
methods = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
kwargs = {'item': <TestCaseFunction test_docker_static>}, firstresult = False

    def _hookexec(
        self,
        hook_name: str,
        methods: Sequence[HookImpl],
        kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        # called from all hookcaller instances.
        # enable_tracing will set its own wrapping function at self._inner_hookexec
>       return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14....../site-packages/pluggy/_manager.py:120: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_runtest_setup'
hook_impls = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
caller_kwargs = {'item': <TestCaseFunction test_docker_static>}
firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            if firstresult:  # first result hooks return a single value
                result = results[0] if results else None
            else:
                result = results
    
            # run all wrapper post-yield blocks
            for teardown in reversed(teardowns):
                try:
                    if exception is not None:
                        try:
                            teardown.throw(exception)
                        except RuntimeError as re:
                            # StopIteration from generator causes RuntimeError
                            # even for coroutine usage - see #544
                            if (
                                isinstance(exception, StopIteration)
                                and re.__cause__ is exception
                            ):
                                teardown.close()
                                continue
                            else:
                                raise
                    else:
                        teardown.send(result)
                    # Following is unreachable for a well behaved hook wrapper.
                    # Try to force finalizers otherwise postponed till GC action.
                    # Note: close() may raise if generator handles GeneratorExit.
                    teardown.close()
                except StopIteration as si:
                    result = si.value
                    exception = None
                    continue
                except BaseException as e:
                    exception = e
                    continue
                _raise_wrapfail(teardown, "has second yield")
    
        if exception is not None:
>           raise exception

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_runtest_setup'
hook_impls = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
caller_kwargs = {'item': <TestCaseFunction test_docker_static>}
firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            if firstresult:  # first result hooks return a single value
                result = results[0] if results else None
            else:
                result = results
    
            # run all wrapper post-yield blocks
            for teardown in reversed(teardowns):
                try:
                    if exception is not None:
                        try:
>                           teardown.throw(exception)

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:139: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.logging.LoggingPlugin object at 0x7f5604817230>
item = <TestCaseFunction test_docker_static>

    @hookimpl(wrapper=True)
    def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None]:
        self.log_cli_handler.set_when("setup")
    
        empty: dict[str, list[logging.LogRecord]] = {}
        item.stash[caplog_records_key] = empty
        with self._runtest_for(item, "setup"):
>           yield

.venv/lib/python3.14.../site-packages/_pytest/logging.py:843: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_runtest_setup'
hook_impls = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
caller_kwargs = {'item': <TestCaseFunction test_docker_static>}
firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            if firstresult:  # first result hooks return a single value
                result = results[0] if results else None
            else:
                result = results
    
            # run all wrapper post-yield blocks
            for teardown in reversed(teardowns):
                try:
                    if exception is not None:
                        try:
>                           teardown.throw(exception)

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:139: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <CaptureManager _method='fd' _global_capturing=<MultiCapture out=<FDCapture 1 oldfd=6 _state='suspended' tmpfile=<Enco...xtIOWrapper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>
item = <TestCaseFunction test_docker_static>

    @hookimpl(wrapper=True)
    def pytest_runtest_setup(self, item: Item) -> Generator[None]:
        with self.item_capture("setup", item):
>           return (yield)
                    ^^^^^

.venv/lib/python3.14.../site-packages/_pytest/capture.py:895: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_runtest_setup'
hook_impls = [<HookImpl plugin_name='threadexception', plugin=<module '_pytest.threadexception' from '.../work/authentik/a...pper name='/dev/null' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>>, ...]
caller_kwargs = {'item': <TestCaseFunction test_docker_static>}
firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
>                       res = hook_impl.function(*args)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:121: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

item = <TestCaseFunction test_docker_static>

    def pytest_runtest_setup(item: Item) -> None:
        _update_current_test_var(item, "setup")
>       item.session._setupstate.setup(item)

.venv/lib/python3.14............/site-packages/_pytest/runner.py:165: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.runner.SetupState object at 0x7f5604817e00>
item = <TestCaseFunction test_docker_static>

    def setup(self, item: Item) -> None:
        """Setup objects along the collector chain to the item."""
        needed_collectors = item.listchain()
    
        # If a collector fails its setup, fail its entire subtree of items.
        # The setup is not retried for each item - the same exception is used.
        for col, (finalizers, exc) in self.stack.items():
            assert col in needed_collectors, "previous item was not torn down properly"
            if exc:
                raise exc[0].with_traceback(exc[1])
    
        for col in needed_collectors[len(self.stack) :]:
            assert col not in self.stack
            # Push onto the stack.
            self.stack[col] = ([col.teardown], None)
            try:
>               col.setup()

.venv/lib/python3.14............/site-packages/_pytest/runner.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <TestCaseFunction test_docker_static>

    def setup(self) -> None:
        # A bound method to be called during teardown() if set (see 'runtest()').
        self._explicit_tearDown: Callable[[], None] | None = None
>       super().setup()

.venv/lib/python3.14....../site-packages/_pytest/unittest.py:227: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <TestCaseFunction test_docker_static>

    def setup(self) -> None:
>       self._request._fillfixtures()

.venv/lib/python3.14.../site-packages/_pytest/python.py:1723: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FixtureRequest for <TestCaseFunction test_docker_static>>

    def _fillfixtures(self) -> None:
        item = self._pyfuncitem
        for argname in item.fixturenames:
            if argname not in item.funcargs:
>               item.funcargs[argname] = self.getfixturevalue(argname)
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:707: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FixtureRequest for <TestCaseFunction test_docker_static>>
argname = '_unittest_setUpClass_fixture_TestProxyDocker'

    def getfixturevalue(self, argname: str) -> Any:
        """Dynamically run a named fixture function.
    
        Declaring fixtures via function argument is recommended where possible.
        But if you can only decide whether to use another fixture at test
        setup time, you may use this function to retrieve it inside a fixture
        or test function body.
    
        This method can be used during the test setup phase or the test run
        phase, but during the test teardown phase a fixture's value may not
        be available.
    
        :param argname:
            The fixture name.
        :raises pytest.FixtureLookupError:
            If the given fixture could not be found.
        """
        # Note that in addition to the use case described in the docstring,
        # getfixturevalue() is also called by pytest itself during item and fixture
        # setup to evaluate the fixtures that are requested statically
        # (using function parameters, autouse, etc).
    
>       fixturedef = self._get_active_fixturedef(argname)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:539: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FixtureRequest for <TestCaseFunction test_docker_static>>
argname = '_unittest_setUpClass_fixture_TestProxyDocker'

    def _get_active_fixturedef(self, argname: str) -> FixtureDef[object]:
        if argname == "request":
            return RequestFixtureDef(self)
    
        # If we already finished computing a fixture by this name in this item,
        # return it.
        fixturedef = self._fixture_defs.get(argname)
        if fixturedef is not None:
            self._check_scope(fixturedef, fixturedef._scope)
            return fixturedef
    
        # Find the appropriate fixturedef.
        fixturedefs = self._arg2fixturedefs.get(argname, None)
        if fixturedefs is None:
            # We arrive here because of a dynamic call to
            # getfixturevalue(argname) which was naturally
            # not known at parsing/collection time.
            fixturedefs = self._fixturemanager.getfixturedefs(argname, self._pyfuncitem)
            if fixturedefs is not None:
                self._arg2fixturedefs[argname] = fixturedefs
        # No fixtures defined with this name.
        if fixturedefs is None:
            raise FixtureLookupError(argname, self)
        # The are no fixtures with this name applicable for the function.
        if not fixturedefs:
            raise FixtureLookupError(argname, self)
    
        # A fixture may override another fixture with the same name, e.g. a
        # fixture in a module can override a fixture in a conftest, a fixture in
        # a class can override a fixture in the module, and so on.
        # An overriding fixture can request its own name (possibly indirectly);
        # in this case it gets the value of the fixture it overrides, one level
        # up.
        # Check how many `argname`s deep we are, and take the next one.
        # `fixturedefs` is sorted from furthest to closest, so use negative
        # indexing to go in reverse.
        index = -1
        for request in self._iter_chain():
            if request.fixturename == argname:
                index -= 1
        # If already consumed all of the available levels, fail.
        if -index > len(fixturedefs):
            raise FixtureLookupError(argname, self)
        fixturedef = fixturedefs[index]
    
        # Prepare a SubRequest object for calling the fixture.
        try:
            callspec = self._pyfuncitem.callspec
        except AttributeError:
            callspec = None
        if callspec is not None and argname in callspec.params:
            param = callspec.params[argname]
            param_index = callspec.indices[argname]
            # The parametrize invocation scope overrides the fixture's scope.
            scope = callspec._arg2scope[argname]
        else:
            param = NOTSET
            param_index = 0
            scope = fixturedef._scope
            self._check_fixturedef_without_param(fixturedef)
        # The parametrize invocation scope only controls caching behavior while
        # allowing wider-scoped fixtures to keep depending on the parametrized
        # fixture. Scope control is enforced for parametrized fixtures
        # by recreating the whole fixture tree on parameter change.
        # Hence `fixturedef._scope`, not `scope`.
        self._check_scope(fixturedef, fixturedef._scope)
        subrequest = SubRequest(
            self, scope, param, param_index, fixturedef, _ispytest=True
        )
    
        # Make sure the fixture value is cached, running it if it isn't
>       fixturedef.execute(request=subrequest)

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:627: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integration/test_proxy_docker.py::TestProxyDocker'>
request = <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>

    def execute(self, request: SubRequest) -> FixtureValue:
        """Return the value of this fixture, executing it if not cached."""
        # Ensure that the dependent fixtures requested by this fixture are loaded.
        # This needs to be done before checking if we have a cached value, since
        # if a dependent fixture has their cache invalidated, e.g. due to
        # parametrization, they finalize themselves and fixtures depending on it
        # (which will likely include this fixture) setting `self.cached_result = None`.
        # See #4871
        requested_fixtures_that_should_finalize_us = []
        for argname in self.argnames:
            fixturedef = request._get_active_fixturedef(argname)
            # Saves requested fixtures in a list so we later can add our finalizer
            # to them, ensuring that if a requested fixture gets torn down we get torn
            # down first. This is generally handled by SetupState, but still currently
            # needed when this fixture is not parametrized but depends on a parametrized
            # fixture.
            requested_fixtures_that_should_finalize_us.append(fixturedef)
    
        # Check for (and return) cached value/exception.
        if self.cached_result is not None:
            request_cache_key = self.cache_key(request)
            cache_key = self.cached_result[1]
            try:
                # Attempt to make a normal == check: this might fail for objects
                # which do not implement the standard comparison (like numpy arrays -- #6497).
                cache_hit = bool(request_cache_key == cache_key)
            except (ValueError, RuntimeError):
                # If the comparison raises, use 'is' as fallback.
                cache_hit = request_cache_key is cache_key
    
            if cache_hit:
                if self.cached_result[2] is not None:
                    exc, exc_tb = self.cached_result[2]
                    raise exc.with_traceback(exc_tb)
                else:
                    return self.cached_result[0]
            # We have a previous but differently parametrized fixture instance
            # so we need to tear it down before creating a new one.
            self.finish(request)
            assert self.cached_result is None
    
        # Add finalizer to requested fixtures we saved previously.
        # We make sure to do this after checking for cached value to avoid
        # adding our finalizer multiple times. (#12135)
        finalizer = functools.partial(self.finish, request=request)
        for parent_fixture in requested_fixtures_that_should_finalize_us:
            parent_fixture.addfinalizer(finalizer)
    
        ihook = request.node.ihook
        try:
            # Setup the fixture, run the code in it, and cache the value
            # in self.cached_result.
>           result: FixtureValue = ihook.pytest_fixture_setup(
                fixturedef=self, request=request
            )

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:1110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <HookCaller 'pytest_fixture_setup'>
kwargs = {'fixturedef': <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integrat...er'>, 'request': <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>}
firstresult = True

    def __call__(self, **kwargs: object) -> Any:
        """Call the hook.
    
        Only accepts keyword arguments, which should match the hook
        specification.
    
        Returns the result(s) of calling all registered plugins, see
        :ref:`calling`.
        """
        assert not self.is_historic(), (
            "Cannot directly call a historic hook - use call_historic instead."
        )
        self._verify_all_args_are_provided(kwargs)
        firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
        # Copy because plugins may register other plugins during iteration (#438).
>       return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14....../site-packages/pluggy/_hooks.py:512: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.config.PytestPluginManager object at 0x7f56064a81a0>
hook_name = 'pytest_fixture_setup'
methods = [<HookImpl plugin_name='fixtures', plugin=<module '_pytest.fixtures' from '.../authentik/authentik/.venv...ytest_plugin' from '.../authentik/authentik/.venv/lib/python3.14...................../site-packages/anyio/pytest_plugin.py'>>]
kwargs = {'fixturedef': <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integrat...er'>, 'request': <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>}
firstresult = True

    def _hookexec(
        self,
        hook_name: str,
        methods: Sequence[HookImpl],
        kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        # called from all hookcaller instances.
        # enable_tracing will set its own wrapping function at self._inner_hookexec
>       return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14....../site-packages/pluggy/_manager.py:120: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_fixture_setup'
hook_impls = [<HookImpl plugin_name='fixtures', plugin=<module '_pytest.fixtures' from '.../authentik/authentik/.venv...ytest_plugin' from '.../authentik/authentik/.venv/lib/python3.14...................../site-packages/anyio/pytest_plugin.py'>>]
caller_kwargs = {'fixturedef': <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integrat...er'>, 'request': <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>}
firstresult = True

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            if firstresult:  # first result hooks return a single value
                result = results[0] if results else None
            else:
                result = results
    
            # run all wrapper post-yield blocks
            for teardown in reversed(teardowns):
                try:
                    if exception is not None:
                        try:
                            teardown.throw(exception)
                        except RuntimeError as re:
                            # StopIteration from generator causes RuntimeError
                            # even for coroutine usage - see #544
                            if (
                                isinstance(exception, StopIteration)
                                and re.__cause__ is exception
                            ):
                                teardown.close()
                                continue
                            else:
                                raise
                    else:
                        teardown.send(result)
                    # Following is unreachable for a well behaved hook wrapper.
                    # Try to force finalizers otherwise postponed till GC action.
                    # Note: close() may raise if generator handles GeneratorExit.
                    teardown.close()
                except StopIteration as si:
                    result = si.value
                    exception = None
                    continue
                except BaseException as e:
                    exception = e
                    continue
                _raise_wrapfail(teardown, "has second yield")
    
        if exception is not None:
>           raise exception

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_fixture_setup'
hook_impls = [<HookImpl plugin_name='fixtures', plugin=<module '_pytest.fixtures' from '.../authentik/authentik/.venv...ytest_plugin' from '.../authentik/authentik/.venv/lib/python3.14...................../site-packages/anyio/pytest_plugin.py'>>]
caller_kwargs = {'fixturedef': <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integrat...er'>, 'request': <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>}
firstresult = True

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            if firstresult:  # first result hooks return a single value
                result = results[0] if results else None
            else:
                result = results
    
            # run all wrapper post-yield blocks
            for teardown in reversed(teardowns):
                try:
                    if exception is not None:
                        try:
>                           teardown.throw(exception)

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:139: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_impl = <HookImpl plugin_name='anyio', plugin=<module 'anyio.pytest_plugin' from '.../authentik/authentik/.venv/lib/python3.14...................../site-packages/anyio/pytest_plugin.py'>>
hook_name = 'pytest_fixture_setup'
args = [<FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integration/test_proxy...stProxyDocker'>, <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>]

    def run_old_style_hookwrapper(
        hook_impl: HookImpl, hook_name: str, args: Sequence[object]
    ) -> Teardown:
        """
        backward compatibility wrapper to run a old style hookwrapper as a wrapper
        """
    
        teardown: Teardown = cast(Teardown, hook_impl.function(*args))
        try:
            next(teardown)
        except StopIteration:
            _raise_wrapfail(teardown, "did not yield")
        try:
            res = yield
            result = Result(res, None)
        except BaseException as exc:
            result = Result(None, exc)
        try:
            teardown.send(result)
        except StopIteration:
            pass
        except BaseException as e:
            _warn_teardown_exception(hook_name, hook_impl, e)
            raise
        else:
            _raise_wrapfail(teardown, "has second yield")
        finally:
            teardown.close()
>       return result.get_result()
               ^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:53: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pluggy._result.Result object at 0x7f560448d200>

    def get_result(self) -> ResultType:
        """Get the result(s) for this hook call.
    
        If the hook was marked as a ``firstresult`` only a single value
        will be returned, otherwise a list of results.
        """
        __tracebackhide__ = True
        exc = self._exception
        tb = self._traceback
        if exc is None:
            return cast(ResultType, self._result)
        else:
>           raise exc.with_traceback(tb)

.venv/lib/python3.14.../site-packages/pluggy/_result.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_impl = <HookImpl plugin_name='anyio', plugin=<module 'anyio.pytest_plugin' from '.../authentik/authentik/.venv/lib/python3.14...................../site-packages/anyio/pytest_plugin.py'>>
hook_name = 'pytest_fixture_setup'
args = [<FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integration/test_proxy...stProxyDocker'>, <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>]

    def run_old_style_hookwrapper(
        hook_impl: HookImpl, hook_name: str, args: Sequence[object]
    ) -> Teardown:
        """
        backward compatibility wrapper to run a old style hookwrapper as a wrapper
        """
    
        teardown: Teardown = cast(Teardown, hook_impl.function(*args))
        try:
            next(teardown)
        except StopIteration:
            _raise_wrapfail(teardown, "did not yield")
        try:
>           res = yield
                  ^^^^^

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:38: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_fixture_setup'
hook_impls = [<HookImpl plugin_name='fixtures', plugin=<module '_pytest.fixtures' from '.../authentik/authentik/.venv...ytest_plugin' from '.../authentik/authentik/.venv/lib/python3.14...................../site-packages/anyio/pytest_plugin.py'>>]
caller_kwargs = {'fixturedef': <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integrat...er'>, 'request': <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>}
firstresult = True

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            if firstresult:  # first result hooks return a single value
                result = results[0] if results else None
            else:
                result = results
    
            # run all wrapper post-yield blocks
            for teardown in reversed(teardowns):
                try:
                    if exception is not None:
                        try:
>                           teardown.throw(exception)

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:139: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

fixturedef = <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integration/test_proxy_docker.py::TestProxyDocker'>
request = <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>

    @pytest.hookimpl(wrapper=True)
    def pytest_fixture_setup(
        fixturedef: FixtureDef[object], request: SubRequest
    ) -> Generator[None, object, object]:
        try:
>           return (yield)
                    ^^^^^

.venv/lib/python3.14.../site-packages/_pytest/setuponly.py:36: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

hook_name = 'pytest_fixture_setup'
hook_impls = [<HookImpl plugin_name='fixtures', plugin=<module '_pytest.fixtures' from '.../authentik/authentik/.venv...ytest_plugin' from '.../authentik/authentik/.venv/lib/python3.14...................../site-packages/anyio/pytest_plugin.py'>>]
caller_kwargs = {'fixturedef': <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integrat...er'>, 'request': <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>}
firstresult = True

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError as e:
                        # coverage bug - this is tested
                        for argname in hook_impl.argnames:  # pragma: no cover
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                ) from e
    
                    if hook_impl.hookwrapper:
                        function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
    
                        next(function_gen)  # first yield
                        teardowns.append(function_gen)
    
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
>                       res = hook_impl.function(*args)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14............................../site-packages/pluggy/_callers.py:121: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

fixturedef = <FixtureDef argname='_unittest_setUpClass_fixture_TestProxyDocker' scope='class' baseid='tests/integration/test_proxy_docker.py::TestProxyDocker'>
request = <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>

    def pytest_fixture_setup(
        fixturedef: FixtureDef[FixtureValue], request: SubRequest
    ) -> FixtureValue:
        """Execution of fixture setup."""
        kwargs = {}
        for argname in fixturedef.argnames:
            kwargs[argname] = request.getfixturevalue(argname)
    
        fixturefunc = resolve_fixture_function(fixturedef, request)
        my_cache_key = fixturedef.cache_key(request)
    
        if inspect.isasyncgenfunction(fixturefunc) or inspect.iscoroutinefunction(
            fixturefunc
        ):
            auto_str = " with autouse=True" if fixturedef._autouse else ""
    
            warnings.warn(
                PytestRemovedIn9Warning(
                    f"{request.node.name!r} requested an async fixture "
                    f"{request.fixturename!r}{auto_str}, with no plugin or hook that "
                    "handled it. This is usually an error, as pytest does not natively "
                    "support it. "
                    "This will turn into an error in pytest 9.\n"
                    "See: https://docs.pytest..../en/stable/deprecations.html#sync-test-depending-on-async-fixture"
                ),
                # no stacklevel will point at users code, so we just point here
                stacklevel=1,
            )
    
        try:
>           result = call_fixture_func(fixturefunc, request, kwargs)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:1202: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

fixturefunc = <function UnitTestCase._register_unittest_setup_class_fixture.<locals>.unittest_setup_class_fixture at 0x7f56049ff110>
request = <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>
kwargs = {'request': <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>}

    def call_fixture_func(
        fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs
    ) -> FixtureValue:
        if inspect.isgeneratorfunction(fixturefunc):
            fixturefunc = cast(Callable[..., Generator[FixtureValue]], fixturefunc)
            generator = fixturefunc(**kwargs)
            try:
>               fixture_result = next(generator)
                                 ^^^^^^^^^^^^^^^

.venv/lib/python3.14................../site-packages/_pytest/fixtures.py:908: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <SubRequest '_unittest_setUpClass_fixture_TestProxyDocker' for <TestCaseFunction test_docker_static>>

    def unittest_setup_class_fixture(
        request: FixtureRequest,
    ) -> Generator[None]:
        cls = request.cls
        if _is_skipped(cls):
            reason = cls.__unittest_skip_why__
            raise skip.Exception(reason, _use_item_location=True)
        if setup is not None:
            try:
>               setup()

.venv/lib/python3.14....../site-packages/_pytest/unittest.py:154: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'tests.integration.test_proxy_docker.TestProxyDocker'>

    @classmethod
    def setUpClass(cls):
        for connection in connections.all():
            if cls._is_in_memory_db(connection):
                raise ImproperlyConfigured(
                    "ChannelLiveServerTestCase can not be used with in memory databases"
                )
    
        super().setUpClass()
    
        cls._live_server_modified_settings = modify_settings(
            ALLOWED_HOSTS={"append": cls.host}
        )
        cls._live_server_modified_settings.enable()
    
        get_application = partial(
            make_application,
            static_wrapper=cls.static_wrapper if cls.serve_static else None,
        )
        cls._server_process = cls.ProtocolServerProcess(
            cls.host,
            get_application,
            setup=set_database_connection,
        )
        cls._server_process.start()
        while True:
>           if not cls._server_process.ready.wait(timeout=1):
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../channels/testing/live.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Event at 0x7f56042ce3c0 set>, timeout = 1

    def wait(self, timeout=None):
        with self._cond:
            if self._flag.acquire(False):
                self._flag.release()
            else:
>               self._cond.wait(timeout)

.../hostedtoolcache/Python/3.14.2........./x64/lib/python3.14/multiprocessing/synchronize.py:363: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Condition(<Lock(owner=None)>, 0)>, timeout = 1

    def wait(self, timeout=None):
        assert self._lock._semlock._is_mine(), \
               'must acquire() condition before using wait()'
    
        # indicate that this thread is going to sleep
        self._sleeping_count.release()
    
        # release lock
        count = self._lock._semlock._count()
        for i in range(count):
            self._lock.release()
    
        try:
            # wait for notification or timeout
>           return self._wait_semaphore.acquire(True, timeout)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.../hostedtoolcache/Python/3.14.2........./x64/lib/python3.14/multiprocessing/synchronize.py:275: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

signum = 14
frame = <frame at 0x7f55ffbb9990, file '.../hostedtoolcache/Python/3.14.2........./x64/lib/python3.14/multiprocessing/synchronize.py', line 281, code wait>

    def handler(signum, frame):
        __tracebackhide__ = True
>       timeout_sigalrm(item, settings)

.venv/lib/python3.14/site-packages/pytest_timeout.py:317: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

item = <TestCaseFunction test_docker_static>
settings = Settings(timeout=120.0, method='signal', func_only=False, disable_debugger_detection=False)

    def timeout_sigalrm(item, settings):
        """Dump stack of threads and raise an exception.
    
        This will output the stacks of any threads other then the
        current to stderr and then raise an AssertionError, thus
        terminating the test.
        """
        if not settings.disable_debugger_detection and is_debugging():
            return
        __tracebackhide__ = True
        nthreads = len(threading.enumerate())
        terminal = item.config.get_terminal_writer()
        if nthreads > 1:
            terminal.sep("+", title="Timeout")
        dump_stacks(terminal)
        if nthreads > 1:
            terminal.sep("+", title="Timeout")
>       pytest.fail(PYTEST_FAILURE_MESSAGE % settings.timeout)

.venv/lib/python3.14/site-packages/pytest_timeout.py:502: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.outcomes._Fail object at 0x7f5605ce3230>
reason = 'Timeout (>120.0s) from pytest-timeout.', pytrace = True

    def __call__(self, reason: str = "", pytrace: bool = True) -> NoReturn:
        __tracebackhide__ = True
>       raise Failed(msg=reason, pytrace=pytrace)
E       Failed: Timeout (>120.0s) from pytest-timeout.

.venv/lib/python3.14.../site-packages/_pytest/outcomes.py:163: Failed
tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC::test_authorization_consent_explicit
Stack Traces | 200s run time
self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:524: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'system/providers-oauth2.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - OAuth2 Provider - Sc... application the ability to access the authentik API\n        # on behalf of the authorizing user\n        return {}\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
    @apply_blueprint("system/providers-oauth2.yaml")
    @reconcile_app("authentik_crypto")
    def test_authorization_consent_explicit(self):
        """test OpenID Provider flow (default authorization flow with explicit consent)"""
        sleep(1)
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider = OAuth2Provider.objects.create(
            name=self.application_slug,
            authorization_flow=authorization_flow,
            client_type=ClientTypes.CONFIDENTIAL,
            client_id=self.client_id,
            client_secret=self.client_secret,
            signing_key=create_test_cert(),
            redirect_uris=[
                RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost:9009/auth/callback")
            ],
        )
        provider.property_mappings.set(
            ScopeMapping.objects.filter(
                scope_name__in=[
                    SCOPE_OPENID,
                    SCOPE_OPENID_EMAIL,
                    SCOPE_OPENID_PROFILE,
                    SCOPE_OFFLINE_ACCESS,
                ]
            )
        )
        app = Application.objects.create(
            name=self.application_slug,
            slug=self.application_slug,
            provider=provider,
        )
        self.setup_client()
    
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/test_provider_oidc.py:264: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="96f85c09a307f5d5aa40893c06324300", element="f.ED214BC18129896A0796B09A9727B67F.d.85375EA73783A004EA19FE39C0E2A88B.e.9")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:379: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fb6a0a07750>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fb6a36c5d20>
message = ''

    def until(self, method: Callable[[D], Literal[False] | T], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Args:
            method: A callable object that takes a WebDriver instance as an
                argument.
            message: Optional message for TimeoutException.
    
        Returns:
            The result of the last call to `method`.
    
        Raises:
            TimeoutException: If 'method' does not return a truthy value within
                the WebDriverWait object's timeout.
    
        Example:
            >>> from selenium.webdriver.common.by import By
            >>> from selenium.webdriver.support.ui import WebDriverWait
            >>> from selenium.webdriver.support import expected_conditions as EC
            >>>
            >>> # Wait until an element is visible on the page
            >>> wait = WebDriverWait(driver, 10)
            >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
            >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)
                        ^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../webdriver/support/wait.py:113: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="96f85c09a307f5d5aa40893c06324300", element="f.ED214BC18129896A0796B09A9727B67F.d.85375EA73783A004EA19FE39C0E2A88B.e.9")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:379: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="96f85c09a307f5d5aa40893c06324300", element="f.ED214BC18129896A0796B09A9727B67F.d.85375EA73783A004EA19FE39C0E2A88B.e.9")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and locator.
    
        Args:
            by: The locating strategy to use. Default is `By.ID`. Supported values include:
                - By.ID: Locate by element ID.
                - By.NAME: Locate by the `name` attribute.
                - By.XPATH: Locate by an XPath expression.
                - By.CSS_SELECTOR: Locate by a CSS selector.
                - By.CLASS_NAME: Locate by the `class` attribute.
                - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
                - By.LINK_TEXT: Locate a link element by its exact text.
                - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
                - RelativeBy: Locate elements relative to a specified root element.
            value: The locator value to use with the specified `by` strategy.
    
        Returns:
            The first matching `WebElement` found on the page.
    
        Example:
            >>> element = driver.find_element(By.ID, "foo")
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            if value and any(char.isspace() for char in value.strip()):
                raise InvalidSelectorException("Compound class names are not allowed.")
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../webdriver/remote/shadowroot.py:81: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="96f85c09a307f5d5aa40893c06324300", element="f.ED214BC18129896A0796B09A9727B67F.d.85375EA73783A004EA19FE39C0E2A88B.e.9")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.ED214BC18129896A0796B09A9727B67F.d.85375EA73783A004EA19FE39C0E2A88B.e.9', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="96f85c09a307f5d5aa40893c06324300")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Args:
            driver_command: The name of the command to execute as a string.
            params: A dictionary of named parameters to send with the command.
    
        Returns:
            The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = cast(RemoteConnection, self.command_executor).execute(driver_command, params)
    
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.14.../webdriver/remote/webdriver.py:432: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fb6a5e4b8c0>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x557850e70cd5 \\u003Cunknown>\\n#28 0x557850e82709 \\u003Cunknown>\\n#29 0x7ff34ad05428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: dict[str, Any]) -> None:
        """Check that a JSON response from the WebDriver does not have an error.
    
        Args:
            response: The JSON response from the WebDriver server as a dictionary
                object.
    
        Raises:
            WebDriverException: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                try:
                    value = json.loads(value_json)
                    if isinstance(value, dict):
                        if len(value) == 1:
                            value = value["value"]
                        status = value.get("error", None)
                        if not status:
                            status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                            message = value.get("value") or value.get("message")
                            if not isinstance(message, str):
                                value = message
                                message = message.get("message") if isinstance(message, dict) else None
                        else:
                            message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x557850e83432 <unknown>
E       #1 0x5578508c871b <unknown>
E       #2 0x5578508dc207 <unknown>
E       #3 0x5578508dae10 <unknown>
E       #4 0x5578508dc719 <unknown>
E       #5 0x5578508cfc64 <unknown>
E       #6 0x5578508ce873 <unknown>
E       #7 0x5578508d1d6f <unknown>
E       #8 0x5578508d1e55 <unknown>
E       #9 0x557850917da4 <unknown>
E       #10 0x5578509187d5 <unknown>
E       #11 0x55785090ccaa <unknown>
E       #12 0x55785093cee1 <unknown>
E       #13 0x55785090cb81 <unknown>
E       #14 0x55785093d0a2 <unknown>
E       #15 0x55785095eb62 <unknown>
E       #16 0x55785093cc67 <unknown>
E       #17 0x55785090b047 <unknown>
E       #18 0x55785090be15 <unknown>
E       #19 0x557850e4e064 <unknown>
E       #20 0x557850e51354 <unknown>
E       #21 0x557850e50e0e <unknown>
E       #22 0x557850e517e9 <unknown>
E       #23 0x557850e37a7a <unknown>
E       #24 0x557850e51b5a <unknown>
E       #25 0x557850e20629 <unknown>
E       #26 0x557850e70ae9 <unknown>
E       #27 0x557850e70cd5 <unknown>
E       #28 0x557850e82709 <unknown>
E       #29 0x7ff34ad05428 <unknown>

.venv/lib/python3.14.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

During handling of the above exception, another exception occurred:

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:524: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'system/providers-oauth2.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - OAuth2 Provider - Sc... application the ability to access the authentik API\n        # on behalf of the authorizing user\n        return {}\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
    @apply_blueprint("system/providers-oauth2.yaml")
    @reconcile_app("authentik_crypto")
    def test_authorization_consent_explicit(self):
        """test OpenID Provider flow (default authorization flow with explicit consent)"""
        sleep(1)
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider = OAuth2Provider.objects.create(
            name=self.application_slug,
            authorization_flow=authorization_flow,
            client_type=ClientTypes.CONFIDENTIAL,
            client_id=self.client_id,
            client_secret=self.client_secret,
            signing_key=create_test_cert(),
            redirect_uris=[
                RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost:9009/auth/callback")
            ],
        )
        provider.property_mappings.set(
            ScopeMapping.objects.filter(
                scope_name__in=[
                    SCOPE_OPENID,
                    SCOPE_OPENID_EMAIL,
                    SCOPE_OPENID_PROFILE,
                    SCOPE_OFFLINE_ACCESS,
                ]
            )
        )
        app = Application.objects.create(
            name=self.application_slug,
            slug=self.application_slug,
            provider=provider,
        )
        self.setup_client()
    
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/test_provider_oidc.py:264: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="ad59cccb7b560dc9653ede6155cdd2a0", element="f.E58080FF66ADAF8C4E54A3A6A15E8C38.d.9AA70C7E3A8FB239A0230E5DF415658C.e.10")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:379: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fb69f7822b0>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fb6a03cb7f0>
message = ''

    def until(self, method: Callable[[D], Literal[False] | T], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Args:
            method: A callable object that takes a WebDriver instance as an
                argument.
            message: Optional message for TimeoutException.
    
        Returns:
            The result of the last call to `method`.
    
        Raises:
            TimeoutException: If 'method' does not return a truthy value within
                the WebDriverWait object's timeout.
    
        Example:
            >>> from selenium.webdriver.common.by import By
            >>> from selenium.webdriver.support.ui import WebDriverWait
            >>> from selenium.webdriver.support import expected_conditions as EC
            >>>
            >>> # Wait until an element is visible on the page
            >>> wait = WebDriverWait(driver, 10)
            >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
            >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)
                        ^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../webdriver/support/wait.py:113: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="ad59cccb7b560dc9653ede6155cdd2a0", element="f.E58080FF66ADAF8C4E54A3A6A15E8C38.d.9AA70C7E3A8FB239A0230E5DF415658C.e.10")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:379: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="ad59cccb7b560dc9653ede6155cdd2a0", element="f.E58080FF66ADAF8C4E54A3A6A15E8C38.d.9AA70C7E3A8FB239A0230E5DF415658C.e.10")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and locator.
    
        Args:
            by: The locating strategy to use. Default is `By.ID`. Supported values include:
                - By.ID: Locate by element ID.
                - By.NAME: Locate by the `name` attribute.
                - By.XPATH: Locate by an XPath expression.
                - By.CSS_SELECTOR: Locate by a CSS selector.
                - By.CLASS_NAME: Locate by the `class` attribute.
                - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
                - By.LINK_TEXT: Locate a link element by its exact text.
                - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
                - RelativeBy: Locate elements relative to a specified root element.
            value: The locator value to use with the specified `by` strategy.
    
        Returns:
            The first matching `WebElement` found on the page.
    
        Example:
            >>> element = driver.find_element(By.ID, "foo")
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            if value and any(char.isspace() for char in value.strip()):
                raise InvalidSelectorException("Compound class names are not allowed.")
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../webdriver/remote/shadowroot.py:81: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="ad59cccb7b560dc9653ede6155cdd2a0", element="f.E58080FF66ADAF8C4E54A3A6A15E8C38.d.9AA70C7E3A8FB239A0230E5DF415658C.e.10")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.E58080FF66ADAF8C4E54A3A6A15E8C38.d.9AA70C7E3A8FB239A0230E5DF415658C.e.10', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="ad59cccb7b560dc9653ede6155cdd2a0")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Args:
            driver_command: The name of the command to execute as a string.
            params: A dictionary of named parameters to send with the command.
    
        Returns:
            The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = cast(RemoteConnection, self.command_executor).execute(driver_command, params)
    
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.14.../webdriver/remote/webdriver.py:432: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fb6a28bead0>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x55f7bbf4acd5 \\u003Cunknown>\\n#28 0x55f7bbf5c709 \\u003Cunknown>\\n#29 0x7f1442b30428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: dict[str, Any]) -> None:
        """Check that a JSON response from the WebDriver does not have an error.
    
        Args:
            response: The JSON response from the WebDriver server as a dictionary
                object.
    
        Raises:
            WebDriverException: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                try:
                    value = json.loads(value_json)
                    if isinstance(value, dict):
                        if len(value) == 1:
                            value = value["value"]
                        status = value.get("error", None)
                        if not status:
                            status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                            message = value.get("value") or value.get("message")
                            if not isinstance(message, str):
                                value = message
                                message = message.get("message") if isinstance(message, dict) else None
                        else:
                            message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x55f7bbf5d432 <unknown>
E       #1 0x55f7bb9a271b <unknown>
E       #2 0x55f7bb9b6207 <unknown>
E       #3 0x55f7bb9b4e10 <unknown>
E       #4 0x55f7bb9b6719 <unknown>
E       #5 0x55f7bb9a9c64 <unknown>
E       #6 0x55f7bb9a8873 <unknown>
E       #7 0x55f7bb9abd6f <unknown>
E       #8 0x55f7bb9abe55 <unknown>
E       #9 0x55f7bb9f1da4 <unknown>
E       #10 0x55f7bb9f27d5 <unknown>
E       #11 0x55f7bb9e6caa <unknown>
E       #12 0x55f7bba16ee1 <unknown>
E       #13 0x55f7bb9e6b81 <unknown>
E       #14 0x55f7bba170a2 <unknown>
E       #15 0x55f7bba38b62 <unknown>
E       #16 0x55f7bba16c67 <unknown>
E       #17 0x55f7bb9e5047 <unknown>
E       #18 0x55f7bb9e5e15 <unknown>
E       #19 0x55f7bbf28064 <unknown>
E       #20 0x55f7bbf2b354 <unknown>
E       #21 0x55f7bbf2ae0e <unknown>
E       #22 0x55f7bbf2b7e9 <unknown>
E       #23 0x55f7bbf11a7a <unknown>
E       #24 0x55f7bbf2bb5a <unknown>
E       #25 0x55f7bbefa629 <unknown>
E       #26 0x55f7bbf4aae9 <unknown>
E       #27 0x55f7bbf4acd5 <unknown>
E       #28 0x55f7bbf5c709 <unknown>
E       #29 0x7f1442b30428 <unknown>

.venv/lib/python3.14.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

During handling of the above exception, another exception occurred:

self = <unittest.case._Outcome object at 0x7fb6a5ec4980>
test_case = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
subTest = False

    @contextlib.contextmanager
    def testPartExecutor(self, test_case, subTest=False):
        old_success = self.success
        self.success = True
        try:
>           yield

.../hostedtoolcache/Python/3.14.2........./x64/lib/python3.14/unittest/case.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
result = <TestCaseFunction test_authorization_consent_explicit>

    def run(self, result=None):
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, 'startTestRun', None)
            stopTestRun = getattr(result, 'stopTestRun', None)
            if startTestRun is not None:
                startTestRun()
        else:
            stopTestRun = None
    
        result.startTest(self)
        try:
            testMethod = getattr(self, self._testMethodName)
            if (getattr(self.__class__, "__unittest_skip__", False) or
                getattr(testMethod, "__unittest_skip__", False)):
                # If the class or method was skipped.
                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
                            or getattr(testMethod, '__unittest_skip_why__', ''))
                _addSkip(result, self, skip_why)
                return result
    
            expecting_failure = (
                getattr(self, "__unittest_expecting_failure__", False) or
                getattr(testMethod, "__unittest_expecting_failure__", False)
            )
            outcome = _Outcome(result)
            start_time = time.perf_counter()
            try:
                self._outcome = outcome
    
                with outcome.testPartExecutor(self):
                    self._callSetUp()
                if outcome.success:
                    outcome.expecting_failure = expecting_failure
                    with outcome.testPartExecutor(self):
>                       self._callTestMethod(testMethod)

.../hostedtoolcache/Python/3.14.2........./x64/lib/python3.14/unittest/case.py:669: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
method = <bound method TestProviderOAuth2OIDC.test_authorization_consent_explicit of <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>>

    def _callTestMethod(self, method):
>       result = method()
                 ^^^^^^^^

.../hostedtoolcache/Python/3.14.2........./x64/lib/python3.14/unittest/case.py:615: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:537: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:537: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
>               raise exc

tests/e2e/utils.py:531: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:524: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'system/providers-oauth2.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - OAuth2 Provider - Sc... application the ability to access the authentik API\n        # on behalf of the authorizing user\n        return {}\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
    @apply_blueprint("system/providers-oauth2.yaml")
    @reconcile_app("authentik_crypto")
    def test_authorization_consent_explicit(self):
        """test OpenID Provider flow (default authorization flow with explicit consent)"""
        sleep(1)
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider = OAuth2Provider.objects.create(
            name=self.application_slug,
            authorization_flow=authorization_flow,
            client_type=ClientTypes.CONFIDENTIAL,
            client_id=self.client_id,
            client_secret=self.client_secret,
            signing_key=create_test_cert(),
            redirect_uris=[
                RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost:9009/auth/callback")
            ],
        )
        provider.property_mappings.set(
            ScopeMapping.objects.filter(
                scope_name__in=[
                    SCOPE_OPENID,
                    SCOPE_OPENID_EMAIL,
                    SCOPE_OPENID_PROFILE,
                    SCOPE_OFFLINE_ACCESS,
                ]
            )
        )
        app = Application.objects.create(
            name=self.application_slug,
            slug=self.application_slug,
            provider=provider,
        )
        self.setup_client()
    
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/test_provider_oidc.py:264: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="4b6b79d186383c3401f149d1ff581bd6", element="f.41F5752ACA3F9B56C9475473424E2B03.d.8E7F0724E76897A14C31C8DD3FDB9298.e.10")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:379: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fb6a039dd30>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fb69f6bf8a0>
message = ''

    def until(self, method: Callable[[D], Literal[False] | T], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Args:
            method: A callable object that takes a WebDriver instance as an
                argument.
            message: Optional message for TimeoutException.
    
        Returns:
            The result of the last call to `method`.
    
        Raises:
            TimeoutException: If 'method' does not return a truthy value within
                the WebDriverWait object's timeout.
    
        Example:
            >>> from selenium.webdriver.common.by import By
            >>> from selenium.webdriver.support.ui import WebDriverWait
            >>> from selenium.webdriver.support import expected_conditions as EC
            >>>
            >>> # Wait until an element is visible on the page
            >>> wait = WebDriverWait(driver, 10)
            >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
            >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)
                        ^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../webdriver/support/wait.py:113: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="4b6b79d186383c3401f149d1ff581bd6", element="f.41F5752ACA3F9B56C9475473424E2B03.d.8E7F0724E76897A14C31C8DD3FDB9298.e.10")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/e2e/utils.py:379: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="4b6b79d186383c3401f149d1ff581bd6", element="f.41F5752ACA3F9B56C9475473424E2B03.d.8E7F0724E76897A14C31C8DD3FDB9298.e.10")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and locator.
    
        Args:
            by: The locating strategy to use. Default is `By.ID`. Supported values include:
                - By.ID: Locate by element ID.
                - By.NAME: Locate by the `name` attribute.
                - By.XPATH: Locate by an XPath expression.
                - By.CSS_SELECTOR: Locate by a CSS selector.
                - By.CLASS_NAME: Locate by the `class` attribute.
                - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
                - By.LINK_TEXT: Locate a link element by its exact text.
                - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
                - RelativeBy: Locate elements relative to a specified root element.
            value: The locator value to use with the specified `by` strategy.
    
        Returns:
            The first matching `WebElement` found on the page.
    
        Example:
            >>> element = driver.find_element(By.ID, "foo")
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            if value and any(char.isspace() for char in value.strip()):
                raise InvalidSelectorException("Compound class names are not allowed.")
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../webdriver/remote/shadowroot.py:81: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="4b6b79d186383c3401f149d1ff581bd6", element="f.41F5752ACA3F9B56C9475473424E2B03.d.8E7F0724E76897A14C31C8DD3FDB9298.e.10")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.41F5752ACA3F9B56C9475473424E2B03.d.8E7F0724E76897A14C31C8DD3FDB9298.e.10', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.venv/lib/python3.14.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="4b6b79d186383c3401f149d1ff581bd6")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Args:
            driver_command: The name of the command to execute as a string.
            params: A dictionary of named parameters to send with the command.
    
        Returns:
            The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = cast(RemoteConnection, self.command_executor).execute(driver_command, params)
    
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.14.../webdriver/remote/webdriver.py:432: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fb6a348ee90>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x557fa9de8cd5 \\u003Cunknown>\\n#28 0x557fa9dfa709 \\u003Cunknown>\\n#29 0x7f0476dce428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: dict[str, Any]) -> None:
        """Check that a JSON response from the WebDriver does not have an error.
    
        Args:
            response: The JSON response from the WebDriver server as a dictionary
                object.
    
        Raises:
            WebDriverException: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                try:
                    value = json.loads(value_json)
                    if isinstance(value, dict):
                        if len(value) == 1:
                            value = value["value"]
                        status = value.get("error", None)
                        if not status:
                            status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                            message = value.get("value") or value.get("message")
                            if not isinstance(message, str):
                                value = message
                                message = message.get("message") if isinstance(message, dict) else None
                        else:
                            message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x557fa9dfb432 <unknown>
E       #1 0x557fa984071b <unknown>
E       #2 0x557fa9854207 <unknown>
E       #3 0x557fa9852e10 <unknown>
E       #4 0x557fa9854719 <unknown>
E       #5 0x557fa9847c64 <unknown>
E       #6 0x557fa9846873 <unknown>
E       #7 0x557fa9849d6f <unknown>
E       #8 0x557fa9849e55 <unknown>
E       #9 0x557fa988fda4 <unknown>
E       #10 0x557fa98907d5 <unknown>
E       #11 0x557fa9884caa <unknown>
E       #12 0x557fa98b4ee1 <unknown>
E       #13 0x557fa9884b81 <unknown>
E       #14 0x557fa98b50a2 <unknown>
E       #15 0x557fa98d6b62 <unknown>
E       #16 0x557fa98b4c67 <unknown>
E       #17 0x557fa9883047 <unknown>
E       #18 0x557fa9883e15 <unknown>
E       #19 0x557fa9dc6064 <unknown>
E       #20 0x557fa9dc9354 <unknown>
E       #21 0x557fa9dc8e0e <unknown>
E       #22 0x557fa9dc97e9 <unknown>
E       #23 0x557fa9dafa7a <unknown>
E       #24 0x557fa9dc9b5a <unknown>
E       #25 0x557fa9d98629 <unknown>
E       #26 0x557fa9de8ae9 <unknown>
E       #27 0x557fa9de8cd5 <unknown>
E       #28 0x557fa9dfa709 <unknown>
E       #29 0x7f0476dce428 <unknown>

.venv/lib/python3.14.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@netlify
Copy link

netlify bot commented Jan 16, 2026

Deploy Preview for authentik-docs ready!

Name Link
🔨 Latest commit b0a4e9d
🔍 Latest deploy log https://app.netlify.com/projects/authentik-docs/deploys/6969ba37f0522e0008950df0
😎 Deploy Preview https://deploy-preview-19514--authentik-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@BeryJu BeryJu merged commit f36f44e into main Jan 16, 2026
133 of 140 checks passed
@BeryJu BeryJu deleted the dependabot/uv/kubernetes-35.0.0 branch January 16, 2026 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants