class_docstr: The Artifact Manager is responsible for handling and transferring artifacts from the local
  client to the server (local/remote).
  
  Parameters
  ----------
  user_id : str, optional
      User ID.
  session_id: str
      An unique identifier of the session which the artifact manager belongs to.
  channel: grpc.Channel
      GRPC Channel instance.
class_docstr_tok: ["The","Artifact","Manager","is","responsible","for","handling","and","transferring","artifacts","from","the","local","client","to","the","server","(","local\/remote",")",".","Parameters","--","--","--","--","--","user_id",":","str",",","optional","User","ID",".","session_id",":","str","An","unique","identifier","of","the","session","which","the","artifact","manager","belongs","to",".","channel",":","grpc.Channel","GRPC","Channel","instance","."]
docstr: Add a number of artifacts to the session.
code: def _add_artifacts(
      self, artifacts: Iterable[Artifact]
  ) -> Iterator[proto.AddArtifactsRequest]:
      """
      Add a number of artifacts to the session.
      """
  
      current_batch: List[Artifact] = []
      current_batch_size = 0
  
      def add_to_batch(dep: Artifact, size: int) -> None:
          nonlocal current_batch
          nonlocal current_batch_size
  
          current_batch.append(dep)
          current_batch_size += size
  
      def write_batch() -> Iterator[proto.AddArtifactsRequest]:
          nonlocal current_batch
          nonlocal current_batch_size
  
          yield from self._add_batched_artifacts(current_batch)
          current_batch = []
          current_batch_size = 0
  
      for artifact in artifacts:
          data = artifact.storage
          size = data.size
          if size > ArtifactManager.CHUNK_SIZE:
              # Payload can either be a batch OR a single chunked artifact.
              # Write batch if non-empty before chunking current artifact.
              if len(current_batch) > 0:
                  yield from write_batch()
              yield from self._add_chunked_artifact(artifact)
          else:
              if current_batch_size + size > ArtifactManager.CHUNK_SIZE:
                  yield from write_batch()
              add_to_batch(artifact, size)
  
      if len(current_batch) > 0:
          yield from write_batch()
  
imports: from pyspark.errors import PySparkRuntimeError, PySparkValueError
  from pyspark.sql.connect.utils import check_dependencies
  from pyspark.sql.connect.client.logging import logger
  import hashlib
  import importlib
  import io
  import sys
  import os
  import zlib
  from itertools import chain
  from typing import List, Iterable, BinaryIO, Iterator, Optional, Tuple
  import abc
  from pathlib import Path
  from urllib.parse import urlparse
  from urllib.request import url2pathname
  from functools import cached_property
  import grpc
  import pyspark.sql.connect.proto
  import pyspark.sql.connect.proto.base_pb2_grpc
label_desc: Use image node_id 9 for calling the ArtifactManager obj's underlying member method code with example usage: obj._add_artifacts(artifacts) without return types