Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5e2d456

Browse files
committed
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.
1 parent c31ebb6 commit 5e2d456

9 files changed

Lines changed: 230 additions & 15 deletions

File tree

Doc/distutils/sourcedist.rst

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@ to create a gzipped tarball and a zip file. The available formats are:
2626
+===========+=========================+=========+
2727
| ``zip`` | zip file (:file:`.zip`) | (1),(3) |
2828
+-----------+-------------------------+---------+
29-
| ``gztar`` | gzip'ed tar file | (2),(4) |
29+
| ``gztar`` | gzip'ed tar file | \(2) |
3030
| | (:file:`.tar.gz`) | |
3131
+-----------+-------------------------+---------+
32-
| ``bztar`` | bzip2'ed tar file | \(4) |
32+
| ``bztar`` | bzip2'ed tar file | |
3333
| | (:file:`.tar.bz2`) | |
3434
+-----------+-------------------------+---------+
3535
| ``ztar`` | compressed tar file | \(4) |
3636
| | (:file:`.tar.Z`) | |
3737
+-----------+-------------------------+---------+
38-
| ``tar`` | tar file (:file:`.tar`) | \(4) |
38+
| ``tar`` | tar file (:file:`.tar`) | |
3939
+-----------+-------------------------+---------+
4040

4141
Notes:
@@ -51,8 +51,16 @@ Notes:
5151
of the standard Python library since Python 1.6)
5252

5353
(4)
54-
requires external utilities: :program:`tar` and possibly one of :program:`gzip`,
55-
:program:`bzip2`, or :program:`compress`
54+
requires the :program:`compress` program. Notice that this format is now
55+
pending for deprecation and will be removed in the future versions of Python.
56+
57+
When using any ``tar`` format (``gztar``, ``bztar``, ``ztar`` or
58+
``tar``), under Unix you can specify the ``owner`` and ``group`` names
59+
that will be set for each member of the archive.
60+
61+
For example, if you want all files of the archive to be owned by root::
62+
63+
python setup.py sdist --owner=root --group=root
5664

5765

5866
.. _manifest:

Lib/distutils/archive_util.py

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,55 @@
1818
from distutils.dir_util import mkpath
1919
from distutils import log
2020

21-
def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0):
21+
try:
22+
from pwd import getpwnam
23+
except AttributeError:
24+
getpwnam = None
25+
26+
try:
27+
from grp import getgrnam
28+
except AttributeError:
29+
getgrnam = None
30+
31+
def _get_gid(name):
32+
"""Returns a gid, given a group name."""
33+
if getgrnam is None or name is None:
34+
return None
35+
try:
36+
result = getgrnam(name)
37+
except KeyError:
38+
result = None
39+
if result is not None:
40+
return result[2]
41+
return None
42+
43+
def _get_uid(name):
44+
"""Returns an uid, given a user name."""
45+
if getpwnam is None or name is None:
46+
return None
47+
try:
48+
result = getpwnam(name)
49+
except KeyError:
50+
result = None
51+
if result is not None:
52+
return result[2]
53+
return None
54+
55+
def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
56+
owner=None, group=None):
2257
"""Create a (possibly compressed) tar file from all the files under
2358
'base_dir'.
2459
2560
'compress' must be "gzip" (the default), "compress", "bzip2", or None.
26-
Both "tar" and the compression utility named by 'compress' must be on
27-
the default program search path, so this is probably Unix-specific.
61+
(compress will be deprecated in Python 3.2)
62+
63+
'owner' and 'group' can be used to define an owner and a group for the
64+
archive that is being built. If not provided, the current owner and group
65+
will be used.
66+
2867
The output tar file will be named 'base_dir' + ".tar", possibly plus
2968
the appropriate compression extension (".gz", ".bz2" or ".Z").
69+
3070
Returns the output filename.
3171
"""
3272
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):
4888
import tarfile # late import so Python build itself doesn't break
4989

5090
log.info('Creating tar archive')
91+
92+
uid = _get_uid(owner)
93+
gid = _get_gid(group)
94+
95+
def _set_uid_gid(tarinfo):
96+
if gid is not None:
97+
tarinfo.gid = gid
98+
tarinfo.gname = group
99+
if uid is not None:
100+
tarinfo.uid = uid
101+
tarinfo.uname = owner
102+
return tarinfo
103+
51104
if not dry_run:
52105
tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
53106
try:
54-
tar.add(base_dir)
107+
tar.add(base_dir, filter=_set_uid_gid)
55108
finally:
56109
tar.close()
57110

