Skip to content

Commit

Permalink
Tar pit (#291)
Browse files Browse the repository at this point in the history
* Fix for broken directory

* use cache

* changelog

* version bump

* remove pyyaml from test requirements

* drop python 3.3

* changelog

* remove 3.3 from CI

* rename _directory to _directory_entries

* version bump
  • Loading branch information
willmcgugan committed Jun 8, 2019
1 parent 4e6031f commit ea33224
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 49 deletions.
3 changes: 0 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ matrix:
- python: "3.4"
env:
- SETUPTOOLS=setuptools PIP=pip
- python: "3.3"
env:
- SETUPTOOLS=setuptools~=39.2 PIP="pip<=18.0"

before_install:
- pip install $SETUPTOOLS $PIP -U
Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.5.6] - Unreleased
## [2.5.6] - 2019-06-08

### Added

Expand All @@ -14,6 +14,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed

- Fixed FTP test suite when time is not UTC-0 @mrg0029
- Fixed issues with paths in tarfs https://github.com/PyFilesystem/pyfilesystem2/issues/284

### Changed

- Dropped Python3.3 support

## [2.4.5] - 2019-05-05

Expand Down
2 changes: 1 addition & 1 deletion fs/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Version, used in module and setup.py.
"""
__version__ = "2.4.6a0"
__version__ = "2.4.6"
35 changes: 25 additions & 10 deletions fs/tarfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .base import FS
from .compress import write_tar
from .enums import ResourceType
from .errors import IllegalBackReference
from .info import Info
from .iotools import RawWrapper
from .opener import open_fs
Expand Down Expand Up @@ -275,14 +276,26 @@ def __init__(self, file, encoding="utf-8"):
self._directory_cache = None

@property
def _directory(self):
def _directory_entries(self):
"""Lazy directory cache."""
if self._directory_cache is None:
_decode = self._decode
_directory = ((_decode(info.name).strip("/"), info) for info in self._tar)
self._directory_cache = OrderedDict(
(name, info) for name, info in _directory if normpath(name)
_directory_entries = (
(_decode(info.name).strip("/"), info) for info in self._tar
)

def _list_tar():
for name, info in _directory_entries:
try:
_name = normpath(name)
except IllegalBackReference:
# Back references outside root, must be up to no good.
pass
else:
if _name:
yield _name, info

self._directory_cache = OrderedDict(_list_tar())
return self._directory_cache

def __repr__(self):
Expand Down Expand Up @@ -327,7 +340,7 @@ def getinfo(self, path, namespaces=None):
else:
try:
implicit = False
member = self._tar.getmember(self._encode(_path))
member = self._directory_entries[_path]
except KeyError:
if not self.isdir(_path):
raise errors.ResourceNotFound(path)
Expand Down Expand Up @@ -370,14 +383,14 @@ def getinfo(self, path, namespaces=None):
def isdir(self, path):
_path = relpath(self.validatepath(path))
try:
return self._directory[_path].isdir()
return self._directory_entries[_path].isdir()
except KeyError:
return any(isbase(_path, name) for name in self._directory)
return any(isbase(_path, name) for name in self._directory_entries)

def isfile(self, path):
_path = relpath(self.validatepath(path))
try:
return self._directory[_path].isfile()
return self._directory_entries[_path].isfile()
except KeyError:
return False

Expand All @@ -393,7 +406,9 @@ def listdir(self, path):
if not self.gettype(path) is ResourceType.directory:
raise errors.DirectoryExpected(path)

children = (frombase(_path, n) for n in self._directory if isbase(_path, n))
children = (
frombase(_path, n) for n in self._directory_entries if isbase(_path, n)
)
content = (parts(child)[1] for child in children if relpath(child))
return list(OrderedDict.fromkeys(content))

Expand All @@ -415,7 +430,7 @@ def openbin(self, path, mode="r", buffering=-1, **options):
raise errors.ResourceReadOnly(path)

try:
member = self._tar.getmember(self._encode(_path))
member = self._directory_entries[_path]
except KeyError:
six.raise_from(errors.ResourceNotFound(path), None)

Expand Down
50 changes: 21 additions & 29 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,24 @@

from setuptools import setup, find_packages

with open('fs/_version.py') as f:
with open("fs/_version.py") as f:
exec(f.read())

CLASSIFIERS = [
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Topic :: System :: Filesystems',
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Topic :: System :: Filesystems",
]

REQUIREMENTS = [
"appdirs~=1.4.3",
"pytz",
"setuptools",
"six~=1.10",
]
REQUIREMENTS = ["appdirs~=1.4.3", "pytz", "setuptools", "six~=1.10"]

setup(
author="Will McGugan",
Expand All @@ -34,21 +28,19 @@
description="Python's filesystem abstraction layer",
install_requires=REQUIREMENTS,
extras_require={
"scandir :python_version < '3.5'": ['scandir~=1.5'],
":python_version < '3.4'": ['enum34~=1.1.6'],
":python_version < '3.6'": ['typing~=3.6'],
":python_version < '3.0'": ['backports.os~=0.1']
"scandir :python_version < '3.5'": ["scandir~=1.5"],
":python_version < '3.4'": ["enum34~=1.1.6"],
":python_version < '3.6'": ["typing~=3.6"],
":python_version < '3.0'": ["backports.os~=0.1"],
},
license="MIT",
name='fs',
name="fs",
packages=find_packages(exclude=("tests",)),
package_data = {
'fs': ['py.typed'],
},
package_data={"fs": ["py.typed"]},
zip_safe=False,
platforms=['any'],
platforms=["any"],
test_suite="nose.collector",
tests_require=['appdirs', 'mock', 'pytz', 'pyftpdlib'],
tests_require=["appdirs", "mock", "pytz", "pyftpdlib"],
url="https://github.com/PyFilesystem/pyfilesystem2",
version=__version__,
)
1 change: 0 additions & 1 deletion testrequirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ appdirs~=1.4.0
coverage
mock
pyftpdlib==1.5.2
PyYAML<4.1 ; python_version == '3.3'
python-coveralls
pytz==2016.7
nose
9 changes: 5 additions & 4 deletions tests/test_tarfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def test_getinfo(self):
self.assertTrue(top.get("tar", "is_file"))


class TestBrokenDir(unittest.TestCase):
class TestBrokenPaths(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.tmpfs = open_fs("temp://tarfstest")
Expand All @@ -200,7 +200,8 @@ def tearDownClass(cls):
def setUp(self):
self.tempfile = self.tmpfs.open("test.tar", "wb+")
with tarfile.open(mode="w", fileobj=self.tempfile) as tf:
tf.addfile(tarfile.TarInfo("."), io.StringIO)
tf.addfile(tarfile.TarInfo("."), io.StringIO())
tf.addfile(tarfile.TarInfo("../foo.txt"), io.StringIO())
self.tempfile.seek(0)
self.fs = tarfs.TarFS(self.tempfile)

Expand Down Expand Up @@ -228,8 +229,8 @@ def setUp(self):
self.tempfile = self.tmpfs.open("test.tar", "wb+")
with tarfile.open(mode="w", fileobj=self.tempfile) as tf:
tf.addfile(tarfile.TarInfo("foo/bar/baz/spam.txt"), io.StringIO())
tf.addfile(tarfile.TarInfo("foo/eggs.bin"), io.StringIO())
tf.addfile(tarfile.TarInfo("foo/yolk/beans.txt"), io.StringIO())
tf.addfile(tarfile.TarInfo("./foo/eggs.bin"), io.StringIO())
tf.addfile(tarfile.TarInfo("./foo/yolk/beans.txt"), io.StringIO())
info = tarfile.TarInfo("foo/yolk")
info.type = tarfile.DIRTYPE
tf.addfile(info, io.BytesIO())
Expand Down

0 comments on commit ea33224

Please sign in to comment.