Hello community,
here is the log from the commit of package python-dephell-archive for
openSUSE:Factory checked in at 2020-03-03 10:18:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-dephell-archive (Old)
and /work/SRC/openSUSE:Factory/.python-dephell-archive.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-dephell-archive"
Tue Mar 3 10:18:55 2020 rev:3 rq:781035 version:0.1.6
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-dephell-archive/python-dephell-archive.changes
2019-09-13 15:05:14.361257744 +0200
+++
/work/SRC/openSUSE:Factory/.python-dephell-archive.new.26092/python-dephell-archive.changes
2020-03-03 10:20:36.751193127 +0100
@@ -1,0 +2,21 @@
+Mon Mar 2 16:44:57 CET 2020 - Matej Cepl <[email protected]>
+
+- Update to 0.1.6:
+ - Tests for recursive iterdir
+ - ArchiveStream: Implement zip dir exists and is_dir
+ - ArchiveStream: Cache implicit zip dirs
+ - fix member_path bool check
+ - group things in stream
+ - less hasattr checks
+ - sort imports
+ - ArchivePath: Implement iterdir() non-recursive
+ - ArchivePath: Implement iterdir() for subpaths
+ - apply changes to the new code
+ - reuse file name getting logic
+ - fix suffix check
+ - simplify prefix check
+ - bump version to 0.1.6
+- Unfortunately, upstream by mistake doesn’t include tests in the
+ release archive (gh#dephell/dephell_archive#20)
+
+-------------------------------------------------------------------
Old:
----
dephell-archive-0.1.5.tar.gz
New:
----
dephell-archive-0.1.6.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-dephell-archive.spec ++++++
--- /var/tmp/diff_new_pack.InHwPm/_old 2020-03-03 10:20:37.639194964 +0100
+++ /var/tmp/diff_new_pack.InHwPm/_new 2020-03-03 10:20:37.647194981 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-dephell-archive
#
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-dephell-archive
-Version: 0.1.5
+Version: 0.1.6
Release: 0
Summary: Pathlib for archives
License: MIT
@@ -52,7 +52,8 @@
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%check
-%pytest
+# gh#dephell/dephell_archive#20
+# %%pytest
%files %{python_files}
%license LICENSE
++++++ dephell-archive-0.1.5.tar.gz -> dephell-archive-0.1.6.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/dephell-archive-0.1.5/PKG-INFO
new/dephell-archive-0.1.6/PKG-INFO
--- old/dephell-archive-0.1.5/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
+++ new/dephell-archive-0.1.6/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: dephell-archive
-Version: 0.1.5
+Version: 0.1.6
Summary: pathlib for archives
Project-URL: Repository, https://github.com/dephell/dephell_archive
Author: Gram
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/dephell-archive-0.1.5/dephell_archive/__init__.py
new/dephell-archive-0.1.6/dephell_archive/__init__.py
--- old/dephell-archive-0.1.5/dephell_archive/__init__.py 2019-05-23
20:40:23.000000000 +0200
+++ new/dephell-archive-0.1.6/dephell_archive/__init__.py 2019-12-30
17:34:50.000000000 +0100
@@ -1,7 +1,9 @@
+# app
from ._path import ArchivePath
from ._stream import ArchiveStream
-__version__ = '0.1.5'
+
+__version__ = '0.1.6'
__author__ = 'Gram (@orsinium)'
__license__ = 'MIT'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/dephell-archive-0.1.5/dephell_archive/_cached_property.py
new/dephell-archive-0.1.6/dephell_archive/_cached_property.py
--- old/dephell-archive-0.1.5/dephell_archive/_cached_property.py
1970-01-01 01:00:00.000000000 +0100
+++ new/dephell-archive-0.1.6/dephell_archive/_cached_property.py
2019-12-30 17:34:10.000000000 +0100
@@ -0,0 +1,19 @@
+
+
+#
https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
+# https://github.com/pydanny/cached-property/blob/master/cached_property.py
+class cached_property(object): # noqa: N801
+ """
+ A property that is only computed once per instance and then replaces itself
+ with an ordinary attribute. Deleting the attribute resets the property.
+ """
+
+ def __init__(self, func):
+ self.__doc__ = func.__doc__
+ self.func = func
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ return self
+ value = obj.__dict__[self.func.__name__] = self.func(obj)
+ return value
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/dephell-archive-0.1.5/dephell_archive/_path.py
new/dephell-archive-0.1.6/dephell_archive/_path.py
--- old/dephell-archive-0.1.5/dephell_archive/_path.py 2019-04-19
10:04:50.000000000 +0200
+++ new/dephell-archive-0.1.6/dephell_archive/_path.py 2019-12-30
17:34:10.000000000 +0100
@@ -2,7 +2,7 @@
from contextlib import contextmanager, suppress
from pathlib import Path, PurePath
from tarfile import TarFile
-from typing import Callable, Iterator, Union
+from typing import Callable, Iterator, List, Optional, Set, Tuple, Union
from zipfile import ZipFile
# external
@@ -38,6 +38,10 @@
# properties
@property
+ def _is_root(self) -> bool:
+ return self.member_path.name == ''
+
+ @property
def extractor(self) -> Callable:
extension = ''
for suffix in reversed(self.archive_path.suffixes):
@@ -52,10 +56,51 @@
@property
def parent(self) -> Union['ArchivePath', Path]:
- if self.member_path:
+ if not self._is_root:
return self.copy(member_path=self.member_path.parent)
return self.archive_path
+ @property
+ def parts(self) -> Tuple[str, ...]:
+ return self.archive_path.parts + self.member_path.parts
+
+ @property
+ def drive(self) -> str:
+ return self.archive_path.drive
+
+ @property
+ def root(self) -> str:
+ return self.archive_path.root
+
+ @property
+ def anchor(self) -> str:
+ return self.archive_path.anchor
+
+ @property
+ def parents(self) -> Tuple[Union['ArchivePath', Path], ...]:
+ parents = [] # type: List[Union[ArchivePath, Path]]
+ for parent in self.member_path.parents:
+ parents.append(self.copy(member_path=parent))
+
+ parents += list(self.archive_path.parents)
+ return tuple(parents)
+
+ @property
+ def suffix(self) -> str:
+ if not self._is_root:
+ return self.member_path.suffix
+ return self.archive_path.suffix
+
+ @property
+ def suffixes(self) -> List[str]:
+ if not self._is_root:
+ return self.member_path.suffixes
+ return self.archive_path.suffixes
+
+ @property
+ def stem(self) -> str:
+ return self.member_path.stem or self.archive_path.stem
+
# context managers
@contextmanager
@@ -79,23 +124,59 @@
@contextmanager
def open(self, mode: str = 'r', encoding=None):
+ if 'w' in mode:
+ raise NotImplementedError
+
+ if self._is_root:
+ raise IsADirectoryError
+
# read from cache
path = self.cache_path / self.member_path
if path.exists():
with path.open(mode, encoding=encoding) as stream:
yield stream
- else:
- with self.get_descriptor() as descriptor:
- yield ArchiveStream(
- descriptor=descriptor,
- cache_path=self.cache_path,
- member_path=self.member_path,
- mode=mode,
- encoding=encoding,
- )
+ return
+
+ # stream from the archivew
+ with self.get_descriptor() as descriptor:
+ yield ArchiveStream(
+ descriptor=descriptor,
+ cache_path=self.cache_path,
+ member_path=self.member_path,
+ mode=mode,
+ encoding=encoding,
+ )
# methods
+ def as_posix(self) -> str:
+ if not self._is_root:
+ return self.archive_path.joinpath(self.member_path).as_posix()
+ return self.archive_path.as_posix()
+
+ def as_uri(self) -> str:
+ if not self._is_root:
+ return self.archive_path.joinpath(self.member_path).as_uri()
+ return self.archive_path.as_uri()
+
+ def is_absolute(self):
+ return self.archive_path.is_absolute()
+
+ def is_reserved(self):
+ return self.archive_path.is_reserved()
+
+ def joinpath(self, *other):
+ member_path = self.member_path.joinpath(*other)
+ return self.copy(member_path=member_path)
+
+ def expanduser(self):
+ archive_path = self.archive_path.expanduser()
+ return self.copy(archive_path=archive_path)
+
+ def resolve(self):
+ archive_path = self.archive_path.resolve()
+ return self.copy(archive_path=archive_path)
+
def copy(self, **kwargs) -> 'ArchivePath':
params = attr.asdict(self, recurse=False)
params.update(kwargs)
@@ -105,22 +186,52 @@
new._descriptor = self._descriptor
return new
- def iterdir(self, recursive: bool = False) -> Iterator['ArchivePath']:
+ def _get_file_name(self, member) -> Optional[str]:
+ name = getattr(member, 'name', None) or member.filename
+ if self._is_root:
+ return name
+ prefix = self.member_path.as_posix() + '/'
+ if not name.startswith(prefix):
+ return None
+ name = name[len(prefix):]
+ return name or None
+
+ def iterdir(self, _recursive: bool = True) -> Iterator['ArchivePath']:
with self.get_descriptor() as descriptor:
if hasattr(descriptor, 'getmembers'):
members = descriptor.getmembers() # tar
else:
members = descriptor.infolist() # zip
+ top_level_items = set() # type: Set[str]
# get files
for member in members:
- name = getattr(member, 'name', None) or member.filename
+ name = self._get_file_name(member=member)
+ if name is None:
+ continue
+
+ if not _recursive:
+ path, _sep, _name = name.partition('/')
+ if path in top_level_items:
+ continue
+
+ top_level_items.add(path)
+ yield self.copy(member_path=PurePath(path))
+ continue
+
yield self.copy(member_path=PurePath(name))
+ if not _recursive:
+ return
+
# get dirs
names = set()
for member in members:
- name = getattr(member, 'name', None) or member.filename
+ name = self._get_file_name(member=member)
+ if name is None:
+ continue
+
+ name = name.rstrip('/')
names.add(name)
dirs = {''}
for name in names:
@@ -131,17 +242,37 @@
yield self.copy(member_path=PurePath(path))
def glob(self, pattern: str) -> Iterator['ArchivePath']:
- for path in self.iterdir(recursive=True):
- if glob_path(path=path.as_posix(), pattern=pattern):
+ for path in self.iterdir(_recursive=True):
+ if glob_path(path=path.member_path.as_posix(), pattern=pattern):
yield path
def exists(self) -> bool:
+ if self._is_root:
+ return True
path = self.cache_path / self.member_path
if path.exists():
return True
with self.open() as stream:
return stream.exists()
+ def is_file(self) -> bool:
+ if self._is_root:
+ return False
+ path = self.cache_path / self.member_path
+ if path.exists():
+ return path.is_file()
+ with self.open() as stream:
+ return stream.is_file()
+
+ def is_dir(self) -> bool:
+ if self._is_root:
+ return True
+ path = self.cache_path / self.member_path
+ if path.exists():
+ return path.is_dir()
+ with self.open() as stream:
+ return stream.is_dir()
+
def read_bytes(self):
"""
Open the file in bytes mode, read it, and close the file.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/dephell-archive-0.1.5/dephell_archive/_stream.py
new/dephell-archive-0.1.6/dephell_archive/_stream.py
--- old/dephell-archive-0.1.5/dephell_archive/_stream.py 2019-04-19
10:04:50.000000000 +0200
+++ new/dephell-archive-0.1.6/dephell_archive/_stream.py 2019-12-30
17:34:10.000000000 +0100
@@ -1,11 +1,28 @@
+# built-in
+from contextlib import suppress
from pathlib import Path, PurePath
-from typing import Optional
+from typing import List, Optional, Set
# external
import attr
+# app
+from ._cached_property import cached_property
[email protected](slots=True)
+
+def _dir_list(filelist: List[str]) -> Set[str]:
+ # paths starting with '/' or containing '.' are not supported
+ dir_list = set() # type: Set[str]
+ for path in filelist:
+ while path:
+ path, _, _ = path.rpartition('/')
+ if not path or path in dir_list:
+ break
+ dir_list.add(path)
+ return dir_list
+
+
[email protected]()
class ArchiveStream:
descriptor = attr.ib()
cache_path = attr.ib(type=Path)
@@ -14,29 +31,69 @@
mode = attr.ib(type=str, default='r')
encoding = attr.ib(type=Optional[str], default=None)
+ # private
+
+ @cached_property
+ def _is_tar(self) -> bool:
+ return hasattr(self.descriptor, 'getmember')
+
+ @cached_property
+ def _dir_list(self) -> Set[str]:
+ return _dir_list(self.descriptor.namelist())
+
+ @cached_property
+ def _info(self):
+ path = self.member_path.as_posix()
+ with suppress(KeyError):
+ if self._is_tar:
+ return self.descriptor.getmember(path)
+ try:
+ return self.descriptor.getinfo(path) # zip file
+ except KeyError:
+ return self.descriptor.getinfo(path + '/') # zip dir
+ return None
+
+ @cached_property
+ def _is_implicit_dir(self) -> bool:
+ # Only zip have implicit dirs
+ if self._is_tar:
+ return False
+ path = self.member_path.as_posix()
+ return path in self._dir_list
+
+ # used from ArchivePath
+
def exists(self) -> bool:
- try:
- if hasattr(self.descriptor, 'getmember'):
- self.descriptor.getmember(str(self.member_path)) # tar
- else:
- self.descriptor.getinfo(str(self.member_path)) # zip
- except KeyError:
+ return self.is_file() or self.is_dir()
+
+ def is_file(self) -> bool:
+ if self._info is None:
return False
- return True
+ if self._is_tar:
+ return self._info.isfile()
+ # zip
+ return self._info.filename[-1] != '/'
+
+ def is_dir(self) -> bool:
+ if self._info is None:
+ return self._is_implicit_dir
+ if self._is_tar:
+ return self._info.isdir()
+ # zip explicit dir entry
+ return self._info.filename[-1] == '/'
+
+ # public interface
def read(self):
+ if not self.member_path.name:
+ raise NotImplementedError
+
path = self.cache_path / self.member_path
if path.exists():
raise FileExistsError('file in cache created between open and
read')
# extract to cache
- if hasattr(self.descriptor, 'getmember'):
- # tar
- member = self.descriptor.getmember(self.member_path.as_posix())
- else:
- # zip
- member = self.descriptor.getinfo(self.member_path.as_posix())
- self.descriptor.extract(member=member, path=str(self.cache_path))
+ self.descriptor.extract(member=self._info, path=str(self.cache_path))
# read from cache
with path.open(self.mode, encoding=self.encoding) as stream:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/dephell-archive-0.1.5/dephell_archive.egg-info/PKG-INFO
new/dephell-archive-0.1.6/dephell_archive.egg-info/PKG-INFO
--- old/dephell-archive-0.1.5/dephell_archive.egg-info/PKG-INFO 1970-01-01
01:00:00.000000000 +0100
+++ new/dephell-archive-0.1.6/dephell_archive.egg-info/PKG-INFO 1970-01-01
01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: dephell-archive
-Version: 0.1.5
+Version: 0.1.6
Summary: pathlib for archives
Project-URL: Repository, https://github.com/dephell/dephell_archive
Author: Gram
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/dephell-archive-0.1.5/dephell_archive.egg-info/SOURCES.txt
new/dephell-archive-0.1.6/dephell_archive.egg-info/SOURCES.txt
--- old/dephell-archive-0.1.5/dephell_archive.egg-info/SOURCES.txt
1970-01-01 01:00:00.000000000 +0100
+++ new/dephell-archive-0.1.6/dephell_archive.egg-info/SOURCES.txt
1970-01-01 01:00:00.000000000 +0100
@@ -2,7 +2,8 @@
README.rst
setup.cfg
setup.py
-dephell_archive/_path.py
dephell_archive/_glob.py
+dephell_archive/_cached_property.py
+dephell_archive/_stream.py
dephell_archive/__init__.py
-dephell_archive/_stream.py
\ No newline at end of file
+dephell_archive/_path.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/dephell-archive-0.1.5/setup.py
new/dephell-archive-0.1.6/setup.py
--- old/dephell-archive-0.1.5/setup.py 2019-05-23 20:40:25.000000000 +0200
+++ new/dephell-archive-0.1.6/setup.py 2019-12-30 17:34:52.000000000 +0100
@@ -21,10 +21,10 @@
setup(
long_description=readme,
name='dephell-archive',
- version='0.1.5',
+ version='0.1.6',
description='pathlib for archives',
python_requires='>=3.5',
- project_urls={'repository': 'https://github.com/dephell/dephell_archive'},
+ project_urls={"repository": "https://github.com/dephell/dephell_archive"},
author='Gram',
author_email='[email protected]',
license='MIT',
@@ -47,6 +47,7 @@
'Operating System :: Unix'
],
packages=['dephell_archive'],
+ package_dir={"": "."},
package_data={},
install_requires=['attrs'],
)
Binary files
old/dephell-archive-0.1.5/tests/__pycache__/__init__.cpython-37.pyc and
new/dephell-archive-0.1.6/tests/__pycache__/__init__.cpython-37.pyc differ
Binary files
old/dephell-archive-0.1.5/tests/__pycache__/test_glob.cpython-37-PYTEST.pyc and
new/dephell-archive-0.1.6/tests/__pycache__/test_glob.cpython-37-PYTEST.pyc
differ
Binary files
old/dephell-archive-0.1.5/tests/__pycache__/test_path.cpython-37-PYTEST.pyc and
new/dephell-archive-0.1.6/tests/__pycache__/test_path.cpython-37-PYTEST.pyc
differ
Binary files old/dephell-archive-0.1.5/tests/requirements/sdist.tar.gz and
new/dephell-archive-0.1.6/tests/requirements/sdist.tar.gz differ
Binary files old/dephell-archive-0.1.5/tests/requirements/wheel.whl and
new/dephell-archive-0.1.6/tests/requirements/wheel.whl differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/dephell-archive-0.1.5/tests/test_glob.py
new/dephell-archive-0.1.6/tests/test_glob.py
--- old/dephell-archive-0.1.5/tests/test_glob.py 2019-04-19
10:04:50.000000000 +0200
+++ new/dephell-archive-0.1.6/tests/test_glob.py 1970-01-01
01:00:00.000000000 +0100
@@ -1,41 +0,0 @@
-# external
-import pytest
-
-# project
-from dephell_archive._glob import glob_path
-
-
[email protected]('path, pattern, ok', [
- ('/lol/', '/lol/', True),
- ('/lol/', '/lal/', False),
-
- ('lol', '/lol/', True),
- ('lol', '/lal/', False),
-
- ('/lol/', '/l*', True),
- ('/tol/', '/l*', False),
- ('l', '/l*', True),
- ('lal', '/l*t', False),
-
- ('lol/lal', '*/lal', True),
-
- ('lol/lal', '**/lal', True),
- ('lol/lal', 'lol/**', True),
- ('lol/lal', 'lal/**', False),
-
- ('lol/lal', '*/lal', True),
- ('lol/lal/kek', '*/lal', False),
- ('lol/lal/kek', '*/kek', False),
- ('lol/lal/kek', '*/lal/*', True),
- ('lol/lal/kek', '**/kek', True),
-
- ('lol.egg', '*.egg', True),
- ('lol/lal.egg', '*.egg', False),
- ('lol/lal/kek.egg', '*.egg', False),
-
- ('lol', '*/*.egg', False),
- ('lol/lal.egg', '*/*.egg', True),
- ('lol/lal/kek.egg', '*/*.egg', False),
-])
-def test_glob_path(path, pattern, ok):
- assert glob_path(path=path, pattern=pattern) is ok
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/dephell-archive-0.1.5/tests/test_path.py
new/dephell-archive-0.1.6/tests/test_path.py
--- old/dephell-archive-0.1.5/tests/test_path.py 2019-04-19
10:04:50.000000000 +0200
+++ new/dephell-archive-0.1.6/tests/test_path.py 1970-01-01
01:00:00.000000000 +0100
@@ -1,68 +0,0 @@
-# built-in
-from pathlib import Path
-
-# project
-from dephell_archive import ArchivePath
-
-
-def test_open_zip(tmpdir):
- path = ArchivePath(
- archive_path=Path('tests', 'requirements', 'wheel.whl'),
- cache_path=Path(str(tmpdir)),
- )
- subpath = path / 'dephell' / '__init__.py'
- with subpath.open() as stream:
- content = stream.read()
- assert 'from .controllers' in content
-
-
-def test_open_tar_gz(tmpdir):
- path = ArchivePath(
- archive_path=Path('tests', 'requirements', 'sdist.tar.gz'),
- cache_path=Path(str(tmpdir)),
- )
- subpath = path / 'dephell-0.2.0' / 'setup.py'
- with subpath.open() as stream:
- content = stream.read()
- assert 'from setuptools import' in content
-
-
-def test_glob_zip(tmpdir):
- path = ArchivePath(
- archive_path=Path('tests', 'requirements', 'wheel.whl'),
- cache_path=Path(str(tmpdir)),
- )
- paths = list(path.glob('*/__init__.py'))
- assert len(paths) == 1
- assert paths[0].as_posix() == 'dephell/__init__.py'
-
-
-def test_glob_tar(tmpdir):
- path = ArchivePath(
- archive_path=Path('tests', 'requirements', 'sdist.tar.gz'),
- cache_path=Path(str(tmpdir)),
- )
- paths = list(path.glob('*/setup.py'))
- assert len(paths) == 1
- assert paths[0].as_posix() == 'dephell-0.2.0/setup.py'
-
-
-def test_glob_dir(tmpdir):
- path = ArchivePath(
- archive_path=Path('tests', 'requirements', 'sdist.tar.gz'),
- cache_path=Path(str(tmpdir)),
- )
- paths = list(path.glob('dephell-*/'))
- assert len(paths) == 1
-
-
-def test_iterdir(tmpdir):
- path = ArchivePath(
- archive_path=Path('tests', 'requirements', 'sdist.tar.gz'),
- cache_path=Path(str(tmpdir)),
- )
- paths = [str(subpath) for subpath in path.iterdir(recursive=True)]
-
- for path in paths:
- assert paths.count(path) == 1, 'duplicate dir: ' + path
- assert 'dephell-0.2.0' in paths