@@ -140,7 +193,7 @@ def check_archive_formats(formats):
140193
return None
141194

142195
def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
143-
dry_run=0):
196+
dry_run=0, owner=None, group=None):
144197
"""Create an archive file (eg. zip or tar).
145198
146199
'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,
153206
ie. 'base_dir' will be the common prefix of all files and
154207
directories in the archive. 'root_dir' and 'base_dir' both default
155208
to the current directory. Returns the name of the archive file.
209+
210+
'owner' and 'group' are used when creating a tar archive. By default,
211+
uses the current owner and group.
156212
"""
157213
save_cwd = os.getcwd()
158214
if root_dir is not None:
@@ -174,6 +230,11 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
174230
func = format_info[0]
175231
for arg, val in format_info[1]:
176232
kwargs[arg] = val
233+
234+
if format != 'zip':
235+
kwargs['owner'] = owner
236+
kwargs['group'] = group
237+
177238
try:
178239
filename = func(base_name, base_dir, **kwargs)
179240
finally:

Lib/distutils/cmd.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,11 @@ def spawn(self, cmd, search_path=1, level=1):
365365
from distutils.spawn import spawn
366366
spawn(cmd, search_path, dry_run=self.dry_run)
367367

368-
def make_archive(self, base_name, format, root_dir=None, base_dir=None):
368+
def make_archive(self, base_name, format, root_dir=None, base_dir=None,
369+
owner=None, group=None):
369370
return archive_util.make_archive(base_name, format, root_dir, base_dir,
370-
dry_run=self.dry_run)
371+
dry_run=self.dry_run,
372+
owner=owner, group=group)
371373

372374
def make_file(self, infiles, outfile, func, args,
373375
exec_msg=None, skip_msg=None, level=1):

Lib/distutils/command/bdist.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ class bdist(Command):
3737
"[default: dist]"),
3838
('skip-build', None,
3939
"skip rebuilding everything (for testing/debugging)"),
40+
('owner=', 'u',
41+
"Owner name used when creating a tar file"
42+
" [default: current user]"),
43+
('group=', 'g',
44+
"Group name used when creating a tar file"
45+
" [default: current group]"),
4046
]
4147

4248
boolean_options = ['skip-build']
@@ -77,6 +83,8 @@ def initialize_options(self):
7783
self.formats = None
7884
self.dist_dir = None
7985
self.skip_build = 0
86+
self.group = None
87+
self.owner = None
8088

8189
def finalize_options(self):
8290
# have to finalize 'plat_name' before 'bdist_base'
@@ -122,6 +130,11 @@ def run(self):
122130
if cmd_name not in self.no_format_option:
123131
sub_cmd.format = self.formats[i]
124132

133+
# passing the owner and group names for tar archiving
134+
if cmd_name == 'bdist_dumb':
135+
sub_cmd.owner = self.owner
136+
sub_cmd.group = self.group
137+
125138
# If we're going to need to run this command again, tell it to
126139
# keep its temporary files around so subsequent runs go faster.
127140
if cmd_name in commands[i+1:]:

Lib/distutils/command/bdist_dumb.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ class bdist_dumb(Command):
3333
('relative', None,
3434
"build the archive using relative paths"
3535
"(default: false)"),
36+
('owner=', 'u',
37+
"Owner name used when creating a tar file"
38+
" [default: current user]"),
39+
('group=', 'g',
40+
"Group name used when creating a tar file"
41+
" [default: current group]"),
3642
]
3743

3844
boolean_options = ['keep-temp', 'skip-build', 'relative']
@@ -48,6 +54,8 @@ def initialize_options(self):
4854
self.dist_dir = None
4955
self.skip_build = None
5056
self.relative = 0
57+
self.owner = None
58+
self.group = None
5159

5260
def finalize_options(self):
5361
if self.bdist_dir is None:
@@ -101,7 +109,8 @@ def run(self):
101109

102110
# Make the archive
103111
filename = self.make_archive(pseudoinstall_root,
104-
self.format, root_dir=archive_root)
112+
self.format, root_dir=archive_root,
113+
owner=self.owner, group=self.group)
105114
if self.distribution.has_ext_modules():
106115
pyversion = get_python_version()
107116
else:

