import datetime  from functools import partial  from typing import Any, Callable, List, Optional, Union    from taipy.config import Config    from .._entity._entity_ids import _EntityIds  from .._manager._manager import _Manager  from .._repository._abstract_repository import _AbstractRepository  from .._version._version_mixin import _VersionMixin  from ..common.warn_if_inputs_not_ready import _warn_if_inputs_not_ready  from ..config.scenario_config import ScenarioConfig  from ..cycle._cycle_manager_factory import _CycleManagerFactory  from ..cycle.cycle import Cycle  from ..data._data_manager_factory import _DataManagerFactory  from ..exceptions.exceptions import (      DeletingPrimaryScenario,      DifferentScenarioConfigs,      DoesNotBelongToACycle,      InsufficientScenarioToCompare,      InvalidSequence,      InvalidSscenario,      NonExistingComparator,      NonExistingScenario,      NonExistingScenarioConfig,      SequenceTaskConfigDoesNotExistInSameScenarioConfig,      UnauthorizedTagError,  )  from ..job._job_manager_factory import _JobManagerFactory  from ..job.job import Job  from ..notification import EventEntityType, EventOperation, Notifier, _make_event  from ..submission._submission_manager_factory import _SubmissionManagerFactory  from ..task._task_manager_factory import _TaskManagerFactory  from .scenario import Scenario  from .scenario_id import ScenarioId      class _ScenarioManager(_Manager[Scenario], _VersionMixin):      _AUTHORIZED_TAGS_KEY = "authorized_tags"      _ENTITY_NAME = Scenario.__name__      _EVENT_ENTITY_TYPE = EventEntityType.SCENARIO        _repository: _AbstractRepository        @classmethod      def _get_all(cls, version_number: Optional[str] = None) -> List[Scenario]:          """          Returns all entities.          """          filters = cls._build_filters_with_version(version_number)          return cls._repository._load_all(filters)        @classmethod      def _subscribe(          cls,          callback: Callable[[Scenario, Job], None],          params: Optional[List[Any]] = None,          scenario: Optional[Scenario] = None,      ):          if scenario is None:              scenarios = cls._get_all()              for scn in scenarios:                  cls.__add_subscriber(callback, params, scn)              return            cls.__add_subscriber(callback, params, scenario)        @classmethod      def _unsubscribe(          cls,          callback: Callable[[Scenario, Job], None],          params: Optional[List[Any]] = None,          scenario: Optional[Scenario] = None,      ):          if scenario is None:              scenarios = cls._get_all()              for scn in scenarios:                  cls.__remove_subscriber(callback, params, scn)              return            cls.__remove_subscriber(callback, params, scenario)        @classmethod      def __add_subscriber(cls, callback, params, scenario: Scenario):          scenario._add_subscriber(callback, params)          Notifier.publish(              _make_event(scenario, EventOperation.UPDATE, attribute_name="subscribers", attribute_value=params)          )        @classmethod      def __remove_subscriber(cls, callback, params, scenario: Scenario):          scenario._remove_subscriber(callback, params)          Notifier.publish(              _make_event(scenario, EventOperation.UPDATE, attribute_name="subscribers", attribute_value=params)          )        @classmethod      def _create(          cls,          config: ScenarioConfig,          creation_date: Optional[datetime.datetime] = None,          name: Optional[str] = None,      ) -> Scenario:          _task_manager = _TaskManagerFactory._build_manager()          _data_manager = _DataManagerFactory._build_manager()            scenario_id = Scenario._new_id(str(config.id))          cycle = (              _CycleManagerFactory._build_manager()._get_or_create(config.frequency, creation_date)              if config.frequency              else None          )          cycle_id = cycle.id if cycle else None          tasks = (              _task_manager._bulk_get_or_create(config.task_configs, cycle_id, scenario_id) if config.task_configs else []          )          additional_data_nodes = (              _data_manager._bulk_get_or_create(config.additional_data_node_configs, cycle_id, scenario_id)              if config.additional_data_node_configs              else {}          )            sequences = {}          tasks_and_config_id_maps = {task.config_id: task for task in tasks}          for sequence_name, sequence_task_configs in config.sequences.items():              sequence_tasks = []              non_existing_sequence_task_config_in_scenario_config = set()              for sequence_task_config in sequence_task_configs:                  if task := tasks_and_config_id_maps.get(sequence_task_config.id):                      sequence_tasks.append(task)                  else:                      non_existing_sequence_task_config_in_scenario_config.add(sequence_task_config.id)              if len(non_existing_sequence_task_config_in_scenario_config) > 0:                  raise SequenceTaskConfigDoesNotExistInSameScenarioConfig(                      list(non_existing_sequence_task_config_in_scenario_config), sequence_name, str(config.id)                  )              sequences[sequence_name] = {Scenario._SEQUENCE_TASKS_KEY: sequence_tasks}            is_primary_scenario = len(cls._get_all_by_cycle(cycle)) == 0 if cycle else False          props = config._properties.copy()          if name:              props["name"] = name          version = cls._get_latest_version()            scenario = Scenario(              config_id=str(config.id),              tasks=set(tasks),              properties=props,              additional_data_nodes=set(additional_data_nodes.values()),              scenario_id=scenario_id,              creation_date=creation_date,              is_primary=is_primary_scenario,              cycle=cycle,              version=version,              sequences=sequences,          )            for task in tasks:              if scenario_id not in task._parent_ids:                  task._parent_ids.update([scenario_id])                  _task_manager._set(task)            for dn in additional_data_nodes.values():              if scenario_id not in dn._parent_ids:                  dn._parent_ids.update([scenario_id])                  _data_manager._set(dn)            cls._set(scenario)            if not scenario._is_consistent():              raise InvalidSscenario(scenario.id)            actual_sequences = scenario._get_sequences()          for sequence_name in sequences.keys():              if not actual_sequences[sequence_name]._is_consistent():                  raise InvalidSequence(actual_sequences[sequence_name].id)              Notifier.publish(_make_event(actual_sequences[sequence_name], EventOperation.CREATION))            Notifier.publish(_make_event(scenario, EventOperation.CREATION))          return scenario        @classmethod      def _is_submittable(cls, scenario: Union[Scenario, ScenarioId]) -> bool:          if isinstance(scenario, str):              scenario = cls._get(scenario)          return isinstance(scenario, Scenario) and scenario.is_ready_to_run()        @classmethod      def _submit(          cls,          scenario: Union[Scenario, ScenarioId],          callbacks: Optional[List[Callable]] = None,          force: bool = False,          wait: bool = False,          timeout: Optional[Union[float, int]] = None,          check_inputs_are_ready: bool = True,      ) -> List[Job]:          scenario_id = scenario.id if isinstance(scenario, Scenario) else scenario          scenario = cls._get(scenario_id)          if scenario is None:              raise NonExistingScenario(scenario_id)          callbacks = callbacks or []          scenario_subscription_callback = cls.__get_status_notifier_callbacks(scenario) + callbacks          if check_inputs_are_ready:              _warn_if_inputs_not_ready(scenario.get_inputs())            jobs = (              _TaskManagerFactory._build_manager()              ._orchestrator()              .submit(scenario, callbacks=scenario_subscription_callback, force=force, wait=wait, timeout=timeout)          )          Notifier.publish(_make_event(scenario, EventOperation.SUBMISSION))          return jobs        @classmethod      def __get_status_notifier_callbacks(cls, scenario: Scenario) -> List:          return [partial(c.callback, *c.params, scenario) for c in scenario.subscribers]        @classmethod      def _get_primary(cls, cycle: Cycle) -> Optional[Scenario]:          scenarios = cls._get_all_by_cycle(cycle)          for scenario in scenarios:              if scenario.is_primary:                  return scenario          return None        @classmethod      def _get_by_tag(cls, cycle: Cycle, tag: str) -> Optional[Scenario]:          scenarios = cls._get_all_by_cycle(cycle)          for scenario in scenarios:              if scenario.has_tag(tag):                  return scenario          return None        @classmethod      def _get_all_by_tag(cls, tag: str) -> List[Scenario]:          scenarios = []          for scenario in cls._get_all():              if scenario.has_tag(tag):                  scenarios.append(scenario)          return scenarios        @classmethod      def _get_all_by_cycle(cls, cycle: Cycle) -> List[Scenario]:          filters = cls._build_filters_with_version("all")            if not filters:              filters = [{}]          for fil in filters:              fil.update({"cycle": cycle.id})          return cls._get_all_by(filters)        @classmethod      def _get_primary_scenarios(cls) -> List[Scenario]:          primary_scenarios = []          for scenario in cls._get_all():              if scenario.is_primary:                  primary_scenarios.append(scenario)          return primary_scenarios        @classmethod      def _is_promotable_to_primary(cls, scenario: Union[Scenario, ScenarioId]) -> bool:          if isinstance(scenario, str):              scenario = cls._get(scenario)          if scenario and not scenario.is_primary and scenario.cycle:              return True          return False        @classmethod      def _set_primary(cls, scenario: Scenario):          if scenario.cycle:              primary_scenario = cls._get_primary(scenario.cycle)              # To prevent SAME scenario updating out of Context Manager              if primary_scenario and primary_scenario != scenario:                  primary_scenario.is_primary = False  # type: ignore              scenario.is_primary = True  # type: ignore          else:              raise DoesNotBelongToACycle(                  f"Can't set scenario {scenario.id} to primary because it doesn't belong to a cycle."              )        @classmethod      def _tag(cls, scenario: Scenario, tag: str):          tags = scenario.properties.get(cls._AUTHORIZED_TAGS_KEY, set())          if len(tags) > 0 and tag not in tags:              raise UnauthorizedTagError(f"Tag `{tag}` not authorized by scenario configuration `{scenario.config_id}`")          if scenario.cycle:              old_tagged_scenario = cls._get_by_tag(scenario.cycle, tag)              if old_tagged_scenario:                  old_tagged_scenario.remove_tag(tag)                  cls._set(old_tagged_scenario)          scenario._add_tag(tag)          cls._set(scenario)          Notifier.publish(              _make_event(scenario, EventOperation.UPDATE, attribute_name="tags", attribute_value=scenario.tags)          )        @classmethod      def _untag(cls, scenario: Scenario, tag: str):          scenario._remove_tag(tag)          cls._set(scenario)          Notifier.publish(              _make_event(scenario, EventOperation.UPDATE, attribute_name="tags", attribute_value=scenario.tags)          )        @classmethod      def _compare(cls, *scenarios: Scenario, data_node_config_id: Optional[str] = None):          if len(scenarios) < 2:              raise InsufficientScenarioToCompare("At least two scenarios are required to compare.")            if not all(scenarios[0].config_id == scenario.config_id for scenario in scenarios):              raise DifferentScenarioConfigs("Scenarios to compare must have the same configuration.")            if scenario_config := _ScenarioManager.__get_config(scenarios[0]):              results = {}              if data_node_config_id:                  if data_node_config_id in scenario_config.comparators.keys():                      dn_comparators = {data_node_config_id: scenario_config.comparators[data_node_config_id]}                  else:                      raise NonExistingComparator(f"Data node config {data_node_config_id} has no comparator.")              else:                  dn_comparators = scenario_config.comparators                for data_node_config_id, comparators in dn_comparators.items():                  data_nodes = [scenario.__getattr__(data_node_config_id).read() for scenario in scenarios]                  results[data_node_config_id] = {                      comparator.__name__: comparator(*data_nodes) for comparator in comparators                  }                return results            else:              raise NonExistingScenarioConfig(scenarios[0].config_id)        @staticmethod      def __get_config(scenario: Scenario):          return Config.scenarios.get(scenario.config_id, None)        @classmethod      def _is_deletable(cls, scenario: Union[Scenario, ScenarioId]) -> bool:          if isinstance(scenario, str):              scenario = cls._get(scenario)          if scenario.is_primary:              if len(cls._get_all_by_cycle(scenario.cycle)) > 1:                  return False          return True        @classmethod      def _delete(cls, scenario_id: ScenarioId):          scenario = cls._get(scenario_id)          if not cls._is_deletable(scenario):              raise DeletingPrimaryScenario(                  f"Scenario {scenario.id}, which has config id {scenario.config_id}, is primary and there are "                  f"other scenarios in the same cycle. "              )          if scenario.is_primary:              _CycleManagerFactory._build_manager()._delete(scenario.cycle.id)          super()._delete(scenario_id)        @classmethod      def _hard_delete(cls, scenario_id: ScenarioId):          scenario = cls._get(scenario_id)          if not cls._is_deletable(scenario):              raise DeletingPrimaryScenario(                  f"Scenario {scenario.id}, which has config id {scenario.config_id}, is primary and there are "                  f"other scenarios in the same cycle. "              )          if scenario.is_primary:              _CycleManagerFactory._build_manager()._hard_delete(scenario.cycle.id)          else:              entity_ids_to_delete = cls._get_children_entity_ids(scenario)              entity_ids_to_delete.scenario_ids.add(scenario.id)              cls._delete_entities_of_multiple_types(entity_ids_to_delete)        @classmethod      def _delete_by_version(cls, version_number: str):          """          Deletes scenario by the version number.            Check if the cycle is only attached to this scenario, then delete it.          """          for scenario in cls._repository._search("version", version_number):              if scenario.cycle and len(cls._get_all_by_cycle(scenario.cycle)) == 1:                  _CycleManagerFactory._build_manager()._delete(scenario.cycle.id)              super()._delete(scenario.id)        @classmethod      def _get_children_entity_ids(cls, scenario: Scenario) -> _EntityIds:          entity_ids = _EntityIds()          for sequence in scenario.sequences.values():              if sequence.owner_id == scenario.id:                  entity_ids.sequence_ids.add(sequence.id)          for task in scenario.tasks.values():              if task.owner_id == scenario.id:                  entity_ids.task_ids.add(task.id)          for data_node in scenario.data_nodes.values():              if data_node.owner_id == scenario.id:                  entity_ids.data_node_ids.add(data_node.id)            jobs = _JobManagerFactory._build_manager()._get_all()          for job in jobs:              if job.task.id in entity_ids.task_ids:                  entity_ids.job_ids.add(job.id)            submissions = _SubmissionManagerFactory._build_manager()._get_all()          submitted_entity_ids = list(entity_ids.scenario_ids.union(entity_ids.sequence_ids, entity_ids.task_ids))          for submission in submissions:              if submission.entity_id in submitted_entity_ids:                  entity_ids.submission_ids.add(submission.id)            return entity_ids        @classmethod      def _get_by_config_id(cls, config_id: str, version_number: Optional[str] = None) -> List[Scenario]:          """          Get all scenarios by its config id.          """          filters = cls._build_filters_with_version(version_number)          if not filters:              filters = [{}]          for fil in filters:              fil.update({"config_id": config_id})          return cls._repository._load_all(filters)


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  from .._repository._sql_repository import _SQLRepository  from ._scenario_converter import _ScenarioConverter  from ._scenario_model import _ScenarioModel      class _ScenarioSQLRepository(_SQLRepository):      def __init__(self):          super().__init__(model_type=_ScenarioModel, converter=_ScenarioConverter)


