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

Skip to content

Commit 05b3034

Browse files
author
Tarek Ziadé
committed
Merged revisions 75192 via svnmerge from
svn+ssh://[email protected]/python/trunk ........ r75192 | tarek.ziade | 2009-10-03 01:49:48 +0200 (Sat, 03 Oct 2009) | 1 line #6516 added owner/group support for tarfiles in Distutils ........
1 parent 574b1d6 commit 05b3034

9 files changed

Lines changed: 233 additions & 17 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 ``tar``), you
58+
can specify under Unix the ``owner`` and ``group`` names that will be set for
59+
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
@@ -14,15 +14,55 @@
1414
from distutils.dir_util import mkpath
1515
from distutils import log
1616

17-
def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0):
17+
try:
18+
from pwd import getpwnam
19+
except AttributeError:
20+
getpwnam = None
21+
22+
try:
23+
from grp import getgrnam
24+
except AttributeError:
25+
getgrnam = None
26+
27+
def _get_gid(name):
28+
"""Returns a gid, given a group name."""
29+
if getgrnam is None or name is None:
30+
return None
31+
try:
32+
result = getgrnam(name)
33+
except KeyError:
34+
result = None
35+
if result is not None:
36+
return result[2]
37+
return None
38+
39+
def _get_uid(name):
40+
"""Returns an uid, given a user name."""
41+
if getpwnam is None or name is None:
42+
return None
43+
try:
44+
result = getpwnam(name)
45+
except KeyError:
46+
result = None
47+
if result is not None:
48+
return result[2]
49+
return None
50+
51+
def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
52+
owner=None, group=None):
1853
"""Create a (possibly compressed) tar file from all the files under
1954
'base_dir'.
2055
2156
'compress' must be "gzip" (the default), "compress", "bzip2", or None.
22-
Both "tar" and the compression utility named by 'compress' must be on
23-
the default program search path, so this is probably Unix-specific.
57+
(compress will be deprecated in Python 3.2)
58+
59+
'owner' and 'group' can be used to define an owner and a group for the
60+
archive that is being built. If not provided, the current owner and group
61+
will be used.
62+
2463
The output tar file will be named 'base_dir' + ".tar", possibly plus
2564
the appropriate compression extension (".gz", ".bz2" or ".Z").
65+
2666
Returns the output filename.
2767
"""
2868
tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''}
@@ -44,10 +84,23 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0):
4484
import tarfile # late import so Python build itself doesn't break
4585

4686
log.info('Creating tar archive')
87+
88+
uid = _get_uid(owner)
89+
gid = _get_gid(group)
90+
91+
def _set_uid_gid(tarinfo):
92+
if gid is not None:
93+
tarinfo.gid = gid
94+
tarinfo.gname = group
95+
if uid is not None:
96+
tarinfo.uid = uid
97+
tarinfo.uname = owner
98+
return tarinfo
99+
47100
if not dry_run:
48101
tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
49102
try:
50-
tar.add(base_dir)
103+
tar.add(base_dir, filter=_set_uid_gid)
51104
finally:
52105
tar.close()
53106

@@ -137,7 +190,7 @@ def check_archive_formats(formats):
137190
return None
138191

139192
def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
140-
dry_run=0):
193+
dry_run=0, owner=None, group=None):
141194
"""Create an archive file (eg. zip or tar).
142195
143196
'base_name' is the name of the file to create, minus any format-specific
@@ -150,6 +203,9 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
150203
ie. 'base_dir' will be the common prefix of all files and
151204
directories in the archive. 'root_dir' and 'base_dir' both default
152205
to the current directory. Returns the name of the archive file.
206+
207+
'owner' and 'group' are used when creating a tar archive. By default,
208+
uses the current owner and group.
153209
"""
154210
save_cwd = os.getcwd()
155211
if root_dir is not None:
@@ -171,6 +227,11 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
171227
func = format_info[0]
172228
for arg, val in format_info[1]:
173229
kwargs[arg] = val
230+
231+
if format != 'zip':
232+
kwargs['owner'] = owner
233+
kwargs['group'] = group
234+
174235
filename = func(base_name, base_dir, **kwargs)
175236

