Skip to content

Commit 8733f59

Browse files
committed
Remove useless try/except now that we use safeissubclass.
1 parent 6958fbb commit 8733f59

File tree

1 file changed

+117
-120
lines changed

1 file changed

+117
-120
lines changed

unpythonic/typecheck.py

Lines changed: 117 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -214,134 +214,131 @@ def get_args(tp):
214214

215215
# We don't have a match yet, so T might still be one of those meta-utilities
216216
# that hate `issubclass` with a passion.
217-
try:
218-
if safeissubclass(T, typing.Text): # https://docs.python.org/3/library/typing.html#typing.Text
219-
return isinstance(value, str) # alias for str
217+
if safeissubclass(T, typing.Text): # https://docs.python.org/3/library/typing.html#typing.Text
218+
return isinstance(value, str) # alias for str
220219

221-
# Subclass test for Python 3.6 only. Python 3.7+ have typing._GenericAlias for the generics.
222-
if safeissubclass(T, typing.Tuple) or get_origin(T) is tuple:
223-
if not isinstance(value, tuple):
224-
return False
225-
# blare `typing.Tuple`, no restrictions on length or element type.
226-
if T.__args__ is None:
227-
return True
228-
# homogeneous element type, arbitrary length
229-
if len(T.__args__) == 2 and T.__args__[1] is Ellipsis:
230-
if not value: # no elements
231-
# An empty tuple has no element type, so to make multiple dispatch
232-
# behave predictably (so it doesn't guess), we must reject it.
233-
return False
234-
U = T.__args__[0]
235-
return all(isoftype(elt, U) for elt in value)
236-
# heterogeneous element types, exact length
237-
if len(value) != len(T.__args__):
220+
# Subclass test for Python 3.6 only. Python 3.7+ have typing._GenericAlias for the generics.
221+
if safeissubclass(T, typing.Tuple) or get_origin(T) is tuple:
222+
if not isinstance(value, tuple):
223+
return False
224+
# blare `typing.Tuple`, no restrictions on length or element type.
225+
if T.__args__ is None:
226+
return True
227+
# homogeneous element type, arbitrary length
228+
if len(T.__args__) == 2 and T.__args__[1] is Ellipsis:
229+
if not value: # no elements
230+
# An empty tuple has no element type, so to make multiple dispatch
231+
# behave predictably (so it doesn't guess), we must reject it.
238232
return False
239-
return all(isoftype(elt, U) for elt, U in zip(value, T.__args__))
233+
U = T.__args__[0]
234+
return all(isoftype(elt, U) for elt in value)
235+
# heterogeneous element types, exact length
236+
if len(value) != len(T.__args__):
237+
return False
238+
return all(isoftype(elt, U) for elt, U in zip(value, T.__args__))
240239