from datetime import datetime  from typing import Dict, List, Optional, Set, Union    from .._repository._abstract_converter import _AbstractConverter  from .._version._utils import _migrate_entity  from ..common import _utils  from ..cycle._cycle_manager_factory import _CycleManagerFactory  from ..cycle.cycle import Cycle, CycleId  from ..data.data_node import DataNode, DataNodeId  from ..scenario._scenario_model import _ScenarioModel  from ..scenario.scenario import Scenario  from ..task.task import Task, TaskId      class _ScenarioConverter(_AbstractConverter):      @classmethod      def _entity_to_model(cls, scenario: Scenario) -> _ScenarioModel:          sequences: Dict[str, Dict[str, Union[List[TaskId], Dict, List]]] = {}          for p_name, sequence_data in scenario._sequences.items():              sequences[p_name] = {                  Scenario._SEQUENCE_TASKS_KEY: [                      t.id if isinstance(t, Task) else t for t in sequence_data.get("tasks", [])                  ],                  Scenario._SEQUENCE_PROPERTIES_KEY: sequence_data.get("properties", {}),                  Scenario._SEQUENCE_SUBSCRIBERS_KEY: _utils._fcts_to_dict(sequence_data.get("subscribers", [])),              }            return _ScenarioModel(              id=scenario.id,              config_id=scenario.config_id,              tasks=[task.id if isinstance(task, Task) else TaskId(str(task)) for task in list(scenario._tasks)],              additional_data_nodes=[                  dn.id if isinstance(dn, DataNode) else DataNodeId(str(dn))                  for dn in list(scenario._additional_data_nodes)              ],              properties=scenario._properties.data,              creation_date=scenario._creation_date.isoformat(),              primary_scenario=scenario._primary_scenario,              subscribers=_utils._fcts_to_dict(scenario._subscribers),              tags=list(scenario._tags),              version=scenario._version,              cycle=scenario._cycle.id if scenario._cycle else None,              sequences=sequences if sequences else None,          )        @classmethod      def _model_to_entity(cls, model: _ScenarioModel) -> Scenario:          tasks: Union[Set[TaskId], Set[Task], Set] = set()          if model.tasks:              tasks = set(model.tasks)          if model.sequences:              for sequence_name, sequence_data in model.sequences.items():                  if subscribers := sequence_data.get(Scenario._SEQUENCE_SUBSCRIBERS_KEY):                      model.sequences[sequence_name][Scenario._SEQUENCE_SUBSCRIBERS_KEY] = [                          _utils._Subscriber(_utils._load_fct(it["fct_module"], it["fct_name"]), it["fct_params"])                          for it in subscribers                      ]            scenario = Scenario(              scenario_id=model.id,              config_id=model.config_id,              tasks=tasks,              additional_data_nodes=set(model.additional_data_nodes),              properties=model.properties,              creation_date=datetime.fromisoformat(model.creation_date),              is_primary=model.primary_scenario,              tags=set(model.tags),              cycle=cls.__to_cycle(model.cycle),              subscribers=[                  _utils._Subscriber(_utils._load_fct(it["fct_module"], it["fct_name"]), it["fct_params"])                  for it in model.subscribers              ],              version=model.version,              sequences=model.sequences,          )          return _migrate_entity(scenario)        @staticmethod      def __to_cycle(cycle_id: Optional[CycleId] = None) -> Optional[Cycle]:          return _CycleManagerFactory._build_manager()._get(cycle_id) if cycle_id else None


from typing import NewType    ScenarioId = NewType("ScenarioId", str)  ScenarioId.__doc__ = """Type that holds a `Scenario^` identifier."""


