mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Issue #19544 and Issue #6516: Restore support for --user and --group parameters to sdist command as found in Python 2.7 and originally slated for Python 3.2 but accidentally rolled back as part of the distutils2 rollback. Closes Issue #6516.
This commit is contained in:
		
							parent
							
								
									c31ebb60f9
								
							
						
					
					
						commit
						5e2d45672c
					
				
					 9 changed files with 230 additions and 15 deletions
				
			
		|  | @ -26,16 +26,16 @@ to create a gzipped tarball and a zip file.  The available formats are: | |||
| +===========+=========================+=========+ | ||||
| | ``zip``   | zip file (:file:`.zip`) | (1),(3) | | ||||
| +-----------+-------------------------+---------+ | ||||
| | ``gztar`` | gzip'ed tar file        | (2),(4) | | ||||
| | ``gztar`` | gzip'ed tar file        | \(2)    | | ||||
| |           | (:file:`.tar.gz`)       |         | | ||||
| +-----------+-------------------------+---------+ | ||||
| | ``bztar`` | bzip2'ed tar file       | \(4)    | | ||||
| | ``bztar`` | bzip2'ed tar file       |         | | ||||
| |           | (:file:`.tar.bz2`)      |         | | ||||
| +-----------+-------------------------+---------+ | ||||
| | ``ztar``  | compressed tar file     | \(4)    | | ||||
| |           | (:file:`.tar.Z`)        |         | | ||||
| +-----------+-------------------------+---------+ | ||||
| | ``tar``   | tar file (:file:`.tar`) | \(4)    | | ||||
| | ``tar``   | tar file (:file:`.tar`) |         | | ||||
| +-----------+-------------------------+---------+ | ||||
| 
 | ||||
| Notes: | ||||
|  | @ -51,8 +51,16 @@ Notes: | |||
|    of the standard Python library since Python 1.6) | ||||
| 
 | ||||
| (4) | ||||
|    requires external utilities: :program:`tar` and possibly one of :program:`gzip`, | ||||
|    :program:`bzip2`, or :program:`compress` | ||||
|    requires the :program:`compress` program. Notice that this format is now | ||||
|    pending for deprecation and will be removed in the future versions of Python. | ||||
| 
 | ||||
| When using any ``tar`` format (``gztar``, ``bztar``, ``ztar`` or | ||||
| ``tar``), under Unix you can specify the ``owner`` and ``group`` names | ||||
| that will be set for each member of the archive. | ||||
| 
 | ||||
| For example, if you want all files of the archive to be owned by root:: | ||||
| 
 | ||||
|     python setup.py sdist --owner=root --group=root | ||||
| 
 | ||||
| 
 | ||||
| .. _manifest: | ||||
|  |  | |||
|  | @ -18,15 +18,55 @@ | |||
| from distutils.dir_util import mkpath | ||||
| from distutils import log | ||||
| 
 | ||||
| def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): | ||||
| try: | ||||
|     from pwd import getpwnam | ||||
| except AttributeError: | ||||
|     getpwnam = None | ||||
| 
 | ||||
| try: | ||||
|     from grp import getgrnam | ||||
| except AttributeError: | ||||
|     getgrnam = None | ||||
| 
 | ||||
| def _get_gid(name): | ||||
|     """Returns a gid, given a group name.""" | ||||
|     if getgrnam is None or name is None: | ||||
|         return None | ||||
|     try: | ||||
|         result = getgrnam(name) | ||||
|     except KeyError: | ||||
|         result = None | ||||
|     if result is not None: | ||||
|         return result[2] | ||||
|     return None | ||||
| 
 | ||||
| def _get_uid(name): | ||||
|     """Returns an uid, given a user name.""" | ||||
|     if getpwnam is None or name is None: | ||||
|         return None | ||||
|     try: | ||||
|         result = getpwnam(name) | ||||
|     except KeyError: | ||||
|         result = None | ||||
|     if result is not None: | ||||
|         return result[2] | ||||
|     return None | ||||
| 
 | ||||
| def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, | ||||
|                  owner=None, group=None): | ||||
|     """Create a (possibly compressed) tar file from all the files under | ||||
|     'base_dir'. | ||||
| 
 | ||||
|     'compress' must be "gzip" (the default), "compress", "bzip2", or None. | ||||
|     Both "tar" and the compression utility named by 'compress' must be on | ||||
|     the default program search path, so this is probably Unix-specific. | ||||
|     (compress will be deprecated in Python 3.2) | ||||
| 
 | ||||
|     'owner' and 'group' can be used to define an owner and a group for the | ||||
|     archive that is being built. If not provided, the current owner and group | ||||
|     will be used. | ||||
| 
 | ||||
|     The output tar file will be named 'base_dir' +  ".tar", possibly plus | ||||
|     the appropriate compression extension (".gz", ".bz2" or ".Z"). | ||||
| 
 | ||||
|     Returns the output filename. | ||||
|     """ | ||||
|     tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''} | ||||
|  | @ -48,10 +88,23 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): | |||
|     import tarfile  # late import so Python build itself doesn't break | ||||
| 
 | ||||
|     log.info('Creating tar archive') | ||||
| 
 | ||||
|     uid = _get_uid(owner) | ||||
|     gid = _get_gid(group) | ||||
| 
 | ||||
|     def _set_uid_gid(tarinfo): | ||||
|         if gid is not None: | ||||
|             tarinfo.gid = gid | ||||
|             tarinfo.gname = group | ||||
|         if uid is not None: | ||||
|             tarinfo.uid = uid | ||||
|             tarinfo.uname = owner | ||||
|         return tarinfo | ||||
| 
 | ||||
|     if not dry_run: | ||||
|         tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) | ||||
|         try: | ||||
|             tar.add(base_dir) | ||||
|             tar.add(base_dir, filter=_set_uid_gid) | ||||
|         finally: | ||||
|             tar.close() | ||||
| 
 | ||||
|  | @ -140,7 +193,7 @@ def check_archive_formats(formats): | |||
|     return None | ||||
| 
 | ||||
| def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, | ||||
|                  dry_run=0): | ||||
|                  dry_run=0, owner=None, group=None): | ||||
|     """Create an archive file (eg. zip or tar). | ||||
| 
 | ||||
|     'base_name' is the name of the file to create, minus any format-specific | ||||
|  | @ -153,6 +206,9 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, | |||
|     ie. 'base_dir' will be the common prefix of all files and | ||||
|     directories in the archive.  'root_dir' and 'base_dir' both default | ||||
|     to the current directory.  Returns the name of the archive file. | ||||
| 
 | ||||
|     'owner' and 'group' are used when creating a tar archive. By default, | ||||
|     uses the current owner and group. | ||||
|     """ | ||||
|     save_cwd = os.getcwd() | ||||
|     if root_dir is not None: | ||||
|  | @ -174,6 +230,11 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, | |||
|     func = format_info[0] | ||||
|     for arg, val in format_info[1]: | ||||
|         kwargs[arg] = val | ||||
| 
 | ||||
|     if format != 'zip': | ||||
|         kwargs['owner'] = owner | ||||
|         kwargs['group'] = group | ||||
| 
 | ||||
|     try: | ||||
|         filename = func(base_name, base_dir, **kwargs) | ||||
|     finally: | ||||
|  |  | |||
|  | @ -365,9 +365,11 @@ def spawn(self, cmd, search_path=1, level=1): | |||
|         from distutils.spawn import spawn | ||||
|         spawn(cmd, search_path, dry_run=self.dry_run) | ||||
| 
 | ||||
|     def make_archive(self, base_name, format, root_dir=None, base_dir=None): | ||||
|     def make_archive(self, base_name, format, root_dir=None, base_dir=None, | ||||
|                      owner=None, group=None): | ||||
|         return archive_util.make_archive(base_name, format, root_dir, base_dir, | ||||
|                                          dry_run=self.dry_run) | ||||
|                                          dry_run=self.dry_run, | ||||
|                                          owner=owner, group=group) | ||||
| 
 | ||||
