Source code for pgmock.exceptions

"""
These are all of the custom exceptions thrown by ``pgmock``. The descriptions of most of the
exceptions talk about common scenarios that can cause the exceptions to be raised.
"""

DOCS_URL = (
    'https://pgmock.readthedocs.io/en/latest/exceptions.html'
)


[docs]class Error(Exception): """The base exception for ``pgmock``"""
[docs]class SQLParseError(Error): """Top-level error thrown when parsing SQL expressions"""
[docs]class InvalidSQLError(SQLParseError): """Thrown when invalid SQL is loaded with pgmock. This error is thrown when no matches are found for enclosing parentheses. For example, matching a CTE with the following SQL:: WITH cte_name AS (SELECT * from table would produce this error since there is no matching right paren for the initial left paren. """
[docs]class StatementParseError(SQLParseError): """Thrown when statements cannot be parsed. This happens when trying to obtain a statement in a SQL query and the index is out of the bounds of the number of statements. Keep in mind that when using `pgmock.statement` that the first argument is the index of the first statement starting at 0. """
[docs]class NoMatchError(SQLParseError): """Thrown when parsing an expression and not finding a match. This error commonly happens when using a selector that takes a name (such as ``pgmock.subquery('subquery_name')``) and not being able to find a match for the given name. Please read the docs for the selector that you are using and check that you have provided all of the proper arguments first. For example, selecting a table that has an alias requires also passing its alias or this error will be raised. If you are certain that your selector is referencing a valid name in your query, contact the authors or open up an issue at https://github.com/CloverHealth/pgmock with the entire exception message provided and code. """
[docs]class MultipleMatchError(SQLParseError): """Thrown when multiple matches are rendered. This error happens when a selector finds multiple occurrences of a SQL expression and it is either rendered or has other selectors chained to it. For example, say your SQL is:: CREATE TABLE a AS ( SELECT * FROM t1 ) ; CREATE TABLE a AS ( SELECT * FROM t2 ) ; Doing:: pgmock.sql(sql, pgmock.create_table_as('a')) Will result in a `MultipleMatchError` since multiple ``CREATE TABLE AS`` expressions were found and ``pgmock`` tried to obtain the SQL for it. One must refine the selection with list syntax to choose which one is rendered like so:: pgmock.sql(sql, pgmock.create_table_as('a')[0]) The above will return the SQL for the first ``CREATE TABLE AS`` expression. The same situation holds true when chaining selectors. A selector cannot be chained to one that results in multiple matches. For example, the following selector is invalid for use in any pgmock function (including `pgmock.patch`):: pgmock.create_table_as('a').body() .. note:: It is possible to select multiple occurences of some expressions when patching them (such as tables). This only holds true for selectors given to `pgmock.patch` like so:: pgmock.sql(sql, pgmock.patch(pgmock.create_table_as('a'), values)) """
[docs]class NestedMatchError(MultipleMatchError): """Thrown when a selector selects a nested pattern For example, imagine we have the following SQL:: SELECT * FROM ( SELECT * FROM ( SELECT * FROM test_table ) bb ) bb The following code will produce this error:: pgmock.sql(sql, pgmock.subquery('bb')) This is because subqueries (along with CTEs) can have nested patterns, and it is ambiguous how pgmock should patch or select them """
[docs]class SelectorChainingError(Error): """Thrown when trying to chain together selectors that can't be chained. ``pgmock`` allows selectors to be chained into one single selector like so:: selector = pgmock.patch(...).patch(...) sql = pgmock.sql(my_sql_string, selector) Sometimes it isn't always feasible to chain together multiple selectors, so pgmock allows multiple selectors to be passed to ``pgmock.sql`` like so:: sql = pgmock.sql(my_sql_string, pgmock.patch(...), pgmock.patch(...)) The syntax from the latter is equivalent to the syntax from the former. This exception is raised when using the syntax from the latter example and using selectors that are impossible to be chained together. For example, pgmock doesn't allow this selector to be constructed:: pgmock.patch(...).subquery(...) The above isn't allowed because patches effectively stop any other selectors from further refining the view of SQL being rendered. This error is raised when an invalid chain such as the one from above is passed to ``pgmock.sql`` or ``pgmock.sql_file`` using multiple selectors. """
[docs]class PatchError(Error): """Top-level patching error"""
[docs]class UnpatchableError(PatchError): """Thrown when an expression cannot be patched. This error is thrown when trying to patch SQL that is not patchable or currently not supported by ``pgmock``. Since patching is only applicable to expressions that can be translated into Postgres ``VALUES`` statements, sometimes it is not possible to patch an expression. For example, trying to patch the first two statements of a query with ``pgmock.patch(pgmock.statement(0, 2), ...)`` would throw this error since it is not possible to patch two entire statements. """
[docs]class ColumnsNeededForPatchError(PatchError): """Thrown when columns are required for patching values. It's possible to use ``pgmock.patch`` without providing columns. This is standard for patching anything that takes a ``VALUES`` list without associated column names (e.g insert into ``VALUES (...)``). However, it's illegal to not provide column names for patching expressions that require them. This error is thrown when trying to patch an expression that has a name or an alias associated with it and not providing the column names. For example, ``pgmock.patch(pgmock.table('table'), [rows])`` or patching a subquery without providing columns would throw this. This error is also thrown when trying to use lists of dictionaries as rows to ``pgmock.patch`` without providing column names """
[docs]class ColumnMismatchInPatchError(PatchError): """ Thrown when creating a patch with a list of dictionaries where the dictionary keys don't match with the column names provided For example, this code will throw this error because the "col1" column is being specified in the row data of a patch but not the columns: .. code-block:: python pgmock.patch(pgmock.table('table'), rows=[{'col1': 'value'}], cols=['col2']) """
[docs]class NoConnectableError(Error): """Thrown when using a mock as a context manager with no connectable."""
[docs]class SideEffectExhaustedError(Error): """Thrown when using a side effect on a patch and the iterable has been exhausted. This is thrown when a ``side_effect`` has been provided for a patch, but the number of queries executed has surpassed the number of results in the side effect. For example, this code would cause this exception:: with pgmock.mock(connectable): # Provide exactly one side effect result pgmock.patch(pgmock.table('table'), side_effect=[pgmock.data(rows, cols)]) # The first query will run fine since the side effect has one value run_code() # The second query will fail because it will try to use the second # value in the side effect run_code() """
[docs]class ValueSerializationError(Error): """Thrown when a Python value cannot be serialized to a postgres ``VALUES`` value. pgmock supports serializing the following Python types: bool, float, int, str, dict (json), UUID, datetime, date, time (all time types support timezones). If the python type being serialized doesn't match, one must supply a column type hint when patching values. Open an issue on pgmock or contact the authors in order to support serializing other types! """
[docs]def throw(exception, msg, sql=None): """Throws an exception with an error message that points to exception docs""" err_msg = msg if sql: if isinstance(sql, str): err_msg += ' The following SQL was used: \n\n{}\n\n'.format(sql.strip()) elif isinstance(sql, (list, tuple)): err_msg += ( ' The following multiple matches of SQL were used: \n\n{}\n\n' ).format('\n---\n'.join(s.strip() for s in sql)) else: raise AssertionError else: err_msg += '' err_msg += 'View the docs for this exception at {} for more information.'.format(DOCS_URL) raise exception(err_msg)