mock.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. # engine/mock.py
  2. # Copyright (C) 2005-2025 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: https://www.opensource.org/licenses/mit-license.php
  7. from __future__ import annotations
  8. from operator import attrgetter
  9. import typing
  10. from typing import Any
  11. from typing import Callable
  12. from typing import cast
  13. from typing import Optional
  14. from typing import Type
  15. from typing import Union
  16. from . import url as _url
  17. from .. import util
  18. if typing.TYPE_CHECKING:
  19. from .base import Engine
  20. from .interfaces import _CoreAnyExecuteParams
  21. from .interfaces import CoreExecuteOptionsParameter
  22. from .interfaces import Dialect
  23. from .url import URL
  24. from ..sql.base import Executable
  25. from ..sql.ddl import InvokeDDLBase
  26. from ..sql.schema import HasSchemaAttr
  27. from ..sql.visitors import Visitable
  28. class MockConnection:
  29. def __init__(self, dialect: Dialect, execute: Callable[..., Any]):
  30. self._dialect = dialect
  31. self._execute_impl = execute
  32. engine: Engine = cast(Any, property(lambda s: s))
  33. dialect: Dialect = cast(Any, property(attrgetter("_dialect")))
  34. name: str = cast(Any, property(lambda s: s._dialect.name))
  35. def connect(self, **kwargs: Any) -> MockConnection:
  36. return self
  37. def schema_for_object(self, obj: HasSchemaAttr) -> Optional[str]:
  38. return obj.schema
  39. def execution_options(self, **kw: Any) -> MockConnection:
  40. return self
  41. def _run_ddl_visitor(
  42. self,
  43. visitorcallable: Type[InvokeDDLBase],
  44. element: Visitable,
  45. **kwargs: Any,
  46. ) -> None:
  47. kwargs["checkfirst"] = False
  48. visitorcallable(
  49. dialect=self.dialect, connection=self, **kwargs
  50. ).traverse_single(element)
  51. def execute(
  52. self,
  53. obj: Executable,
  54. parameters: Optional[_CoreAnyExecuteParams] = None,
  55. execution_options: Optional[CoreExecuteOptionsParameter] = None,
  56. ) -> Any:
  57. return self._execute_impl(obj, parameters)
  58. def create_mock_engine(
  59. url: Union[str, URL], executor: Any, **kw: Any
  60. ) -> MockConnection:
  61. """Create a "mock" engine used for echoing DDL.
  62. This is a utility function used for debugging or storing the output of DDL
  63. sequences as generated by :meth:`_schema.MetaData.create_all`
  64. and related methods.
  65. The function accepts a URL which is used only to determine the kind of
  66. dialect to be used, as well as an "executor" callable function which
  67. will receive a SQL expression object and parameters, which can then be
  68. echoed or otherwise printed. The executor's return value is not handled,
  69. nor does the engine allow regular string statements to be invoked, and
  70. is therefore only useful for DDL that is sent to the database without
  71. receiving any results.
  72. E.g.::
  73. from sqlalchemy import create_mock_engine
  74. def dump(sql, *multiparams, **params):
  75. print(sql.compile(dialect=engine.dialect))
  76. engine = create_mock_engine("postgresql+psycopg2://", dump)
  77. metadata.create_all(engine, checkfirst=False)
  78. :param url: A string URL which typically needs to contain only the
  79. database backend name.
  80. :param executor: a callable which receives the arguments ``sql``,
  81. ``*multiparams`` and ``**params``. The ``sql`` parameter is typically
  82. an instance of :class:`.ExecutableDDLElement`, which can then be compiled
  83. into a string using :meth:`.ExecutableDDLElement.compile`.
  84. .. versionadded:: 1.4 - the :func:`.create_mock_engine` function replaces
  85. the previous "mock" engine strategy used with
  86. :func:`_sa.create_engine`.
  87. .. seealso::
  88. :ref:`faq_ddl_as_string`
  89. """
  90. # create url.URL object
  91. u = _url.make_url(url)
  92. dialect_cls = u.get_dialect()
  93. dialect_args = {}
  94. # consume dialect arguments from kwargs
  95. for k in util.get_cls_kwargs(dialect_cls):
  96. if k in kw:
  97. dialect_args[k] = kw.pop(k)
  98. # create dialect
  99. dialect = dialect_cls(**dialect_args)
  100. return MockConnection(dialect, executor)