From 23820d3927cd7eecd39acf3fcb7feaecb5afedb9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 24 Mar 2020 10:49:27 -0400 Subject: [PATCH 01/14] Add resource reader as proposed by Gregory in https://gitlab.com/python-devs/importlib_resources/-/issues/90#note_307473902 --- importlib_resources/abc.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 4371a77c..e4f1eb3a 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -112,6 +112,34 @@ def name(self) -> str: """ +class ResourceReader: + # Holds the string name of the virtual Python package this is a resource loader for. + # For filesystems, this is effectively a reader for a directory at `fullname.replace('.', '/')` + fullname = None + + def child_readers(self) -> [ResourceReader] + """Obtain an iterable of ResourceReader for available child virtual packages of this one. + + On filesystems, this essentially returns instances corresponding to immediate child directories. + """ + + def resources(self) -> [str] + """Obtain available named resources for this virtual package. + + On filesystems, this essentially returns files in the current directory. + TODO consider returning a special type that exposes an `open()`, etc. + """" + + def open_binary(self, resource) -> File + """Obtain a File-like for a named resource. + + On filesystems, this attempts to open os.path.join(self, resource). + + Attempting to open a non-resource entity (such as a subdirectory) or a missing + resource raises NotAResourceError. + """ + + class TraversableResources(ResourceReader): @abc.abstractmethod def files(self): From 0355a88c68aaf44c855d1e13beea813cb0b2aee7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 24 Mar 2020 10:50:42 -0400 Subject: [PATCH 02/14] Fix syntax --- importlib_resources/abc.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index e4f1eb3a..164e0195 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -112,25 +112,25 @@ def name(self) -> str: """ -class ResourceReader: +class SimpleReader: # Holds the string name of the virtual Python package this is a resource loader for. # For filesystems, this is effectively a reader for a directory at `fullname.replace('.', '/')` fullname = None - def child_readers(self) -> [ResourceReader] + def child_readers(self) -> ['SimpleReader']: """Obtain an iterable of ResourceReader for available child virtual packages of this one. On filesystems, this essentially returns instances corresponding to immediate child directories. """ - def resources(self) -> [str] + def resources(self) -> [str]: """Obtain available named resources for this virtual package. On filesystems, this essentially returns files in the current directory. TODO consider returning a special type that exposes an `open()`, etc. - """" + """ - def open_binary(self, resource) -> File + def open_binary(self, resource) -> BinaryIO: """Obtain a File-like for a named resource. On filesystems, this attempts to open os.path.join(self, resource). From 93b1c6d57cca3fe587a574ad09c35530c1e2c691 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 24 Mar 2020 10:56:01 -0400 Subject: [PATCH 03/14] Convert to an ABC --- importlib_resources/abc.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 164e0195..39db2461 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -112,31 +112,31 @@ def name(self) -> str: """ -class SimpleReader: - # Holds the string name of the virtual Python package this is a resource loader for. - # For filesystems, this is effectively a reader for a directory at `fullname.replace('.', '/')` - fullname = None +class SimpleReader(ABC): - def child_readers(self) -> ['SimpleReader']: - """Obtain an iterable of ResourceReader for available child virtual packages of this one. + @abc.abstractproperty + def package(self): + """ + The name of the package for which this reader loads resources. + """ - On filesystems, this essentially returns instances corresponding to immediate child directories. + @abc.abstractmethod + def child_readers(self) -> ['SimpleReader']: + """ + Obtain an iterable of ResourceReader for available + child virtual packages of this one. """ + @abc.abstractmethod def resources(self) -> [str]: - """Obtain available named resources for this virtual package. - - On filesystems, this essentially returns files in the current directory. - TODO consider returning a special type that exposes an `open()`, etc. + """ + Obtain available named resources for this virtual package. """ + @abc.abstractmethod def open_binary(self, resource) -> BinaryIO: - """Obtain a File-like for a named resource. - - On filesystems, this attempts to open os.path.join(self, resource). - - Attempting to open a non-resource entity (such as a subdirectory) or a missing - resource raises NotAResourceError. + """ + Obtain a File-like for a named resource. """ From 2d86bc3b710ebc699c1901d39105100df068883f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 18 Jan 2021 18:58:11 -0500 Subject: [PATCH 04/14] Implement TraversableReader, based on SimpleReader --- importlib_resources/abc.py | 59 +++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 39db2461..2473c749 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -1,4 +1,6 @@ import abc +import io +import itertools from typing import BinaryIO, Iterable, Text from ._compat import runtime_checkable, Protocol @@ -112,7 +114,7 @@ def name(self) -> str: """ -class SimpleReader(ABC): +class SimpleReader(abc.ABC): @abc.abstractproperty def package(self): @@ -139,6 +141,10 @@ def open_binary(self, resource) -> BinaryIO: Obtain a File-like for a named resource. """ + @property + def name(self): + return self.package.split('.')[-1] + class TraversableResources(ResourceReader): @abc.abstractmethod @@ -156,3 +162,54 @@ def is_resource(self, path): def contents(self): return (item.name for item in self.files().iterdir()) + + +class ResourceHandle(Traversable): + def __init__(self, reader, name): + self.reader = reader + self.name = name + + def is_file(self): + return True + + def is_dir(self): + return False + + def open(self, mode='r', *args, **kwargs): + stream = self.reader.open_binary(self.name) + if 'b' not in mode: + stream = io.TextIOWrapper(*args, **kwargs) + return stream + + +class ResourceContainer(Traversable): + def __init__(self, reader: SimpleReader): + self.reader = reader + + def is_dir(self): + return True + + def is_file(self): + return False + + def iterdir(self): + files = ( + ResourceHandle(self, name) + for name in self.resources + ) + dirs = map(ResourceContainer, self.child_readers()) + return itertools.chain(files, dirs) + + def open(self, *args, **kwargs): + raise IsADirectoryError() + + def joinpath(self, name): + return next( + traversable + for traversable in self.iterdir() + if traversable.name == name) + + +class TraversableReader(TraversableResources, SimpleReader): + def files(self): + return ResourceContainer(self) From 0de43b9721dd9cba3a3cd8e66f4acee90d8ae5b4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 24 Mar 2020 11:22:55 -0400 Subject: [PATCH 05/14] Make read_bytes and read_text and __truediv__ concrete implementations based on other abstract methods. --- importlib_resources/abc.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 2473c749..7ccadee5 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -61,17 +61,19 @@ def iterdir(self): Yield Traversable objects in self """ - @abc.abstractmethod def read_bytes(self): """ Read contents of self as bytes """ + with self.open('rb') as strm: + return strm.read() - @abc.abstractmethod def read_text(self, encoding=None): """ - Read contents of self as bytes + Read contents of self as text """ + with self.open(encoding=encoding) as strm: + return strm.read() @abc.abstractmethod def is_dir(self): @@ -91,11 +93,11 @@ def joinpath(self, child): Return Traversable child in self """ - @abc.abstractmethod def __truediv__(self, child): """ Return Traversable child in self """ + return self.joinpath(child) @abc.abstractmethod def open(self, mode='r', *args, **kwargs): From 72feafeb4b90f6ba111eaebef9dacce39180f489 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 18 Jan 2021 18:59:03 -0500 Subject: [PATCH 06/14] Fix typing syntax --- importlib_resources/abc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 7ccadee5..cc398dfc 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -1,7 +1,7 @@ import abc import io import itertools -from typing import BinaryIO, Iterable, Text +from typing import BinaryIO, Iterable, Text, List from ._compat import runtime_checkable, Protocol @@ -125,14 +125,14 @@ def package(self): """ @abc.abstractmethod - def child_readers(self) -> ['SimpleReader']: + def child_readers(self) -> List['SimpleReader']: """ Obtain an iterable of ResourceReader for available child virtual packages of this one. """ @abc.abstractmethod - def resources(self) -> [str]: + def resources(self) -> List[str]: """ Obtain available named resources for this virtual package. """ From b7ee6fbc41e7737cebfa690b66c39f22a370dc9d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 24 Mar 2020 11:34:59 -0400 Subject: [PATCH 07/14] Implement abstract method on ResourceHandle and add docstrings --- importlib_resources/abc.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index cc398dfc..47541b21 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -167,6 +167,10 @@ def contents(self): class ResourceHandle(Traversable): + """ + Handle to a named resource in a ResourceReader. + """ + def __init__(self, reader, name): self.reader = reader self.name = name @@ -183,8 +187,15 @@ def open(self, mode='r', *args, **kwargs): stream = io.TextIOWrapper(*args, **kwargs) return stream + def joinpath(self, name): + raise RuntimeError("Cannot traverse into a resource") + class ResourceContainer(Traversable): + """ + Traversable container for a package's resources via its reader. + """ + def __init__(self, reader: SimpleReader): self.reader = reader From 35297321ab56c9b411bcb38d364ec5b4a9f4c72e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 11 Apr 2020 11:41:26 -0400 Subject: [PATCH 08/14] Update wording on SimpleReader.child_readers. Rename 'child_readers' to simply 'children'. --- importlib_resources/abc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 47541b21..23a7b590 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -125,10 +125,10 @@ def package(self): """ @abc.abstractmethod - def child_readers(self) -> List['SimpleReader']: + def children(self) -> List['SimpleReader']: """ - Obtain an iterable of ResourceReader for available - child virtual packages of this one. + Obtain an iterable of SimpleReader for available + child containers (e.g. directories). """ @abc.abstractmethod @@ -210,7 +210,7 @@ def iterdir(self): ResourceHandle(self, name) for name in self.resources ) - dirs = map(ResourceContainer, self.child_readers()) + dirs = map(ResourceContainer, self.children()) return itertools.chain(files, dirs) def open(self, *args, **kwargs): From 27e9d7c70706c84f0ea019da3a71fe0b893e8b87 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 11 Apr 2020 15:08:37 -0400 Subject: [PATCH 09/14] Add compatibility for Python 2.7 --- importlib_resources/abc.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 23a7b590..af3277e0 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -125,20 +125,23 @@ def package(self): """ @abc.abstractmethod - def children(self) -> List['SimpleReader']: + def children(self): + # type: () -> List['SimpleReader'] """ Obtain an iterable of SimpleReader for available child containers (e.g. directories). """ @abc.abstractmethod - def resources(self) -> List[str]: + def resources(self): + # type: () -> List[str] """ Obtain available named resources for this virtual package. """ @abc.abstractmethod - def open_binary(self, resource) -> BinaryIO: + def open_binary(self, resource): + # type: (str) -> BinaryIO """ Obtain a File-like for a named resource. """ @@ -196,7 +199,8 @@ class ResourceContainer(Traversable): Traversable container for a package's resources via its reader. """ - def __init__(self, reader: SimpleReader): + def __init__(self, reader): + # type: (SimpleReader) -> None self.reader = reader def is_dir(self): From 04b02010cc58f7033588d12290f9576fba7c2ba1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 16 Apr 2020 19:53:11 -0400 Subject: [PATCH 10/14] Add docstrings and type annotations. Fix references to parent and reader between ResourceHandle and ResourceContainer. --- importlib_resources/abc.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index af3277e0..0942f8ab 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -77,12 +77,14 @@ def read_text(self, encoding=None): @abc.abstractmethod def is_dir(self): + # type: () -> bool """ Return True if self is a dir """ @abc.abstractmethod def is_file(self): + # type: () -> bool """ Return True if self is a file """ @@ -117,9 +119,14 @@ def name(self) -> str: class SimpleReader(abc.ABC): + """ + The minimum, low-level interface required from a resource + provider. + """ @abc.abstractproperty def package(self): + # type: () -> str """ The name of the package for which this reader loads resources. """ @@ -152,6 +159,11 @@ def name(self): class TraversableResources(ResourceReader): + """ + The required interface for providing traversable + resources. + """ + @abc.abstractmethod def files(self): """Return a Traversable object for the loaded package.""" @@ -174,8 +186,9 @@ class ResourceHandle(Traversable): Handle to a named resource in a ResourceReader. """ - def __init__(self, reader, name): - self.reader = reader + def __init__(self, parent, name): + # type: (ResourceContainer, str) -> None + self.parent = parent self.name = name def is_file(self): @@ -185,7 +198,7 @@ def is_dir(self): return False def open(self, mode='r', *args, **kwargs): - stream = self.reader.open_binary(self.name) + stream = self.parent.reader.open_binary(self.name) if 'b' not in mode: stream = io.TextIOWrapper(*args, **kwargs) return stream @@ -212,9 +225,9 @@ def is_file(self): def iterdir(self): files = ( ResourceHandle(self, name) - for name in self.resources + for name in self.reader.resources ) - dirs = map(ResourceContainer, self.children()) + dirs = map(ResourceContainer, self.reader.children()) return itertools.chain(files, dirs) def open(self, *args, **kwargs): @@ -228,5 +241,10 @@ def joinpath(self, name): class TraversableReader(TraversableResources, SimpleReader): + """ + A TraversableResources based on SimpleReader. Resource providers + may derive from this class to provide the TraversableResources + interface by supplying the SimpleReader interface. + """ def files(self): return ResourceContainer(self) From 4f486494858d3d916c369a4e43fcc7c64f79b084 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 18 Jan 2021 19:02:30 -0500 Subject: [PATCH 11/14] =?UTF-8?q?=E2=9A=AB=20Fade=20to=20black.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- importlib_resources/abc.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 0942f8ab..40d61819 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -223,10 +223,7 @@ def is_file(self): return False def iterdir(self): - files = ( - ResourceHandle(self, name) - for name in self.reader.resources - ) + files = (ResourceHandle(self, name) for name in self.reader.resources) dirs = map(ResourceContainer, self.reader.children()) return itertools.chain(files, dirs) @@ -235,9 +232,8 @@ def open(self, *args, **kwargs): def joinpath(self, name): return next( - traversable - for traversable in self.iterdir() - if traversable.name == name) + traversable for traversable in self.iterdir() if traversable.name == name + ) class TraversableReader(TraversableResources, SimpleReader): @@ -246,5 +242,6 @@ class TraversableReader(TraversableResources, SimpleReader): may derive from this class to provide the TraversableResources interface by supplying the SimpleReader interface. """ + def files(self): return ResourceContainer(self) From f60d050afc427ef85e5d77cd63317ededa035cee Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 18 Jan 2021 19:02:40 -0500 Subject: [PATCH 12/14] =?UTF-8?q?=F0=9F=A7=8E=E2=80=8D=E2=99=80=EF=B8=8F?= =?UTF-8?q?=20Genuflect=20to=20the=20types.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- importlib_resources/abc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 40d61819..205a7d91 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -189,7 +189,7 @@ class ResourceHandle(Traversable): def __init__(self, parent, name): # type: (ResourceContainer, str) -> None self.parent = parent - self.name = name + self.name = name # type: ignore def is_file(self): return True From fbd44f05abf991250513f9bfec7611fbf6f620cd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 18 Jan 2021 19:21:23 -0500 Subject: [PATCH 13/14] Move interfaces for low-level readers to their own module. --- importlib_resources/abc.py | 110 +------------------------------- importlib_resources/simple.py | 116 ++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 109 deletions(-) create mode 100644 importlib_resources/simple.py diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 1b2230f8..56dc8127 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -1,7 +1,5 @@ import abc -import io -import itertools -from typing import BinaryIO, Iterable, Text, List +from typing import BinaryIO, Iterable, Text from ._compat import runtime_checkable, Protocol @@ -116,46 +114,6 @@ def name(self) -> str: """ -class SimpleReader(abc.ABC): - """ - The minimum, low-level interface required from a resource - provider. - """ - - @abc.abstractproperty - def package(self): - # type: () -> str - """ - The name of the package for which this reader loads resources. - """ - - @abc.abstractmethod - def children(self): - # type: () -> List['SimpleReader'] - """ - Obtain an iterable of SimpleReader for available - child containers (e.g. directories). - """ - - @abc.abstractmethod - def resources(self): - # type: () -> List[str] - """ - Obtain available named resources for this virtual package. - """ - - @abc.abstractmethod - def open_binary(self, resource): - # type: (str) -> BinaryIO - """ - Obtain a File-like for a named resource. - """ - - @property - def name(self): - return self.package.split('.')[-1] - - class TraversableResources(ResourceReader): """ The required interface for providing traversable @@ -177,69 +135,3 @@ def is_resource(self, path): def contents(self): return (item.name for item in self.files().iterdir()) - - -class ResourceHandle(Traversable): - """ - Handle to a named resource in a ResourceReader. - """ - - def __init__(self, parent, name): - # type: (ResourceContainer, str) -> None - self.parent = parent - self.name = name # type: ignore - - def is_file(self): - return True - - def is_dir(self): - return False - - def open(self, mode='r', *args, **kwargs): - stream = self.parent.reader.open_binary(self.name) - if 'b' not in mode: - stream = io.TextIOWrapper(*args, **kwargs) - return stream - - def joinpath(self, name): - raise RuntimeError("Cannot traverse into a resource") - - -class ResourceContainer(Traversable): - """ - Traversable container for a package's resources via its reader. - """ - - def __init__(self, reader): - # type: (SimpleReader) -> None - self.reader = reader - - def is_dir(self): - return True - - def is_file(self): - return False - - def iterdir(self): - files = (ResourceHandle(self, name) for name in self.reader.resources) - dirs = map(ResourceContainer, self.reader.children()) - return itertools.chain(files, dirs) - - def open(self, *args, **kwargs): - raise IsADirectoryError() - - def joinpath(self, name): - return next( - traversable for traversable in self.iterdir() if traversable.name == name - ) - - -class TraversableReader(TraversableResources, SimpleReader): - """ - A TraversableResources based on SimpleReader. Resource providers - may derive from this class to provide the TraversableResources - interface by supplying the SimpleReader interface. - """ - - def files(self): - return ResourceContainer(self) diff --git a/importlib_resources/simple.py b/importlib_resources/simple.py new file mode 100644 index 00000000..da073cbd --- /dev/null +++ b/importlib_resources/simple.py @@ -0,0 +1,116 @@ +""" +Interface adapters for low-level readers. +""" + +import abc +import io +import itertools +from typing import BinaryIO, List + +from .abc import Traversable, TraversableResources + + +class SimpleReader(abc.ABC): + """ + The minimum, low-level interface required from a resource + provider. + """ + + @abc.abstractproperty + def package(self): + # type: () -> str + """ + The name of the package for which this reader loads resources. + """ + + @abc.abstractmethod + def children(self): + # type: () -> List['SimpleReader'] + """ + Obtain an iterable of SimpleReader for available + child containers (e.g. directories). + """ + + @abc.abstractmethod + def resources(self): + # type: () -> List[str] + """ + Obtain available named resources for this virtual package. + """ + + @abc.abstractmethod + def open_binary(self, resource): + # type: (str) -> BinaryIO + """ + Obtain a File-like for a named resource. + """ + + @property + def name(self): + return self.package.split('.')[-1] + + +class ResourceHandle(Traversable): + """ + Handle to a named resource in a ResourceReader. + """ + + def __init__(self, parent, name): + # type: (ResourceContainer, str) -> None + self.parent = parent + self.name = name # type: ignore + + def is_file(self): + return True + + def is_dir(self): + return False + + def open(self, mode='r', *args, **kwargs): + stream = self.parent.reader.open_binary(self.name) + if 'b' not in mode: + stream = io.TextIOWrapper(*args, **kwargs) + return stream + + def joinpath(self, name): + raise RuntimeError("Cannot traverse into a resource") + + +class ResourceContainer(Traversable): + """ + Traversable container for a package's resources via its reader. + """ + + def __init__(self, reader): + # type: (SimpleReader) -> None + self.reader = reader + + def is_dir(self): + return True + + def is_file(self): + return False + + def iterdir(self): + files = (ResourceHandle(self, name) for name in self.reader.resources) + dirs = map(ResourceContainer, self.reader.children()) + return itertools.chain(files, dirs) + + def open(self, *args, **kwargs): + raise IsADirectoryError() + + def joinpath(self, name): + return next( + traversable for traversable in self.iterdir() if traversable.name == name + ) + + +class TraversableReader(TraversableResources, SimpleReader): + """ + A TraversableResources based on SimpleReader. Resource providers + may derive from this class to provide the TraversableResources + interface by supplying the SimpleReader interface. + """ + + def files(self): + return ResourceContainer(self) From 446699153a9681242e951212aefdc725c0fff08c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 18 Jan 2021 19:23:03 -0500 Subject: [PATCH 14/14] Update changelog. --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 995f8391..2d4d6cea 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v5.1.0 +====== + +* Added ``simple`` module implementing adapters from + a low-level resource reader interface to a + ``TraversableResources`` interface. Closes #90. + v5.0.1 ======