241-
# Check mapping types that allow non-destructive iteration.
242-
def ismapping(statictype, runtimetype):
240+
# Check mapping types that allow non-destructive iteration.
241+
def ismapping(statictype, runtimetype):
242+
if not isinstance(value, runtimetype):
243+
return False
244+
if T.__args__ is None:
245+
raise TypeError("Missing mandatory key, value type arguments of `{}`".format(statictype))
246+
assert len(T.__args__) == 2
247+
if not value: # An empty dict has no key and value types.
248+
return False
249+
K, V = T.__args__
250+
return all(isoftype(k, K) and isoftype(v, V) for k, v in value.items())
251+
for statictype, runtimetype in ((typing.Dict, dict),
252+
(typing.MutableMapping, collections.abc.MutableMapping),
253+
(typing.Mapping, collections.abc.Mapping)):
254+
if safeissubclass(T, statictype) or get_origin(T) is runtimetype:
255+
return ismapping(statictype, runtimetype)
256+
257+
# ItemsView is a special-case mapping in that we must not call
258+
# `.items()` on `value`.
259+
if safeissubclass(T, typing.ItemsView) or get_origin(T) is collections.abc.ItemsView:
260+
if not isinstance(value, collections.abc.ItemsView):
261+
return False
262+
if T.__args__ is None:
263+
raise TypeError("Missing mandatory key, value type arguments of `{}`".format(statictype))
264+
assert len(T.__args__) == 2
265+
if not value: # An empty dict has no key and value types.
266+
return False
267+
K, V = T.__args__
268+
return all(isoftype(k, K) and isoftype(v, V) for k, v in value)
269+
270+
# Check iterable types that allow non-destructive iteration.
271+
#
272+
# Special-case strings; they match typing.Sequence, but they're not
273+
# generics; the class has no `__args__` so this code doesn't apply to
274+
# them.
275+
if T not in (str, bytes):
276+
def iscollection(statictype, runtimetype):
243277
if not isinstance(value, runtimetype):
244278
return False
245-
if T.__args__ is None:
246-
raise TypeError("Missing mandatory key, value type arguments of `{}`".format(statictype))
247-
assert len(T.__args__) == 2
248-
if not value: # An empty dict has no key and value types.
279+
if safeissubclass(statictype, typing.ByteString) or get_origin(statictype) is collections.abc.ByteString:
280+
# WTF? A ByteString is a Sequence[int], but only statically.
281+
# At run time, the `__args__` are actually empty - it looks
282+
# like a bare Sequence, which is invalid. HACK the special case.
283+
typeargs = (int,)
284+
else:
285+
typeargs = T.__args__
286+
if typeargs is None:
287+
raise TypeError("Missing mandatory element type argument of `{}`".format(statictype))
288+
# Judging by the docs, List takes one type argument. The rest are similar.
289+
# https://docs.python.org/3/library/typing.html#typing.List
290+
assert len(typeargs) == 1
291+
if not value: # An empty collection has no element type.
249292
return False
250-
K, V = T.__args__
251-
return all(isoftype(k, K) and isoftype(v, V) for k, v in value.items())
252-
for statictype, runtimetype in ((typing.Dict, dict),
253-
(typing.MutableMapping, collections.abc.MutableMapping),
254-
(typing.Mapping, collections.abc.Mapping)):
293+
U = typeargs[0]
294+
return all(isoftype(elt, U) for elt in value)
295+
for statictype, runtimetype in ((typing.List, list),
296+
(typing.FrozenSet, frozenset),
297+
(typing.Set, set),
298+
(typing.Deque, collections.deque),
299+
(typing.ByteString, collections.abc.ByteString), # must check before Sequence
300+
(typing.MutableSet, collections.abc.MutableSet), # must check mutable first
301+
# because a mutable value has *also* the interface of the immutable variant
302+
# (e.g. MutableSet is a subtype of AbstractSet)
303+
(typing.KeysView, collections.abc.KeysView),
304+
(typing.ValuesView, collections.abc.ValuesView),
305+
(typing.MappingView, collections.abc.MappingView), # MappingView has one type argument so it goes here?
306+
(typing.AbstractSet, collections.abc.Set),
307+
(typing.MutableSequence, collections.abc.MutableSequence),
308+
(typing.MappingView, collections.abc.MappingView),
309+
(typing.Sequence, collections.abc.Sequence)):
255310
if safeissubclass(T, statictype) or get_origin(T) is runtimetype:
256-
return ismapping(statictype, runtimetype)
311+
return iscollection(statictype, runtimetype)
257312

