Skip to content

Commit a2b0d8c

Browse files
committed
Bump min Python version from 3.5.4 to 3.6.2
1 parent b53855f commit a2b0d8c

File tree

11 files changed

+179
-199
lines changed

11 files changed

+179
-199
lines changed

.travis.yml

-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ dist: bionic # VirtualEnv is too old on xenial
55

66
matrix:
77
include:
8-
- python: "3.5"
98
- python: "3.6"
109
- python: "3.7"
1110
- python: "3.8"
@@ -23,9 +22,6 @@ matrix:
2322

2423
# Testing on macOS/Darwin tends to be much slower so only test the bare minimum
2524
#
26-
# Minimum testing version is 3.6, since the 3.5 binaries from python.org fail
27-
# with TLS error when trying to install `tox`.
28-
#
2925
# When changing any version here also update the relevant checksum below with
3026
# the values found on the https://python.org/ website.
3127
- os: osx

ipfshttpclient/client/base.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
from .. import multipart, http, utils
1010

1111

12+
#TODO: Review if this is still necessary after requiring Python 3.7.
1213
if ty.TYPE_CHECKING:
1314
import typing_extensions as ty_ext
1415
else:
1516
ty_ext = utils
1617

18+
# Ensure that proper typings show up when generating documentation without hard-depending on
19+
# the cid package.
1720
if "cid" in sys.modules:
1821
import cid # type: ignore[import]
1922
cid_t = ty.Union[str, cid.CIDv0, cid.CIDv1]
@@ -29,7 +32,7 @@
2932
json_primitive_t = utils.json_primitive_t
3033
json_value_t = utils.json_value_t
3134

32-
# The following would be much more useful once GH/python/mypy#4441 is implemented…
35+
# The following would be more useful with https://github.com/python/mypy/issues/4441
3336
if ty.TYPE_CHECKING:
3437
# Lame workaround for type checkers
3538
CommonArgs = ty.Union[bool, http.auth_t, http.cookies_t, http.reqdata_sync_t,
@@ -48,7 +51,7 @@
4851
else:
4952
CommonArgs = ty.Dict[str, ty.Any]
5053

51-
# work around GH/mypy/mypy#731: no recursive structural types yet
54+
#TODO: work around https://github.com/python/mypy/issues/731: no recursive structural types yet
5255
response_item_t = ty.Union[
5356
json_primitive_t,
5457
"ResponseBase",

ipfshttpclient/exceptions.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
The class hierachy for exceptions is::
2+
The class hierarchy for exceptions is:
33
44
Error
55
├── VersionMismatch
@@ -161,4 +161,4 @@ class ConnectionError(CommunicationError):
161161

162162
class TimeoutError(CommunicationError):
163163
"""Raised when the daemon didn't respond in time."""
164-
__slots__ = ()
164+
__slots__ = ()

ipfshttpclient/filescanner.py

+80-88
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,23 @@
2121
re_pattern_t = re_pattern_type = type(re.compile(""))
2222

2323

24-
if hasattr(enum, "auto"): #PY36+
25-
enum_auto = enum.auto
26-
elif not ty.TYPE_CHECKING: #PY35
27-
_counter = 0
28-
29-
def enum_auto() -> int:
30-
global _counter
31-
_counter += 1
32-
return _counter
24+
enum_auto = enum.auto
3325

3426

3527
O_DIRECTORY = getattr(os, "O_DIRECTORY", 0) # type: int
3628

3729

38-
HAVE_FWALK = hasattr(os, "fwalk") # type: bool
39-
HAVE_FWALK_BYTES = HAVE_FWALK and sys.version_info >= (3, 7) # type: bool
30+
HAVE_FWALK: bool = hasattr(os, "fwalk")
31+
HAVE_FWALK_BYTES = HAVE_FWALK and sys.version_info >= (3, 7)
4032

4133

4234
class Matcher(ty.Generic[ty.AnyStr], metaclass=abc.ABCMeta):
4335
"""Represents a type that can match on file paths and decide whether they
4436
should be included in some file scanning/adding operation"""
4537
__slots__ = ("is_binary",)
46-
#is_binary: bool
47-
38+
4839
def __init__(self, is_binary: bool = False) -> None:
49-
self.is_binary = is_binary # type: bool
40+
self.is_binary: bool = is_binary
5041

5142
@abc.abstractmethod
5243
def should_descend(self, path: ty.AnyStr) -> bool:
@@ -111,8 +102,8 @@ def should_report(self, path: ty.AnyStr, *, is_dir: bool) -> utils.Literal_False
111102
return False
112103

113104

114-
MATCH_ALL = MatchAll() # type: MatchAll[str]
115-
MATCH_NONE = MatchNone() # type: MatchNone[str]
105+
MATCH_ALL: MatchAll[str] = MatchAll()
106+
MATCH_NONE: MatchNone[str] = MatchNone()
116107

117108

118109
class GlobMatcher(Matcher[ty.AnyStr], ty.Generic[ty.AnyStr]):
@@ -136,11 +127,7 @@ class emulates. If your are accustomed the globing on real Unix shells
136127
pasting it into a real shell works this may be why.
137128
"""
138129
__slots__ = ("period_special", "_sep", "_pat", "_dir_only")
139-
#period_special: bool
140-
#_sep: ty.AnyStr
141-
#_pat: ty.List[ty.Optional[re_pattern_t[ty.AnyStr]]]
142-
#_dir_only: bool
143-
130+
144131
def __init__(self, pat: ty.AnyStr, *, period_special: bool = True):
145132
"""
146133
Arguments
@@ -153,14 +140,14 @@ def __init__(self, pat: ty.AnyStr, *, period_special: bool = True):
153140
shells allow one to disable this behaviour
154141
"""
155142
super().__init__(isinstance(pat, bytes))
156-
157-
self.period_special = period_special # type: bool
158-
159-
self._sep = utils.maybe_fsencode(os.path.sep, pat) # type: ty.AnyStr
160-
dblstar = utils.maybe_fsencode("**", pat) # type: ty.AnyStr
161-
dot = utils.maybe_fsencode(".", pat) # type: ty.AnyStr
162-
pat_ndot = utils.maybe_fsencode(r"(?![.])", pat) # type: ty.AnyStr
163-
143+
144+
self.period_special: bool = period_special
145+
146+
self._sep: ty.AnyStr = utils.maybe_fsencode(os.path.sep, pat)
147+
dblstar = utils.maybe_fsencode("**", pat)
148+
dot = utils.maybe_fsencode(".", pat)
149+
pat_ndot = utils.maybe_fsencode(r"(?![.])", pat)
150+
164151
# Normalize path separator
165152
if os.path.altsep:
166153
pat = pat.replace(utils.maybe_fsencode(os.path.altsep, pat), self._sep)
@@ -174,9 +161,9 @@ def __init__(self, pat: ty.AnyStr, *, period_special: bool = True):
174161
# (TBH, I find it hard to see how that is useful, but everybody does it
175162
# and it keeps things consistent overall – something to only match files
176163
# would be nice however.)
177-
self._dir_only = pat.endswith(self._sep) # type: bool
178-
179-
self._pat = [] # type: ty.List[ty.Optional[re_pattern_t[ty.AnyStr]]]
164+
self._dir_only: bool = pat.endswith(self._sep)
165+
166+
self._pat: ty.List[ty.Optional[re_pattern_t[ty.AnyStr]]] = []
180167
for label in pat.split(self._sep):
181168
# Skip over useless path components
182169
if len(label) < 1 or label == dot:
@@ -193,7 +180,6 @@ def __init__(self, pat: ty.AnyStr, *, period_special: bool = True):
193180
"an issue if you need this!".format(os.fsdecode(label))
194181
)
195182
else:
196-
#re_expr: ty.AnyStr
197183
if not isinstance(label, bytes):
198184
re_expr = fnmatch.translate(label)
199185
else:
@@ -308,32 +294,30 @@ class ReMatcher(Matcher[ty.AnyStr], ty.Generic[ty.AnyStr]):
308294
own matcher with a proper :meth:`Matcher.should_descend` method.
309295
"""
310296
__slots__ = ("_pat",)
311-
#_pat: re_pattern_t[ty.AnyStr]
312-
297+
313298
def __init__(self, pat: ty.Union[ty.AnyStr, "re_pattern_t[ty.AnyStr]"]):
314-
self._pat = re.compile(pat) # type: re_pattern_t[ty.AnyStr]
315-
299+
self._pat: "re_pattern_t[ty.AnyStr]" = re.compile(pat)
300+
316301
super().__init__(not (self._pat.flags & re.UNICODE))
317302

318303
def should_descend(self, path: ty.AnyStr) -> bool:
319304
return True
320305

321306
def should_report(self, path: ty.AnyStr, *, is_dir: bool) -> bool:
322-
suffix = utils.maybe_fsencode(os.path.sep, path) if is_dir else type(path)() # type: ty.AnyStr
307+
suffix: ty.AnyStr = utils.maybe_fsencode(os.path.sep, path) if is_dir else type(path)()
323308
return bool(self._pat.match(path + suffix))
324309

325310

326311
class MetaMatcher(Matcher[ty.AnyStr], ty.Generic[ty.AnyStr]):
327312
"""Match files and directories by delegating to other matchers"""
328313
__slots__ = ("_children",)
329-
#_children: ty.List[Matcher[ty.AnyStr]]
330-
314+
331315
def __init__(self, children: ty.List[Matcher[ty.AnyStr]]):
332316
assert len(children) > 0
333317
super().__init__(children[0].is_binary)
334-
335-
self._children = children # type: ty.List[Matcher[ty.AnyStr]]
336-
318+
319+
self._children: ty.List[Matcher[ty.AnyStr]] = children
320+
337321
def should_descend(self, path: ty.AnyStr) -> bool:
338322
return any(m.should_descend(path) for m in self._children)
339323

@@ -350,13 +334,12 @@ class NoRecusionAdapterMatcher(Matcher[ty.AnyStr], ty.Generic[ty.AnyStr]):
350334
scanner and hence provides ``recursive=False`` semantics.
351335
"""
352336
__slots__ = ("_child",)
353-
#_child: Matcher[ty.AnyStr]
354-
337+
355338
def __init__(self, child: Matcher[ty.AnyStr]):
356339
super().__init__(child.is_binary)
357-
358-
self._child = child # type: Matcher[ty.AnyStr]
359-
340+
341+
self._child: Matcher[ty.AnyStr] = child
342+
360343
def should_descend(self, path: ty.AnyStr) -> bool:
361344
return False
362345

@@ -367,8 +350,9 @@ def should_report(self, path: ty.AnyStr, *, is_dir: bool) -> bool:
367350

368351
if ty.TYPE_CHECKING:
369352
_match_spec_t = ty.Union[ty.AnyStr, re_pattern_t[ty.AnyStr], Matcher[ty.AnyStr]]
370-
else: # Using `re_pattern_t` here like in the type checking case makes
371-
# sphinx_autodoc_typehints explode # noqa: E114
353+
else:
354+
# Using `re_pattern_t` here like in the type checking case makes
355+
# sphinx_autodoc_typehints explode
372356
_match_spec_t = ty.Union[ty.AnyStr, re_pattern_t, Matcher[ty.AnyStr]]
373357
match_spec_t = ty.Union[
374358
ty.Iterable[_match_spec_t[ty.AnyStr]],
@@ -404,16 +388,20 @@ def matcher_from_spec(spec: match_spec_t[ty.AnyStr], *, # type: ignore[misc] #
404388
elif isinstance(spec, (str, bytes)):
405389
return GlobMatcher(spec, period_special=period_special)
406390
elif isinstance(spec, collections.abc.Iterable) and not isinstance(spec, Matcher):
407-
spec = ty.cast(ty.Iterable[_match_spec_t[ty.AnyStr]], spec) # type: ignore[redundant-cast]
408-
409-
matchers = [matcher_from_spec(s, # type: ignore[arg-type] # mypy bug
410-
recursive=recursive, period_special=period_special) for s in spec]
391+
matchers: ty.List[Matcher[ty.AnyStr]] = [
392+
matcher_from_spec(
393+
s, # type: ignore[arg-type]
394+
recursive=recursive,
395+
period_special=period_special)
396+
for s in spec
397+
]
398+
411399
if len(matchers) == 0: # Edge case: Empty list of matchers
412-
return MATCH_NONE # type: ignore[return-value]
400+
return MatchNone()
413401
elif len(matchers) == 1: # Edge case: List of exactly one matcher
414-
return matchers[0] # type: ignore[return-value] # same mypy bug
402+
return matchers[0]
415403
else: # Actual list of matchers (plural)
416-
return MetaMatcher(matchers) # type: ignore[arg-type] # same mypy bug
404+
return MetaMatcher(matchers)
417405
else:
418406
return spec
419407

@@ -436,10 +424,7 @@ class FSNodeType(enum.Enum):
436424

437425
class walk(ty.Generator[FSNodeEntry, ty.Any, None], ty.Generic[ty.AnyStr]):
438426
__slots__ = ("_generator", "_close_fd")
439-
#_generator: ty.Generator[FSNodeEntry, ty.Any, None]
440-
#_close_fd: ty.Optional[int]
441-
442-
427+
443428
def __init__(
444429
self,
445430
directory: ty.Union[ty.AnyStr, utils.PathLike[ty.AnyStr], int],
@@ -482,43 +467,44 @@ def __init__(
482467
:class:`NoRecusionAdapterMatcher` and hence prevent the scanner from
483468
doing any recursion.
484469
"""
485-
self._close_fd = None # type: ty.Optional[int]
486-
470+
self._close_fd: ty.Optional[int] = None
471+
487472
# Create matcher object
488-
matcher = matcher_from_spec(
489-
match_spec, recursive=recursive, period_special=period_special
490-
) # type: Matcher[ty.AnyStr] # type: ignore[assignment]
491-
473+
matcher = matcher_from_spec( # type: ignore[type-var]
474+
match_spec, recursive=recursive, period_special=period_special # type: ignore[arg-type]
475+
)
476+
492477
# Convert directory path to string …
493478
if isinstance(directory, int):
494479
if not HAVE_FWALK:
495480
raise NotImplementedError("Passing a file descriptor as directory is "
496481
"not supported on this platform")
497-
498-
self._generator = self._walk(
499-
directory, None, matcher, follow_symlinks, intermediate_dirs
500-
) # type: ty.Generator[FSNodeEntry, ty.Any, None]
482+
483+
self._generator: ty.Generator[FSNodeEntry, None, None] = self._walk(
484+
directory, None, matcher, follow_symlinks, intermediate_dirs # type: ignore[arg-type]
485+
)
501486
else:
502-
#directory_str: ty.AnyStr
503-
if hasattr(os, "fspath"): #PY36+
504-
directory_str = os.fspath(directory)
505-
elif not ty.TYPE_CHECKING: #PY35
506-
directory_str = utils.convert_path(directory)
507-
487+
directory_str = os.fspath(directory)
488+
508489
# Best-effort ensure that target directory exists if it is accessed by path
509490
os.stat(directory_str)
510491

511492
# … and possibly open it as a FD if this is supported by the platform
512493
#
513494
# Note: `os.fwalk` support for binary paths was only added in 3.7+.
514-
directory_str_or_fd = directory_str # type: ty.Union[ty.AnyStr, int]
495+
directory_str_or_fd = directory_str
515496
if HAVE_FWALK and (not isinstance(directory_str, bytes) or HAVE_FWALK_BYTES):
516-
self._close_fd = directory_str_or_fd = os.open(directory_str, os.O_RDONLY | O_DIRECTORY)
517-
497+
fd = os.open(directory_str, os.O_RDONLY | O_DIRECTORY)
498+
self._close_fd = directory_str_or_fd = fd # type: ignore[assignment]
499+
518500
self._generator = self._walk(
519-
directory_str_or_fd, directory_str, matcher, follow_symlinks, intermediate_dirs
501+
directory_str_or_fd,
502+
directory_str,
503+
matcher, # type: ignore[arg-type]
504+
follow_symlinks,
505+
intermediate_dirs
520506
)
521-
507+
522508
def __iter__(self) -> 'walk[ty.AnyStr]':
523509
return self
524510

@@ -548,7 +534,8 @@ def throw(self, typ: ty.Union[ty.Type[BaseException], BaseException],
548534
tb: ty.Optional[types.TracebackType] = None) -> FSNodeEntry:
549535
try:
550536
if isinstance(typ, type):
551-
return self._generator.throw(typ, val, tb)
537+
bt = ty.cast(ty.Type[BaseException], typ) # type: ignore[redundant-cast]
538+
return self._generator.throw(bt, val, tb)
552539
else:
553540
assert val is None
554541
return self._generator.throw(typ, val, tb)
@@ -595,8 +582,8 @@ def _walk(
595582
while directory.endswith(sep):
596583
directory = directory[:-len(sep)]
597584
prefix = (directory if not isinstance(directory, int) else dot) + sep
598-
599-
reported_directories = set() # type: ty.Set[ty.AnyStr]
585+
586+
reported_directories: ty.Set[ty.AnyStr] = set()
600587

601588
# Always report the top-level directory even if nothing therein is matched
602589
reported_directories.add(utils.maybe_fsencode("", sep))
@@ -616,14 +603,19 @@ def _walk(
616603
try:
617604
for result in walk_iter:
618605
dirpath, dirnames, filenames = result[0:3]
619-
dirfd = result[3] if len(result) > 3 else None # type:ty.Optional[int] # type: ignore[misc]
620-
606+
607+
if len(result) <= 3:
608+
dirfd: ty.Optional[int] = None
609+
else:
610+
# mypy wrongly believes this will produce an index-out-of-range exception.
611+
dirfd = result[3] # type: ignore[misc]
612+
621613
# Remove the directory prefix from the received path
622614
_, _, dirpath = dirpath.partition(prefix)
623615

624616
# Keep track of reported intermediaries, so that we only check for
625617
# these at most once per directory base
626-
intermediates_reported = False # type: bool
618+
intermediates_reported = False
627619

628620
for filename, is_dir in self._join_dirs_and_files(list(dirnames), filenames):
629621
filepath = os.path.join(dirpath, filename)

0 commit comments

Comments
 (0)