import itertools  from datetime import datetime  from multiprocessing import Lock  from queue import Queue  from time import sleep  from typing import Callable, Iterable, List, Optional, Set, Union    from taipy.config.config import Config  from taipy.logger._taipy_logger import _TaipyLogger    from .._entity.submittable import Submittable  from ..data._data_manager_factory import _DataManagerFactory  from ..job._job_manager_factory import _JobManagerFactory  from ..job.job import Job  from ..job.job_id import JobId  from ..submission._submission_manager_factory import _SubmissionManagerFactory  from ..task.task import Task  from ._abstract_orchestrator import _AbstractOrchestrator      class _Orchestrator(_AbstractOrchestrator):      """      Handles the functional orchestrating.      """        jobs_to_run: Queue = Queue()      blocked_jobs: List = []      lock = Lock()      __logger = _TaipyLogger._get_logger()        @classmethod      def initialize(cls):          pass        @classmethod      def submit(          cls,          submittable: Submittable,          callbacks: Optional[Iterable[Callable]] = None,          force: bool = False,          wait: bool = False,          timeout: Optional[Union[float, int]] = None,      ) -> List[Job]:          """Submit the given `Scenario^` or `Sequence^` for an execution.            Parameters:               submittable (Union[SCenario^, Sequence^]): The scenario or sequence to submit for execution.               callbacks: The optional list of functions that should be executed on jobs status change.               force (bool) : Enforce execution of the scenario's or sequence's tasks even if their output data                  nodes are cached.               wait (bool): Wait for the orchestrated jobs created from the scenario or sequence submission to be                  finished in asynchronous mode.               timeout (Union[float, int]): The optional maximum number of seconds to wait for the jobs to be finished                  before returning.          Returns:              The created Jobs.          """          submission = _SubmissionManagerFactory._build_manager()._create(              submittable.id, submittable._ID_PREFIX  # type: ignore          )            jobs = []          tasks = submittable._get_sorted_tasks()          with cls.lock:              for ts in tasks:                  for task in ts:                      jobs.append(                          cls._lock_dn_output_and_create_job(                              task,                              submission.id,                              submission.entity_id,                              callbacks=itertools.chain([submission._update_submission_status], callbacks or []),                              force=force,  # type: ignore                          )                      )            submission.jobs = jobs  # type: ignore            cls._orchestrate_job_to_run_or_block(jobs)            if Config.job_config.is_development:              cls._check_and_execute_jobs_if_development_mode()          else:              if wait:                  cls.__wait_until_job_finished(jobs, timeout=timeout)            return jobs        @classmethod      def submit_task(          cls,          task: Task,          callbacks: Optional[Iterable[Callable]] = None,          force: bool = False,          wait: bool = False,          timeout: Optional[Union[float, int]] = None,      ) -> Job:          """Submit the given `Task^` for an execution.            Parameters:               task (Task^): The task to submit for execution.               submit_id (str): The optional id to differentiate each submission.               callbacks: The optional list of functions that should be executed on job status change.               force (bool): Enforce execution of the task even if its output data nodes are cached.               wait (bool): Wait for the orchestrated job created from the task submission to be finished                  in asynchronous mode.               timeout (Union[float, int]): The optional maximum number of seconds to wait for the job                  to be finished before returning.          Returns:              The created `Job^`.          """          submission = _SubmissionManagerFactory._build_manager()._create(task.id, task._ID_PREFIX)          submit_id = submission.id          with cls.lock:              job = cls._lock_dn_output_and_create_job(                  task,                  submit_id,                  submission.entity_id,                  itertools.chain([submission._update_submission_status], callbacks or []),                  force,              )            jobs = [job]          submission.jobs = jobs  # type: ignore            cls._orchestrate_job_to_run_or_block(jobs)            if Config.job_config.is_development:              cls._check_and_execute_jobs_if_development_mode()          else:              if wait:                  cls.__wait_until_job_finished(job, timeout=timeout)            return job        @classmethod      def _lock_dn_output_and_create_job(          cls,          task: Task,          submit_id: str,          submit_entity_id: str,          callbacks: Optional[Iterable[Callable]] = None,          force: bool = False,      ) -> Job:          for dn in task.output.values():              dn.lock_edit()          job = _JobManagerFactory._build_manager()._create(              task, itertools.chain([cls._on_status_change], callbacks or []), submit_id, submit_entity_id, force=force          )            return job        @classmethod      def _orchestrate_job_to_run_or_block(cls, jobs: List[Job]):          blocked_jobs = []          pending_jobs = []            for job in jobs:              if cls._is_blocked(job):                  job.blocked()                  blocked_jobs.append(job)              else:                  job.pending()                  pending_jobs.append(job)            cls.blocked_jobs.extend(blocked_jobs)          for job in pending_jobs:              cls.jobs_to_run.put(job)        @classmethod      def __wait_until_job_finished(cls, jobs: Union[List[Job], Job], timeout: Optional[Union[float, int]] = None):          def __check_if_timeout(start, timeout):              if timeout:                  return (datetime.now() - start).seconds < timeout              return True            start = datetime.now()          jobs = jobs if isinstance(jobs, Iterable) else [jobs]          index = 0            while __check_if_timeout(start, timeout) and index < len(jobs):              try:                  if jobs[index]._is_finished():                      index = index + 1                  else:                      sleep(0.5)  # Limit CPU usage                except Exception:                  pass        @classmethod      def _is_blocked(cls, obj: Union[Task, Job]) -> bool:          """Returns True if the execution of the `Job^` or the `Task^` is blocked by the execution of another `Job^`.            Parameters:               obj (Union[Task^, Job^]): The job or task entity to run.            Returns:               True if one of its input data nodes is blocked.          """          input_data_nodes = obj.task.input.values() if isinstance(obj, Job) else obj.input.values()          data_manager = _DataManagerFactory._build_manager()          return any(not data_manager._get(dn.id).is_ready_for_reading for dn in input_data_nodes)        @staticmethod      def _unlock_edit_on_jobs_outputs(jobs: Union[Job, List[Job], Set[Job]]):          jobs = [jobs] if isinstance(jobs, Job) else jobs          for job in jobs:              job._unlock_edit_on_outputs()        @classmethod      def _on_status_change(cls, job: Job):          if job.is_completed() or job.is_skipped():              cls.__unblock_jobs()          elif job.is_failed():              cls._fail_subsequent_jobs(job)        @classmethod      def __unblock_jobs(cls):          for job in cls.blocked_jobs:              if not cls._is_blocked(job):                  with cls.lock:                      job.pending()                      cls.__remove_blocked_job(job)                      cls.jobs_to_run.put(job)        @classmethod      def __remove_blocked_job(cls, job):          try:  # In case the job has been removed from the list of blocked_jobs.              cls.blocked_jobs.remove(job)          except Exception:              cls.__logger.warning(f"{job.id} is not in the blocked list anymore.")        @classmethod      def cancel_job(cls, job: Job):          if job.is_canceled():              cls.__logger.info(f"{job.id} has already been canceled.")          elif job.is_abandoned():              cls.__logger.info(f"{job.id} has already been abandoned and cannot be canceled.")          elif job.is_failed():              cls.__logger.info(f"{job.id} has already failed and cannot be canceled.")          else:              with cls.lock:                  to_cancel_or_abandon_jobs = set([job])                  to_cancel_or_abandon_jobs.update(cls.__find_subsequent_jobs(job.submit_id, set(job.task.output.keys())))                  cls.__remove_blocked_jobs(to_cancel_or_abandon_jobs)                  cls.__remove_jobs_to_run(to_cancel_or_abandon_jobs)                  cls._cancel_jobs(job.id, to_cancel_or_abandon_jobs)                  cls._unlock_edit_on_jobs_outputs(to_cancel_or_abandon_jobs)        @classmethod      def __find_subsequent_jobs(cls, submit_id, output_dn_config_ids: Set) -> Set[Job]:          next_output_dn_config_ids = set()          subsequent_jobs = set()          for job in cls.blocked_jobs:              job_input_dn_config_ids = job.task.input.keys()              if job.submit_id == submit_id and len(output_dn_config_ids.intersection(job_input_dn_config_ids)) > 0:                  next_output_dn_config_ids.update(job.task.output.keys())                  subsequent_jobs.update([job])          if len(next_output_dn_config_ids) > 0:              subsequent_jobs.update(                  cls.__find_subsequent_jobs(submit_id, output_dn_config_ids=next_output_dn_config_ids)              )          return subsequent_jobs        @classmethod      def __remove_blocked_jobs(cls, jobs):          for job in jobs:              cls.__remove_blocked_job(job)        @classmethod      def __remove_jobs_to_run(cls, jobs):          new_jobs_to_run: Queue = Queue()          while not cls.jobs_to_run.empty():              current_job = cls.jobs_to_run.get()              if current_job not in jobs:                  new_jobs_to_run.put(current_job)          cls.jobs_to_run = new_jobs_to_run        @classmethod      def _fail_subsequent_jobs(cls, failed_job: Job):          with cls.lock:              to_fail_or_abandon_jobs = set()              to_fail_or_abandon_jobs.update(                  cls.__find_subsequent_jobs(failed_job.submit_id, set(failed_job.task.output.keys()))              )              for job in to_fail_or_abandon_jobs:                  job.abandoned()              to_fail_or_abandon_jobs.update([failed_job])              cls.__remove_blocked_jobs(to_fail_or_abandon_jobs)              cls.__remove_jobs_to_run(to_fail_or_abandon_jobs)              cls._unlock_edit_on_jobs_outputs(to_fail_or_abandon_jobs)        @classmethod      def _cancel_jobs(cls, job_id_to_cancel: JobId, jobs: Set[Job]):          from ._orchestrator_factory import _OrchestratorFactory            for job in jobs:              if job.id in _OrchestratorFactory._dispatcher._dispatched_processes.keys():  # type: ignore                  cls.__logger.info(f"{job.id} is running and cannot be canceled.")              elif job.is_completed():                  cls.__logger.info(f"{job.id} has already been completed and cannot be canceled.")              elif job.is_skipped():                  cls.__logger.info(f"{job.id} has already been skipped and cannot be canceled.")              else:                  if job_id_to_cancel == job.id:                      job.canceled()                  else:                      job.abandoned()        @staticmethod      def _check_and_execute_jobs_if_development_mode():          from ._orchestrator_factory import _OrchestratorFactory            if dispatcher := _OrchestratorFactory._dispatcher:              dispatcher._execute_jobs_synchronously()


from abc import abstractmethod  from typing import Callable, Iterable, List, Optional, Union    from ..job.job import Job  from ..task.task import Task      class _AbstractOrchestrator:      """Creates, enqueues, and orchestrates jobs as instances of `Job^` class."""        @classmethod      @abstractmethod      def initialize(cls):          raise NotImplementedError        @classmethod      @abstractmethod      def submit(          cls,          sequence,          callbacks: Optional[Iterable[Callable]],          force: bool = False,          wait: bool = False,          timeout: Optional[Union[float, int]] = None,      ) -> List[Job]:          raise NotImplementedError        @classmethod      @abstractmethod      def submit_task(          cls,          task: Task,          callbacks: Optional[Iterable[Callable]] = None,          force: bool = False,          wait: bool = False,          timeout: Optional[Union[float, int]] = None,      ) -> Job:          raise NotImplementedError        @classmethod      @abstractmethod      def cancel_job(cls, job):          raise NotImplementedError


from importlib import util  from typing import Optional, Type    from taipy.config.config import Config    from ..common._utils import _load_fct  from ..exceptions.exceptions import ModeNotAvailable, OrchestratorNotBuilt  from ._abstract_orchestrator import _AbstractOrchestrator  from ._dispatcher import _DevelopmentJobDispatcher, _JobDispatcher, _StandaloneJobDispatcher  from ._orchestrator import _Orchestrator      class _OrchestratorFactory:      _TAIPY_ENTERPRISE_MODULE = "taipy.enterprise"      _TAIPY_ENTERPRISE_CORE_ORCHESTRATOR_MODULE = _TAIPY_ENTERPRISE_MODULE + ".core._orchestrator._orchestrator"      _TAIPY_ENTERPRISE_CORE_DISPATCHER_MODULE = _TAIPY_ENTERPRISE_MODULE + ".core._orchestrator._dispatcher"      __TAIPY_ENTERPRISE_BUILD_DISPATCHER_METHOD = "_build_dispatcher"        _orchestrator: Optional[_Orchestrator] = None      _dispatcher: Optional[_JobDispatcher] = None        @classmethod      def _build_orchestrator(cls) -> Type[_AbstractOrchestrator]:          if util.find_spec(cls._TAIPY_ENTERPRISE_MODULE) is not None:              cls._orchestrator = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_ORCHESTRATOR_MODULE,                  "Orchestrator",              )  # type: ignore          else:              cls._orchestrator = _Orchestrator  # type: ignore            cls._orchestrator.initialize()  # type: ignore            return cls._orchestrator  # type: ignore        @classmethod      def _build_dispatcher(cls, force_restart=False) -> Optional[_JobDispatcher]:          if not cls._orchestrator:              raise OrchestratorNotBuilt          if Config.job_config.is_standalone:              cls.__build_standalone_job_dispatcher(force_restart=force_restart)          elif Config.job_config.is_development:              cls.__build_development_job_dispatcher()          elif util.find_spec(cls._TAIPY_ENTERPRISE_MODULE):              cls.__build_enterprise_job_dispatcher(force_restart=force_restart)          else:              raise ModeNotAvailable(f"Job mode {Config.job_config.mode} is not available.")          return cls._dispatcher        @classmethod      def _remove_dispatcher(cls) -> Optional[_JobDispatcher]:          if cls._dispatcher is not None and not isinstance(cls._dispatcher, _DevelopmentJobDispatcher):              cls._dispatcher.stop()          cls._dispatcher = None          return cls._dispatcher        @classmethod      def __build_standalone_job_dispatcher(cls, force_restart=False):          if isinstance(cls._dispatcher, _StandaloneJobDispatcher):              if force_restart:                  cls._dispatcher.stop()              else:                  return            if util.find_spec(cls._TAIPY_ENTERPRISE_MODULE) is not None:              cls._dispatcher = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_DISPATCHER_MODULE, cls.__TAIPY_ENTERPRISE_BUILD_DISPATCHER_METHOD              )(cls._orchestrator)          else:              cls._dispatcher = _StandaloneJobDispatcher(cls._orchestrator)  # type: ignore          cls._dispatcher.start()  # type: ignore        @classmethod      def __build_development_job_dispatcher(cls):          if isinstance(cls._dispatcher, _StandaloneJobDispatcher):              cls._dispatcher.stop()          cls._dispatcher = _DevelopmentJobDispatcher(cls._orchestrator)  # type: ignore        @classmethod      def __build_enterprise_job_dispatcher(cls, force_restart=False):          cls._dispatcher = _load_fct(              cls._TAIPY_ENTERPRISE_CORE_DISPATCHER_MODULE, cls.__TAIPY_ENTERPRISE_BUILD_DISPATCHER_METHOD          )(cls._orchestrator, force_restart)          if cls._dispatcher:              cls._dispatcher.start()          else:              raise ModeNotAvailable(f"Job mode {Config.job_config.mode} is not available.")


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.