|     def make_file(self, infiles, outfile, func, args, | ||||
|                   exec_msg=None, skip_msg=None, level=1): | ||||
|  |  | |||
|  | @ -37,6 +37,12 @@ class bdist(Command): | |||
|                      "[default: dist]"), | ||||
|                     ('skip-build', None, | ||||
|                      "skip rebuilding everything (for testing/debugging)"), | ||||
|                     ('owner=', 'u', | ||||
|                      "Owner name used when creating a tar file" | ||||
|                      " [default: current user]"), | ||||
|                     ('group=', 'g', | ||||
|                      "Group name used when creating a tar file" | ||||
|                      " [default: current group]"), | ||||
|                    ] | ||||
| 
 | ||||
|     boolean_options = ['skip-build'] | ||||
|  | @ -77,6 +83,8 @@ def initialize_options(self): | |||
|         self.formats = None | ||||
|         self.dist_dir = None | ||||
|         self.skip_build = 0 | ||||
|         self.group = None | ||||
|         self.owner = None | ||||
| 
 | ||||
|     def finalize_options(self): | ||||
|         # have to finalize 'plat_name' before 'bdist_base' | ||||
|  | @ -122,6 +130,11 @@ def run(self): | |||
|             if cmd_name not in self.no_format_option: | ||||
|                 sub_cmd.format = self.formats[i] | ||||
| 
 | ||||
|             # passing the owner and group names for tar archiving | ||||
|             if cmd_name == 'bdist_dumb': | ||||
|                 sub_cmd.owner = self.owner | ||||
|                 sub_cmd.group = self.group | ||||
| 
 | ||||
|             # If we're going to need to run this command again, tell it to | ||||
|             # keep its temporary files around so subsequent runs go faster. | ||||
|             if cmd_name in commands[i+1:]: | ||||
|  |  | |||
|  | @ -33,6 +33,12 @@ class bdist_dumb(Command): | |||
|                     ('relative', None, | ||||
|                      "build the archive using relative paths" | ||||
|                      "(default: false)"), | ||||
|                     ('owner=', 'u', | ||||
|                      "Owner name used when creating a tar file" | ||||
|                      " [default: current user]"), | ||||
|                     ('group=', 'g', | ||||
|                      "Group name used when creating a tar file" | ||||
|                      " [default: current group]"), | ||||
|                    ] | ||||
| 
 | ||||
|     boolean_options = ['keep-temp', 'skip-build', 'relative'] | ||||
|  | @ -48,6 +54,8 @@ def initialize_options(self): | |||
|         self.dist_dir = None | ||||
|         self.skip_build = None | ||||
|         self.relative = 0 | ||||
|         self.owner = None | ||||
|         self.group = None | ||||
| 
 | ||||
|     def finalize_options(self): | ||||
|         if self.bdist_dir is None: | ||||
|  | @ -101,7 +109,8 @@ def run(self): | |||
| 
 | ||||
|         # Make the archive | ||||
|         filename = self.make_archive(pseudoinstall_root, | ||||
|                                      self.format, root_dir=archive_root) | ||||
|                                      self.format, root_dir=archive_root, | ||||
|                                      owner=self.owner, group=self.group) | ||||
|         if self.distribution.has_ext_modules(): | ||||
|             pyversion = get_python_version() | ||||
|         else: | ||||
|  |  | |||
|  | @ -74,6 +74,10 @@ def checking_metadata(self): | |||
|         ('metadata-check', None, | ||||
|          "Ensure that all required elements of meta-data " | ||||
|          "are supplied. Warn if any missing. [default]"), | ||||
|         ('owner=', 'u', | ||||
|          "Owner name used when creating a tar file [default: current user]"), | ||||
|         ('group=', 'g', | ||||
|          "Group name used when creating a tar file [default: current group]"), | ||||
|         ] | ||||
| 
 | ||||
