### Overview
Some Executors, currently a subset of the local Executors, run in a single threaded fashion and have certain limitations and requirements, many of which are hardcoded. To add a new single threaded Executor would require changes to core Airflow code.
Note: This coupling often shows up with SQLite compatibility checks since it does not support multiple connections.
### Examples
- **2a**) SQLite check done in configuration.py: https://github.com/apache/airflow/blob/26f94c5370587f73ebd935cecf208c6a36bdf9b6/airflow/configuration.py#L412-L419
- **2b**) When running in standalone mode SQLite compatibility is checked: https://github.com/apache/airflow/blob/26f94c5370587f73ebd935cecf208c6a36bdf9b6/airflow/cli/commands/standalone_command.py#L160-L165
- **2c**) Sensors in `poke` mode can block execution of DAGs when running with single process Executors, currently hardcoded to DebugExecutor (although should also include SequentialExecutor): https://github.com/apache/airflow/blob/27e2101f6ee5567b2843cbccf1dca0b0e7c96186/airflow/sensors/base.py#L243
### Proposal
A static method or attribute on the Executor class which can be checked by core code.

There is a precedent already set with the `supports_ad_hoc_ti_run` attribute, see:
https://github.com/apache/airflow/blob/fb741fd87254e235f99d7d67e558dafad601f253/airflow/executors/kubernetes_executor.py#L435 https://github.com/apache/airflow/blob/26f94c5370587f73ebd935cecf208c6a36bdf9b6/airflow/www/views.py#L1735-L1737
"""Heartbeat sent to trigger new jobs in celery and kubernetes executor."""
        self.celery_executor.heartbeat()
        self.kubernetes_executor.heartbeat()
    def get_event_buffer(
        self, dag_ids: list[str] | None = None
    ) -> dict[TaskInstanceKey, EventBufferValueType]:
        """
        Return and flush the event buffer from celery and kubernetes executor.
        :param dag_ids: dag_ids to return events for, if None returns all
        :return: a dict of events
        """
        cleared_events_from_celery = self.celery_executor.get_event_buffer(dag_ids)
        cleared_events_from_kubernetes = self.kubernetes_executor.get_event_buffer(dag_ids)
        return {**cleared_events_from_celery, **cleared_events_from_kubernetes}
    def try_adopt_task_instances(self, tis: Sequence[TaskInstance]) -> Sequence[TaskInstance]:
        """
        Try to adopt running task instances that have been abandoned by a SchedulerJob dying.
        Anything that is not adopted will be cleared by the scheduler (and then become eligible for
        re-scheduling)
        :return: any TaskInstances that were unable to be adopted
        """
        celery_tis = [ti for ti in tis if ti.queue != self.KUBERNETES_QUEUE]
        kubernetes_tis = [ti for ti in tis if ti.queue == self.KUBERNETES_QUEUE]
        return [
            *self.celery_executor.try_adopt_task_instances(celery_tis),
            *self.kubernetes_executor.try_adopt_task_instances(kubernetes_tis),
        ]
    def end(self) -> None: