Skip to content

Commit 5ff1d32

Browse files
authored
Upath annotate storage options for implementations (#432)
* upath.types: add storage_options submodule * upath.types: fix *PathLike types * upath.implementations: add storage-options types * typesafety: add tests ensuring that storage_options are typed * upath.types: fix missing import
1 parent 023414d commit 5ff1d32

File tree

14 files changed

+746
-31
lines changed

14 files changed

+746
-31
lines changed

typesafety/test_upath_types.yml

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
reveal_type(p) # N: Revealed type is "{{ cls_fqn }}"
130130
131131
- case: upath__new__empty_protocol
132-
disable_cache: true
132+
disable_cache: false
133133
skip: sys.platform == "win32"
134134
main: |
135135
from upath.core import UPath
@@ -138,10 +138,149 @@
138138
reveal_type(p) # N: Revealed type is "upath.implementations.local.PosixUPath"
139139
140140
- case: get_upath_class_pathlib_win
141-
disable_cache: true
141+
disable_cache: false
142142
skip: sys.platform != "win32"
143143
main: |
144144
from upath.core import UPath
145145
146146
p = UPath("", protocol="")
147147
reveal_type(p) # N: Revealed type is "upath.implementations.local.WindowsUPath"
148+
149+
- case: upath_constructor_sopts_correct_type
150+
disable_cache: false
151+
parametrized:
152+
- module: upath.implementations.cached
153+
cls: SimpleCachePath
154+
supported_example_name: check_files
155+
supported_example_value: True
156+
- module: upath.implementations.cloud
157+
cls: GCSPath
158+
supported_example_name: project
159+
supported_example_value: '"my-project"'
160+
- module: upath.implementations.cloud
161+
cls: S3Path
162+
supported_example_name: anon
163+
supported_example_value: True
164+
- module: upath.implementations.cloud
165+
cls: AzurePath
166+
supported_example_name: account_name
167+
supported_example_value: '"myaccount"'
168+
- module: upath.implementations.data
169+
cls: DataPath
170+
supported_example_name: use_listings_cache
171+
supported_example_value: False
172+
- module: upath.implementations.github
173+
cls: GitHubPath
174+
supported_example_name: org
175+
supported_example_value: '"fsspec"'
176+
- module: upath.implementations.hdfs
177+
cls: HDFSPath
178+
supported_example_name: host
179+
supported_example_value: '"localhost"'
180+
- module: upath.implementations.http
181+
cls: HTTPPath
182+
supported_example_name: simple_links
183+
supported_example_value: True
184+
- module: upath.implementations.local
185+
cls: FilePath
186+
supported_example_name: auto_mkdir
187+
supported_example_value: True
188+
- module: upath.implementations.memory
189+
cls: MemoryPath
190+
supported_example_name: use_listings_cache
191+
supported_example_value: True
192+
- module: upath.implementations.sftp
193+
cls: SFTPPath
194+
supported_example_name: host
195+
supported_example_value: '"sftp.example.com"'
196+
- module: upath.implementations.smb
197+
cls: SMBPath
198+
supported_example_name: timeout
199+
supported_example_value: 60
200+
- module: upath.implementations.webdav
201+
cls: WebdavPath
202+
supported_example_name: base_url
203+
supported_example_value: '"https://webdav.example.com"'
204+
main: |
205+
from {{ module }} import {{ cls }}
206+
207+
p = {{ cls }}(".", {{ supported_example_name }}={{ supported_example_value }})
208+
reveal_type(p) # N: Revealed type is "{{ module }}.{{ cls }}"
209+
210+
- case: upath_constructor_sopts_incorrect_type
211+
disable_cache: false
212+
parametrized:
213+
- module: upath.implementations.cached
214+
cls: SimpleCachePath
215+
supported_example_name: check_files
216+
unsupported_example_value: '"blub"'
217+
- module: upath.implementations.cloud
218+
cls: GCSPath
219+
supported_example_name: version_aware
220+
unsupported_example_value: '"yes"'
221+
- module: upath.implementations.cloud
222+
cls: S3Path
223+
supported_example_name: anon
224+
unsupported_example_value: '"blah"'
225+
- module: upath.implementations.cloud
226+
cls: AzurePath
227+
supported_example_name: account_name
228+
unsupported_example_value: '123'
229+
- module: upath.implementations.data
230+
cls: DataPath
231+
supported_example_name: use_listings_cache
232+
unsupported_example_value: '"blub"'
233+
- module: upath.implementations.github
234+
cls: GitHubPath
235+
supported_example_name: repo
236+
unsupported_example_value: 'True'
237+
- module: upath.implementations.hdfs
238+
cls: HDFSPath
239+
supported_example_name: host
240+
unsupported_example_value: '8020'
241+
- module: upath.implementations.http
242+
cls: HTTPPath
243+
supported_example_name: simple_links
244+
unsupported_example_value: '"no"'
245+
- module: upath.implementations.local
246+
cls: FilePath
247+
supported_example_name: auto_mkdir
248+
unsupported_example_value: '"abc"'
249+
- module: upath.implementations.memory
250+
cls: MemoryPath
251+
supported_example_name: use_listings_cache
252+
unsupported_example_value: '{}'
253+
- module: upath.implementations.sftp
254+
cls: SFTPPath
255+
supported_example_name: host
256+
unsupported_example_value: '[]'
257+
- module: upath.implementations.smb
258+
cls: SMBPath
259+
supported_example_name: host
260+
unsupported_example_value: 'False'
261+
- module: upath.implementations.webdav
262+
cls: WebdavPath
263+
supported_example_name: base_url
264+
unsupported_example_value: '123'
265+
main: |
266+
from {{ module }} import {{ cls }}
267+
268+
p = {{ cls }}(".", {{ supported_example_name }}={{ unsupported_example_value }}) # ER: Argument "{{ supported_example_name }}" to "{{ cls }}" has incompatible type.*
269+
270+
# FIXME: mypy emits a 'defined here' note when emitting the error below.
271+
# seems to be a limitation/bug in pytest-mypy-plugins that I can't match it
272+
#
273+
# - case: upath_constructor_sopts_incorrect_name
274+
# disable_cache: false
275+
# regex: yes
276+
# parametrized:
277+
# - module: upath.implementations.cached
278+
# cls: SimpleCachePath
279+
# unsupported_example_name: idontexist
280+
# main: |
281+
# from {{ module }} import {{ cls }}
282+
#
283+
# p = {{ cls }}(".", {{ unsupported_example_name }}=1)
284+
# out: |
285+
# \\.\\..*
286+
# main:3: error: Unexpected keyword argument "idontexist" to "{{ cls }}".*

upath/implementations/cached.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,35 @@
11
from __future__ import annotations
22

3+
import sys
4+
from typing import TYPE_CHECKING
5+
36
from upath.core import UPath
7+
from upath.types import JoinablePathLike
8+
9+
if TYPE_CHECKING:
10+
from typing import Literal
11+
12+
if sys.version_info >= (3, 11):
13+
from typing import Unpack
14+
else:
15+
from typing_extensions import Unpack
16+
17+
from upath._chain import FSSpecChainParser
18+
from upath.types.storage_options import SimpleCacheStorageOptions
19+
20+
21+
__all__ = ["SimpleCachePath"]
422

523

624
class SimpleCachePath(UPath):
7-
pass
25+
__slots__ = ()
26+
27+
if TYPE_CHECKING:
28+
29+
def __init__(
30+
self,
31+
*args: JoinablePathLike,
32+
protocol: Literal["simplecache"] | None = ...,
33+
chain_parser: FSSpecChainParser = ...,
34+
**storage_options: Unpack[SimpleCacheStorageOptions],
35+
) -> None: ...

upath/implementations/cloud.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,25 @@
55
from typing import TYPE_CHECKING
66
from typing import Any
77

8+
from upath._chain import DEFAULT_CHAIN_PARSER
89
from upath._flavour import upath_strip_protocol
910
from upath.core import UPath
1011
from upath.types import JoinablePathLike
1112

1213
if TYPE_CHECKING:
14+
from typing import Literal
15+
1316
if sys.version_info >= (3, 11):
1417
from typing import Self
18+
from typing import Unpack
1519
else:
1620
from typing_extensions import Self
21+
from typing_extensions import Unpack
22+
23+
from upath._chain import FSSpecChainParser
24+
from upath.types.storage_options import AzureBlobStorageOptions
25+
from upath.types.storage_options import GCSStorageOptions
26+
from upath.types.storage_options import S3StorageOptions
1727

1828
__all__ = [
1929
"CloudPath",
@@ -84,10 +94,13 @@ class GCSPath(CloudPath):
8494
def __init__(
8595
self,
8696
*args: JoinablePathLike,
87-
protocol: str | None = None,
88-
**storage_options: Any,
97+
protocol: Literal["gcs", "gs"] | None = None,
98+
chain_parser: FSSpecChainParser = DEFAULT_CHAIN_PARSER,
99+
**storage_options: Unpack[GCSStorageOptions],
89100
) -> None:
90-
super().__init__(*args, protocol=protocol, **storage_options)
101+
super().__init__(
102+
*args, protocol=protocol, chain_parser=chain_parser, **storage_options
103+
)
91104
if not self.drive and len(self.parts) > 1:
92105
raise ValueError("non key-like path provided (bucket/container missing)")
93106

@@ -114,10 +127,13 @@ class S3Path(CloudPath):
114127
def __init__(
115128
self,
116129
*args: JoinablePathLike,
117-
protocol: str | None = None,
118-
**storage_options: Any,
130+
protocol: Literal["s3", "s3a"] | None = None,
131+
chain_parser: FSSpecChainParser = DEFAULT_CHAIN_PARSER,
132+
**storage_options: Unpack[S3StorageOptions],
119133
) -> None:
120-
super().__init__(*args, protocol=protocol, **storage_options)
134+
super().__init__(
135+
*args, protocol=protocol, chain_parser=chain_parser, **storage_options
136+
)
121137
if not self.drive and len(self.parts) > 1:
122138
raise ValueError("non key-like path provided (bucket/container missing)")
123139

@@ -128,9 +144,12 @@ class AzurePath(CloudPath):
128144
def __init__(
129145
self,
130146
*args: JoinablePathLike,
131-
protocol: str | None = None,
132-
**storage_options: Any,
147+
protocol: Literal["abfs", "abfss", "adl", "az"] | None = None,
148+
chain_parser: FSSpecChainParser = DEFAULT_CHAIN_PARSER,
149+
**storage_options: Unpack[AzureBlobStorageOptions],
133150
) -> None:
134-
super().__init__(*args, protocol=protocol, **storage_options)
151+
super().__init__(
152+
*args, protocol=protocol, chain_parser=chain_parser, **storage_options
153+
)
135154
if not self.drive and len(self.parts) > 1:
136155
raise ValueError("non key-like path provided (bucket/container missing)")

upath/implementations/data.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,33 @@
88
from upath.types import JoinablePathLike
99

1010
if TYPE_CHECKING:
11-
if sys.version_info > (3, 11):
11+
from typing import Literal
12+
13+
if sys.version_info >= (3, 11):
1214
from typing import Self
15+
from typing import Unpack
1316
else:
1417
from typing_extensions import Self
18+
from typing_extensions import Unpack
19+
20+
from upath._chain import FSSpecChainParser
21+
from upath.types.storage_options import DataStorageOptions
22+
23+
__all__ = ["DataPath"]
1524

1625

1726
class DataPath(UPath):
27+
__slots__ = ()
28+
29+
if TYPE_CHECKING:
30+
31+
def __init__(
32+
self,
33+
*args: JoinablePathLike,
34+
protocol: Literal["data"] | None = ...,
35+
chain_parser: FSSpecChainParser = ...,
36+
**storage_options: Unpack[DataStorageOptions],
37+
) -> None: ...
1838

1939
@property
2040
def parts(self) -> Sequence[str]:

upath/implementations/github.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,42 @@
99
from collections.abc import Sequence
1010
from typing import TYPE_CHECKING
1111

12-
import upath.core
12+
from upath.core import UPath
13+
from upath.types import JoinablePathLike
1314

1415
if TYPE_CHECKING:
15-
if sys.version_info > (3, 11):
16+
from typing import Literal
17+
18+
if sys.version_info >= (3, 11):
1619
from typing import Self
20+
from typing import Unpack
1721
else:
1822
from typing_extensions import Self
23+
from typing_extensions import Unpack
24+
25+
from upath._chain import FSSpecChainParser
26+
from upath.types.storage_options import GithubStorageOptions
27+
28+
__all__ = ["GitHubPath"]
1929

2030

21-
class GitHubPath(upath.core.UPath):
31+
class GitHubPath(UPath):
2232
"""
2333
GitHubPath supporting the fsspec.GitHubFileSystem
2434
"""
2535

36+
__slots__ = ()
37+
38+
if TYPE_CHECKING:
39+
40+
def __init__(
41+
self,
42+
*args: JoinablePathLike,
43+
protocol: Literal["github"] | None = ...,
44+
chain_parser: FSSpecChainParser = ...,
45+
**storage_options: Unpack[GithubStorageOptions],
46+
) -> None: ...
47+
2648
@property
2749
def path(self) -> str:
2850
pth = super().path

upath/implementations/hdfs.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,37 @@
55
from typing import TYPE_CHECKING
66

77
from upath.core import UPath
8+
from upath.types import JoinablePathLike
89

910
if TYPE_CHECKING:
10-
if sys.version_info > (3, 11):
11+
from typing import Literal
12+
13+
if sys.version_info >= (3, 11):
1114
from typing import Self
15+
from typing import Unpack
1216
else:
1317
from typing_extensions import Self
18+
from typing_extensions import Unpack
19+
20+
from upath._chain import FSSpecChainParser
21+
from upath.types.storage_options import HDFSStorageOptions
1422

1523
__all__ = ["HDFSPath"]
1624

1725

1826
class HDFSPath(UPath):
1927
__slots__ = ()
2028

29+
if TYPE_CHECKING:
30+
31+
def __init__(
32+
self,
33+
*args: JoinablePathLike,
34+
protocol: Literal["hdfs"] | None = ...,
35+
chain_parser: FSSpecChainParser = ...,
36+
**storage_options: Unpack[HDFSStorageOptions],
37+
) -> None: ...
38+
2139
def mkdir(
2240
self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False
2341
) -> None:

0 commit comments

Comments
 (0)