From 07e76172c05c3036404aec158df54f24ea71cbf8 Mon Sep 17 00:00:00 2001 From: Austin Noto-Moniz Date: Thu, 18 Jul 2024 14:58:37 -0400 Subject: [PATCH 1/2] [PNE-203] Fix reading file links on Windows. If FileCollection.read is called with a FileLink which encodes a local path (i.e. starts with file:///), then it's a local file. Due to the differences in path handling on Windows and Unix, we need to explicitly convert the path portion of that URL into a filepath. --- src/citrine/__version__.py | 2 +- src/citrine/resources/file_link.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/citrine/__version__.py b/src/citrine/__version__.py index 903a158ad..a5cfdf595 100644 --- a/src/citrine/__version__.py +++ b/src/citrine/__version__.py @@ -1 +1 @@ -__version__ = "3.4.0" +__version__ = "3.4.1" diff --git a/src/citrine/resources/file_link.py b/src/citrine/resources/file_link.py index ca95ae2dd..c4e3763b2 100644 --- a/src/citrine/resources/file_link.py +++ b/src/citrine/resources/file_link.py @@ -5,6 +5,7 @@ from tempfile import TemporaryDirectory from typing import Optional, Tuple, Union, Dict, Iterable, Sequence from urllib.parse import urlparse, unquote_plus +from urllib.request import url2pathname from uuid import UUID from citrine._rest.collection import Collection @@ -647,7 +648,7 @@ def read(self, *, file_link: Union[str, UUID, FileLink]) -> bytes: file_link = self._resolve_file_link(file_link) if self._is_local_url(file_link.url): # Read the local file - path = Path(unquote_plus(urlparse(file_link.url).path)) + path = Path(unquote_plus(url2pathname(urlparse(file_link.url).path))) return path.read_bytes() if self._is_external_url(file_link.url): # Pull it from where ever it lives From cbd53a5210685a8ebc21f6dca8cd3b934e3e1369 Mon Sep 17 00:00:00 2001 From: Ken Kroenlein Date: Wed, 28 Aug 2024 12:24:09 -0600 Subject: [PATCH 2/2] PNE-203 Windows path compliance for file upload --- src/citrine/resources/file_link.py | 8 ++++++-- tests/resources/test_file_link.py | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/citrine/resources/file_link.py b/src/citrine/resources/file_link.py index c4e3763b2..537386ad8 100644 --- a/src/citrine/resources/file_link.py +++ b/src/citrine/resources/file_link.py @@ -4,7 +4,7 @@ from pathlib import Path from tempfile import TemporaryDirectory from typing import Optional, Tuple, Union, Dict, Iterable, Sequence -from urllib.parse import urlparse, unquote_plus +from urllib.parse import urlparse from urllib.request import url2pathname from uuid import UUID @@ -648,7 +648,11 @@ def read(self, *, file_link: Union[str, UUID, FileLink]) -> bytes: file_link = self._resolve_file_link(file_link) if self._is_local_url(file_link.url): # Read the local file - path = Path(unquote_plus(url2pathname(urlparse(file_link.url).path))) + parsed_url = urlparse(file_link.url) + if parsed_url.netloc not in {'', '.', 'localhost'}: + raise ValueError("Non-local UNCs (e.g., Windows network paths) are not supported.") + # Space should have been encoded as %20, but just in case it was a + + path = Path(url2pathname(parsed_url.path.replace('+', '%20'))) return path.read_bytes() if self._is_external_url(file_link.url): # Pull it from where ever it lives diff --git a/tests/resources/test_file_link.py b/tests/resources/test_file_link.py index 49218cf79..1b10585e1 100644 --- a/tests/resources/test_file_link.py +++ b/tests/resources/test_file_link.py @@ -837,3 +837,6 @@ def test_exceptions(collection: FileCollection, session): ) with pytest.raises(NotFound): collection.get(uid="name") + + with pytest.raises(ValueError, match="Windows"): + collection.read(file_link=FileLink('File', 'file://remote/network/file.txt'))