diff --git a/CHANGES.rst b/CHANGES.rst index 7a82dbb..1b82bcf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog In Development ============== +* Preserving file permissions when using zip files. +* Update tox.ini to not use a specific pypi index server. Removed support + for Python 2.6. +* Allow for stripping/removing comments (needed by certain tools which + bundle python-archive) +* Use relative import to allow module relocation. * Moved tests outside of the archive package directory. diff --git a/archive/__init__.py b/archive/__init__.py index e7dc1b8..f1aec8c 100644 --- a/archive/__init__.py +++ b/archive/__init__.py @@ -3,15 +3,17 @@ import tarfile import zipfile -from archive.compat import IS_PY2, is_string +from .compat import IS_PY2, is_string class ArchiveException(Exception): """Base exception class for all archive errors.""" + pass class UnrecognizedArchiveFormat(ArchiveException): """Error raised when passed file is not a recognized archive format.""" + pass class UnsafeArchive(ArchiveException): @@ -19,6 +21,7 @@ class UnsafeArchive(ArchiveException): Error raised when passed file contains paths that would be extracted outside of the target directory. """ + pass def extract(path, to_path='', ext='', **kwargs): @@ -157,6 +160,28 @@ def list(self, *args, **kwargs): def filenames(self): return self._archive.namelist() + def _extract_file(self, info, to_path): + out_path = self._archive.extract(info.filename, path=to_path) + + permissions = info.external_attr >> 16 + + # If no specific file permissions are specified we use the + # default used by Python when creating the files. + if permissions: + os.chmod(out_path, permissions) + + def _extract(self, to_path): + """ + Overrides the BaseArchive _extract method. For zip files we need + to take special care to perserve file permissions: + + burgundywall.com/post/preserving-file-perms-with-python-zipfile-module + + """ + for info in self._archive.infolist(): + self._extract_file(info=info, to_path=to_path) + + extension_map = { '.docx': ZipArchive, '.egg': ZipArchive, diff --git a/tests/files/endian-4.0.0.zip b/tests/files/endian-4.0.0.zip new file mode 100644 index 0000000..9a1bca4 Binary files /dev/null and b/tests/files/endian-4.0.0.zip differ diff --git a/tests/test_archive.py b/tests/test_archive.py index 1b84dcd..8e0dc1d 100644 --- a/tests/test_archive.py +++ b/tests/test_archive.py @@ -190,3 +190,21 @@ class TestOutsideRelative(UnsafeArchiveTester, unittest.TestCase): class TestOutsideAbsolute(UnsafeArchiveTester, unittest.TestCase): """An archive that goes outside the destination using absolute paths.""" archive = pathjoin('bad', 'absolute.tar.gz') + +class PerserveFilePermissionsTests(TempDirMixin, unittest.TestCase): + """ + The zipfile module in Pyhton does not perserve file permissions. See + burgundywall.com/post/preserving-file-perms-with-python-zipfile-module + """ + def setUp(self): + super(PerserveFilePermissionsTests, self).setUp() + + def test_extract_function(self): + + endianzip = pathjoin(TEST_DIR, 'files', 'endian-4.0.0.zip') + + extract(endianzip, self.tmpdir) + filepath = pathjoin(self.tmpdir, 'endian-4.0.0', 'waf') + + self.assertTrue(isfile(filepath)) + self.assertTrue(os.access(filepath, os.X_OK)) diff --git a/tox.ini b/tox.ini index 3778393..efe102d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,10 @@ [tox] -envlist = py26, py27, py32 -indexserver = - pypi = http://pypi.python.org/simple +envlist = py27, py35 [testenv] deps= - :pypi:pytest - :pypi:pep8 + pytest + pep8 commands = py.test -v tests