from ._development_job_dispatcher import _DevelopmentJobDispatcher  from ._job_dispatcher import _JobDispatcher  from ._standalone_job_dispatcher import _StandaloneJobDispatcher


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  from typing import Optional    from ...job.job import Job  from .._abstract_orchestrator import _AbstractOrchestrator  from ._job_dispatcher import _JobDispatcher  from ._task_function_wrapper import _TaskFunctionWrapper      class _DevelopmentJobDispatcher(_JobDispatcher):      """Manages job dispatching (instances of `Job^` class) in a synchronous way."""        def __init__(self, orchestrator: Optional[_AbstractOrchestrator]):          super().__init__(orchestrator)        def start(self):          raise NotImplementedError        def is_running(self) -> bool:          return True        def stop(self):          raise NotImplementedError        def run(self):          raise NotImplementedError        def _dispatch(self, job: Job):          """Dispatches the given `Job^` on an available worker for execution.            Parameters:              job (Job^): The job to submit on an executor with an available worker.          """          rs = _TaskFunctionWrapper(job.id, job.task).execute()          self._update_job_status(job, rs)


from concurrent.futures import ProcessPoolExecutor  from functools import partial  from typing import Optional    from taipy.config._serializer._toml_serializer import _TomlSerializer  from taipy.config.config import Config    from ...job.job import Job  from .._abstract_orchestrator import _AbstractOrchestrator  from ._job_dispatcher import _JobDispatcher  from ._task_function_wrapper import _TaskFunctionWrapper      class _StandaloneJobDispatcher(_JobDispatcher):      """Manages job dispatching (instances of `Job^` class) in an asynchronous way using a ProcessPoolExecutor."""        def __init__(self, orchestrator: Optional[_AbstractOrchestrator]):          super().__init__(orchestrator)          self._executor = ProcessPoolExecutor(Config.job_config.max_nb_of_workers or 1)  # type: ignore          self._nb_available_workers = self._executor._max_workers  # type: ignore        def _dispatch(self, job: Job):          """Dispatches the given `Job^` on an available worker for execution.            Parameters:              job (Job^): The job to submit on an executor with an available worker.          """          self._nb_available_workers -= 1            config_as_string = _TomlSerializer()._serialize(Config._applied_config)          future = self._executor.submit(_TaskFunctionWrapper(job.id, job.task), config_as_string=config_as_string)            self._set_dispatched_processes(job.id, future)  # type: ignore          future.add_done_callback(self._release_worker)          future.add_done_callback(partial(self._update_job_status_from_future, job))        def _release_worker(self, _):          self._nb_available_workers += 1        def _update_job_status_from_future(self, job: Job, ft):          self._pop_dispatched_process(job.id)  # type: ignore          self._update_job_status(job, ft.result())


import threading  from abc import abstractmethod  from typing import Dict, Optional    from taipy.config.config import Config  from taipy.logger._taipy_logger import _TaipyLogger    from ...data._data_manager_factory import _DataManagerFactory  from ...job._job_manager_factory import _JobManagerFactory  from ...job.job import Job  from ...task.task import Task  from .._abstract_orchestrator import _AbstractOrchestrator      class _JobDispatcher(threading.Thread):      """Manages job dispatching (instances of `Job^` class) on executors."""        _STOP_FLAG = False      _dispatched_processes: Dict = {}      __logger = _TaipyLogger._get_logger()      _nb_available_workers: int = 1        def __init__(self, orchestrator: Optional[_AbstractOrchestrator]):          threading.Thread.__init__(self, name="Thread-Taipy-JobDispatcher")          self.daemon = True          self.orchestrator = orchestrator          self.lock = self.orchestrator.lock  # type: ignore          Config.block_update()        def start(self):          """Start the dispatcher"""          threading.Thread.start(self)        def is_running(self) -> bool:          """Return True if the dispatcher is running"""          return self.is_alive()        def stop(self):          """Stop the dispatcher"""          self._STOP_FLAG = True        def run(self):          _TaipyLogger._get_logger().info("Start job dispatcher...")          while not self._STOP_FLAG:              try:                  if self._can_execute():                      with self.lock:                          job = self.orchestrator.jobs_to_run.get(block=True, timeout=0.1)                      self._execute_job(job)              except Exception:  # In case the last job of the queue has been removed.                  pass        def _can_execute(self) -> bool:          """Returns True if the dispatcher have resources to execute a new job."""          return self._nb_available_workers > 0        def _execute_job(self, job: Job):          if job.force or self._needs_to_run(job.task):              if job.force:                  self.__logger.info(f"job {job.id} is forced to be executed.")              job.running()              self._dispatch(job)          else:              job._unlock_edit_on_outputs()              job.skipped()              self.__logger.info(f"job {job.id} is skipped.")        def _execute_jobs_synchronously(self):          while not self.orchestrator.jobs_to_run.empty():              with self.lock:                  try:                      job = self.orchestrator.jobs_to_run.get()                  except Exception:  # In case the last job of the queue has been removed.                      self.__logger.warning(f"{job.id} is no longer in the list of jobs to run.")              self._execute_job(job)        @staticmethod      def _needs_to_run(task: Task) -> bool:          """          Returns True if the task has no output or if at least one input was modified since the latest run.            Parameters:               task (Task^): The task to run.          Returns:               True if the task needs to run. False otherwise.          """          if not task.skippable:              return True          data_manager = _DataManagerFactory._build_manager()          if len(task.output) == 0:              return True          are_outputs_in_cache = all(data_manager._get(dn.id).is_valid for dn in task.output.values())          if not are_outputs_in_cache:              return True          if len(task.input) == 0:              return False          input_last_edit = max(data_manager._get(dn.id).last_edit_date for dn in task.input.values())          output_last_edit = min(data_manager._get(dn.id).last_edit_date for dn in task.output.values())          return input_last_edit > output_last_edit        @abstractmethod      def _dispatch(self, job: Job):          """          Dispatches the given `Job^` on an available worker for execution.            Parameters:              job (Job^): The job to submit on an executor with an available worker.          """          raise NotImplementedError        @staticmethod      def _update_job_status(job: Job, exceptions):          job.update_status(exceptions)          _JobManagerFactory._build_manager()._set(job)        @classmethod      def _set_dispatched_processes(cls, job_id, process):          cls._dispatched_processes[job_id] = process        @classmethod      def _pop_dispatched_process(cls, job_id, default=None):          return cls._dispatched_processes.pop(job_id, default)  # type: ignore


