class_docstr: Timetable that schedules DAG runs at specific listed datetimes.
  
  Suitable for predictable but truly irregular scheduling such as sporting events.
  
  :param event_dates: List of datetimes for the DAG to run at. Duplicates will be ignored. Must be finite
                      and of reasonable size as it will be loaded in its entirety.
  :param restrict_to_events: Whether manual runs should use the most recent event or
      the current time
  :param presorted: if True, event_dates will be assumed to be in ascending order. Provides modest
      performance improvement for larger lists of event_dates.
  :param description: A name for the timetable to display in the UI. Default None will be shown as
                      "X Events" where X is the len of event_dates
class_docstr_tok: ["Timetable","that","schedules","DAG","runs","at","specific","listed","datetimes",".","Suitable","for","predictable","but","truly","irregular","scheduling","such","as","sporting","events",".",":","param","event_dates",":","List","of","datetimes","for","the","DAG","to","run","at",".","Duplicates","will","be","ignored",".","Must","be","finite","and","of","reasonable","size","as","it","will","be","loaded","in","its","entirety",".",":","param","restrict_to_events",":","Whether","manual","runs","should","use","the","most","recent","event","or","the","current","time",":","param","presorted",":","if","True",",","event_dates","will","be","assumed","to","be","in","ascending","order",".","Provides","modest","performance","improvement","for","larger","lists","of","event_dates",".",":","param","description",":","A","name","for","the","timetable","to","display","in","the","UI",".","Default","None","will","be","shown","as","``","X","Events","''","where","X","is","the","len","of","event_dates"]
code: def next_dagrun_info(
      self,
      *,
      last_automated_data_interval: DataInterval | None,
      restriction: TimeRestriction,
  ) -> DagRunInfo | None:
      earliest = restriction.earliest
      if not restriction.catchup:
          current_time = timezone.utcnow()
          if earliest is None or current_time > earliest:
              earliest = pendulum.instance(current_time)
  
      for next_event in self.event_dates:
          if earliest and next_event < earliest:
              continue
          if (
              last_automated_data_interval
              and next_event <= last_automated_data_interval.end
          ):
              continue
          break
      else:
          # We need to return None if self.event_dates is empty or,
          # if not empty, when no suitable event can be found.
          return None
  
      if (
          restriction.latest is not None
          and next_event > restriction.latest
      ):
          return None
  
      return DagRunInfo.exact(next_event)
  
file_path: airflow/airflow/timetables/events.py
imports: from __future__ import annotations
  import itertools
  from typing import TYPE_CHECKING, Iterable
  import pendulum
  from airflow.timetables.base import DagRunInfo, DataInterval, Timetable
  from airflow.utils import timezone
label_desc: Use image node_id 4 for calling the EventsTimetable obj's underlying member method code with example usage: obj.next_dagrun_info() and returns: DagRunInfo, None, None