psycodict.utils

Helpers shared across psycodict: DelayCommit for batching many writes into one transaction, the whitelist parser that admits limited raw arithmetic into queries (filter_sql_injection()), identifier wrapping with array slicers (IdentifierWrapper()), the exceptions raised during query parsing and locking, and small formatting utilities.

exception psycodict.utils.SearchParsingError(*args, **kwargs)[source]

Bases: ValueError

Used for errors raised when parsing search boxes

psycodict.utils.filter_sql_injection(clause, col, col_type, op, table, join_context=None)[source]

Build a comparison clause from an untrusted arithmetic expression, admitting only whitelisted column names, numbers and the arithmetic operators +-*/^(). Returns a pair of an SQL composable and the list of values to substitute into it.

INPUT:

  • clause – a plain string, obtained from the website UI so NOT SAFE

  • col – an SQL Identifier for a column in a table

  • col_type – a string giving the type of the column

  • op – a string giving the operator to use (= or one of the values in the postgres_infix_ops dictionary above)

  • table – a PostgresTable object for determining which columns are valid

  • join_context – when filtering an expression inside a joined query, a dictionary mapping joined table names to their table objects. Names then resolve like query keys – bare names are columns of table, while “joinedtable.column” names a joined table’s column – and every identifier is emitted table-qualified.

psycodict.utils.IdentifierWrapper(name, convert=True)[source]

Returns a composable representing an SQL identifier.

This is wrapper for psycopg.sql.Identifier that supports ARRAY slicers and converts them (if desired) from the Python format to SQL, as SQL starts at 1, and it is inclusive at the end

EXAMPLES:

sage: IdentifierWrapper('name')
Identifier('name')
sage: print(IdentifierWrapper('name[:10]').as_string(db.conn))
"name"[:10]
sage: print(IdentifierWrapper('name[1:10]').as_string(db.conn))
"name"[2:10]
sage: print(IdentifierWrapper('name[1:10]', convert = False).as_string(db.conn))
"name"[1:10]
sage: print(IdentifierWrapper('name[1:10:3]').as_string(db.conn))
"name"[2:10:3]
sage: print(IdentifierWrapper('name[1:10:3][0:2]').as_string(db.conn))
"name"[2:10:3][1:2]
sage: print(IdentifierWrapper('name[1:10:3][0::1]').as_string(db.conn))
"name"[2:10:3][1::1]
sage: print(IdentifierWrapper('name[1:10:3][0]').as_string(db.conn))
"name"[2:10:3][1]
exception psycodict.utils.LockError[source]

Bases: RuntimeError

Raised when a data-changing operation cannot proceed because another operation holds a conflicting lock on the table (for example, a reload in progress, or a staged _tmp copy awaiting its swap).

class psycodict.utils.QueryLogFilter[source]

Bases: object

A filter used when logging slow queries.

filter(record)[source]

Keep only records emitted from base.py (the query-execution path).

class psycodict.utils.DelayCommit(obj, final_commit=True, silence=None, active=True)[source]

Bases: object

Used to set default behavior for whether to commit changes to the database connection.

Entering this context in a with statement will cause _execute calls to not commit by default. When the final DelayCommit is exited, the connection will commit.

Setting active=False disables the DelayCommit completely, which can be helpful since it’s often used in a with context and conditionally entering that context is annoying to write with if statements.

psycodict.utils.reraise(exc_type, exc_value, exc_traceback=None)[source]

Reraise an exception, possibly with a different message, type, or traceback.

psycodict.utils.range_formatter(x)[source]

Format a query-language range constraint for human display: {"$gte": 2, "$lte": 7} becomes "2-7", one-sided constraints become "2-" or "..7", and None becomes "Unknown".

class psycodict.utils.KeyedDefaultDict[source]

Bases: defaultdict

A defaultdict where the default value takes the key as input.

psycodict.utils.make_tuple(val)[source]

Converts lists and dictionaries into tuples, recursively. The main application is so that the result can be used as a dictionary key.