test_inspect_args.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. import functools
  2. import inspect
  3. import itertools
  4. import operator
  5. import sys
  6. import cytoolz
  7. from cytoolz.functoolz import (curry, is_valid_args, is_partial_args, is_arity,
  8. num_required_args, has_varargs, has_keywords)
  9. from cytoolz._signatures import builtins
  10. import cytoolz._signatures as _sigs
  11. from cytoolz.utils import raises
  12. def make_func(param_string, raise_if_called=True):
  13. if not param_string.startswith('('):
  14. param_string = '(%s)' % param_string
  15. if raise_if_called:
  16. body = 'raise ValueError("function should not be called")'
  17. else:
  18. body = 'return True'
  19. d = {}
  20. exec('def func%s:\n %s' % (param_string, body), globals(), d)
  21. return d['func']
  22. def test_make_func():
  23. f = make_func('')
  24. assert raises(ValueError, lambda: f())
  25. assert raises(TypeError, lambda: f(1))
  26. f = make_func('', raise_if_called=False)
  27. assert f()
  28. assert raises(TypeError, lambda: f(1))
  29. f = make_func('x, y=1', raise_if_called=False)
  30. assert f(1)
  31. assert f(x=1)
  32. assert f(1, 2)
  33. assert f(x=1, y=2)
  34. assert raises(TypeError, lambda: f(1, 2, 3))
  35. f = make_func('(x, y=1)', raise_if_called=False)
  36. assert f(1)
  37. assert f(x=1)
  38. assert f(1, 2)
  39. assert f(x=1, y=2)
  40. assert raises(TypeError, lambda: f(1, 2, 3))
  41. def test_is_valid(check_valid=is_valid_args, incomplete=False):
  42. orig_check_valid = check_valid
  43. check_valid = lambda func, *args, **kwargs: orig_check_valid(func, args, kwargs)
  44. f = make_func('')
  45. assert check_valid(f)
  46. assert check_valid(f, 1) is False
  47. assert check_valid(f, x=1) is False
  48. f = make_func('x')
  49. assert check_valid(f) is incomplete
  50. assert check_valid(f, 1)
  51. assert check_valid(f, x=1)
  52. assert check_valid(f, 1, x=2) is False
  53. assert check_valid(f, 1, y=2) is False
  54. assert check_valid(f, 1, 2) is False
  55. assert check_valid(f, x=1, y=2) is False
  56. f = make_func('x=1')
  57. assert check_valid(f)
  58. assert check_valid(f, 1)
  59. assert check_valid(f, x=1)
  60. assert check_valid(f, 1, x=2) is False
  61. assert check_valid(f, 1, y=2) is False
  62. assert check_valid(f, 1, 2) is False
  63. assert check_valid(f, x=1, y=2) is False
  64. f = make_func('*args')
  65. assert check_valid(f)
  66. assert check_valid(f, 1)
  67. assert check_valid(f, 1, 2)
  68. assert check_valid(f, x=1) is False
  69. f = make_func('**kwargs')
  70. assert check_valid(f)
  71. assert check_valid(f, x=1)
  72. assert check_valid(f, x=1, y=2)
  73. assert check_valid(f, 1) is False
  74. f = make_func('x, *args')
  75. assert check_valid(f) is incomplete
  76. assert check_valid(f, 1)
  77. assert check_valid(f, 1, 2)
  78. assert check_valid(f, x=1)
  79. assert check_valid(f, 1, x=1) is False
  80. assert check_valid(f, 1, y=1) is False
  81. f = make_func('x, y=1, **kwargs')
  82. assert check_valid(f) is incomplete
  83. assert check_valid(f, 1)
  84. assert check_valid(f, x=1)
  85. assert check_valid(f, 1, 2)
  86. assert check_valid(f, x=1, y=2, z=3)
  87. assert check_valid(f, 1, 2, y=3) is False
  88. f = make_func('a, b, c=3, d=4')
  89. assert check_valid(f) is incomplete
  90. assert check_valid(f, 1) is incomplete
  91. assert check_valid(f, 1, 2)
  92. assert check_valid(f, 1, c=3) is incomplete
  93. assert check_valid(f, 1, e=3) is False
  94. assert check_valid(f, 1, 2, e=3) is False
  95. assert check_valid(f, 1, 2, b=3) is False
  96. assert check_valid(1) is False
  97. def test_is_valid_py3(check_valid=is_valid_args, incomplete=False):
  98. orig_check_valid = check_valid
  99. check_valid = lambda func, *args, **kwargs: orig_check_valid(func, args, kwargs)
  100. f = make_func('x, *, y=1')
  101. assert check_valid(f) is incomplete
  102. assert check_valid(f, 1)
  103. assert check_valid(f, x=1)
  104. assert check_valid(f, 1, y=2)
  105. assert check_valid(f, 1, 2) is False
  106. assert check_valid(f, 1, z=2) is False
  107. f = make_func('x, *args, y=1')
  108. assert check_valid(f) is incomplete
  109. assert check_valid(f, 1)
  110. assert check_valid(f, x=1)
  111. assert check_valid(f, 1, y=2)
  112. assert check_valid(f, 1, 2, y=2)
  113. assert check_valid(f, 1, 2)
  114. assert check_valid(f, 1, z=2) is False
  115. f = make_func('*, y=1')
  116. assert check_valid(f)
  117. assert check_valid(f, 1) is False
  118. assert check_valid(f, y=1)
  119. assert check_valid(f, z=1) is False
  120. f = make_func('x, *, y')
  121. assert check_valid(f) is incomplete
  122. assert check_valid(f, 1) is incomplete
  123. assert check_valid(f, x=1) is incomplete
  124. assert check_valid(f, 1, y=2)
  125. assert check_valid(f, x=1, y=2)
  126. assert check_valid(f, 1, 2) is False
  127. assert check_valid(f, 1, z=2) is False
  128. assert check_valid(f, 1, y=1, z=2) is False
  129. f = make_func('x=1, *, y, z=3')
  130. assert check_valid(f) is incomplete
  131. assert check_valid(f, 1, z=3) is incomplete
  132. assert check_valid(f, y=2)
  133. assert check_valid(f, 1, y=2)
  134. assert check_valid(f, x=1, y=2)
  135. assert check_valid(f, x=1, y=2, z=3)
  136. assert check_valid(f, 1, x=1, y=2) is False
  137. assert check_valid(f, 1, 3, y=2) is False
  138. f = make_func('w, x=2, *args, y, z=4')
  139. assert check_valid(f) is incomplete
  140. assert check_valid(f, 1) is incomplete
  141. assert check_valid(f, 1, y=3)
  142. f = make_func('a, b, c=3, d=4, *args, e=5, f=6, g, h')
  143. assert check_valid(f) is incomplete
  144. assert check_valid(f, 1) is incomplete
  145. assert check_valid(f, 1, 2) is incomplete
  146. assert check_valid(f, 1, 2, g=7) is incomplete
  147. assert check_valid(f, 1, 2, g=7, h=8)
  148. assert check_valid(f, 1, 2, 3, 4, 5, 6, 7, 8, 9) is incomplete
  149. f = make_func('a: int, b: float')
  150. assert check_valid(f) is incomplete
  151. assert check_valid(f, 1) is incomplete
  152. assert check_valid(f, b=1) is incomplete
  153. assert check_valid(f, 1, 2)
  154. f = make_func('(a: int, b: float) -> float')
  155. assert check_valid(f) is incomplete
  156. assert check_valid(f, 1) is incomplete
  157. assert check_valid(f, b=1) is incomplete
  158. assert check_valid(f, 1, 2)
  159. f.__signature__ = 34
  160. assert check_valid(f) is False
  161. class RaisesValueError(object):
  162. def __call__(self):
  163. pass
  164. @property
  165. def __signature__(self):
  166. raise ValueError('Testing Python 3.4')
  167. f = RaisesValueError()
  168. assert check_valid(f) is None
  169. def test_is_partial():
  170. test_is_valid(check_valid=is_partial_args, incomplete=True)
  171. test_is_valid_py3(check_valid=is_partial_args, incomplete=True)
  172. def test_is_valid_curry():
  173. def check_curry(func, args, kwargs, incomplete=True):
  174. try:
  175. curry(func)(*args, **kwargs)
  176. curry(func, *args)(**kwargs)
  177. curry(func, **kwargs)(*args)
  178. curry(func, *args, **kwargs)()
  179. if not isinstance(func, type(lambda: None)):
  180. return None
  181. return incomplete
  182. except ValueError:
  183. return True
  184. except TypeError:
  185. return False
  186. check_valid = functools.partial(check_curry, incomplete=True)
  187. test_is_valid(check_valid=check_valid, incomplete=True)
  188. test_is_valid_py3(check_valid=check_valid, incomplete=True)
  189. check_valid = functools.partial(check_curry, incomplete=False)
  190. test_is_valid(check_valid=check_valid, incomplete=False)
  191. test_is_valid_py3(check_valid=check_valid, incomplete=False)
  192. def test_func_keyword():
  193. def f(func=None):
  194. pass
  195. assert is_valid_args(f, (), {})
  196. assert is_valid_args(f, (None,), {})
  197. assert is_valid_args(f, (), {'func': None})
  198. assert is_valid_args(f, (None,), {'func': None}) is False
  199. assert is_partial_args(f, (), {})
  200. assert is_partial_args(f, (None,), {})
  201. assert is_partial_args(f, (), {'func': None})
  202. assert is_partial_args(f, (None,), {'func': None}) is False
  203. def test_has_unknown_args():
  204. assert has_varargs(1) is False
  205. assert has_varargs(map)
  206. assert has_varargs(make_func('')) is False
  207. assert has_varargs(make_func('x, y, z')) is False
  208. assert has_varargs(make_func('*args'))
  209. assert has_varargs(make_func('**kwargs')) is False
  210. assert has_varargs(make_func('x, y, *args, **kwargs'))
  211. assert has_varargs(make_func('x, y, z=1')) is False
  212. assert has_varargs(make_func('x, y, z=1, **kwargs')) is False
  213. f = make_func('*args')
  214. f.__signature__ = 34
  215. assert has_varargs(f) is False
  216. class RaisesValueError(object):
  217. def __call__(self):
  218. pass
  219. @property
  220. def __signature__(self):
  221. raise ValueError('Testing Python 3.4')
  222. f = RaisesValueError()
  223. assert has_varargs(f) is None
  224. def test_num_required_args():
  225. assert num_required_args(lambda: None) == 0
  226. assert num_required_args(lambda x: None) == 1
  227. assert num_required_args(lambda x, *args: None) == 1
  228. assert num_required_args(lambda x, **kwargs: None) == 1
  229. assert num_required_args(lambda x, y, *args, **kwargs: None) == 2
  230. assert num_required_args(map) == 2
  231. assert num_required_args(dict) is None
  232. def test_has_keywords():
  233. assert has_keywords(lambda: None) is False
  234. assert has_keywords(lambda x: None) is False
  235. assert has_keywords(lambda x=1: None)
  236. assert has_keywords(lambda **kwargs: None)
  237. assert has_keywords(int)
  238. assert has_keywords(sorted)
  239. assert has_keywords(max)
  240. assert has_keywords(map) is False
  241. assert has_keywords(bytearray) is None
  242. def test_has_varargs():
  243. assert has_varargs(lambda: None) is False
  244. assert has_varargs(lambda *args: None)
  245. assert has_varargs(lambda **kwargs: None) is False
  246. assert has_varargs(map)
  247. assert has_varargs(max) is None
  248. def test_is_arity():
  249. assert is_arity(0, lambda: None)
  250. assert is_arity(1, lambda: None) is False
  251. assert is_arity(1, lambda x: None)
  252. assert is_arity(3, lambda x, y, z: None)
  253. assert is_arity(1, lambda x, *args: None) is False
  254. assert is_arity(1, lambda x, **kwargs: None) is False
  255. assert is_arity(1, all)
  256. assert is_arity(2, map) is False
  257. assert is_arity(2, range) is None
  258. def test_introspect_curry_valid_py3(check_valid=is_valid_args, incomplete=False):
  259. orig_check_valid = check_valid
  260. check_valid = lambda _func, *args, **kwargs: orig_check_valid(_func, args, kwargs)
  261. f = cytoolz.curry(make_func('x, y, z=0'))
  262. assert check_valid(f)
  263. assert check_valid(f, 1)
  264. assert check_valid(f, 1, 2)
  265. assert check_valid(f, 1, 2, 3)
  266. assert check_valid(f, 1, 2, 3, 4) is False
  267. assert check_valid(f, invalid_keyword=True) is False
  268. assert check_valid(f(1))
  269. assert check_valid(f(1), 2)
  270. assert check_valid(f(1), 2, 3)
  271. assert check_valid(f(1), 2, 3, 4) is False
  272. assert check_valid(f(1), x=2) is False
  273. assert check_valid(f(1), y=2)
  274. assert check_valid(f(x=1), 2) is False
  275. assert check_valid(f(x=1), y=2)
  276. assert check_valid(f(y=2), 1)
  277. assert check_valid(f(y=2), 1, z=3)
  278. assert check_valid(f(y=2), 1, 3) is False
  279. f = cytoolz.curry(make_func('x, y, z=0'), 1, x=1)
  280. assert check_valid(f) is False
  281. assert check_valid(f, z=3) is False
  282. f = cytoolz.curry(make_func('x, y, *args, z'))
  283. assert check_valid(f)
  284. assert check_valid(f, 0)
  285. assert check_valid(f(1), 0)
  286. assert check_valid(f(1, 2), 0)
  287. assert check_valid(f(1, 2, 3), 0)
  288. assert check_valid(f(1, 2, 3, 4), 0)
  289. assert check_valid(f(1, 2, 3, 4), z=4)
  290. assert check_valid(f(x=1))
  291. assert check_valid(f(x=1), 1) is False
  292. assert check_valid(f(x=1), y=2)
  293. def test_introspect_curry_partial_py3():
  294. test_introspect_curry_valid_py3(check_valid=is_partial_args, incomplete=True)
  295. def test_introspect_curry_py3():
  296. f = cytoolz.curry(make_func(''))
  297. assert num_required_args(f) == 0
  298. assert is_arity(0, f)
  299. assert has_varargs(f) is False
  300. assert has_keywords(f) is False
  301. f = cytoolz.curry(make_func('x'))
  302. assert num_required_args(f) == 0
  303. assert is_arity(0, f) is False
  304. assert is_arity(1, f) is False
  305. assert has_varargs(f) is False
  306. assert has_keywords(f) # A side-effect of being curried
  307. f = cytoolz.curry(make_func('x, y, z=0'))
  308. assert num_required_args(f) == 0
  309. assert is_arity(0, f) is False
  310. assert is_arity(1, f) is False
  311. assert is_arity(2, f) is False
  312. assert is_arity(3, f) is False
  313. assert has_varargs(f) is False
  314. assert has_keywords(f)
  315. f = cytoolz.curry(make_func('*args, **kwargs'))
  316. assert num_required_args(f) == 0
  317. assert has_varargs(f)
  318. assert has_keywords(f)
  319. def test_introspect_builtin_modules():
  320. mods = [builtins, functools, itertools, operator, cytoolz,
  321. cytoolz.functoolz, cytoolz.itertoolz, cytoolz.dicttoolz, cytoolz.recipes]
  322. denylist = set()
  323. def add_denylist(mod, attr):
  324. if hasattr(mod, attr):
  325. denylist.add(getattr(mod, attr))
  326. add_denylist(builtins, 'basestring')
  327. add_denylist(builtins, 'NoneType')
  328. add_denylist(builtins, '__metaclass__')
  329. add_denylist(builtins, 'sequenceiterator')
  330. def is_missing(modname, name, func):
  331. if name.startswith('_') and not name.startswith('__'):
  332. return False
  333. if name.startswith('__pyx_unpickle_') or name.endswith('_cython__'):
  334. return False
  335. try:
  336. if issubclass(func, BaseException):
  337. return False
  338. except TypeError:
  339. pass
  340. try:
  341. return (callable(func)
  342. and func.__module__ is not None
  343. and modname in func.__module__
  344. and is_partial_args(func, (), {}) is not True
  345. and func not in denylist)
  346. except AttributeError:
  347. return False
  348. missing = {}
  349. for mod in mods:
  350. modname = mod.__name__
  351. for name, func in vars(mod).items():
  352. if is_missing(modname, name, func):
  353. if modname not in missing:
  354. missing[modname] = []
  355. missing[modname].append(name)
  356. if missing:
  357. messages = []
  358. for modname, names in sorted(missing.items()):
  359. msg = '{}:\n {}'.format(modname, '\n '.join(sorted(names)))
  360. messages.append(msg)
  361. message = 'Missing introspection for the following callables:\n\n'
  362. raise AssertionError(message + '\n\n'.join(messages))
  363. def test_inspect_signature_property():
  364. # By adding AddX to our signature registry, we can inspect the class
  365. # itself and objects of the class. `inspect.signature` doesn't like
  366. # it when `obj.__signature__` is a property.
  367. class AddX(object):
  368. def __init__(self, func):
  369. self.func = func
  370. def __call__(self, addx, *args, **kwargs):
  371. return addx + self.func(*args, **kwargs)
  372. @property
  373. def __signature__(self):
  374. sig = inspect.signature(self.func)
  375. params = list(sig.parameters.values())
  376. kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
  377. newparam = inspect.Parameter('addx', kind)
  378. params = [newparam] + params
  379. return sig.replace(parameters=params)
  380. addx = AddX(lambda x: x)
  381. sig = inspect.signature(addx)
  382. assert sig == inspect.Signature(parameters=[
  383. inspect.Parameter('addx', inspect.Parameter.POSITIONAL_OR_KEYWORD),
  384. inspect.Parameter('x', inspect.Parameter.POSITIONAL_OR_KEYWORD)])
  385. assert num_required_args(AddX) is False
  386. _sigs.signatures[AddX] = (_sigs.expand_sig((0, lambda func: None)),)
  387. assert num_required_args(AddX) == 1
  388. del _sigs.signatures[AddX]
  389. def test_inspect_wrapped_property():
  390. class Wrapped(object):
  391. def __init__(self, func):
  392. self.func = func
  393. def __call__(self, *args, **kwargs):
  394. return self.func(*args, **kwargs)
  395. @property
  396. def __wrapped__(self):
  397. return self.func
  398. func = lambda x: x
  399. wrapped = Wrapped(func)
  400. assert inspect.signature(func) == inspect.signature(wrapped)
  401. # inspect.signature did not used to work properly on wrappers,
  402. # but it was fixed in Python 3.11.9, Python 3.12.3 and Python
  403. # 3.13+
  404. inspectbroken = True
  405. if sys.version_info.major > 3:
  406. inspectbroken = False
  407. if sys.version_info.major == 3:
  408. if sys.version_info.minor == 11 and sys.version_info.micro > 8:
  409. inspectbroken = False
  410. if sys.version_info.minor == 12 and sys.version_info.micro > 2:
  411. inspectbroken = False
  412. if sys.version_info.minor > 12:
  413. inspectbroken = False
  414. if inspectbroken:
  415. assert num_required_args(Wrapped) is None
  416. _sigs.signatures[Wrapped] = (_sigs.expand_sig((0, lambda func: None)),)
  417. assert num_required_args(Wrapped) == 1
  418. else:
  419. assert num_required_args(Wrapped) is 1