From 020d7859b09dcfc53e04fcc76190a3cdec9069c1 Mon Sep 17 00:00:00 2001 From: Xiaozhou Li Date: Wed, 25 Aug 2021 16:52:07 +0200 Subject: [PATCH 1/5] do not replace non-top-level directories --- Charon/filetypes/OpenPackagingConvention.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Charon/filetypes/OpenPackagingConvention.py b/Charon/filetypes/OpenPackagingConvention.py index 8ca6fc2..8a0562f 100644 --- a/Charon/filetypes/OpenPackagingConvention.py +++ b/Charon/filetypes/OpenPackagingConvention.py @@ -298,7 +298,11 @@ def _processAliases(self, virtual_path: str) -> str: # Replace all aliases. for regex, replacement in self._aliases.items(): - virtual_path = re.sub(regex, replacement, virtual_path) + if regex.startswith("/"): + expression = r"^" + regex + else: + expression = regex + virtual_path = re.sub(expression, replacement, virtual_path) return virtual_path From 140f2bdfb66ca92edb0057e7cfc30d4979e0d7b8 Mon Sep 17 00:00:00 2001 From: Xiaozhou Li Date: Wed, 1 Sep 2021 16:48:01 +0200 Subject: [PATCH 2/5] Add test for aliases replacement --- .../filetypes/TestOpenPackagingConvention.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/filetypes/TestOpenPackagingConvention.py b/tests/filetypes/TestOpenPackagingConvention.py index 9d7c1d0..f29882b 100644 --- a/tests/filetypes/TestOpenPackagingConvention.py +++ b/tests/filetypes/TestOpenPackagingConvention.py @@ -5,6 +5,8 @@ import pytest #This module contains unit tests. import zipfile #To inspect the contents of the zip archives. import xml.etree.ElementTree as ET #To inspect the contents of the OPC-spec files in the archives. +from collections import OrderedDict +from typing import List from Charon.filetypes.OpenPackagingConvention import OpenPackagingConvention, OPCError # The class we're testing. from Charon.OpenMode import OpenMode #To open archives. @@ -89,6 +91,29 @@ def test_cycleSetDataGetData(virtual_path: str): assert result[virtual_path] == test_data #The data itself is still correct. +@pytest.mark.parametrize("virtual_path, path_list", [ + ("/foo/materials", ["/foo/materials", "/[Content_Types].xml", "/_rels/.rels"]), + ("/materials", ["/files/materials", "/[Content_Types].xml", "/_rels/.rels"]) +]) +def test_aliases_replacement(virtual_path: str, path_list: List[str]): + test_data = b"Let's see if we can read this data back." + + stream = io.BytesIO() + package = OpenPackagingConvention() + package._aliases = OrderedDict([ + (r"/materials", "/files/materials") + ]) + package.openStream(stream, mode = OpenMode.WriteOnly) + package.setData({virtual_path: test_data}) + package.close() + + stream.seek(0) + package = OpenPackagingConvention() + package.openStream(stream) + result = package.listPaths() + + assert result == path_list + ## Tests writing data via a stream to an archive, then reading it back via a # stream. @pytest.mark.parametrize("virtual_path", ["/dir/file", "/file", "/Metadata"]) From 774dec4cdb83da492466689887aec80a67986f5e Mon Sep 17 00:00:00 2001 From: Xiaozhou Li Date: Mon, 6 Sep 2021 12:02:06 +0200 Subject: [PATCH 3/5] resolve mypy complaints --- Charon/filetypes/GCodeFile.py | 4 ++-- Charon/filetypes/OpenPackagingConvention.py | 2 +- tests/filetypes/TestOpenPackagingConvention.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Charon/filetypes/GCodeFile.py b/Charon/filetypes/GCodeFile.py index 9b45af4..7c995ef 100644 --- a/Charon/filetypes/GCodeFile.py +++ b/Charon/filetypes/GCodeFile.py @@ -96,7 +96,7 @@ def parseHeader(stream: IO[bytes], *, prefix: str = "") -> Dict[str, Any]: @staticmethod def __insertKeyValuePair( metadata: Dict[str, Any], - key_elements: Union[str, List[str]], + key_elements: Any, value: Any ) -> Any: if not key_elements: @@ -151,7 +151,7 @@ def __cleanGriffinHeader(metadata: Dict[str, Any]) -> None: # must exist on the location of that key element # @return True if the key is available and not empty @staticmethod - def __isAvailable(metadata: Dict[str, Any], keys: List[str]) -> None: + def __isAvailable(metadata: Dict[str, Any], keys: List[Any]) -> bool: if not keys: return True diff --git a/Charon/filetypes/OpenPackagingConvention.py b/Charon/filetypes/OpenPackagingConvention.py index 8a0562f..8681edf 100644 --- a/Charon/filetypes/OpenPackagingConvention.py +++ b/Charon/filetypes/OpenPackagingConvention.py @@ -25,7 +25,7 @@ class OpenPackagingConvention(FileInterface): _global_metadata_file = "/Metadata/OPC_Global.json" # Where the global metadata file is. _opc_metadata_relationship_type = "http://schemas.ultimaker.org/package/2018/relationships/opc_metadata" # Unique identifier of the relationship type that relates OPC metadata to files. _metadata_prefix = "/metadata" - _aliases = OrderedDict([]) # A standard OPC file doest not have default aliases. These must be implemented in inherited classes. + _aliases = OrderedDict([]) # type: Dict[str, Any] # A standard OPC file doest not have default aliases. These must be implemented in inherited classes. mime_type = "application/x-opc" diff --git a/tests/filetypes/TestOpenPackagingConvention.py b/tests/filetypes/TestOpenPackagingConvention.py index f29882b..69cb1ce 100644 --- a/tests/filetypes/TestOpenPackagingConvention.py +++ b/tests/filetypes/TestOpenPackagingConvention.py @@ -6,7 +6,7 @@ import zipfile #To inspect the contents of the zip archives. import xml.etree.ElementTree as ET #To inspect the contents of the OPC-spec files in the archives. from collections import OrderedDict -from typing import List +from typing import List, Generator from Charon.filetypes.OpenPackagingConvention import OpenPackagingConvention, OPCError # The class we're testing. from Charon.OpenMode import OpenMode #To open archives. @@ -16,7 +16,7 @@ # The package has no resources at all, so reading from it will not find # anything. @pytest.fixture() -def empty_read_opc() -> OpenPackagingConvention: +def empty_read_opc() -> Generator[OpenPackagingConvention, None, None]: result = OpenPackagingConvention() result.openStream(open(os.path.join(os.path.dirname(__file__), "resources", "empty.opc"), "rb")) yield result @@ -28,7 +28,7 @@ def empty_read_opc() -> OpenPackagingConvention: # The file is called "hello.txt" and contains the text "Hello world!" encoded # in UTF-8. @pytest.fixture() -def single_resource_read_opc() -> OpenPackagingConvention: +def single_resource_read_opc() -> Generator[OpenPackagingConvention, None, None]: result = OpenPackagingConvention() result.openStream(open(os.path.join(os.path.dirname(__file__), "resources", "hello.opc"), "rb")) yield result @@ -40,7 +40,7 @@ def single_resource_read_opc() -> OpenPackagingConvention: # Note that you can't really test the output of the write since you don't have # the stream it writes to. @pytest.fixture() -def empty_write_opc() -> OpenPackagingConvention: +def empty_write_opc() -> Generator[OpenPackagingConvention, None, None]: result = OpenPackagingConvention() result.openStream(io.BytesIO(), "application/x-opc", OpenMode.WriteOnly) yield result From 204aa63162e243395ab2e9778488261aebbb707c Mon Sep 17 00:00:00 2001 From: Xiaozhou Li Date: Mon, 6 Sep 2021 12:54:03 +0200 Subject: [PATCH 4/5] Update Charon/filetypes/OpenPackagingConvention.py Co-authored-by: Coen Schalkwijk --- Charon/filetypes/OpenPackagingConvention.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Charon/filetypes/OpenPackagingConvention.py b/Charon/filetypes/OpenPackagingConvention.py index 8681edf..5350cdf 100644 --- a/Charon/filetypes/OpenPackagingConvention.py +++ b/Charon/filetypes/OpenPackagingConvention.py @@ -25,7 +25,7 @@ class OpenPackagingConvention(FileInterface): _global_metadata_file = "/Metadata/OPC_Global.json" # Where the global metadata file is. _opc_metadata_relationship_type = "http://schemas.ultimaker.org/package/2018/relationships/opc_metadata" # Unique identifier of the relationship type that relates OPC metadata to files. _metadata_prefix = "/metadata" - _aliases = OrderedDict([]) # type: Dict[str, Any] # A standard OPC file doest not have default aliases. These must be implemented in inherited classes. + _aliases:Dict[str, str] = OrderedDict([]) # A standard OPC file doest not have default aliases. These must be implemented in inherited classes. mime_type = "application/x-opc" From c6c08c89f486426307f3bcf071118d4a775c3c0b Mon Sep 17 00:00:00 2001 From: Xiaozhou Li Date: Mon, 6 Sep 2021 13:07:29 +0200 Subject: [PATCH 5/5] Use expression = rf"^{regex}" - f-string is better! --- Charon/filetypes/OpenPackagingConvention.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Charon/filetypes/OpenPackagingConvention.py b/Charon/filetypes/OpenPackagingConvention.py index 5350cdf..f395d4d 100644 --- a/Charon/filetypes/OpenPackagingConvention.py +++ b/Charon/filetypes/OpenPackagingConvention.py @@ -299,7 +299,7 @@ def _processAliases(self, virtual_path: str) -> str: # Replace all aliases. for regex, replacement in self._aliases.items(): if regex.startswith("/"): - expression = r"^" + regex + expression = rf"^{regex}" else: expression = regex virtual_path = re.sub(expression, replacement, virtual_path)