|     boolean_options = ['use-defaults', 'prune', | ||||
|  | @ -113,6 +117,8 @@ def initialize_options(self): | |||
| 
 | ||||
|         self.archive_files = None | ||||
|         self.metadata_check = 1 | ||||
|         self.owner = None | ||||
|         self.group = None | ||||
| 
 | ||||
|     def finalize_options(self): | ||||
|         if self.manifest is None: | ||||
|  | @ -444,7 +450,8 @@ def make_distribution(self): | |||
|             self.formats.append(self.formats.pop(self.formats.index('tar'))) | ||||
| 
 | ||||
|         for fmt in self.formats: | ||||
|             file = self.make_archive(base_name, fmt, base_dir=base_dir) | ||||
|             file = self.make_archive(base_name, fmt, base_dir=base_dir, | ||||
|                                      owner=self.owner, group=self.group) | ||||
|             archive_files.append(file) | ||||
|             self.distribution.dist_files.append(('sdist', '', file)) | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,13 @@ | |||
| from distutils.tests import support | ||||
| from test.support import check_warnings, run_unittest, patch | ||||
| 
 | ||||
| try: | ||||
|     import grp | ||||
|     import pwd | ||||
|     UID_GID_SUPPORT = True | ||||
| except ImportError: | ||||
|     UID_GID_SUPPORT = False | ||||
| 
 | ||||
| try: | ||||
|     import zipfile | ||||
|     ZIP_SUPPORT = True | ||||
|  | @ -77,7 +84,7 @@ def _make_tarball(self, target_name): | |||
| 
 | ||||
|         tmpdir2 = self.mkdtemp() | ||||
|         unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], | ||||
|                             "Source and target should be on same drive") | ||||
|                             "source and target should be on same drive") | ||||
| 
 | ||||
|         base_name = os.path.join(tmpdir2, target_name) | ||||
| 
 | ||||
|  | @ -275,6 +282,58 @@ def _breaks(*args, **kw): | |||
|         finally: | ||||
|             del ARCHIVE_FORMATS['xxx'] | ||||
| 
 | ||||
|     def test_make_archive_owner_group(self): | ||||
|         # testing make_archive with owner and group, with various combinations | ||||
|         # this works even if there's not gid/uid support | ||||
|         if UID_GID_SUPPORT: | ||||
|             group = grp.getgrgid(0)[0] | ||||
|             owner = pwd.getpwuid(0)[0] | ||||
|         else: | ||||
|             group = owner = 'root' | ||||
| 
 | ||||
|         base_dir, root_dir, base_name =  self._create_files() | ||||
|         base_name = os.path.join(self.mkdtemp() , 'archive') | ||||
|         res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, | ||||
|                            group=group) | ||||
|         self.assertTrue(os.path.exists(res)) | ||||
| 
 | ||||
|         res = make_archive(base_name, 'zip', root_dir, base_dir) | ||||
|         self.assertTrue(os.path.exists(res)) | ||||
| 
 | ||||
|         res = make_archive(base_name, 'tar', root_dir, base_dir, | ||||
|                            owner=owner, group=group) | ||||
|         self.assertTrue(os.path.exists(res)) | ||||
| 
 | ||||
|         res = make_archive(base_name, 'tar', root_dir, base_dir, | ||||
|                            owner='kjhkjhkjg', group='oihohoh') | ||||
|         self.assertTrue(os.path.exists(res)) | ||||
| 
 | ||||
|     @unittest.skipUnless(zlib, "Requires zlib") | ||||
|     @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") | ||||
|     def test_tarfile_root_owner(self): | ||||
|         tmpdir, tmpdir2, base_name =  self._create_files() | ||||
|         old_dir = os.getcwd() | ||||
|         os.chdir(tmpdir) | ||||
|         group = grp.getgrgid(0)[0] | ||||
|         owner = pwd.getpwuid(0)[0] | ||||
|         try: | ||||
|             archive_name = make_tarball(base_name, 'dist', compress=None, | ||||
|                                         owner=owner, group=group) | ||||
|         finally: | ||||
|             os.chdir(old_dir) | ||||
| 
 | ||||
