@@ -57,6 +57,46 @@ def fget(self):
5757 return self ._fget
5858
5959
60+ # In the following check_foo() functions, the first parameter starts with an
61+ # underscore because it is intended to be positional-only (e.g., so that
62+ # `_api.check_isinstance([...], types=foo)` doesn't fail.
63+
64+ def check_isinstance (_types , ** kwargs ):
65+ """
66+ For each *key, value* pair in *kwargs*, check that *value* is an instance
67+ of one of *_types*; if not, raise an appropriate TypeError.
68+
69+ As a special case, a ``None`` entry in *_types* is treated as NoneType.
70+
71+ Examples
72+ --------
73+ >>> _api.check_isinstance((SomeClass, None), arg=arg)
74+ """
75+ types = _types
76+ none_type = type (None )
77+ types = ((types ,) if isinstance (types , type ) else
78+ (none_type ,) if types is None else
79+ tuple (none_type if tp is None else tp for tp in types ))
80+
81+ def type_name (tp ):
82+ return ("None" if tp is none_type
83+ else tp .__qualname__ if tp .__module__ == "builtins"
84+ else f"{ tp .__module__ } .{ tp .__qualname__ } " )
85+
86+ for k , v in kwargs .items ():
87+ if not isinstance (v , types ):
88+ names = [* map (type_name , types )]
89+ if "None" in names : # Move it to the end for better wording.
90+ names .remove ("None" )
91+ names .append ("None" )
92+ raise TypeError (
93+ "{!r} must be an instance of {}, not a {}" .format (
94+ k ,
95+ ", " .join (names [:- 1 ]) + " or " + names [- 1 ]
96+ if len (names ) > 1 else names [0 ],
97+ type_name (type (v ))))
98+
99+
60100def check_in_list (_values , * , _print_supported_values = True , ** kwargs ):
61101 """
62102 For each *key, value* pair in *kwargs*, check that *value* is in *_values*.
0 commit comments