176237
if root_dir is not None:

Lib/distutils/cmd.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,11 @@ def spawn(self, cmd, search_path=1, level=1):
367367
from distutils.spawn import spawn
368368
spawn(cmd, search_path, dry_run=self.dry_run)
369369

370-
def make_archive(self, base_name, format, root_dir=None, base_dir=None):
371-
return archive_util.make_archive(base_name, format, root_dir, base_dir,
372-
dry_run=self.dry_run)
370+
def make_archive(self, base_name, format, root_dir=None, base_dir=None,
371+
owner=None, group=None):
372+
return archive_util.make_archive(base_name, format, root_dir,
373+
base_dir, dry_run=self.dry_run,
374+
owner=owner, group=group)
373375

374376
def make_file(self, infiles, outfile, func, args,
375377
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
@@ -39,6 +39,12 @@ class bdist(Command):
3939
"[default: dist]"),
4040
('skip-build', None,
4141
"skip rebuilding everything (for testing/debugging)"),
42+
('owner=', 'u',
43+
"Owner name used when creating a tar file"
44+
" [default: current user]"),
45+
('group=', 'g',
46+
"Group name used when creating a tar file"
47+
" [default: current group]"),
4248
]
4349

4450
boolean_options = ['skip-build']
@@ -80,6 +86,8 @@ def initialize_options(self):
8086
self.formats = None
8187
self.dist_dir = None
8288
self.skip_build = 0
89+
self.group = None
90+
self.owner = None
8391

8492
def finalize_options(self):
8593
# have to finalize 'plat_name' before 'bdist_base'
@@ -125,6 +133,11 @@ def run(self):
125133
if cmd_name not in self.no_format_option:
126134
sub_cmd.format = self.formats[i]
127135

136+
# passing the owner and group names for tar archiving
137+
if cmd_name == 'bdist_dumb':
138+
sub_cmd.owner = self.owner
139+
sub_cmd.group = self.group
140+
128141
# If we're going to need to run this command again, tell it to
129142
# keep its temporary files around so subsequent runs go faster.
130143
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
@@ -36,6 +36,12 @@ class bdist_dumb(Command):
3636
('relative', None,
3737
"build the archive using relative paths"
3838
"(default: false)"),
39+
('owner=', 'u',
40+
"Owner name used when creating a tar file"
41+
" [default: current user]"),
42+
('group=', 'g',
43+
"Group name used when creating a tar file"
44+
" [default: current group]"),
3945
]
4046

4147
boolean_options = ['keep-temp', 'skip-build', 'relative']
@@ -52,6 +58,8 @@ def initialize_options(self):
5258
self.dist_dir = None
5359
self.skip_build = 0
5460
self.relative = 0
61+
self.owner = None
62+
self.group = None
5563

5664
def finalize_options(self):
5765
if self.bdist_dir is None:
@@ -109,7 +117,8 @@ def run(self):
109117

110118
# Make the archive
111119
filename = self.make_archive(pseudoinstall_root,
112-
self.format, root_dir=archive_root)
120+
self.format, root_dir=archive_root,
121+
owner=self.owner, group=self.group)
113122
if self.distribution.has_ext_modules():
114123
pyversion = get_python_version()
115124
else:

Lib/distutils/command/sdist.py

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