258-
# ItemsView is a special-case mapping in that we must not call
259-
# `.items()` on `value`.
260-
if safeissubclass(T, typing.ItemsView) or get_origin(T) is collections.abc.ItemsView:
261-
if not isinstance(value, collections.abc.ItemsView):
262-
return False
263-
if T.__args__ is None:
264-
raise TypeError("Missing mandatory key, value type arguments of `{}`".format(statictype))
265-
assert len(T.__args__) == 2
266-
if not value: # An empty dict has no key and value types.
267-
return False
268-
K, V = T.__args__
269-
return all(isoftype(k, K) and isoftype(v, V) for k, v in value)
270-
271-
# Check iterable types that allow non-destructive iteration.
272-
#
273-
# Special-case strings; they match typing.Sequence, but they're not
274-
# generics; the class has no `__args__` so this code doesn't apply to
275-
# them.
276-
if T not in (str, bytes):
277-
def iscollection(statictype, runtimetype):
278-
if not isinstance(value, runtimetype):
279-
return False
280-
if safeissubclass(statictype, typing.ByteString) or get_origin(statictype) is collections.abc.ByteString:
281-
# WTF? A ByteString is a Sequence[int], but only statically.
282-
# At run time, the `__args__` are actually empty - it looks
283-
# like a bare Sequence, which is invalid. HACK the special case.
284-
typeargs = (int,)
285-
else:
286-
typeargs = T.__args__
287-
if typeargs is None:
288-
raise TypeError("Missing mandatory element type argument of `{}`".format(statictype))
289-
# Judging by the docs, List takes one type argument. The rest are similar.
290-
# https://docs.python.org/3/library/typing.html#typing.List
291-
assert len(typeargs) == 1
292-
if not value: # An empty collection has no element type.
293-
return False
294-
U = typeargs[0]
295-
return all(isoftype(elt, U) for elt in value)
296-
for statictype, runtimetype in ((typing.List, list),
297-
(typing.FrozenSet, frozenset),
298-
(typing.Set, set),
299-
(typing.Deque, collections.deque),
300-
(typing.ByteString, collections.abc.ByteString), # must check before Sequence
301-
(typing.MutableSet, collections.abc.MutableSet), # must check mutable first
302-
# because a mutable value has *also* the interface of the immutable variant
303-
# (e.g. MutableSet is a subtype of AbstractSet)
304-
(typing.KeysView, collections.abc.KeysView),
305-
(typing.ValuesView, collections.abc.ValuesView),
306-
(typing.MappingView, collections.abc.MappingView), # MappingView has one type argument so it goes here?
307-
(typing.AbstractSet, collections.abc.Set),
308-
(typing.MutableSequence, collections.abc.MutableSequence),
309-
(typing.MappingView, collections.abc.MappingView),
310-
(typing.Sequence, collections.abc.Sequence)):
311-
if safeissubclass(T, statictype) or get_origin(T) is runtimetype:
312-
return iscollection(statictype, runtimetype)
313-
314-
if safeissubclass(T, typing.Callable) or get_origin(T) is collections.abc.Callable:
315-
if not callable(value):
316-
return False
317-
return True
318-
# # TODO: analyze Callable[[a0, a1, ...], ret], Callable[..., ret].
319-
# if T.__args__ is None: # bare `typing.Callable`, no restrictions on arg/return types.
320-
# return True
321-
# sig = typing.get_type_hints(value)
322-
# *argtypes, rettype = T.__args__
323-
# if len(argtypes) == 1 and argtypes[0] is Ellipsis:
324-
# pass # argument types not specified
325-
# else:
326-
# # TODO: we need the names of the positional arguments of the `value` callable here.
327-
# for a in argtypes:
328-
# # TODO: We need to compare two specs against each other, not a value against T.
329-
# # This needs an `issubtype` function.
330-
# #
331-
# # Note arguments behave contravariantly, while return values behave covariantly:
332-
# # - f(x: animal) is a *subtype* of f(x: cat), since any use site that passes
333-
# # in a cat (more specific) works fine with a function that accepts any animal
334-
# # (more general).
335-
# # - g() -> int is a subtype of g() -> Number, because any use site that
336-
# # expects a Number (more general) can deal with an int (more specific).
337-
# # https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
338-
# if not issubtype(???, a):
339-
# return False
340-
# if not issubtype(rettype, sig["return"]):
341-
# return False
342-
# return True
343-
except TypeError: # probably one of those meta-utilities that hates issubclass.
344-
raise # DEBUG
313+
if safeissubclass(T, typing.Callable) or get_origin(T) is collections.abc.Callable:
314+
if not callable(value):
315+
return False
316+
return True
317+
# # TODO: analyze Callable[[a0, a1, ...], ret], Callable[..., ret].
318+
# if T.__args__ is None: # bare `typing.Callable`, no restrictions on arg/return types.
319+
# return True
320+
# sig = typing.get_type_hints(value)
321+
# *argtypes, rettype = T.__args__
322+
# if len(argtypes) == 1 and argtypes[0] is Ellipsis:
323+
# pass # argument types not specified
324+
# else:
325+
# # TODO: we need the names of the positional arguments of the `value` callable here.
326+
# for a in argtypes:
327+
# # TODO: We need to compare two specs against each other, not a value against T.
328+
# # This needs an `issubtype` function.
329+
# #
330+
# # Note arguments behave contravariantly, while return values behave covariantly:
331+
# # - f(x: animal) is a *subtype* of f(x: cat), since any use site that passes
332+
# # in a cat (more specific) works fine with a function that accepts any animal
333+
# # (more general).
334+
# # - g() -> int is a subtype of g() -> Number, because any use site that
335+
# # expects a Number (more general) can deal with an int (more specific).
336+
# # https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
337+
# if not issubtype(???, a):
338+
# return False
339+
# if not issubtype(rettype, sig["return"]):
340+
# return False
341+
# return True
345342

346343
# Catch any `typing` meta-utilities we don't currently support.
347344
if hasattr(T, "__module__") and T.__module__ == "typing":

0 commit comments

Comments
 (0)