from typing import Any, List    from taipy.config._serializer._toml_serializer import _TomlSerializer  from taipy.config.config import Config  from taipy.logger._taipy_logger import _TaipyLogger    from ...data._data_manager_factory import _DataManagerFactory  from ...data.data_node import DataNode  from ...exceptions import DataNodeWritingError  from ...job.job_id import JobId  from ...task.task import Task    logger = _TaipyLogger._get_logger()      class _TaskFunctionWrapper:      """Wrapper around task function."""        def __init__(self, job_id: JobId, task: Task):          self.job_id = job_id          self.task = task        def __call__(self, **kwargs):          """Make this object callable as a function. Actually calls `execute`."""          return self.execute(**kwargs)        def execute(self, **kwargs):          """Execute the wrapped function. If `config_as_string` is given, then it will be reapplied to the config."""          try:              config_as_string = kwargs.pop("config_as_string", None)              if config_as_string:                  logger.info("Updating with given config.")                  Config._applied_config._update(_TomlSerializer()._deserialize(config_as_string))                  Config.block_update()                inputs = list(self.task.input.values())              outputs = list(self.task.output.values())                arguments = self._read_inputs(inputs)              results = self._execute_fct(arguments)              return self._write_data(outputs, results, self.job_id)          except Exception as e:              logger.error("Error during task function execution!", exc_info=1)              return [e]        def _read_inputs(self, inputs: List[DataNode]) -> List[Any]:          data_manager = _DataManagerFactory._build_manager()          return [data_manager._get(dn.id).read_or_raise() for dn in inputs]        def _write_data(self, outputs: List[DataNode], results, job_id: JobId):          data_manager = _DataManagerFactory._build_manager()          try:              if outputs:                  _results = self._extract_results(outputs, results)                  exceptions = []                  for res, dn in zip(_results, outputs):                      try:                          data_node = data_manager._get(dn.id)                          data_node.write(res, job_id=job_id)                          data_manager._set(data_node)                      except Exception as e:                          logger.error("Error during write", exc_info=1)                          exceptions.append(DataNodeWritingError(f"Error writing in datanode id {dn.id}: {e}"))                  return exceptions          except Exception as e:              return [e]        def _execute_fct(self, arguments: List[Any]) -> Any:          return self.task.function(*arguments)        def _extract_results(self, outputs: List[DataNode], results: Any) -> List[Any]:          _results: List[Any] = [results] if len(outputs) == 1 else results          if len(_results) != len(outputs):              raise DataNodeWritingError("Error: wrong number of result or task output")          return _results


import uuid  from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Set, Union    from taipy.config.common._template_handler import _TemplateHandler as _tpl  from taipy.config.common._validate_id import _validate_id  from taipy.config.common.scope import Scope    from .._entity._entity import _Entity  from .._entity._labeled import _Labeled  from .._entity._properties import _Properties  from .._entity._reload import _Reloader, _self_reload, _self_setter  from .._version._version_manager_factory import _VersionManagerFactory  from ..data._data_manager_factory import _DataManagerFactory  from ..data.data_node import DataNode  from ..exceptions.exceptions import NonExistingDataNode  from ..notification.event import Event, EventEntityType, EventOperation, _make_event  from .task_id import TaskId    if TYPE_CHECKING:      from ..job.job import Job      class Task(_Entity, _Labeled):      """Hold a user function that will be executed, its parameters and the results.        A `Task` brings together the user code as function, the inputs and the outputs as data nodes      (instances of the `DataNode^` class).        Attributes:          config_id (str): The identifier of the `TaskConfig^`.          properties (dict[str, Any]): A dictionary of additional properties.          function (callable): The python function to execute. The _function_ must take as parameter the              data referenced by inputs data nodes, and must return the data referenced by outputs data nodes.          input (Union[DataNode^, List[DataNode^]]): The list of inputs.          output (Union[DataNode^, List[DataNode^]]): The list of outputs.          id (str): The unique identifier of the task.          owner_id (str):  The identifier of the owner (sequence_id, scenario_id, cycle_id) or None.          parent_ids (Optional[Set[str]]): The set of identifiers of the parent sequences.          version (str): The string indicates the application version of the task to instantiate. If not provided, the              latest version is used.          skippable (bool): If True, indicates that the task can be skipped if no change has been made on inputs. The              default value is _False_.        """        _ID_PREFIX = "TASK"      __ID_SEPARATOR = "_"      _MANAGER_NAME = "task"        def __init__(          self,          config_id: str,          properties: Dict[str, Any],          function,          input: Optional[Iterable[DataNode]] = None,          output: Optional[Iterable[DataNode]] = None,          id: Optional[TaskId] = None,          owner_id: Optional[str] = None,          parent_ids: Optional[Set[str]] = None,          version: Optional[str] = None,          skippable: bool = False,      ):          self.config_id = _validate_id(config_id)          self.id = id or TaskId(self.__ID_SEPARATOR.join([self._ID_PREFIX, self.config_id, str(uuid.uuid4())]))          self.owner_id = owner_id          self._parent_ids = parent_ids or set()          self.__input = {dn.config_id: dn for dn in input or []}          self.__output = {dn.config_id: dn for dn in output or []}          self._function = function          self._version = version or _VersionManagerFactory._build_manager()._get_latest_version()          self._skippable = skippable          self._properties = _Properties(self, **properties)        def __hash__(self):          return hash(self.id)        def __eq__(self, other):          return self.id == other.id        def __getstate__(self):          return vars(self)        def __setstate__(self, state):          vars(self).update(state)        def __getattr__(self, attribute_name):          protected_attribute_name = _validate_id(attribute_name)          if protected_attribute_name in self._properties:              return _tpl._replace_templates(self._properties[protected_attribute_name])          if protected_attribute_name in self.input:              return self.input[protected_attribute_name]          if protected_attribute_name in self.output:              return self.output[protected_attribute_name]          raise AttributeError(f"{attribute_name} is not an attribute of task {self.id}")        @property      def properties(self):          self._properties = _Reloader()._reload(self._MANAGER_NAME, self)._properties          return self._properties        def get_parents(self):          """Get parents of the task."""          from ... import core as tp            return tp.get_parents(self)        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def parent_ids(self):          return self._parent_ids        @property      def input(self) -> Dict[str, DataNode]:          return self.__input        @property      def output(self) -> Dict[str, DataNode]:          return self.__output        @property      def data_nodes(self) -> Dict[str, DataNode]:          return {**self.input, **self.output}        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def function(self):          return self._function        @function.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def function(self, val):          self._function = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def skippable(self):          return self._skippable        @skippable.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def skippable(self, val):          self._skippable = val        @property      def scope(self) -> Scope:          """Retrieve the lowest scope of the task based on its data nodes.            Returns:              The lowest scope present in input and output data nodes or GLOBAL if there are                  either no input or no output.          """          data_nodes = list(self.__input.values()) + list(self.__output.values())          scope = Scope(min(dn.scope for dn in data_nodes)) if len(data_nodes) != 0 else Scope.GLOBAL          return scope        @property      def version(self):          return self._version        def submit(          self,          callbacks: Optional[List[Callable]] = None,          force: bool = False,          wait: bool = False,          timeout: Optional[Union[float, int]] = None,      ) -> "Job":  # noqa          """Submit the task for execution.            Parameters:              callbacks (List[Callable]): The list of callable functions to be called on status                  change.              force (bool): Force execution even if the data nodes are in cache.              wait (bool): Wait for the orchestrated job created from the task submission to be finished in asynchronous                  mode.              timeout (Union[float, int]): The maximum number of seconds to wait for the job to be finished before                  returning.            Returns:              The created `Job^`.          """          from ._task_manager_factory import _TaskManagerFactory            return _TaskManagerFactory._build_manager()._submit(self, callbacks, force, wait, timeout)        def get_label(self) -> str:          """Returns the task simple label prefixed by its owner label.            Returns:              The label of the task as a string.          """          return self._get_label()        def get_simple_label(self) -> str:          """Returns the task simple label.            Returns:              The simple label of the task as a string.          """          return self._get_simple_label()      @_make_event.register(Task)  def _make_event_for_task(      task: Task,      operation: EventOperation,      /,      attribute_name: Optional[str] = None,      attribute_value: Optional[Any] = None,      **kwargs,  ) -> Event:      metadata = {"version": task.version, "config_id": task.config_id, **kwargs}      return Event(          entity_type=EventEntityType.TASK,          entity_id=task.id,          operation=operation,          attribute_name=attribute_name,          attribute_value=attribute_value,          metadata=metadata,      )