8084
boolean_options = ['use-defaults', 'prune',
@@ -114,6 +118,8 @@ def initialize_options(self):
114118

115119
self.archive_files = None
116120
self.metadata_check = 1
121+
self.owner = None
122+
self.group = None
117123

118124
def finalize_options(self):
119125
if self.manifest is None:
@@ -449,7 +455,8 @@ def make_distribution(self):
449455
self.formats.append(self.formats.pop(self.formats.index('tar')))
450456

451457
for fmt in self.formats:
452-
file = self.make_archive(base_name, fmt, base_dir=base_dir)
458+
file = self.make_archive(base_name, fmt, base_dir=base_dir,
459+
owner=self.owner, group=self.group)
453460
archive_files.append(file)
454461
self.distribution.dist_files.append(('sdist', '', file))
455462

Lib/distutils/tests/test_archive_util.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
from distutils.tests import support
1414
from test.support import check_warnings
1515

16+
try:
17+
import grp
18+
import pwd
19+
UID_GID_SUPPORT = True
20+
except ImportError:
21+
UID_GID_SUPPORT = False
22+
1623
try:
1724
import zipfile
1825
ZIP_SUPPORT = True
@@ -30,7 +37,7 @@ class ArchiveUtilTestCase(support.TempdirManager,
3037
support.LoggingSilencer,
3138
unittest.TestCase):
3239

33-
@unittest.skipUnless(zlib, "Requires zlib")
40+
@unittest.skipUnless(zlib, "requires zlib")
3441
def test_make_tarball(self):
3542
# creating something to tar
3643
tmpdir = self.mkdtemp()
@@ -41,7 +48,7 @@ def test_make_tarball(self):
4148

4249
tmpdir2 = self.mkdtemp()
4350
unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
44-
"Source and target should be on same drive")
51+
"source and target should be on same drive")
4552

4653
base_name = os.path.join(tmpdir2, 'archive')
4754

@@ -202,6 +209,58 @@ def test_make_archive(self):
202209
base_name = os.path.join(tmpdir, 'archive')
203210
self.assertRaises(ValueError, make_archive, base_name, 'xxx')
204211

212+
def test_make_archive_owner_group(self):
213+
# testing make_archive with owner and group, with various combinations
214+
# this works even if there's not gid/uid support
215+
if UID_GID_SUPPORT:
216+
group = grp.getgrgid(0)[0]
217+
owner = pwd.getpwuid(0)[0]
218+
else:
219+
group = owner = 'root'
220+
221+
base_dir, root_dir, base_name = self._create_files()
222+
base_name = os.path.join(self.mkdtemp() , 'archive')
223+
res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
224+
group=group)
225+
self.assertTrue(os.path.exists(res))
226+
227+
res = make_archive(base_name, 'zip', root_dir, base_dir)
228+
self.assertTrue(os.path.exists(res))
229+
230+
res = make_archive(base_name, 'tar', root_dir, base_dir,
231+
owner=owner, group=group)
232+
self.assertTrue(os.path.exists(res))
233+
234+
res = make_archive(base_name, 'tar', root_dir, base_dir,
235+
owner='kjhkjhkjg', group='oihohoh')
236+
self.assertTrue(os.path.exists(res))
237+
238+
@unittest.skipUnless(zlib, "Requires zlib")
239+
@unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
240+
def test_tarfile_root_owner(self):
241+
tmpdir, tmpdir2, base_name = self._create_files()
242+
old_dir = os.getcwd()
243+
os.chdir(tmpdir)
244+
group = grp.getgrgid(0)[0]
245+
owner = pwd.getpwuid(0)[0]
246+
try:
247+
archive_name = make_tarball(base_name, 'dist', compress=None,
248+
owner=owner, group=group)
249+
finally:
250+
os.chdir(old_dir)
251+
252+
# check if the compressed tarball was created
253+
self.assertTrue(os.path.exists(archive_name))
254+
255+
# now checks the rights
256+
archive = tarfile.open(archive_name)
257+
try:
258+
for member in archive.getmembers():
259+
self.assertEquals(member.uid, 0)
260+
self.assertEquals(member.gid, 0)
261+
finally:
262+
archive.close()
263+
205264
def test_suite():
206265
return unittest.makeSuite(ArchiveUtilTestCase)
207266

0 commit comments

Comments
 (0)