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
|
@ -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.spawn import spawn
|
|||
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 @@ class Command:
|
|||
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 @@ class bdist(Command):
|
|||
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 @@ class bdist(Command):
|
|||
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 @@ class bdist_dumb(Command):
|
|||
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 @@ class bdist_dumb(Command):
|
|||
|
||||
# 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 @@ class sdist(Command):
|
|||
('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 @@ class sdist(Command):
|
|||
|
||||
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 @@ class sdist(Command):
|
|||
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.spawn import find_executable, spawn
|
|||
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 @@ class ArchiveUtilTestCase(support.TempdirManager,
|
|||
|
||||
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 @@ class ArchiveUtilTestCase(support.TempdirManager,
|
|||
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 @@ try:
|
|||
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 @@ class SDistTestCase(PyPIRCCommandTestCase):
|
|||
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…
Reference in New Issue