util.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. # testing/util.py
  2. # Copyright (C) 2005-2019 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  7. from __future__ import annotations
  8. import types
  9. from typing import Union
  10. from sqlalchemy.util import inspect_getfullargspec
  11. from ..util import sqla_2
  12. def flag_combinations(*combinations):
  13. """A facade around @testing.combinations() oriented towards boolean
  14. keyword-based arguments.
  15. Basically generates a nice looking identifier based on the keywords
  16. and also sets up the argument names.
  17. E.g.::
  18. @testing.flag_combinations(
  19. dict(lazy=False, passive=False),
  20. dict(lazy=True, passive=False),
  21. dict(lazy=False, passive=True),
  22. dict(lazy=False, passive=True, raiseload=True),
  23. )
  24. would result in::
  25. @testing.combinations(
  26. ('', False, False, False),
  27. ('lazy', True, False, False),
  28. ('lazy_passive', True, True, False),
  29. ('lazy_passive', True, True, True),
  30. id_='iaaa',
  31. argnames='lazy,passive,raiseload'
  32. )
  33. """
  34. from sqlalchemy.testing import config
  35. keys = set()
  36. for d in combinations:
  37. keys.update(d)
  38. keys = sorted(keys)
  39. return config.combinations(
  40. *[
  41. ("_".join(k for k in keys if d.get(k, False)),)
  42. + tuple(d.get(k, False) for k in keys)
  43. for d in combinations
  44. ],
  45. id_="i" + ("a" * len(keys)),
  46. argnames=",".join(keys),
  47. )
  48. def resolve_lambda(__fn, **kw):
  49. """Given a no-arg lambda and a namespace, return a new lambda that
  50. has all the values filled in.
  51. This is used so that we can have module-level fixtures that
  52. refer to instance-level variables using lambdas.
  53. """
  54. pos_args = inspect_getfullargspec(__fn)[0]
  55. pass_pos_args = {arg: kw.pop(arg) for arg in pos_args}
  56. glb = dict(__fn.__globals__)
  57. glb.update(kw)
  58. new_fn = types.FunctionType(__fn.__code__, glb)
  59. return new_fn(**pass_pos_args)
  60. def metadata_fixture(ddl="function"):
  61. """Provide MetaData for a pytest fixture."""
  62. from sqlalchemy.testing import config
  63. from . import fixture_functions
  64. def decorate(fn):
  65. def run_ddl(self):
  66. from sqlalchemy import schema
  67. metadata = self.metadata = schema.MetaData()
  68. try:
  69. result = fn(self, metadata)
  70. metadata.create_all(config.db)
  71. # TODO:
  72. # somehow get a per-function dml erase fixture here
  73. yield result
  74. finally:
  75. metadata.drop_all(config.db)
  76. return fixture_functions.fixture(scope=ddl)(run_ddl)
  77. return decorate
  78. def _safe_int(value: str) -> Union[int, str]:
  79. try:
  80. return int(value)
  81. except:
  82. return value
  83. def testing_engine(url=None, options=None, future=False):
  84. from sqlalchemy.testing import config
  85. from sqlalchemy.testing.engines import testing_engine
  86. if not future:
  87. future = getattr(config._current.options, "future_engine", False)
  88. if not sqla_2:
  89. kw = {"future": future} if future else {}
  90. else:
  91. kw = {}
  92. return testing_engine(url, options, **kw)