Lib/distutils/command/sdist.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ def checking_metadata(self):
7474
('metadata-check', None,
7575
"Ensure that all required elements of meta-data "
7676
"are supplied. Warn if any missing. [default]"),
77+
('owner=', 'u',
78+
"Owner name used when creating a tar file [default: current user]"),
79+
('group=', 'g',
80+
"Group name used when creating a tar file [default: current group]"),
7781
]
7882

7983
boolean_options = ['use-defaults', 'prune',
@@ -113,6 +117,8 @@ def initialize_options(self):
113117

114118
self.archive_files = None
115119
self.metadata_check = 1
120+
self.owner = None
121+
self.group = None
116122

117123
def finalize_options(self):
118124
if self.manifest is None:
@@ -444,7 +450,8 @@ def make_distribution(self):
444450
self.formats.append(self.formats.pop(self.formats.index('tar')))
445451

446452
for fmt in self.formats:
447-
file = self.make_archive(base_name, fmt, base_dir=base_dir)
453+
file = self.make_archive(base_name, fmt, base_dir=base_dir,
454+
owner=self.owner, group=self.group)
448455
archive_files.append(file)
449456
self.distribution.dist_files.append(('sdist', '', file))
450457

Lib/distutils/tests/test_archive_util.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
from distutils.tests import support
1616
from test.support import check_warnings, run_unittest, patch
1717

18+
try:
19+
import grp
20+
import pwd
21+
UID_GID_SUPPORT = True
22+
except ImportError:
23+
UID_GID_SUPPORT = False
24+
1825
try:
1926
import zipfile
2027
ZIP_SUPPORT = True
@@ -77,7 +84,7 @@ def _make_tarball(self, target_name):
7784

7885
tmpdir2 = self.mkdtemp()
7986
unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
80-
"Source and target should be on same drive")
87+
"source and target should be on same drive")
8188

8289
base_name = os.path.join(tmpdir2, target_name)
8390

@@ -275,6 +282,58 @@ def _breaks(*args, **kw):
275282
finally:
276283
del ARCHIVE_FORMATS['xxx']
277284

285+
def test_make_archive_owner_group(self):
286+
# testing make_archive with owner and group, with various combinations
287+
# this works even if there's not gid/uid support
288+
if UID_GID_SUPPORT:
289+
group = grp.getgrgid(0)[0]
290+
owner = pwd.getpwuid(0)[0]
291+
else:
292+
group = owner = 'root'
293+
294+
base_dir, root_dir, base_name = self._create_files()
295+
base_name = os.path.join(self.mkdtemp() , 'archive')
296+
res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
297+
group=group)
298+
self.assertTrue(os.path.exists(res))
299+
300+
res = make_archive(base_name, 'zip', root_dir, base_dir)
301+
self.assertTrue(os.path.exists(res))
302+
303+
res = make_archive(base_name, 'tar', root_dir, base_dir,
304+
owner=owner, group=group)
305+
self.assertTrue(os.path.exists(res))
306+
307+
res = make_archive(base_name, 'tar', root_dir, base_dir,
308+
owner='kjhkjhkjg', group='oihohoh')
309+
self.assertTrue(os.path.exists(res))
310+
311+
@unittest.skipUnless(zlib, "Requires zlib")
312+
@unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
313+
def test_tarfile_root_owner(self):
314+
tmpdir, tmpdir2, base_name = self._create_files()
315+
old_dir = os.getcwd()
316+
os.chdir(tmpdir)
317+
group = grp.getgrgid(0)[0]
318+
owner = pwd.getpwuid(0)[0]
319+
try:
320+
archive_name = make_tarball(base_name, 'dist', compress=None,
321+
owner=owner, group=group)
322+
finally:
323+
os.chdir(old_dir)
324+
325+
# check if the compressed tarball was created
326+
self.assertTrue(os.path.exists(archive_name))
327+
328+
# now checks the rights
329+
archive = tarfile.open(archive_name)
330+
try:
331+
for member in archive.getmembers():
332+
self.assertEquals(member.uid, 0)
333+
self.assertEquals(member.gid, 0)
334+
finally:
335+
archive.close()
336+
278337
def test_suite():
279338
return unittest.makeSuite(ArchiveUtilTestCase)
280339

0 commit comments

Comments
 (0)