From e1dad4d3a64de1d89707f310be7648bb8d437a26 Mon Sep 17 00:00:00 2001 From: Ken Dreyer Date: Tue, 26 Nov 2019 16:36:28 -0700 Subject: [PATCH] import initial package (rhbz#1772578) --- .gitignore | 1 + ...aise-NotImplementedError-on-Python-2.patch | 302 ++++++++++++++++++ package.cfg | 2 - python-importlib-resources.spec | 72 +++++ sources | 1 + 5 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 0001-raise-NotImplementedError-on-Python-2.patch delete mode 100644 package.cfg create mode 100644 python-importlib-resources.spec create mode 100644 sources diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be4fd43 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/importlib_resources-1.0.2.tar.gz diff --git a/0001-raise-NotImplementedError-on-Python-2.patch b/0001-raise-NotImplementedError-on-Python-2.patch new file mode 100644 index 0000000..f3b6989 --- /dev/null +++ b/0001-raise-NotImplementedError-on-Python-2.patch @@ -0,0 +1,302 @@ +From a6a3a716c97b438ed64d5368e3462c6787ba0b85 Mon Sep 17 00:00:00 2001 +From: Ken Dreyer +Date: Fri, 8 Nov 2019 09:13:38 -0700 +Subject: [PATCH] raise NotImplementedError on Python 2 + +--- + importlib_resources/__init__.py | 4 +- + importlib_resources/_py2.py | 270 ---------------------------------------- + 2 files changed, 1 insertion(+), 273 deletions(-) + delete mode 100644 importlib_resources/_py2.py + +diff --git a/importlib_resources/__init__.py b/importlib_resources/__init__.py +index fab437a..b012e5c 100644 +--- a/importlib_resources/__init__.py ++++ b/importlib_resources/__init__.py +@@ -28,9 +28,7 @@ elif sys.version_info >= (3,): + from importlib_resources.abc import ResourceReader + __all__.extend(['Package', 'Resource', 'ResourceReader']) + else: +- from importlib_resources._py2 import ( +- contents, is_resource, open_binary, open_text, path, read_binary, +- read_text) ++ raise NotImplementedError('Fedora package does not support Python 2') + + + __version__ = read_text('importlib_resources', 'version.txt').strip() +diff --git a/importlib_resources/_py2.py b/importlib_resources/_py2.py +deleted file mode 100644 +index 376f0e3..0000000 +--- a/importlib_resources/_py2.py ++++ /dev/null +@@ -1,270 +0,0 @@ +-import os +-import errno +-import tempfile +- +-from ._compat import FileNotFoundError +-from contextlib import contextmanager +-from importlib import import_module +-from io import BytesIO, TextIOWrapper, open as io_open +-from pathlib2 import Path +-from zipfile import ZipFile +- +- +-def _get_package(package): +- """Normalize a path by ensuring it is a string. +- +- If the resulting string contains path separators, an exception is raised. +- """ +- if isinstance(package, basestring): # noqa: F821 +- module = import_module(package) +- else: +- module = package +- if not hasattr(module, '__path__'): +- raise TypeError("{!r} is not a package".format(package)) +- return module +- +- +-def _normalize_path(path): +- """Normalize a path by ensuring it is a string. +- +- If the resulting string contains path separators, an exception is raised. +- """ +- str_path = str(path) +- parent, file_name = os.path.split(str_path) +- if parent: +- raise ValueError("{!r} must be only a file name".format(path)) +- else: +- return file_name +- +- +-def open_binary(package, resource): +- """Return a file-like object opened for binary reading of the resource.""" +- resource = _normalize_path(resource) +- package = _get_package(package) +- # Using pathlib doesn't work well here due to the lack of 'strict' argument +- # for pathlib.Path.resolve() prior to Python 3.6. +- package_path = os.path.dirname(package.__file__) +- relative_path = os.path.join(package_path, resource) +- full_path = os.path.abspath(relative_path) +- try: +- return io_open(full_path, 'rb') +- except IOError: +- # This might be a package in a zip file. zipimport provides a loader +- # with a functioning get_data() method, however we have to strip the +- # archive (i.e. the .zip file's name) off the front of the path. This +- # is because the zipimport loader in Python 2 doesn't actually follow +- # PEP 302. It should allow the full path, but actually requires that +- # the path be relative to the zip file. +- try: +- loader = package.__loader__ +- full_path = relative_path[len(loader.archive)+1:] +- data = loader.get_data(full_path) +- except (IOError, AttributeError): +- package_name = package.__name__ +- message = '{!r} resource not found in {!r}'.format( +- resource, package_name) +- raise FileNotFoundError(message) +- else: +- return BytesIO(data) +- +- +-def open_text(package, resource, encoding='utf-8', errors='strict'): +- """Return a file-like object opened for text reading of the resource.""" +- resource = _normalize_path(resource) +- package = _get_package(package) +- # Using pathlib doesn't work well here due to the lack of 'strict' argument +- # for pathlib.Path.resolve() prior to Python 3.6. +- package_path = os.path.dirname(package.__file__) +- relative_path = os.path.join(package_path, resource) +- full_path = os.path.abspath(relative_path) +- try: +- return io_open(full_path, mode='r', encoding=encoding, errors=errors) +- except IOError: +- # This might be a package in a zip file. zipimport provides a loader +- # with a functioning get_data() method, however we have to strip the +- # archive (i.e. the .zip file's name) off the front of the path. This +- # is because the zipimport loader in Python 2 doesn't actually follow +- # PEP 302. It should allow the full path, but actually requires that +- # the path be relative to the zip file. +- try: +- loader = package.__loader__ +- full_path = relative_path[len(loader.archive)+1:] +- data = loader.get_data(full_path) +- except (IOError, AttributeError): +- package_name = package.__name__ +- message = '{!r} resource not found in {!r}'.format( +- resource, package_name) +- raise FileNotFoundError(message) +- else: +- return TextIOWrapper(BytesIO(data), encoding, errors) +- +- +-def read_binary(package, resource): +- """Return the binary contents of the resource.""" +- resource = _normalize_path(resource) +- package = _get_package(package) +- with open_binary(package, resource) as fp: +- return fp.read() +- +- +-def read_text(package, resource, encoding='utf-8', errors='strict'): +- """Return the decoded string of the resource. +- +- The decoding-related arguments have the same semantics as those of +- bytes.decode(). +- """ +- resource = _normalize_path(resource) +- package = _get_package(package) +- with open_text(package, resource, encoding, errors) as fp: +- return fp.read() +- +- +-@contextmanager +-def path(package, resource): +- """A context manager providing a file path object to the resource. +- +- If the resource does not already exist on its own on the file system, +- a temporary file will be created. If the file was created, the file +- will be deleted upon exiting the context manager (no exception is +- raised if the file was deleted prior to the context manager +- exiting). +- """ +- resource = _normalize_path(resource) +- package = _get_package(package) +- package_directory = Path(package.__file__).parent +- file_path = package_directory / resource +- # If the file actually exists on the file system, just return it. +- # Otherwise, it's probably in a zip file, so we need to create a temporary +- # file and copy the contents into that file, hence the contextmanager to +- # clean up the temp file resource. +- if file_path.exists(): +- yield file_path +- else: +- with open_binary(package, resource) as fp: +- data = fp.read() +- # Not using tempfile.NamedTemporaryFile as it leads to deeper 'try' +- # blocks due to the need to close the temporary file to work on Windows +- # properly. +- fd, raw_path = tempfile.mkstemp() +- try: +- os.write(fd, data) +- os.close(fd) +- yield Path(raw_path) +- finally: +- try: +- os.remove(raw_path) +- except FileNotFoundError: +- pass +- +- +-def is_resource(package, name): +- """True if name is a resource inside package. +- +- Directories are *not* resources. +- """ +- package = _get_package(package) +- _normalize_path(name) +- try: +- package_contents = set(contents(package)) +- except OSError as error: +- if error.errno not in (errno.ENOENT, errno.ENOTDIR): +- # We won't hit this in the Python 2 tests, so it'll appear +- # uncovered. We could mock os.listdir() to return a non-ENOENT or +- # ENOTDIR, but then we'd have to depend on another external +- # library since Python 2 doesn't have unittest.mock. It's not +- # worth it. +- raise # pragma: nocover +- return False +- if name not in package_contents: +- return False +- # Just because the given file_name lives as an entry in the package's +- # contents doesn't necessarily mean it's a resource. Directories are not +- # resources, so let's try to find out if it's a directory or not. +- path = Path(package.__file__).parent / name +- if path.is_file(): +- return True +- if path.is_dir(): +- return False +- # If it's not a file and it's not a directory, what is it? Well, this +- # means the file doesn't exist on the file system, so it probably lives +- # inside a zip file. We have to crack open the zip, look at its table of +- # contents, and make sure that this entry doesn't have sub-entries. +- archive_path = package.__loader__.archive # type: ignore +- package_directory = Path(package.__file__).parent +- with ZipFile(archive_path) as zf: +- toc = zf.namelist() +- relpath = package_directory.relative_to(archive_path) +- candidate_path = relpath / name +- for entry in toc: # pragma: nobranch +- try: +- relative_to_candidate = Path(entry).relative_to(candidate_path) +- except ValueError: +- # The two paths aren't relative to each other so we can ignore it. +- continue +- # Since directories aren't explicitly listed in the zip file, we must +- # infer their 'directory-ness' by looking at the number of path +- # components in the path relative to the package resource we're +- # looking up. If there are zero additional parts, it's a file, i.e. a +- # resource. If there are more than zero it's a directory, i.e. not a +- # resource. It has to be one of these two cases. +- return len(relative_to_candidate.parts) == 0 +- # I think it's impossible to get here. It would mean that we are looking +- # for a resource in a zip file, there's an entry matching it in the return +- # value of contents(), but we never actually found it in the zip's table of +- # contents. +- raise AssertionError('Impossible situation') +- +- +-def contents(package): +- """Return an iterable of entries in `package`. +- +- Note that not all entries are resources. Specifically, directories are +- not considered resources. Use `is_resource()` on each entry returned here +- to check if it is a resource or not. +- """ +- package = _get_package(package) +- package_directory = Path(package.__file__).parent +- try: +- return os.listdir(str(package_directory)) +- except OSError as error: +- if error.errno not in (errno.ENOENT, errno.ENOTDIR): +- # We won't hit this in the Python 2 tests, so it'll appear +- # uncovered. We could mock os.listdir() to return a non-ENOENT or +- # ENOTDIR, but then we'd have to depend on another external +- # library since Python 2 doesn't have unittest.mock. It's not +- # worth it. +- raise # pragma: nocover +- # The package is probably in a zip file. +- archive_path = getattr(package.__loader__, 'archive', None) +- if archive_path is None: +- raise +- relpath = package_directory.relative_to(archive_path) +- with ZipFile(archive_path) as zf: +- toc = zf.namelist() +- subdirs_seen = set() # type: Set +- subdirs_returned = [] +- for filename in toc: +- path = Path(filename) +- # Strip off any path component parts that are in common with the +- # package directory, relative to the zip archive's file system +- # path. This gives us all the parts that live under the named +- # package inside the zip file. If the length of these subparts is +- # exactly 1, then it is situated inside the package. The resulting +- # length will be 0 if it's above the package, and it will be +- # greater than 1 if it lives in a subdirectory of the package +- # directory. +- # +- # However, since directories themselves don't appear in the zip +- # archive as a separate entry, we need to return the first path +- # component for any case that has > 1 subparts -- but only once! +- if path.parts[:len(relpath.parts)] != relpath.parts: +- continue +- subparts = path.parts[len(relpath.parts):] +- if len(subparts) == 1: +- subdirs_returned.append(subparts[0]) +- elif len(subparts) > 1: # pragma: nobranch +- subdir = subparts[0] +- if subdir not in subdirs_seen: +- subdirs_seen.add(subdir) +- subdirs_returned.append(subdir) +- return subdirs_returned diff --git a/package.cfg b/package.cfg deleted file mode 100644 index 66ea79d..0000000 --- a/package.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[koji] -targets = epel8 epel8-playground \ No newline at end of file diff --git a/python-importlib-resources.spec b/python-importlib-resources.spec new file mode 100644 index 0000000..0ffa2d6 --- /dev/null +++ b/python-importlib-resources.spec @@ -0,0 +1,72 @@ +%global pypi_name importlib_resources + +%global desc \ +importlib_resources is a backport of Python 3.7's standard library\ +importlib.resources module for 3.4 through 3.6. Users of Python 3.7 and\ +beyond should use the standard library module, since for these\ +versions, importlib_resources just delegates to that module. + + +Name: python-importlib-resources +Version: 1.0.2 +Release: 1%{?dist} +Summary: Read resources from Python packages + +License: ASL 2.0 +URL: https://importlib-resources.readthedocs.io/ +Source0: %pypi_source + +Patch0001: 0001-raise-NotImplementedError-on-Python-2.patch + +BuildArch: noarch + +BuildRequires: python3-devel +BuildRequires: python3dist(sphinx) +BuildRequires: python3dist(wheel) + +%description %{desc} + +%package -n python3-importlib-resources +Summary: %{summary} +%{?python_provide:%python_provide python3-%{pypi_name}} +%description -n python3-importlib-resources %{desc} + +%package doc +Summary: importlib_resources documentation +%description doc +Documentation for importlib_resources + +%prep +%autosetup -p1 -n %{pypi_name}-%{version} +# Remove bundled egg-info +rm -rf %{pypi_name}.egg-info + +%build +%py3_build +# generate html docs +PYTHONPATH=${PWD} sphinx-build-3 importlib_resources/docs html +# remove the sphinx-build leftovers +rm -rf html/.{doctrees,buildinfo} + +%install +%py3_install + +# Don't ship docs sources or tests +rm -r %{buildroot}/%{python3_sitelib}/%{pypi_name}/{docs,tests}/ + +%check +%{__python3} setup.py test + +%files -n python3-importlib-resources +%license LICENSE +%doc README.rst +%{python3_sitelib}/%{pypi_name} +%{python3_sitelib}/%{pypi_name}-%{version}-py?.?.egg-info + +%files doc +%license LICENSE +%doc html + +%changelog +* Thu Nov 07 2019 Ken Dreyer - 1.0.2-1 +- Initial package. diff --git a/sources b/sources new file mode 100644 index 0000000..4243c4e --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA512 (importlib_resources-1.0.2.tar.gz) = 50bfc5130a2c9c9354efef1cd7132e805ed0f13467ba67172f83e11d907212bef3957aeef51fd904b73996c8280008d99c918637956a470448dfd67ef4807f82