python (3.11.7)

(root)/
lib/
python3.11/
site-packages/
setuptools/
_distutils/
archive_util.py
       1  """distutils.archive_util
       2  
       3  Utility functions for creating archive files (tarballs, zip files,
       4  that sort of thing)."""
       5  
       6  import os
       7  from warnings import warn
       8  import sys
       9  
      10  try:
      11      import zipfile
      12  except ImportError:
      13      zipfile = None
      14  
      15  
      16  from distutils.errors import DistutilsExecError
      17  from distutils.spawn import spawn
      18  from distutils.dir_util import mkpath
      19  from distutils import log
      20  
      21  try:
      22      from pwd import getpwnam
      23  except ImportError:
      24      getpwnam = None
      25  
      26  try:
      27      from grp import getgrnam
      28  except ImportError:
      29      getgrnam = None
      30  
      31  
      32  def _get_gid(name):
      33      """Returns a gid, given a group name."""
      34      if getgrnam is None or name is None:
      35          return None
      36      try:
      37          result = getgrnam(name)
      38      except KeyError:
      39          result = None
      40      if result is not None:
      41          return result[2]
      42      return None
      43  
      44  
      45  def _get_uid(name):
      46      """Returns an uid, given a user name."""
      47      if getpwnam is None or name is None:
      48          return None
      49      try:
      50          result = getpwnam(name)
      51      except KeyError:
      52          result = None
      53      if result is not None:
      54          return result[2]
      55      return None
      56  
      57  
      58  def make_tarball(
      59      base_name, base_dir, compress="gzip", verbose=0, dry_run=0, owner=None, group=None
      60  ):
      61      """Create a (possibly compressed) tar file from all the files under
      62      'base_dir'.
      63  
      64      'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or
      65      None.  ("compress" will be deprecated in Python 3.2)
      66  
      67      'owner' and 'group' can be used to define an owner and a group for the
      68      archive that is being built. If not provided, the current owner and group
      69      will be used.
      70  
      71      The output tar file will be named 'base_dir' +  ".tar", possibly plus
      72      the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z").
      73  
      74      Returns the output filename.
      75      """
      76      tar_compression = {
      77          'gzip': 'gz',
      78          'bzip2': 'bz2',
      79          'xz': 'xz',
      80          None: '',
      81          'compress': '',
      82      }
      83      compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', 'compress': '.Z'}
      84  
      85      # flags for compression program, each element of list will be an argument
      86      if compress is not None and compress not in compress_ext.keys():
      87          raise ValueError(
      88              "bad value for 'compress': must be None, 'gzip', 'bzip2', "
      89              "'xz' or 'compress'"
      90          )
      91  
      92      archive_name = base_name + '.tar'
      93      if compress != 'compress':
      94          archive_name += compress_ext.get(compress, '')
      95  
      96      mkpath(os.path.dirname(archive_name), dry_run=dry_run)
      97  
      98      # creating the tarball
      99      import tarfile  # late import so Python build itself doesn't break
     100  
     101      log.info('Creating tar archive')
     102  
     103      uid = _get_uid(owner)
     104      gid = _get_gid(group)
     105  
     106      def _set_uid_gid(tarinfo):
     107          if gid is not None:
     108              tarinfo.gid = gid
     109              tarinfo.gname = group
     110          if uid is not None:
     111              tarinfo.uid = uid
     112              tarinfo.uname = owner
     113          return tarinfo
     114  
     115      if not dry_run:
     116          tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
     117          try:
     118              tar.add(base_dir, filter=_set_uid_gid)
     119          finally:
     120              tar.close()
     121  
     122      # compression using `compress`
     123      if compress == 'compress':
     124          warn("'compress' is deprecated.", DeprecationWarning)
     125          # the option varies depending on the platform
     126          compressed_name = archive_name + compress_ext[compress]
     127          if sys.platform == 'win32':
     128              cmd = [compress, archive_name, compressed_name]
     129          else:
     130              cmd = [compress, '-f', archive_name]
     131          spawn(cmd, dry_run=dry_run)
     132          return compressed_name
     133  
     134      return archive_name
     135  
     136  
     137  def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):  # noqa: C901
     138      """Create a zip file from all the files under 'base_dir'.
     139  
     140      The output zip file will be named 'base_name' + ".zip".  Uses either the
     141      "zipfile" Python module (if available) or the InfoZIP "zip" utility
     142      (if installed and found on the default search path).  If neither tool is
     143      available, raises DistutilsExecError.  Returns the name of the output zip
     144      file.
     145      """
     146      zip_filename = base_name + ".zip"
     147      mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
     148  
     149      # If zipfile module is not available, try spawning an external
     150      # 'zip' command.
     151      if zipfile is None:
     152          if verbose:
     153              zipoptions = "-r"
     154          else:
     155              zipoptions = "-rq"
     156  
     157          try:
     158              spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run)
     159          except DistutilsExecError:
     160              # XXX really should distinguish between "couldn't find
     161              # external 'zip' command" and "zip failed".
     162              raise DistutilsExecError(
     163                  (
     164                      "unable to create zip file '%s': "
     165                      "could neither import the 'zipfile' module nor "
     166                      "find a standalone zip utility"
     167                  )
     168                  % zip_filename
     169              )
     170  
     171      else:
     172          log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
     173  
     174          if not dry_run:
     175              try:
     176                  zip = zipfile.ZipFile(
     177                      zip_filename, "w", compression=zipfile.ZIP_DEFLATED
     178                  )
     179              except RuntimeError:
     180                  zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED)
     181  
     182              with zip:
     183                  if base_dir != os.curdir:
     184                      path = os.path.normpath(os.path.join(base_dir, ''))
     185                      zip.write(path, path)
     186                      log.info("adding '%s'", path)
     187                  for dirpath, dirnames, filenames in os.walk(base_dir):
     188                      for name in dirnames:
     189                          path = os.path.normpath(os.path.join(dirpath, name, ''))
     190                          zip.write(path, path)
     191                          log.info("adding '%s'", path)
     192                      for name in filenames:
     193                          path = os.path.normpath(os.path.join(dirpath, name))
     194                          if os.path.isfile(path):
     195                              zip.write(path, path)
     196                              log.info("adding '%s'", path)
     197  
     198      return zip_filename
     199  
     200  
     201  ARCHIVE_FORMATS = {
     202      'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
     203      'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
     204      'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"),
     205      'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"),
     206      'tar': (make_tarball, [('compress', None)], "uncompressed tar file"),
     207      'zip': (make_zipfile, [], "ZIP file"),
     208  }
     209  
     210  
     211  def check_archive_formats(formats):
     212      """Returns the first format from the 'format' list that is unknown.
     213  
     214      If all formats are known, returns None
     215      """
     216      for format in formats:
     217          if format not in ARCHIVE_FORMATS:
     218              return format
     219      return None
     220  
     221  
     222  def make_archive(
     223      base_name,
     224      format,
     225      root_dir=None,
     226      base_dir=None,
     227      verbose=0,
     228      dry_run=0,
     229      owner=None,
     230      group=None,
     231  ):
     232      """Create an archive file (eg. zip or tar).
     233  
     234      'base_name' is the name of the file to create, minus any format-specific
     235      extension; 'format' is the archive format: one of "zip", "tar", "gztar",
     236      "bztar", "xztar", or "ztar".
     237  
     238      'root_dir' is a directory that will be the root directory of the
     239      archive; ie. we typically chdir into 'root_dir' before creating the
     240      archive.  'base_dir' is the directory where we start archiving from;
     241      ie. 'base_dir' will be the common prefix of all files and
     242      directories in the archive.  'root_dir' and 'base_dir' both default
     243      to the current directory.  Returns the name of the archive file.
     244  
     245      'owner' and 'group' are used when creating a tar archive. By default,
     246      uses the current owner and group.
     247      """
     248      save_cwd = os.getcwd()
     249      if root_dir is not None:
     250          log.debug("changing into '%s'", root_dir)
     251          base_name = os.path.abspath(base_name)
     252          if not dry_run:
     253              os.chdir(root_dir)
     254  
     255      if base_dir is None:
     256          base_dir = os.curdir
     257  
     258      kwargs = {'dry_run': dry_run}
     259  
     260      try:
     261          format_info = ARCHIVE_FORMATS[format]
     262      except KeyError:
     263          raise ValueError("unknown archive format '%s'" % format)
     264  
     265      func = format_info[0]
     266      for arg, val in format_info[1]:
     267          kwargs[arg] = val
     268  
     269      if format != 'zip':
     270          kwargs['owner'] = owner
     271          kwargs['group'] = group
     272  
     273      try:
     274          filename = func(base_name, base_dir, **kwargs)
     275      finally:
     276          if root_dir is not None:
     277              log.debug("changing back to '%s'", save_cwd)
     278              os.chdir(save_cwd)
     279  
     280      return filename