class_docstr: A graph of processing steps.
  
  The processing steps can have multiple parents, and multiple children (next processing steps, found automatically
    by traversing the graph).
  The graph can have multiple roots.
  
  Args:
      specification (ProcessingGraphSpecification): The specification of the graph.
  
  Raises:
      ValueError: If the graph is not a DAG.
      ValueError: If a processing step provides dataset config names but its input type is not 'dataset', or if a
        processing step provides config split names but its input type is not 'config'.
      ValueError: If a root processing step (ie. a processing step with no parent) is not a dataset processing step.
class_docstr_tok: ["A","graph","of","processing","steps",".","The","processing","steps","can","have","multiple","parents",",","and","multiple","children","(","next","processing","steps",",","found","automatically","by","traversing","the","graph",")",".","The","graph","can","have","multiple","roots",".","Args",":","specification","(","ProcessingGraphSpecification",")",":","The","specification","of","the","graph",".","Raises",":","ValueError",":","If","the","graph","is","not","a","DAG",".","ValueError",":","If","a","processing","step","provides","dataset","config","names","but","its","input","type","is","not","'dataset","'",",","or","if","a","processing","step","provides","config","split","names","but","its","input","type","is","not","'config","'",".","ValueError",":","If","a","root","processing","step","(","ie",".","a","processing","step","with","no","parent",")","is","not","a","dataset","processing","step","."]
docstr: Get a processing step by its job type.
  
  The returned processing step is a copy of the original one, so that it can be modified without affecting the
  original one.
  
  Args:
      job_type (str): The job type of the processing step
  
  Returns:
      ProcessingStep: The processing step
docstr_tok: ["Get","a","processing","step","by","its","job","type",".","The","returned","processing","step","is","a","copy","of","the","original","one",",","so","that","it","can","be","modified","without","affecting","the","original","one",".","Args",":","job_type","(","str",")",":","The","job","type","of","the","processing","step","Returns",":","ProcessingStep",":","The","processing","step"]
code: def get_processing_step_by_job_type(
      self, job_type: str
  ) -> ProcessingStep:
      """
      Get a processing step by its job type.
  
      The returned processing step is a copy of the original one, so that it can be modified without affecting the
      original one.
  
      Args:
          job_type (str): The job type of the processing step
  
      Returns:
          ProcessingStep: The processing step
      """
      # for now: the job_type is just an alias for the processing step name
      return self.get_processing_step(job_type)
  
imports: from __future__ import annotations
  from collections.abc import Mapping
  from dataclasses import dataclass, field
  from typing import Any, Literal, Optional, TypedDict, Union, get_args
  import networkx
  from libcommon.constants import DEFAULT_DIFFICULTY, DEFAULT_INPUT_TYPE, DEFAULT_JOB_RUNNER_VERSION, MIN_BYTES_FOR_BONUS_DIFFICULTY
  from libcommon.utils import inputs_to_string
label_desc: Use image node_id 3 for calling the ProcessingGraph obj's underlying member method code with example usage: obj.get_processing_step_by_job_type(job_type) and returns: self