|         # check if the compressed tarball was created | ||||
|         self.assertTrue(os.path.exists(archive_name)) | ||||
| 
 | ||||
|         # now checks the rights | ||||
|         archive = tarfile.open(archive_name) | ||||
|         try: | ||||
|             for member in archive.getmembers(): | ||||
|                 self.assertEquals(member.uid, 0) | ||||
|                 self.assertEquals(member.gid, 0) | ||||
|         finally: | ||||
|             archive.close() | ||||
| 
 | ||||
| def test_suite(): | ||||
|     return unittest.makeSuite(ArchiveUtilTestCase) | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,6 +14,12 @@ | |||
| except ImportError: | ||||
|     ZLIB_SUPPORT = False | ||||
| 
 | ||||
| try: | ||||
|     import grp | ||||
|     import pwd | ||||
|     UID_GID_SUPPORT = True | ||||
| except ImportError: | ||||
|     UID_GID_SUPPORT = False | ||||
| 
 | ||||
| from distutils.command.sdist import sdist, show_formats | ||||
| from distutils.core import Distribution | ||||
|  | @ -425,6 +431,53 @@ def test_manual_manifest(self): | |||
|         self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', | ||||
|                                              'fake-1.0/README.manual']) | ||||
| 
 | ||||
|     @unittest.skipUnless(zlib, "requires zlib") | ||||
|     @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") | ||||
|     def test_make_distribution_owner_group(self): | ||||
| 
 | ||||
|         # check if tar and gzip are installed | ||||
|         if (find_executable('tar') is None or | ||||
|             find_executable('gzip') is None): | ||||
|             return | ||||
| 
 | ||||
|         # now building a sdist | ||||
|         dist, cmd = self.get_cmd() | ||||
| 
 | ||||
|         # creating a gztar and specifying the owner+group | ||||
|         cmd.formats = ['gztar'] | ||||
|         cmd.owner = pwd.getpwuid(0)[0] | ||||
|         cmd.group = grp.getgrgid(0)[0] | ||||
|         cmd.ensure_finalized() | ||||
|         cmd.run() | ||||
| 
 | ||||
|         # making sure we have the good rights | ||||
|         archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') | ||||
|         archive = tarfile.open(archive_name) | ||||
|         try: | ||||
|             for member in archive.getmembers(): | ||||
|                 self.assertEquals(member.uid, 0) | ||||
|                 self.assertEquals(member.gid, 0) | ||||
|         finally: | ||||
|             archive.close() | ||||
| 
 | ||||
|         # building a sdist again | ||||
|         dist, cmd = self.get_cmd() | ||||
| 
 | ||||
|         # creating a gztar | ||||
|         cmd.formats = ['gztar'] | ||||
|         cmd.ensure_finalized() | ||||
|         cmd.run() | ||||
| 
 | ||||
|         # making sure we have the good rights | ||||
|         archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') | ||||
|         archive = tarfile.open(archive_name) | ||||
|         try: | ||||
|             for member in archive.getmembers(): | ||||
|                 self.assertEquals(member.uid, os.getuid()) | ||||
|                 self.assertEquals(member.gid, os.getgid()) | ||||
|         finally: | ||||
|             archive.close() | ||||
| 
 | ||||
| def test_suite(): | ||||
|     return unittest.makeSuite(SDistTestCase) | ||||
| 
 | ||||
|  |  | |||
|  | @ -47,6 +47,9 @@ Core and Builtins | |||
| Library | ||||
| ------- | ||||
| 
 | ||||
| - Issue #19544 and #6516: Restore support for --user and --group parameters to | ||||
|   sdist command accidentally rolled back as part of the distutils2 rollback. | ||||
| 
 | ||||
| - Issue #13674: Prevented time.strftime from crashing on Windows when given | ||||
|   a year before 1900 and a format of %y. | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andrew Kuchling
						Andrew Kuchling