docs: async def upload_file(
              self, file, *, part_size_kb=None, file_name=None, use_cache=None,
              progress_callback=None):
          """
          Uploads the specified file and returns a handle (an instance of
          :tl:`InputFile` or :tl:`InputFileBig`, as required) which can be
          later used before it expires (they are usable during less than a day).
  
          Uploading a file will simply return a "handle" to the file stored
          remotely in the Telegram servers, which can be later used on. This
          will **not** upload the file to your own chat or any chat at all.
  
          Args:
              file (`str` | `bytes` | `file`):
                  The path of the file, byte array, or stream that will be sent.
                  Note that if a byte array or a stream is given, a filename
                  or its type won't be inferred, and it will be sent as an
                  "unnamed application/octet-stream".
  
              part_size_kb (`int`, optional):
                  Chunk size when uploading files. The larger, the less
                  requests will be made (up to 512KB maximum).
  
              file_name (`str`, optional):
                  The file name which will be used on the resulting InputFile.
                  If not specified, the name will be taken from the ``file``
                  and if this is not a ``str``, it will be ``"unnamed"``.
  
              use_cache (`type`, optional):
                  The type of cache to use (currently either :tl:`InputDocument`
                  or :tl:`InputPhoto`). If present and the file is small enough
                  to need the MD5, it will be checked against the database,
                  and if a match is found, the upload won't be made. Instead,
                  an instance of type ``use_cache`` will be returned.
  
              progress_callback (`callable`, optional):
                  A callback function accepting two parameters:
                  ``(sent bytes, total)``.
  
          Returns:
              :tl:`InputFileBig` if the file size is larger than 10MB,
              `telethon.tl.custom.inputsizedfile.InputSizedFile`
              (subclass of :tl:`InputFile`) otherwise.
          """
          if isinstance(file, (types.InputFile, types.InputFileBig)):
              return file  # Already uploaded
  
          if not file_name and getattr(file, 'name', None):
              file_name = file.name
  
          if isinstance(file, str):
              file_size = os.path.getsize(file)
          elif isinstance(file, bytes):
              file_size = len(file)
          else:
              if isinstance(file, io.IOBase) and file.seekable():
                  pos = file.tell()
              else:
                  pos = None
  
              # TODO Don't load the entire file in memory always
              data = file.read()
              if pos is not None:
                  file.seek(pos)
  
              file = data
              file_size = len(file)
  
          # File will now either be a string or bytes
          if not part_size_kb:
              part_size_kb = utils.get_appropriated_part_size(file_size)
  
          if part_size_kb > 512:
              raise ValueError('The part size must be less or equal to 512KB')
  
          part_size = int(part_size_kb * 1024)
          if part_size % 1024 != 0:
              raise ValueError(
                  'The part size must be evenly divisible by 1024')
  
          # Set a default file name if None was specified
          file_id = helpers.generate_random_long()
          if not file_name:
              if isinstance(file, str):
                  file_name = os.path.basename(file)
              else:
                  file_name = str(file_id)
  
          # If the file name lacks extension, add it if possible.
          # Else Telegram complains with `PHOTO_EXT_INVALID_ERROR`
          # even if the uploaded image is indeed a photo.
          if not os.path.splitext(file_name)[-1]:
              file_name += utils._get_extension(file)
  
          # Determine whether the file is too big (over 10MB) or not
          # Telegram does make a distinction between smaller or larger files
          is_large = file_size > 10 * 1024 * 1024
          hash_md5 = hashlib.md5()
          if not is_large:
              # Calculate the MD5 hash before anything else.
              # As this needs to be done always for small files,
              # might as well do it before anything else and
              # check the cache.
              if isinstance(file, str):
                  with open(file, 'rb') as stream:
                      file = stream.read()
              hash_md5.update(file)
              if use_cache:
                  cached = self.session.get_file(
                      hash_md5.digest(), file_size, cls=_CacheType(use_cache)
                  )
                  if cached:
                      return cached
  
          part_count = (file_size + part_size - 1) // part_size
          self._log[__name__].info('Uploading file of %d bytes in %d chunks of %d',
                                   file_size, part_count, part_size)
  
          with open(file, 'rb') if isinstance(file, str) else BytesIO(file)\
                  as stream:
              for part_index in range(part_count):
                  # Read the file by in chunks of size part_size
                  part = stream.read(part_size)
  
                  # The SavePartRequest is different depending on whether
                  # the file is too large or not (over or less than 10MB)
                  if is_large:
                      request = functions.upload.SaveBigFilePartRequest(
                          file_id, part_index, part_count, part)
                  else:
                      request = functions.upload.SaveFilePartRequest(
                          file_id, part_index, part)
  
                  result = await self(request)
                  if result:
                      self._log[__name__].debug('Uploaded %d/%d',
                                                part_index + 1, part_count)
                      if progress_callback:
                          progress_callback(stream.tell(), file_size)
                  else:
                      raise RuntimeError(
                          'Failed to upload file part {}.'.format(part_index))
  
          if is_large:
              return types.InputFileBig(file_id, part_count, file_name)
          else:
              return custom.InputSizedFile(
                  file_id, part_count, file_name, md5=hash_md5, size=file_size
              )