test_embedded_sigs.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import inspect
  2. import cytoolz
  3. from types import BuiltinFunctionType
  4. from cytoolz import curry, identity, keyfilter, valfilter, merge_with
  5. from dev_skip_test import dev_skip_test
  6. @curry
  7. def isfrommod(modname, func):
  8. mod = getattr(func, '__module__', '') or ''
  9. return mod.startswith(modname) or 'toolz.functoolz.curry' in str(type(func))
  10. @dev_skip_test
  11. def test_class_sigs():
  12. """ Test that all ``cdef class`` extension types in ``cytoolz`` have
  13. correctly embedded the function signature as done in ``toolz``.
  14. """
  15. import toolz
  16. # only consider items created in both `toolz` and `cytoolz`
  17. toolz_dict = valfilter(isfrommod('toolz'), toolz.__dict__)
  18. cytoolz_dict = valfilter(isfrommod('cytoolz'), cytoolz.__dict__)
  19. # only test `cdef class` extensions from `cytoolz`
  20. cytoolz_dict = valfilter(lambda x: not isinstance(x, BuiltinFunctionType),
  21. cytoolz_dict)
  22. # full API coverage should be tested elsewhere
  23. toolz_dict = keyfilter(lambda x: x in cytoolz_dict, toolz_dict)
  24. cytoolz_dict = keyfilter(lambda x: x in toolz_dict, cytoolz_dict)
  25. d = merge_with(identity, toolz_dict, cytoolz_dict)
  26. for key, (toolz_func, cytoolz_func) in d.items():
  27. if key in ['excepts', 'juxt', 'memoize', 'flip']:
  28. continue
  29. try:
  30. # function
  31. toolz_spec = inspect.getargspec(toolz_func)
  32. except TypeError:
  33. try:
  34. # curried or partial object
  35. toolz_spec = inspect.getargspec(toolz_func.func)
  36. except (TypeError, AttributeError):
  37. # class
  38. toolz_spec = inspect.getargspec(toolz_func.__init__)
  39. # For Cython < 0.25
  40. toolz_sig = toolz_func.__name__ + inspect.formatargspec(*toolz_spec)
  41. doc = cytoolz_func.__doc__
  42. # For Cython >= 0.25
  43. toolz_sig_alt = toolz_func.__name__ + inspect.formatargspec(
  44. *toolz_spec,
  45. **{'formatvalue': lambda x: '=' + getattr(x, '__name__', repr(x))}
  46. )
  47. doc_alt = doc.replace('Py_ssize_t ', '')
  48. if not (toolz_sig in doc or toolz_sig_alt in doc_alt):
  49. message = ('cytoolz.%s does not have correct function signature.'
  50. '\n\nExpected: %s'
  51. '\n\nDocstring in cytoolz is:\n%s'
  52. % (key, toolz_sig, cytoolz_func.__doc__))
  53. assert False, message
  54. skip_sigs = ['identity']
  55. aliases = {'comp': 'compose'}
  56. @dev_skip_test
  57. def test_sig_at_beginning():
  58. """ Test that the function signature is at the beginning of the docstring
  59. and is followed by exactly one blank line.
  60. """
  61. cytoolz_dict = valfilter(isfrommod('cytoolz'), cytoolz.__dict__)
  62. cytoolz_dict = keyfilter(lambda x: x not in skip_sigs, cytoolz_dict)
  63. for key, val in cytoolz_dict.items():
  64. doclines = val.__doc__.splitlines()
  65. assert len(doclines) > 2, (
  66. 'cytoolz.%s docstring too short:\n\n%s' % (key, val.__doc__))
  67. sig = '%s(' % aliases.get(key, key)
  68. assert sig in doclines[0], (
  69. 'cytoolz.%s docstring missing signature at beginning:\n\n%s'
  70. % (key, val.__doc__))
  71. assert not doclines[1], (
  72. 'cytoolz.%s docstring missing blank line after signature:\n\n%s'
  73. % (key, val.__doc__))
  74. assert doclines[2], (
  75. 'cytoolz.%s docstring too many blank lines after signature:\n\n%s'
  76. % (key, val.__doc__))