from typing import Callable, List, Optional, Type, Union    from taipy.config import Config  from taipy.config.common.scope import Scope    from .._entity._entity_ids import _EntityIds  from .._manager._manager import _Manager  from .._orchestrator._abstract_orchestrator import _AbstractOrchestrator  from .._repository._abstract_repository import _AbstractRepository  from .._version._version_manager_factory import _VersionManagerFactory  from .._version._version_mixin import _VersionMixin  from ..common.warn_if_inputs_not_ready import _warn_if_inputs_not_ready  from ..config.task_config import TaskConfig  from ..cycle.cycle_id import CycleId  from ..data._data_manager_factory import _DataManagerFactory  from ..exceptions.exceptions import NonExistingTask  from ..notification import EventEntityType, EventOperation, Notifier, _make_event  from ..scenario.scenario_id import ScenarioId  from ..sequence.sequence_id import SequenceId  from ..task.task import Task  from .task_id import TaskId      class _TaskManager(_Manager[Task], _VersionMixin):      _ENTITY_NAME = Task.__name__      _repository: _AbstractRepository      _EVENT_ENTITY_TYPE = EventEntityType.TASK        @classmethod      def _orchestrator(cls) -> Type[_AbstractOrchestrator]:          from .._orchestrator._orchestrator_factory import _OrchestratorFactory            return _OrchestratorFactory._build_orchestrator()        @classmethod      def _set(cls, task: Task):          cls.__save_data_nodes(task.input.values())          cls.__save_data_nodes(task.output.values())          super()._set(task)        @classmethod      def _bulk_get_or_create(          cls,          task_configs: List[TaskConfig],          cycle_id: Optional[CycleId] = None,          scenario_id: Optional[ScenarioId] = None,      ) -> List[Task]:          data_node_configs = set()          for task_config in task_configs:              data_node_configs.update([Config.data_nodes[dnc.id] for dnc in task_config.input_configs])              data_node_configs.update([Config.data_nodes[dnc.id] for dnc in task_config.output_configs])            data_nodes = _DataManagerFactory._build_manager()._bulk_get_or_create(              list(data_node_configs), cycle_id, scenario_id          )          tasks_configs_and_owner_id = []          for task_config in task_configs:              task_dn_configs = [Config.data_nodes[dnc.id] for dnc in task_config.output_configs] + [                  Config.data_nodes[dnc.id] for dnc in task_config.input_configs              ]              task_config_data_nodes = [data_nodes[dn_config] for dn_config in task_dn_configs]              scope = min(dn.scope for dn in task_config_data_nodes) if len(task_config_data_nodes) != 0 else Scope.GLOBAL              owner_id: Union[Optional[SequenceId], Optional[ScenarioId], Optional[CycleId]]              if scope == Scope.SCENARIO:                  owner_id = scenario_id              elif scope == Scope.CYCLE:                  owner_id = cycle_id              else:                  owner_id = None                tasks_configs_and_owner_id.append((task_config, owner_id))            tasks_by_config = cls._repository._get_by_configs_and_owner_ids(  # type: ignore              tasks_configs_and_owner_id, cls._build_filters_with_version(None)          )            tasks = []          for task_config, owner_id in tasks_configs_and_owner_id:              if task := tasks_by_config.get((task_config, owner_id)):                  tasks.append(task)              else:                  version = _VersionManagerFactory._build_manager()._get_latest_version()                  inputs = [                      data_nodes[input_config]                      for input_config in [Config.data_nodes[dnc.id] for dnc in task_config.input_configs]                  ]                  outputs = [                      data_nodes[output_config]                      for output_config in [Config.data_nodes[dnc.id] for dnc in task_config.output_configs]                  ]                  skippable = task_config.skippable                  task = Task(                      str(task_config.id),                      dict(**task_config._properties),                      task_config.function,                      inputs,                      outputs,                      owner_id=owner_id,                      parent_ids=set(),                      version=version,                      skippable=skippable,                  )                  for dn in set(inputs + outputs):                      dn._parent_ids.update([task.id])                  cls._set(task)                  Notifier.publish(_make_event(task, EventOperation.CREATION))                  tasks.append(task)          return tasks        @classmethod      def _get_all(cls, version_number: Optional[str] = None) -> List[Task]:          """          Returns all entities.          """          filters = cls._build_filters_with_version(version_number)          return cls._repository._load_all(filters)        @classmethod      def __save_data_nodes(cls, data_nodes):          data_manager = _DataManagerFactory._build_manager()          for i in data_nodes:              data_manager._set(i)        @classmethod      def _hard_delete(cls, task_id: TaskId):          task = cls._get(task_id)          entity_ids_to_delete = cls._get_children_entity_ids(task)          entity_ids_to_delete.task_ids.add(task.id)          cls._delete_entities_of_multiple_types(entity_ids_to_delete)        @classmethod      def _get_children_entity_ids(cls, task: Task):          entity_ids = _EntityIds()            from ..job._job_manager_factory import _JobManagerFactory          from ..submission._submission_manager_factory import _SubmissionManagerFactory            jobs = _JobManagerFactory._build_manager()._get_all()            for job in jobs:              if job.task.id == task.id:                  entity_ids.job_ids.add(job.id)            submissions = _SubmissionManagerFactory._build_manager()._get_all()          submitted_entity_ids = list(entity_ids.task_ids)          for submission in submissions:              if submission.entity_id in submitted_entity_ids:                  entity_ids.submission_ids.add(submission.id)            return entity_ids        @classmethod      def _is_submittable(cls, task: Union[Task, TaskId]) -> bool:          if isinstance(task, str):              task = cls._get(task)          return isinstance(task, Task) and all(input_dn.is_ready_for_reading for input_dn in task.input.values())        @classmethod      def _submit(          cls,          task: Union[TaskId, Task],          callbacks: Optional[List[Callable]] = None,          force: bool = False,          wait: bool = False,          timeout: Optional[Union[float, int]] = None,          check_inputs_are_ready: bool = True,      ):          task_id = task.id if isinstance(task, Task) else task          task = cls._get(task_id)          if task is None:              raise NonExistingTask(task_id)          if check_inputs_are_ready:              _warn_if_inputs_not_ready(task.input.values())          job = cls._orchestrator().submit_task(task, callbacks=callbacks, force=force, wait=wait, timeout=timeout)          Notifier.publish(_make_event(task, EventOperation.SUBMISSION))          return job        @classmethod      def _get_by_config_id(cls, config_id: str, version_number: Optional[str] = None) -> List[Task]:          """          Get all tasks by its config id.          """          filters = cls._build_filters_with_version(version_number)          if not filters:              filters = [{}]          for fil in filters:              fil.update({"config_id": config_id})          return cls._repository._load_all(filters)


from typing import Type    from .._manager._manager_factory import _ManagerFactory  from ..common._utils import _load_fct  from ._task_fs_repository import _TaskFSRepository  from ._task_manager import _TaskManager  from ._task_sql_repository import _TaskSQLRepository      class _TaskManagerFactory(_ManagerFactory):        __REPOSITORY_MAP = {"default": _TaskFSRepository, "sql": _TaskSQLRepository}        @classmethod      def _build_manager(cls) -> Type[_TaskManager]:  # type: ignore          if cls._using_enterprise():              task_manager = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + ".task._task_manager", "_TaskManager"              )  # type: ignore              build_repository = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + ".task._task_manager_factory", "_TaskManagerFactory"              )._build_repository  # type: ignore          else:              task_manager = _TaskManager              build_repository = cls._build_repository          task_manager._repository = build_repository()  # type: ignore          return task_manager  # type: ignore        @classmethod      def _build_repository(cls):          return cls._get_repository_with_repo_map(cls.__REPOSITORY_MAP)()


from .._repository._abstract_converter import _AbstractConverter  from .._version._utils import _migrate_entity  from ..common._utils import _load_fct  from ..data._data_manager_factory import _DataManagerFactory  from ..exceptions import NonExistingDataNode  from ..task._task_model import _TaskModel  from ..task.task import Task  from .task import TaskId      class _TaskConverter(_AbstractConverter):      @classmethod      def _entity_to_model(cls, task: Task) -> _TaskModel:          return _TaskModel(              id=task.id,              owner_id=task.owner_id,              parent_ids=list(task._parent_ids),              config_id=task.config_id,              input_ids=cls.__to_ids(task.input.values()),              function_name=task._function.__name__,              function_module=task._function.__module__,              output_ids=cls.__to_ids(task.output.values()),              version=task._version,              skippable=task._skippable,              properties=task._properties.data.copy(),          )        @classmethod      def _model_to_entity(cls, model: _TaskModel) -> Task:          task = Task(              id=TaskId(model.id),              owner_id=model.owner_id,              parent_ids=set(model.parent_ids),              config_id=model.config_id,              function=_load_fct(model.function_module, model.function_name),              input=cls.__to_data_nodes(model.input_ids),              output=cls.__to_data_nodes(model.output_ids),              version=model.version,              skippable=model.skippable,              properties=model.properties,          )          return _migrate_entity(task)        @staticmethod      def __to_ids(data_nodes):          return [i.id for i in data_nodes]        @staticmethod      def __to_data_nodes(data_nodes_ids):          data_nodes = []          data_manager = _DataManagerFactory._build_manager()          for _id in data_nodes_ids:              if data_node := data_manager._get(_id):                  data_nodes.append(data_node)              else:                  raise NonExistingDataNode(_id)          return data_nodes


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  from .._repository._sql_repository import _SQLRepository  from ._task_converter import _TaskConverter  from ._task_model import _TaskModel      class _TaskSQLRepository(_SQLRepository):      def __init__(self):          super().__init__(model_type=_TaskModel, converter=_TaskConverter)


from dataclasses import dataclass  from typing import Any, Dict, List, Optional    from sqlalchemy import JSON, Boolean, Column, String, Table    from .._repository._base_taipy_model import _BaseModel  from .._repository.db._sql_base_model import mapper_registry      @mapper_registry.mapped  @dataclass  class _TaskModel(_BaseModel):      __table__ = Table(          "task",          mapper_registry.metadata,          Column("id", String, primary_key=True),          Column("owner_id", String),          Column("parent_ids", JSON),          Column("config_id", String),          Column("input_ids", JSON),          Column("function_name", String),          Column("function_module", String),          Column("output_ids", JSON),          Column("version", String),          Column("skippable", Boolean),          Column("properties", JSON),      )      id: str      owner_id: Optional[str]      parent_ids: List[str]      config_id: str      input_ids: List[str]      function_name: str      function_module: str      output_ids: List[str]      version: str      skippable: bool      properties: Dict[str, Any]        @staticmethod      def from_dict(data: Dict[str, Any]):          return _TaskModel(              id=data["id"],              owner_id=data.get("owner_id"),              parent_ids=_BaseModel._deserialize_attribute(data.get("parent_ids", [])),              config_id=data["config_id"],              input_ids=_BaseModel._deserialize_attribute(data["input_ids"]),              function_name=data["function_name"],              function_module=data["function_module"],              output_ids=_BaseModel._deserialize_attribute(data["output_ids"]),              version=data["version"],              skippable=data["skippable"],              properties=_BaseModel._deserialize_attribute(data["properties"] if "properties" in data.keys() else {}),          )        def to_list(self):          return [              self.id,              self.owner_id,              _BaseModel._serialize_attribute(self.parent_ids),              self.config_id,              _BaseModel._serialize_attribute(self.input_ids),              self.function_name,              self.function_module,              _BaseModel._serialize_attribute(self.output_ids),              self.version,              self.skippable,              _BaseModel._serialize_attribute(self.properties),          ]


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  from .._repository._filesystem_repository import _FileSystemRepository  from ._task_converter import _TaskConverter  from ._task_model import _TaskModel      class _TaskFSRepository(_FileSystemRepository):      def __init__(self):          super().__init__(model_type=_TaskModel, converter=_TaskConverter, dir_name="tasks")


from typing import NewType    TaskId = NewType("TaskId", str)  TaskId.__doc__ = """Type that holds a `Task^` identifier."""


from dataclasses import dataclass  from typing import Any, Dict, List    from sqlalchemy import JSON, Boolean, Column, Enum, String, Table    from .._repository._base_taipy_model import _BaseModel  from .._repository.db._sql_base_model import mapper_registry  from .job_id import JobId  from .status import Status      @mapper_registry.mapped  @dataclass  class _JobModel(_BaseModel):      __table__ = Table(          "job",          mapper_registry.metadata,          Column("id", String, primary_key=True),          Column("task_id", String),          Column("status", Enum(Status)),          Column("force", Boolean),          Column("submit_id", String),          Column("submit_entity_id", String),          Column("creation_date", String),          Column("subscribers", JSON),          Column("stacktrace", JSON),          Column("version", String),      )      id: JobId      task_id: str      status: Status      force: bool      submit_id: str      submit_entity_id: str      creation_date: str      subscribers: List[Dict]      stacktrace: List[str]      version: str        @staticmethod      def from_dict(data: Dict[str, Any]):          return _JobModel(              id=data["id"],              task_id=data["task_id"],              status=Status._from_repr(data["status"]),              force=data["force"],              submit_id=data["submit_id"],              submit_entity_id=data["submit_entity_id"],              creation_date=data["creation_date"],              subscribers=_BaseModel._deserialize_attribute(data["subscribers"]),              stacktrace=_BaseModel._deserialize_attribute(data["stacktrace"]),              version=data["version"],          )        def to_list(self):          return [              self.id,              self.task_id,              repr(self.status),              self.force,              self.submit_id,              self.submit_entity_id,              self.creation_date,              _BaseModel._serialize_attribute(self.subscribers),              _BaseModel._serialize_attribute(self.stacktrace),              self.version,          ]


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  from .._repository._filesystem_repository import _FileSystemRepository  from ._job_converter import _JobConverter  from ._job_model import _JobModel      class _JobFSRepository(_FileSystemRepository):      def __init__(self):          super().__init__(model_type=_JobModel, converter=_JobConverter, dir_name="jobs")


__all__ = ["Job"]    import traceback  from datetime import datetime  from typing import Any, Callable, List, Optional    from taipy.logger._taipy_logger import _TaipyLogger    from .._entity._entity import _Entity  from .._entity._labeled import _Labeled  from .._entity._reload import _self_reload, _self_setter  from .._version._version_manager_factory import _VersionManagerFactory  from ..common._utils import _fcts_to_dict  from ..notification.event import Event, EventEntityType, EventOperation, _make_event  from ..task.task import Task  from .job_id import JobId  from .status import Status      def _run_callbacks(fn):      def __run_callbacks(job):          fn(job)          for fct in job._subscribers:              fct(job)        return __run_callbacks      class Job(_Entity, _Labeled):      """Execution of a `Task^`.        A job handles the status of the execution, contains the stacktrace of exceptions that were      raised during the execution, and notifies subscribers on status change.        Attributes:          id (str): The identifier of this job.          task (Task^): The task of this job.          force (bool): Enforce the job's execution whatever the output data nodes are in cache or              not.          status (Status^): The current status of this job.          creation_date (datetime): The date of this job's creation.          stacktrace (List[str]): The list of stacktraces of the exceptions raised during the execution.          version (str): The string indicates the application version of the job to instantiate. If not provided,              the latest version is used.      """        _MANAGER_NAME = "job"      _ID_PREFIX = "JOB"        def __init__(self, id: JobId, task: Task, submit_id: str, submit_entity_id: str, force=False, version=None):          self.id = id          self._task = task          self._force = force          self._status = Status.SUBMITTED          self._creation_date = datetime.now()          self._submit_id: str = submit_id          self._submit_entity_id: str = submit_entity_id          self._subscribers: List[Callable] = []          self._stacktrace: List[str] = []          self.__logger = _TaipyLogger._get_logger()          self._version = version or _VersionManagerFactory._build_manager()._get_latest_version()        def get_event_context(self):          return {"task_config_id": self._task.config_id}        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def task(self):          return self._task        @task.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def task(self, val):          self._task = val        @property      def owner_id(self) -> str:          return self.task.id        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def force(self):          return self._force        @force.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def force(self, val):          self._force = val        @property      def submit_id(self):          return self._submit_id        @property      def submit_entity_id(self):          return self._submit_entity_id        @property  # type: ignore      def submit_entity(self):          from ..taipy import get as tp_get            return tp_get(self._submit_entity_id)        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def status(self):          return self._status        @status.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def status(self, val):          self._status = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def creation_date(self):          return self._creation_date        @creation_date.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def creation_date(self, val):          self._creation_date = val        @property      def stacktrace(self) -> List[str]:          return self._stacktrace        @property      def version(self):          return self._version        def __contains__(self, task: Task):          return self.task.id == task.id        def __lt__(self, other):          return self.creation_date.timestamp() < other.creation_date.timestamp()        def __le__(self, other):          return self.creation_date.timestamp() <= other.creation_date.timestamp()        def __gt__(self, other):          return self.creation_date.timestamp() > other.creation_date.timestamp()        def __ge__(self, other):          return self.creation_date.timestamp() >= other.creation_date.timestamp()        def __eq__(self, other):          return self.id == other.id        @_run_callbacks      def blocked(self):          """Set the status to _blocked_ and notify subscribers."""          self.status = Status.BLOCKED        @_run_callbacks      def pending(self):          """Set the status to _pending_ and notify subscribers."""          self.status = Status.PENDING        @_run_callbacks      def running(self):          """Set the status to _running_ and notify subscribers."""          self.status = Status.RUNNING        @_run_callbacks      def canceled(self):          """Set the status to _canceled_ and notify subscribers."""          self.status = Status.CANCELED        @_run_callbacks      def abandoned(self):          """Set the status to _abandoned_ and notify subscribers."""          self.status = Status.ABANDONED        @_run_callbacks      def failed(self):          """Set the status to _failed_ and notify subscribers."""          self.status = Status.FAILED        @_run_callbacks      def completed(self):          """Set the status to _completed_ and notify subscribers."""          self.status = Status.COMPLETED        @_run_callbacks      def skipped(self):          """Set the status to _skipped_ and notify subscribers."""          self.status = Status.SKIPPED        def is_failed(self) -> bool:          """Indicate if the job has failed.            Returns:              True if the job has failed.          """          return self.status == Status.FAILED        def is_blocked(self) -> bool:          """Indicate if the job is blocked.            Returns:              True if the job is blocked.          """          return self.status == Status.BLOCKED        def is_canceled(self) -> bool:          """Indicate if the job was canceled.            Returns:              True if the job was canceled.          """          return self.status == Status.CANCELED        def is_abandoned(self) -> bool:          """Indicate if the job was abandoned.            Returns:              True if the job was abandoned.          """          return self.status == Status.ABANDONED        def is_submitted(self) -> bool:          """Indicate if the job is submitted.            Returns:              True if the job is submitted.          """          return self.status == Status.SUBMITTED        def is_completed(self) -> bool:          """Indicate if the job has completed.            Returns:              True if the job has completed.          """          return self.status == Status.COMPLETED        def is_skipped(self) -> bool:          """Indicate if the job was skipped.            Returns:              True if the job was skipped.          """          return self.status == Status.SKIPPED        def is_running(self) -> bool:          """Indicate if the job is running.            Returns:              True if the job is running.          """          return self.status == Status.RUNNING        def is_pending(self) -> bool:          """Indicate if the job is pending.            Returns:              True if the job is pending.          """          return self.status == Status.PENDING        def is_finished(self) -> bool:          """Indicate if the job is finished.            Returns:              True if the job is finished.          """          return self.is_completed() or self.is_failed() or self.is_canceled() or self.is_skipped() or self.is_abandoned()        def _is_finished(self) -> bool:          """Indicate if the job is finished. This function will not triggered the persistency feature like is_finished().            Returns:              True if the job is finished.          """          return self._status in [Status.COMPLETED, Status.FAILED, Status.CANCELED, Status.SKIPPED, Status.ABANDONED]        def _on_status_change(self, *functions):          """Get a notification when the status of the job changes.            Job are assigned different statuses (_submitted_, _pending_, etc.) before being finished.          You can be triggered on each change through this function except for the _submitted_          status.            Parameters:              functions: Callables that will be called on each status change.          """          functions = list(functions)          function = functions.pop()          self._subscribers.append(function)            if self.status != Status.SUBMITTED:              function(self)            if functions:              self._on_status_change(*functions)        def update_status(self, exceptions):          """Update the job status based on the success or the failure of its execution."""          if exceptions:              self.failed()              self.__logger.error(f" {len(exceptions)} errors occurred during execution of job {self.id}")              for e in exceptions:                  st = "".join(traceback.format_exception(type(e), value=e, tb=e.__traceback__))                  self._stacktrace.append(st)                  self.__logger.error(st)          else:              self.completed()              self.__logger.info(f"job {self.id} is completed.")        def __hash__(self):          return hash(self.id)        def _unlock_edit_on_outputs(self):          for dn in self.task.output.values():              dn.unlock_edit()        @staticmethod      def _serialize_subscribers(subscribers: List) -> List:          return _fcts_to_dict(subscribers)        def get_label(self) -> str:          """Returns the job simple label prefixed by its owner label.            Returns:              The label of the job as a string.          """          return self._get_label()        def get_simple_label(self) -> str:          """Returns the job simple label.            Returns:              The simple label of the job as a string.          """          return self._get_simple_label()        def is_deletable(self) -> bool:          """Indicate if the job can be deleted.            Returns:              True if the job can be deleted. False otherwise.          """          from ... import core as tp            return tp.is_deletable(self)      @_make_event.register(Job)  def _make_event_for_job(      job: Job,      operation: EventOperation,      /,      attribute_name: Optional[str] = None,      attribute_value: Optional[Any] = None,      **kwargs,  ) -> Event:      metadata = {          "creation_date": job._creation_date,          "task_config_id": job._task.config_id,          "version": job._version,          **kwargs,      }      return Event(          entity_type=EventEntityType.JOB,          entity_id=job.id,          operation=operation,          attribute_name=attribute_name,          attribute_value=attribute_value,          metadata=metadata,      )


import uuid  from typing import Callable, Iterable, List, Optional, Union    from .._manager._manager import _Manager  from .._repository._abstract_repository import _AbstractRepository  from .._version._version_manager_factory import _VersionManagerFactory  from .._version._version_mixin import _VersionMixin  from ..exceptions.exceptions import JobNotDeletedException  from ..notification import EventEntityType, EventOperation, Notifier, _make_event  from ..task.task import Task  from .job import Job  from .job_id import JobId      class _JobManager(_Manager[Job], _VersionMixin):      _ENTITY_NAME = Job.__name__      _ID_PREFIX = "JOB_"      _repository: _AbstractRepository      _EVENT_ENTITY_TYPE = EventEntityType.JOB        @classmethod      def _get_all(cls, version_number: Optional[str] = None) -> List[Job]:          """          Returns all entities.          """          filters = cls._build_filters_with_version(version_number)          return cls._repository._load_all(filters)        @classmethod      def _create(          cls, task: Task, callbacks: Iterable[Callable], submit_id: str, submit_entity_id: str, force=False      ) -> Job:          version = _VersionManagerFactory._build_manager()._get_latest_version()          job = Job(              id=JobId(f"{Job._ID_PREFIX}_{task.config_id}_{uuid.uuid4()}"),              task=task,              submit_id=submit_id,              submit_entity_id=submit_entity_id,              force=force,              version=version,          )          cls._set(job)          Notifier.publish(_make_event(job, EventOperation.CREATION))          job._on_status_change(*callbacks)          return job        @classmethod      def _delete(cls, job: Job, force=False):          if job.is_finished() or force:              super()._delete(job.id)              from .._orchestrator._dispatcher._job_dispatcher import _JobDispatcher                _JobDispatcher._pop_dispatched_process(job.id)          else:              err = JobNotDeletedException(job.id)              cls._logger.warning(err)              raise err        @classmethod      def _cancel(cls, job: Union[str, Job]):          job = cls._get(job) if isinstance(job, str) else job            from .._orchestrator._orchestrator_factory import _OrchestratorFactory            _OrchestratorFactory._build_orchestrator().cancel_job(job)        @classmethod      def _get_latest(cls, task: Task) -> Optional[Job]:          jobs_of_task = list(filter(lambda job: task in job, cls._get_all()))          if len(jobs_of_task) == 0:              return None          if len(jobs_of_task) == 1:              return jobs_of_task[0]          else:              return max(jobs_of_task)        @classmethod      def _is_deletable(cls, job: Union[Job, JobId]) -> bool:          if isinstance(job, str):              job = cls._get(job)          if job.is_finished():              return True          return False


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.


from typing import NewType    JobId = NewType("JobId", str)  JobId.__doc__ = """Type that holds a `Job^` identifier."""


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  from .._repository._sql_repository import _SQLRepository  from ._job_converter import _JobConverter  from ._job_model import _JobModel      class _JobSQLRepository(_SQLRepository):      def __init__(self):          super().__init__(model_type=_JobModel, converter=_JobConverter)


from datetime import datetime  from typing import List    from .._repository._abstract_converter import _AbstractConverter  from ..common._utils import _fcts_to_dict, _load_fct  from ..exceptions import InvalidSubscriber  from ..job._job_model import _JobModel  from ..job.job import Job  from ..task._task_manager_factory import _TaskManagerFactory      class _JobConverter(_AbstractConverter):      @classmethod      def _entity_to_model(cls, job: Job) -> _JobModel:          return _JobModel(              job.id,              job._task.id,              job._status,              job._force,              job.submit_id,              job.submit_entity_id,              job._creation_date.isoformat(),              cls.__serialize_subscribers(job._subscribers),              job._stacktrace,              version=job._version,          )        @classmethod      def _model_to_entity(cls, model: _JobModel) -> Job:          task_manager = _TaskManagerFactory._build_manager()          task_repository = task_manager._repository            job = Job(              id=model.id,              task=task_repository._load(model.task_id),              submit_id=model.submit_id,              submit_entity_id=model.submit_entity_id,              version=model.version,          )            job._status = model.status  # type: ignore          job._force = model.force  # type: ignore          job._creation_date = datetime.fromisoformat(model.creation_date)  # type: ignore          for it in model.subscribers:              try:                  fct_module, fct_name = it.get("fct_module"), it.get("fct_name")                  job._subscribers.append(_load_fct(fct_module, fct_name))  # type: ignore              except AttributeError:                  raise InvalidSubscriber(f"The subscriber function {it.get('fct_name')} cannot be loaded.")          job._stacktrace = model.stacktrace            return job        @staticmethod      def __serialize_subscribers(subscribers: List) -> List:          return _fcts_to_dict(subscribers)


from ..common._repr_enum import _ReprEnum      class Status(_ReprEnum):      """Execution status of a `Job^`.        It is implemented as an enumeration.        The possible values are:        - `SUBMITTED`: A `SUBMITTED` job has been submitted for execution but not processed yet by          the orchestrator.        - `PENDING`: A `PENDING` job has been enqueued by the orchestrator. It is waiting for an executor          to be available for its execution.        - `BLOCKED`: A `BLOCKED` job has been blocked because its input data nodes are not ready yet.          It is waiting for the completion of another `Job^`        - `RUNNING`: A `RUNNING` job is currently executed by a dedicated executor.        - `CANCELED`: A `CANCELED` job has been submitted but its execution has been canceled.        - `FAILED`: A `FAILED` job raised an exception during its execution.        - `COMPLETED`: A `COMPLETED` job has successfully been executed.        - `SKIPPED`: A `SKIPPED` job has not been executed because its outputs were already computed.        - `ABANDONED`: An `ABANDONED` job has not been executed because it depends on a job that could not complete (          cancelled, failed, or abandoned).      """        SUBMITTED = 1      BLOCKED = 2      PENDING = 3      RUNNING = 4      CANCELED = 5      FAILED = 6      COMPLETED = 7      SKIPPED = 8      ABANDONED = 9


from typing import Type    from .._manager._manager_factory import _ManagerFactory  from ..common._utils import _load_fct  from ._job_fs_repository import _JobFSRepository  from ._job_manager import _JobManager  from ._job_sql_repository import _JobSQLRepository      class _JobManagerFactory(_ManagerFactory):        __REPOSITORY_MAP = {"default": _JobFSRepository, "sql": _JobSQLRepository}        @classmethod      def _build_manager(cls) -> Type[_JobManager]:  # type: ignore          if cls._using_enterprise():              job_manager = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + ".job._job_manager", "_JobManager"              )  # type: ignore              build_repository = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + ".job._job_manager_factory", "_JobManagerFactory"              )._build_repository  # type: ignore          else:              job_manager = _JobManager              build_repository = cls._build_repository          job_manager._repository = build_repository()  # type: ignore          return job_manager  # type: ignore        @classmethod      def _build_repository(cls):          return cls._get_repository_with_repo_map(cls.__REPOSITORY_MAP)()