Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

139 lignes
4.3 KiB

  1. try:
  2. import unittest2 as unittest
  3. except ImportError:
  4. import unittest
  5. import os
  6. import re
  7. import sys
  8. import types
  9. import functools
  10. def ensure_in_path(path):
  11. """
  12. Ensure that a given path is in the sys.path array
  13. """
  14. if not os.path.isdir(path):
  15. raise RuntimeError('Tried to add nonexisting path')
  16. def _samefile(x, y):
  17. try:
  18. return os.path.samefile(x, y)
  19. except (IOError, OSError):
  20. return False
  21. except AttributeError:
  22. # Probably on Windows.
  23. path1 = os.path.abspath(x).lower()
  24. path2 = os.path.abspath(y).lower()
  25. return path1 == path2
  26. # Remove existing copies of it.
  27. for pth in sys.path:
  28. if _samefile(pth, path):
  29. sys.path.remove(pth)
  30. # Add it at the beginning.
  31. sys.path.insert(0, path)
  32. # Check if pytest is imported. If so, we use it to create marking decorators.
  33. # If not, we just create a function that does nothing.
  34. try:
  35. import pytest
  36. except ImportError:
  37. pytest = None
  38. if pytest is not None:
  39. slow_test = pytest.mark.slow_test
  40. xfail = pytest.mark.xfail
  41. else:
  42. slow_test = lambda x: x
  43. def xfail(*args, **kwargs):
  44. if len(args) > 0 and isinstance(args[0], types.FunctionType):
  45. return args[0]
  46. return lambda x: x
  47. # We don't use the py.test parametrizing function, since it seems to break
  48. # with unittest.TestCase subclasses.
  49. def parametrize(field_names, field_values):
  50. # If we're not given a list of field names, we make it.
  51. if not isinstance(field_names, (tuple, list)):
  52. field_names = (field_names,)
  53. field_values = [(val,) for val in field_values]
  54. # Create a decorator that saves this list of field names and values on the
  55. # function for later parametrizing.
  56. def decorator(func):
  57. func.__dict__['param_names'] = field_names
  58. func.__dict__['param_values'] = field_values
  59. return func
  60. return decorator
  61. # This is a metaclass that actually performs the parametrization.
  62. class ParametrizingMetaclass(type):
  63. IDENTIFIER_RE = re.compile('[^A-Za-z0-9]')
  64. def __new__(klass, name, bases, attrs):
  65. new_attrs = attrs.copy()
  66. for attr_name, attr in attrs.items():
  67. # We only care about functions
  68. if not isinstance(attr, types.FunctionType):
  69. continue
  70. param_names = attr.__dict__.pop('param_names', None)
  71. param_values = attr.__dict__.pop('param_values', None)
  72. if param_names is None or param_values is None:
  73. continue
  74. # Create multiple copies of the function.
  75. for i, values in enumerate(param_values):
  76. assert len(param_names) == len(values)
  77. # Get a repr of the values, and fix it to be a valid identifier
  78. human = '_'.join(
  79. [klass.IDENTIFIER_RE.sub('', repr(x)) for x in values]
  80. )
  81. # Create a new name.
  82. # new_name = attr.__name__ + "_%d" % i
  83. new_name = attr.__name__ + "__" + human
  84. # Create a replacement function.
  85. def create_new_func(func, names, values):
  86. # Create a kwargs dictionary.
  87. kwargs = dict(zip(names, values))
  88. @functools.wraps(func)
  89. def new_func(self):
  90. return func(self, **kwargs)
  91. # Manually set the name and return the new function.
  92. new_func.__name__ = new_name
  93. return new_func
  94. # Actually create the new function.
  95. new_func = create_new_func(attr, param_names, values)
  96. # Save this new function in our attrs dict.
  97. new_attrs[new_name] = new_func
  98. # Remove the old attribute from our new dictionary.
  99. del new_attrs[attr_name]
  100. # We create the class as normal, except we use our new attributes.
  101. return type.__new__(klass, name, bases, new_attrs)
  102. # This is a class decorator that actually applies the above metaclass.
  103. def parametrize_class(klass):
  104. return ParametrizingMetaclass(klass.__name__,
  105. klass.__bases__,
  106. klass.__dict__)