nodes: 0, module; 1, function_definition; 2, function_name:_checkpoint; 3, parameters; 4, block; 5, identifier:self; 6, identifier:stage; 7, expression_statement; 8, comment:# For null stage, short-circuit and indicate no file write.; 9, comment:# This handles case in which we're timestamping prospectively and; 10, comment:# previously weren't in a stage.; 11, if_statement; 12, try_statement; 13, expression_statement; 14, if_statement; 15, return_statement; 16, comment:"""
          Decide whether to stop processing of a pipeline. This is the hook
  
          A pipeline can report various "checkpoints" as sort of status markers
          that designate the logical processing phase that's just been completed.
          The initiation of a pipeline can preordain one of those as a "stopping
          point" that when reached, should stop the pipeline's execution.
  
          :param pypiper.Stage | str stage: Pipeline processing stage/phase just completed.
          :return bool: Whether a checkpoint was created (i.e., whether it didn't
              already exist)
          :raise ValueError: If the stage is specified as an absolute filepath,
              and that path indicates a location that's not immediately within
              the main output folder, raise a ValueError.
          """; 17, comparison_operator:stage is None; 18, block; 19, block; 20, except_clause; 21, else_clause; 22, call; 23, call; 24, block; 25, else_clause; 26, call; 27, identifier:stage; 28, None; 29, return_statement; 30, expression_statement; 31, identifier:AttributeError; 32, comment:# Maybe we have a raw function, not a stage.; 33, block; 34, block; 35, identifier:print; 36, argument_list; 37, attribute; 38, argument_list; 39, expression_statement; 40, block; 41, attribute; 42, argument_list; 43, False; 44, assignment; 45, if_statement; 46, if_statement; 47, expression_statement; 48, call; 49, attribute; 50, identifier:isabs; 51, identifier:stage; 52, assignment; 53, expression_statement; 54, identifier:self; 55, identifier:_touch_checkpoint; 56, identifier:check_fpath; 57, identifier:is_checkpoint; 58, attribute; 59, call; 60, block; 61, else_clause; 62, not_operator; 63, block; 64, assignment; 65, attribute; 66, argument_list; 67, identifier:os; 68, identifier:path; 69, identifier:check_fpath; 70, identifier:stage; 71, assignment; 72, identifier:stage; 73, identifier:checkpoint; 74, identifier:hasattr; 75, argument_list; 76, expression_statement; 77, comment:# Maybe we have a stage name not a Stage.; 78, comment:# In that case, we can proceed as-is, with downstream; 79, comment:# processing handling Stage vs. stage name disambiguation.; 80, comment:# Here, though, warn about inputs that appear filename/path-like.; 81, comment:# We can't rely on raw text being a filepath or filename,; 82, comment:# because that would ruin the ability to pass stage name rather; 83, comment:# than actual stage. We can issue a warning message based on the; 84, comment:# improbability of a stage name containing the '.' that would; 85, comment:# be expected to characterize the extension of a file name/path.; 86, block; 87, identifier:is_checkpoint; 88, expression_statement; 89, return_statement; 90, identifier:stage; 91, attribute; 92, string:"Checkpointing: '{}'"; 93, identifier:format; 94, identifier:stage; 95, identifier:check_fpath; 96, call; 97, identifier:stage; 98, string:"__call__"; 99, assignment; 100, expression_statement; 101, if_statement; 102, call; 103, False; 104, identifier:stage; 105, identifier:name; 106, identifier:checkpoint_filepath; 107, argument_list; 108, identifier:stage; 109, attribute; 110, assignment; 111, boolean_operator; 112, block; 113, identifier:print; 114, argument_list; 115, identifier:stage; 116, keyword_argument; 117, identifier:stage; 118, identifier:__name__; 119, pattern_list; 120, call; 121, identifier:ext; 122, comparison_operator:"." not in base; 123, expression_statement; 124, call; 125, identifier:pm; 126, identifier:self; 127, identifier:base; 128, identifier:ext; 129, attribute; 130, argument_list; 131, string:"."; 132, identifier:base; 133, call; 134, attribute; 135, argument_list; 136, attribute; 137, identifier:splitext; 138, identifier:stage; 139, identifier:print; 140, argument_list; 141, string:"Not a checkpoint: {}"; 142, identifier:format; 143, identifier:stage; 144, identifier:os; 145, identifier:path; 146, call; 147, attribute; 148, argument_list; 149, concatenated_string; 150, identifier:format; 151, identifier:stage; 152, string:"WARNING: '{}' looks like it may be the name or path of "; 153, string:"a file; for such a checkpoint, use touch_checkpoint."
code: def _checkpoint(self, stage):
          """
          Decide whether to stop processing of a pipeline. This is the hook
  
          A pipeline can report various "checkpoints" as sort of status markers
          that designate the logical processing phase that's just been completed.
          The initiation of a pipeline can preordain one of those as a "stopping
          point" that when reached, should stop the pipeline's execution.
  
          :param pypiper.Stage | str stage: Pipeline processing stage/phase just completed.
          :return bool: Whether a checkpoint was created (i.e., whether it didn't
              already exist)
          :raise ValueError: If the stage is specified as an absolute filepath,
              and that path indicates a location that's not immediately within
              the main output folder, raise a ValueError.
          """
  
          # For null stage, short-circuit and indicate no file write.
          # This handles case in which we're timestamping prospectively and
          # previously weren't in a stage.
          if stage is None:
              return False
  
          try:
              is_checkpoint = stage.checkpoint
          except AttributeError:
              # Maybe we have a raw function, not a stage.
              if hasattr(stage, "__call__"):
                  stage = stage.__name__
              else:
                  # Maybe we have a stage name not a Stage.
                  # In that case, we can proceed as-is, with downstream
                  # processing handling Stage vs. stage name disambiguation.
                  # Here, though, warn about inputs that appear filename/path-like.
                  # We can't rely on raw text being a filepath or filename,
                  # because that would ruin the ability to pass stage name rather
                  # than actual stage. We can issue a warning message based on the
                  # improbability of a stage name containing the '.' that would
                  # be expected to characterize the extension of a file name/path.
                  base, ext = os.path.splitext(stage)
                  if ext and "." not in base:
                      print("WARNING: '{}' looks like it may be the name or path of "
                            "a file; for such a checkpoint, use touch_checkpoint.".
                            format(stage))
          else:
              if not is_checkpoint:
                  print("Not a checkpoint: {}".format(stage))
                  return False
              stage = stage.name
  
          print("Checkpointing: '{}'".format(stage))
          if os.path.isabs(stage):
              check_fpath = stage
          else:
              check_fpath = checkpoint_filepath(stage, pm=self)
          return self._touch_checkpoint(check_fpath)