code: def _zip_files(files, root):
      
      zip_data = StringIO()
      with ZipFile(zip_data, 'w', ZIP_DEFLATED) as zip_file:
          for fname in files:
              zip_file.write(os.path.join(root, fname), fname)
  
          # Fix file permissions to avoid any issues - only care whether a file
          # is executable or not, choosing between modes 755 and 644 accordingly.
          for zip_entry in zip_file.filelist:
              perms = (zip_entry.external_attr & ZIP_PERMS_MASK) >> 16
              if perms & stat.S_IXUSR != 0:
                  new_perms = 0o755
              else:
                  new_perms = 0o644
  
              if new_perms != perms:
                  logger.debug("lambda: fixing perms: %s: %o => %o",
                               zip_entry.filename, perms, new_perms)
                  new_attr = ((zip_entry.external_attr & ~ZIP_PERMS_MASK) |
                              (new_perms << 16))
                  zip_entry.external_attr = new_attr
  
      contents = zip_data.getvalue()
      zip_data.close()
      content_hash = _calculate_hash(files, root)
  
      return contents, content_hash
docs: Generates a ZIP file in-memory from a list of files.
  
      Files will be stored in the archive with relative names, and have their
      UNIX permissions forced to 755 or 644 (depending on whether they are
      user-executable in the source filesystem).
  
      Args:
          files (list[str]): file names to add to the archive, relative to
              ``root``.
          root (str): base directory to retrieve files from.
  
      Returns:
          str: content of the ZIP file as a byte string.
          str: A calculated hash of all the files.