diff --git a/CHANGELOG.md b/CHANGELOG.md index 3596425..d3e79ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### 0.17.0 + +- feat: Add support for Postgres sqlbody functions. + ## 0.16 ### 0.16.4 diff --git a/pyproject.toml b/pyproject.toml index ca96adb..7059c27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sqlalchemy-declarative-extensions" -version = "0.16.4" +version = "0.16.5" authors = [ {name = "Dan Cardin", email = "ddcardin@gmail.com"}, ] @@ -45,9 +45,8 @@ changelog = "https://github.com/DanCardin/sqlalchemy-declarative-extensions/blob alembic = ["alembic >= 1.0"] parse = ["sqlglot"] -[tool.uv] -environments = ["python_version < '3.9'", "python_version >= '3.9' and python_version < '4'"] -dev-dependencies = [ +[dependency-groups] +dev = [ "alembic-utils >= 0.8.1", "coverage >= 5", "mypy == 1.8.0", @@ -58,7 +57,7 @@ dev-dependencies = [ "pytest-xdist", "ruff >= 0.5.0", "sqlalchemy[mypy] >= 1.4", - "psycopg", + "psycopg[binary]", "psycopg2-binary", # snowflake @@ -67,6 +66,9 @@ dev-dependencies = [ "snowflake-sqlalchemy >= 1.6.0; python_version >= '3.9'", ] +[tool.uv] +environments = ["python_version < '3.9'", "python_version >= '3.9' and python_version < '4'"] + [tool.mypy] strict_optional = true ignore_missing_imports = true diff --git a/src/sqlalchemy_declarative_extensions/dialects/postgresql/function.py b/src/sqlalchemy_declarative_extensions/dialects/postgresql/function.py index b72d21c..fe95d26 100644 --- a/src/sqlalchemy_declarative_extensions/dialects/postgresql/function.py +++ b/src/sqlalchemy_declarative_extensions/dialects/postgresql/function.py @@ -1,6 +1,7 @@ from __future__ import annotations import enum +import re import textwrap from dataclasses import dataclass, replace from typing import Any, List, Literal, Sequence, Tuple, cast @@ -10,6 +11,22 @@ from sqlalchemy_declarative_extensions.function import base from sqlalchemy_declarative_extensions.sql import quote_name +_sqlbody_regex = re.compile(r"\W*(BEGIN ATOMIC|RETURN)\W", re.IGNORECASE | re.MULTILINE) +"""sql_body +The body of a LANGUAGE SQL function. This can either be a single statement + +RETURN expression + +or a block + +BEGIN ATOMIC + statement; + statement; + ... + statement; +END +""" + @enum.unique class FunctionSecurity(enum.Enum): @@ -47,6 +64,12 @@ class Function(base.Function): parameters: Sequence[FunctionParam | str] | None = None # type: ignore volatility: FunctionVolatility = FunctionVolatility.VOLATILE + @property + def _has_sqlbody(self) -> bool: + return self.language.lower() == "sql" and bool( + _sqlbody_regex.match(self.definition) + ) + def to_sql_create(self, replace=False) -> list[str]: components = ["CREATE"] @@ -72,7 +95,10 @@ def to_sql_create(self, replace=False) -> list[str]: components.append(self.volatility.value) components.append(f"LANGUAGE {self.language}") - components.append(f"AS $${self.definition}$$") + if self._has_sqlbody: + components.append(self.definition) + else: + components.append(f"AS $${self.definition}$$") return [" ".join(components) + ";"] @@ -96,6 +122,8 @@ def with_security_definer(self): def normalize(self) -> Function: definition = textwrap.dedent(self.definition) + if self._has_sqlbody: + definition = definition.strip() # Normalize parameter types parameters = [] diff --git a/src/sqlalchemy_declarative_extensions/dialects/postgresql/procedure.py b/src/sqlalchemy_declarative_extensions/dialects/postgresql/procedure.py index 707bd44..a7c2bb5 100644 --- a/src/sqlalchemy_declarative_extensions/dialects/postgresql/procedure.py +++ b/src/sqlalchemy_declarative_extensions/dialects/postgresql/procedure.py @@ -1,6 +1,7 @@ from __future__ import annotations import enum +import re import textwrap from dataclasses import dataclass, replace @@ -9,6 +10,18 @@ from sqlalchemy_declarative_extensions.procedure import base from sqlalchemy_declarative_extensions.sql import quote_name +_sqlbody_regex = re.compile(r"\W?(BEGIN ATOMIC)\W", re.IGNORECASE | re.MULTILINE) +"""sql_body +The body of a LANGUAGE SQL procedure. This should be a block + +BEGIN ATOMIC + statement; + statement; + ... + statement; +END +""" + @enum.unique class ProcedureSecurity(enum.Enum): @@ -27,6 +40,12 @@ class Procedure(base.Procedure): security: ProcedureSecurity = ProcedureSecurity.invoker + @property + def _has_sqlbody(self) -> bool: + return self.language.lower() == "sql" and bool( + _sqlbody_regex.match(self.definition) + ) + def to_sql_create(self, replace=False) -> list[str]: components = ["CREATE"] @@ -40,7 +59,10 @@ def to_sql_create(self, replace=False) -> list[str]: components.append("SECURITY DEFINER") components.append(f"LANGUAGE {self.language}") - components.append(f"AS $${self.definition}$$") + if self._has_sqlbody: + components.append(self.definition) + else: + components.append(f"AS $${self.definition}$$") return [" ".join(components) + ";"] @@ -49,6 +71,8 @@ def to_sql_update(self) -> list[str]: def normalize(self) -> Self: definition = textwrap.dedent(self.definition) + if self._has_sqlbody: + definition = definition.strip() return replace(self, definition=definition) def with_security(self, security: ProcedureSecurity): diff --git a/src/sqlalchemy_declarative_extensions/dialects/postgresql/query.py b/src/sqlalchemy_declarative_extensions/dialects/postgresql/query.py index e3916f4..a6938e5 100644 --- a/src/sqlalchemy_declarative_extensions/dialects/postgresql/query.py +++ b/src/sqlalchemy_declarative_extensions/dialects/postgresql/query.py @@ -27,10 +27,10 @@ databases_query, default_acl_query, extensions_query, - functions_query, + get_functions_query, + get_procedures_query, object_acl_query, objects_query, - procedures_query, roles_query, schema_exists_query, schemas_query, @@ -195,6 +195,7 @@ def get_view_postgresql(connection: Connection, name: str, schema: str = "public def get_procedures_postgresql(connection: Connection) -> Sequence[BaseProcedure]: procedures = [] + procedures_query = get_procedures_query(connection.dialect.server_version_info) for f in connection.execute(procedures_query).fetchall(): name = f.name definition = f.source @@ -225,6 +226,7 @@ def get_functions_postgresql(connection: Connection) -> Sequence[BaseFunction]: ) functions = [] + functions_query = get_functions_query(connection.dialect.server_version_info) for f in connection.execute(functions_query).fetchall(): name = f.name diff --git a/src/sqlalchemy_declarative_extensions/dialects/postgresql/schema.py b/src/sqlalchemy_declarative_extensions/dialects/postgresql/schema.py index ba3442b..71f9fdb 100644 --- a/src/sqlalchemy_declarative_extensions/dialects/postgresql/schema.py +++ b/src/sqlalchemy_declarative_extensions/dialects/postgresql/schema.py @@ -17,6 +17,7 @@ union, ) from sqlalchemy.dialects.postgresql import ARRAY, CHAR, REGCLASS, aggregate_order_by +from sqlalchemy.sql.functions import coalesce from sqlalchemy_declarative_extensions.sqlalchemy import select @@ -317,65 +318,90 @@ def get_types(arg_type_oids): ) -procedures_query = ( - select( - pg_proc.c.proname.label("name"), - pg_namespace.c.nspname.label("schema"), - pg_language.c.lanname.label("language"), - pg_type.c.typname.label("return_type"), - pg_proc.c.prosrc.label("source"), - pg_proc.c.prosecdef.label("security_definer"), - pg_proc.c.prokind.label("kind"), - pg_proc.c.proargnames.label("arg_names"), - pg_proc.c.proargmodes.label("arg_modes"), - func.coalesce( - get_types(pg_proc.c.proallargtypes), get_types(pg_proc.c.proargtypes) - ).label("arg_types"), - func.pg_get_expr( - pg_proc.c.proargdefaults, func.cast(literal("pg_proc"), REGCLASS) - ).label("arg_defaults"), - ) - .select_from( - pg_proc.join(pg_namespace, pg_proc.c.pronamespace == pg_namespace.c.oid) - .join(pg_language, pg_proc.c.prolang == pg_language.c.oid) - .join(pg_type, pg_proc.c.prorettype == pg_type.c.oid) +def get_procedures_query(version_info): + source = get_source_column(version_info) + return ( + select( + pg_proc.c.proname.label("name"), + pg_namespace.c.nspname.label("schema"), + pg_language.c.lanname.label("language"), + pg_type.c.typname.label("return_type"), + source.label("source"), + pg_proc.c.prosecdef.label("security_definer"), + pg_proc.c.prokind.label("kind"), + pg_proc.c.proargnames.label("arg_names"), + pg_proc.c.proargmodes.label("arg_modes"), + func.coalesce( + get_types(pg_proc.c.proallargtypes), get_types(pg_proc.c.proargtypes) + ).label("arg_types"), + func.pg_get_expr( + pg_proc.c.proargdefaults, func.cast(literal("pg_proc"), REGCLASS) + ).label("arg_defaults"), + ) + .select_from( + pg_proc.join(pg_namespace, pg_proc.c.pronamespace == pg_namespace.c.oid) + .join(pg_language, pg_proc.c.prolang == pg_language.c.oid) + .join(pg_type, pg_proc.c.prorettype == pg_type.c.oid) + ) + .where(pg_namespace.c.nspname.notin_(["pg_catalog", "information_schema"])) + .where(pg_proc.c.prokind == "p") + .where(_schema_not_from_extension()) + .where(_not_from_extension(pg_proc.c.oid, "pg_proc")) ) - .where(pg_namespace.c.nspname.notin_(["pg_catalog", "information_schema"])) - .where(pg_proc.c.prokind == "p") - .where(_schema_not_from_extension()) - .where(_not_from_extension(pg_proc.c.oid, "pg_proc")) -) -functions_query = ( - select( - pg_proc.c.proname.label("name"), - pg_namespace.c.nspname.label("schema"), - pg_language.c.lanname.label("language"), - pg_type.c.typname.label("base_return_type"), - pg_proc.c.prosrc.label("source"), - pg_proc.c.prosecdef.label("security_definer"), - cast(pg_proc.c.prokind, Text).label("kind"), - func.pg_get_function_arguments(pg_proc.c.oid).label("parameters"), - cast(pg_proc.c.provolatile, Text).label("volatility"), - func.pg_get_function_result(pg_proc.c.oid).label("return_type_string"), - pg_proc.c.proargnames.label("arg_names"), - pg_proc.c.proargmodes.label("arg_modes"), - func.coalesce( - get_types(pg_proc.c.proallargtypes), get_types(pg_proc.c.proargtypes) - ).label("arg_types"), - func.pg_get_expr( - pg_proc.c.proargdefaults, func.cast(literal("pg_proc"), REGCLASS) - ).label("arg_defaults"), - ) - .select_from( - pg_proc.join(pg_namespace, pg_proc.c.pronamespace == pg_namespace.c.oid) - .join(pg_language, pg_proc.c.prolang == pg_language.c.oid) - .join(pg_type, pg_proc.c.prorettype == pg_type.c.oid) + +def get_functions_query(version_info): + source = get_source_column(version_info) + return ( + select( + pg_proc.c.proname.label("name"), + pg_namespace.c.nspname.label("schema"), + pg_language.c.lanname.label("language"), + pg_type.c.typname.label("base_return_type"), + source.label("source"), + pg_proc.c.prosecdef.label("security_definer"), + cast(pg_proc.c.prokind, Text).label("kind"), + func.pg_get_function_arguments(pg_proc.c.oid).label("parameters"), + cast(pg_proc.c.provolatile, Text).label("volatility"), + func.pg_get_function_result(pg_proc.c.oid).label("return_type_string"), + pg_proc.c.proargnames.label("arg_names"), + pg_proc.c.proargmodes.label("arg_modes"), + func.coalesce( + get_types(pg_proc.c.proallargtypes), get_types(pg_proc.c.proargtypes) + ).label("arg_types"), + func.pg_get_expr( + pg_proc.c.proargdefaults, func.cast(literal("pg_proc"), REGCLASS) + ).label("arg_defaults"), + ) + .select_from( + pg_proc.join(pg_namespace, pg_proc.c.pronamespace == pg_namespace.c.oid) + .join(pg_language, pg_proc.c.prolang == pg_language.c.oid) + .join(pg_type, pg_proc.c.prorettype == pg_type.c.oid) + ) + .where(pg_namespace.c.nspname.notin_(["pg_catalog", "information_schema"])) + .where(pg_proc.c.prokind != "p") + .where(_not_from_extension(pg_proc.c.oid, "pg_proc")) ) - .where(pg_namespace.c.nspname.notin_(["pg_catalog", "information_schema"])) - .where(pg_proc.c.prokind != "p") - .where(_not_from_extension(pg_proc.c.oid, "pg_proc")) -) + + +def get_source_column(version_info): + """Postgres 14 introduced SQL-standard function and procedure bodies. + + When writing a function or procedure in SQL-standard syntax, the body is parsed + immediately and stored as a parse tree. This allows better tracking of function + dependencies, and can have security benefits. + + For these sqlbody functions, the pg_proc.prosrc column is an empty string. + The pre-parsed SQL function body is stored in pg_proc.prosqlbody as pg_node_tree. + The text representation can be returned along with the full ddl with + pg_get_functiondef. + Alternatively pg_get_function_sqlbody(pg_proc.oid) can be called to just get the + body. This function is not documented, see source: + https://doxygen.postgresql.org/ruleutils_8c.html#a99a3f975518b6b1707a3159c5f80427e + """ + if version_info >= (14, 0): + return coalesce(func.pg_get_function_sqlbody(pg_proc.c.oid), pg_proc.c.prosrc) + return pg_proc.c.prosrc rel_nsp = pg_namespace.alias("rel_nsp") diff --git a/tests/dialect/postgresql/test_postgres_14_sqlbody.py b/tests/dialect/postgresql/test_postgres_14_sqlbody.py new file mode 100644 index 0000000..2180185 --- /dev/null +++ b/tests/dialect/postgresql/test_postgres_14_sqlbody.py @@ -0,0 +1,34 @@ +import pytest +from pytest_mock_resources import PostgresConfig, create_postgres_fixture +from sqlalchemy import text + +from sqlalchemy_declarative_extensions import Functions +from sqlalchemy_declarative_extensions.dialects.postgresql import ( + Function, + FunctionVolatility, +) +from sqlalchemy_declarative_extensions.function.compare import compare_functions + +pg = create_postgres_fixture(scope="function", engine_kwargs={"echo": True}) + + +@pytest.fixture +def pmr_postgres_config(): + return PostgresConfig(image="postgres:14", port=None, ci_port=None) + + +def test_functions(pg): + add_stable_function = Function( + name="add_stable", + definition="RETURN (i + 1)", + parameters=["i integer"], + returns="INTEGER", + volatility=FunctionVolatility.STABLE, + ).normalize() + create_function = add_stable_function.to_sql_create() + functions = Functions([add_stable_function]) + with pg.connect() as connection: + connection.execute(text("\n".join(create_function))) + diff = compare_functions(connection, functions) + for op in diff: + assert op.from_function == op.function diff --git a/tests/examples/test_function_create_pg_sqlbody/alembic.ini b/tests/examples/test_function_create_pg_sqlbody/alembic.ini new file mode 100644 index 0000000..9a7dfa6 --- /dev/null +++ b/tests/examples/test_function_create_pg_sqlbody/alembic.ini @@ -0,0 +1,36 @@ +[alembic] +script_location = migrations + +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S \ No newline at end of file diff --git a/tests/examples/test_function_create_pg_sqlbody/migrations/env.py b/tests/examples/test_function_create_pg_sqlbody/migrations/env.py new file mode 100644 index 0000000..85d03a1 --- /dev/null +++ b/tests/examples/test_function_create_pg_sqlbody/migrations/env.py @@ -0,0 +1,27 @@ +from alembic import context + +# isort: split +from models import Base # Imports from the example's models.py +from sqlalchemy import engine_from_config, pool + +from sqlalchemy_declarative_extensions import register_alembic_events + +target_metadata = Base.metadata + +# Ensure functions=True is enabled +register_alembic_events(functions=True) + +connectable = context.config.attributes.get("connection", None) + +if connectable is None: + connectable = engine_from_config( + context.config.get_section(context.config.config_ini_section), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + +with connectable.connect() as connection: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() diff --git a/tests/examples/test_function_create_pg_sqlbody/migrations/script.py.mako b/tests/examples/test_function_create_pg_sqlbody/migrations/script.py.mako new file mode 100644 index 0000000..50a043f --- /dev/null +++ b/tests/examples/test_function_create_pg_sqlbody/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ''} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else 'pass'} + + +def downgrade(): + ${downgrades if downgrades else 'pass'} \ No newline at end of file diff --git a/tests/examples/test_function_create_pg_sqlbody/migrations/versions/.gitkeep b/tests/examples/test_function_create_pg_sqlbody/migrations/versions/.gitkeep new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/tests/examples/test_function_create_pg_sqlbody/migrations/versions/.gitkeep @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/examples/test_function_create_pg_sqlbody/models.py b/tests/examples/test_function_create_pg_sqlbody/models.py new file mode 100644 index 0000000..bac30c0 --- /dev/null +++ b/tests/examples/test_function_create_pg_sqlbody/models.py @@ -0,0 +1,50 @@ +from sqlalchemy import Column, types +from sqlalchemy.orm import declarative_base + +from sqlalchemy_declarative_extensions import Functions, declarative_database +from sqlalchemy_declarative_extensions.dialects.postgresql import ( + Function, + FunctionVolatility, +) +from sqlalchemy_declarative_extensions.dialects.postgresql.function import ( + FunctionReturn, +) + +_Base = declarative_base() + + +@declarative_database +class Base(_Base): # type: ignore + __abstract__ = True + + functions = Functions().are( + Function( + "add_stable", + "RETURN (i + 1)", + parameters=["i integer"], + returns="INTEGER", + volatility=FunctionVolatility.STABLE, + ), + # NEW FUNCTION: Multiple params, RETURNS TABLE + Function( + "get_users_by_group", + """ + BEGIN ATOMIC + SELECT dt.id, + (dt.id)::text AS name + FROM dummy_table dt + WHERE (dt.id = ANY (get_users_by_group.group_ids)); + END + """, + parameters=["group_ids integer[]"], + returns=FunctionReturn(table=["id integer", "name text"]), + language="sql", + volatility=FunctionVolatility.STABLE, + ), + ) + + +# Include a dummy table just in case it's needed +class DummyTable(Base): + __tablename__ = "dummy_table" + id = Column(types.Integer, primary_key=True) diff --git a/tests/examples/test_function_create_pg_sqlbody/test_migrations.py b/tests/examples/test_function_create_pg_sqlbody/test_migrations.py new file mode 100644 index 0000000..b124d63 --- /dev/null +++ b/tests/examples/test_function_create_pg_sqlbody/test_migrations.py @@ -0,0 +1,69 @@ +import pytest +from pytest_alembic import ( + MigrationContext, + tests, # Import the tests module +) +from pytest_mock_resources import PostgresConfig, create_postgres_fixture +from sqlalchemy import text + +# Use the postgres fixture provided by pytest-mock-resources +alembic_engine = create_postgres_fixture(scope="function", engine_kwargs={"echo": True}) + + +@pytest.fixture(scope="session") +def pmr_postgres_config(): + """Override the default config to avoid port conflicts.""" + return PostgresConfig(image="postgres:14", port=None, ci_port=None) + + +def test_apply_autogenerated_revision(alembic_runner: MigrationContext, alembic_engine): + """Check that autogenerate detects the new function and the migration runs.""" + # Generate the revision based on the models + alembic_runner.generate_revision( + autogenerate=True, + prevent_file_generation=False, + message="Add add_stable function", + ) + + # Apply the generated migration + alembic_runner.migrate_up_one() + + # Verify the function exists and works + with alembic_engine.connect() as conn: + result = conn.execute(text("select add_stable(5)")).scalar() + assert result == 6 + + # Also verify the multi-param function + # Create some dummy data first + conn.execute(text("INSERT INTO dummy_table (id) VALUES (1), (3), (5)")) + conn.commit() + + multi_result = conn.execute( + text("select * from get_users_by_group(ARRAY[1, 5, 7])") + ).fetchall() + + assert len(multi_result) == 2 + assert {row.id for row in multi_result} == {1, 5} + assert {row.name for row in multi_result} == {"1", "5"} + + # Verify that a subsequent autogenerate is empty using pytest-alembic helper + tests.test_model_definitions_match_ddl(alembic_runner) + + # Apply the downgrade + alembic_runner.migrate_down_one() + + # Verify functions are gone by querying pg_proc + with alembic_engine.connect() as conn: + # We need the schema OID first + schema_oid = conn.execute( + text("SELECT oid FROM pg_namespace WHERE nspname = 'public'") + ).scalar() + + count = conn.execute( + text( + "SELECT count(*) FROM pg_proc " + "WHERE pronamespace = :schema_oid AND proname IN ('add_stable', 'get_users_by_group')" + ), + {"schema_oid": schema_oid}, + ).scalar() + assert count == 0 diff --git a/tests/function/test_alembic.py b/tests/function/test_alembic.py index 16aae48..4dd04cc 100644 --- a/tests/function/test_alembic.py +++ b/tests/function/test_alembic.py @@ -13,6 +13,11 @@ def test_function_create_pg_postgis(pytester): successful_test_run(pytester, count=1) +@pytest.mark.alembic +def test_function_create_pg_sqlbody(pytester): + successful_test_run(pytester, count=1) + + @pytest.mark.alembic def test_function_update(pytester): successful_test_run(pytester, count=1) diff --git a/uv.lock b/uv.lock index c1015ba..58e26b7 100644 --- a/uv.lock +++ b/uv.lock @@ -849,6 +849,82 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ce/21/534b8f5bd9734b7a2fcd3a16b1ee82ef6cad81a4796e95ebf4e0c6a24119/psycopg-3.2.3-py3-none-any.whl", hash = "sha256:644d3973fe26908c73d4be746074f6e5224b03c1101d302d9a53bf565ad64907", size = 197934, upload-time = "2024-09-29T21:21:19.623Z" }, ] +[package.optional-dependencies] +binary = [ + { name = "psycopg-binary", marker = "implementation_name != 'pypy'" }, +] + +[[package]] +name = "psycopg-binary" +version = "3.2.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/1c/1fc9d53844c15059b98b27d7037a8af87e43832e367c88c8ee43b8bb650f/psycopg_binary-3.2.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:965455eac8547f32b3181d5ec9ad8b9be500c10fe06193543efaaebe3e4ce70c", size = 3383146, upload-time = "2024-09-29T21:21:26.196Z" }, + { url = "https://files.pythonhosted.org/packages/fb/80/0d0eca43756578738a14f747b3d27e8e22ba468765071eaf61cd517c52a3/psycopg_binary-3.2.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:71adcc8bc80a65b776510bc39992edf942ace35b153ed7a9c6c573a6849ce308", size = 3504185, upload-time = "2024-09-29T21:21:30.806Z" }, + { url = "https://files.pythonhosted.org/packages/7c/02/1db86752a2a663cf59d410374e9aced220d1a883a64b7256ed1171685a27/psycopg_binary-3.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f73adc05452fb85e7a12ed3f69c81540a8875960739082e6ea5e28c373a30774", size = 4469268, upload-time = "2024-09-29T21:21:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/59/04/b8cbc84f494247fa887dcc5cba15f99d261dc44b94fbb10fdaa44c4d6dac/psycopg_binary-3.2.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8630943143c6d6ca9aefc88bbe5e76c90553f4e1a3b2dc339e67dc34aa86f7e", size = 4270625, upload-time = "2024-09-29T21:21:41.934Z" }, + { url = "https://files.pythonhosted.org/packages/74/94/851a58aeab1e2aa30a564133f84229242b2fc774eabb3fc5c164b2423dcd/psycopg_binary-3.2.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bffb61e198a91f712cc3d7f2d176a697cb05b284b2ad150fb8edb308eba9002", size = 4515573, upload-time = "2024-09-29T21:21:51.34Z" }, + { url = "https://files.pythonhosted.org/packages/5a/95/e3e600687e59df7d5214e81d9aa2d324f2c5dece32068d66b03a4fd6edf6/psycopg_binary-3.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4fa2240c9fceddaa815a58f29212826fafe43ce80ff666d38c4a03fb036955", size = 4214078, upload-time = "2024-09-29T21:21:59.624Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1e/4b50e1a2c35a7ee1fc65f8a5fed36026c16b05c9549dc4247914dfbfa2f5/psycopg_binary-3.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:192a5f8496e6e1243fdd9ac20e117e667c0712f148c5f9343483b84435854c78", size = 3139319, upload-time = "2024-09-29T21:22:05.51Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bb/fc88304a7b759d87ad79f538f1b605c23802f36963d207b6e8e9062a57bd/psycopg_binary-3.2.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64dc6e9ec64f592f19dc01a784e87267a64a743d34f68488924251253da3c818", size = 3118977, upload-time = "2024-09-29T21:22:09.422Z" }, + { url = "https://files.pythonhosted.org/packages/92/19/88e14b615291b472b616bb3078206eac63dd6cb806c79b12119b7c39e519/psycopg_binary-3.2.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:79498df398970abcee3d326edd1d4655de7d77aa9aecd578154f8af35ce7bbd2", size = 3224533, upload-time = "2024-09-29T21:22:13.059Z" }, + { url = "https://files.pythonhosted.org/packages/98/cd/6cedff641f1ffb7008b6c511233814d2934df8caf2ec93c50412c37e5f91/psycopg_binary-3.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:949551752930d5e478817e0b49956350d866b26578ced0042a61967e3fcccdea", size = 3258089, upload-time = "2024-09-29T21:22:17.568Z" }, + { url = "https://files.pythonhosted.org/packages/31/2c/8059fbcd513d4b7c9e25dd93c438ab174e8ce389b85d8432b4ce3c0e8958/psycopg_binary-3.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:80a2337e2dfb26950894c8301358961430a0304f7bfe729d34cc036474e9c9b1", size = 2921689, upload-time = "2024-09-29T21:22:22.002Z" }, + { url = "https://files.pythonhosted.org/packages/3d/78/8e8b4063b5cd1cc91cc100fc3e9296b96f52c9a709750b24ade6cfa8021b/psycopg_binary-3.2.3-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:6d8f2144e0d5808c2e2aed40fbebe13869cd00c2ae745aca4b3b16a435edb056", size = 3391535, upload-time = "2024-09-29T21:22:28.111Z" }, + { url = "https://files.pythonhosted.org/packages/36/7f/04eed0c415d158a0fb1c196957b9c7faec43c7b50d20db05c62e5bd22c93/psycopg_binary-3.2.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:94253be2b57ef2fea7ffe08996067aabf56a1eb9648342c9e3bad9e10c46e045", size = 3509175, upload-time = "2024-09-29T21:22:34.989Z" }, + { url = "https://files.pythonhosted.org/packages/0d/91/042fe504220a6e1a423e6a26d24f198da976b9cce11bc9ab7e9415bac08f/psycopg_binary-3.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fda0162b0dbfa5eaed6cdc708179fa27e148cb8490c7d62e5cf30713909658ea", size = 4465647, upload-time = "2024-09-29T21:22:42.729Z" }, + { url = "https://files.pythonhosted.org/packages/35/7c/4cf02ee263431b306453b7b086ec8e91dcbd5008382d711e82afa829f73e/psycopg_binary-3.2.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c0419cdad8c70eaeb3116bb28e7b42d546f91baf5179d7556f230d40942dc78", size = 4267051, upload-time = "2024-09-29T21:22:48.362Z" }, + { url = "https://files.pythonhosted.org/packages/f5/9b/cea713d8d75621481ece2dfc7edae6e4f05dfbcaab28fac0dbff9b96fc3a/psycopg_binary-3.2.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74fbf5dd3ef09beafd3557631e282f00f8af4e7a78fbfce8ab06d9cd5a789aae", size = 4517398, upload-time = "2024-09-29T21:22:56.618Z" }, + { url = "https://files.pythonhosted.org/packages/56/65/cd4165c45359f4117147b861c16c7b85afbd93cc9efac6116b13f62bc725/psycopg_binary-3.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d784f614e4d53050cbe8abf2ae9d1aaacf8ed31ce57b42ce3bf2a48a66c3a5c", size = 4210644, upload-time = "2024-09-29T21:23:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/f3/80/14e7bf67613c4344e74fe6ac5c9876a7acb4ddc15e5455c54e24cdc087f8/psycopg_binary-3.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4e76ce2475ed4885fe13b8254058be710ec0de74ebd8ef8224cf44a9a3358e5f", size = 3138032, upload-time = "2024-09-29T21:23:06.387Z" }, + { url = "https://files.pythonhosted.org/packages/7e/81/e18c36de78e0f7a491a754dc74c1bb6b16469d8c240b2add1e856801d567/psycopg_binary-3.2.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5938b257b04c851c2d1e6cb2f8c18318f06017f35be9a5fe761ee1e2e344dfb7", size = 3114329, upload-time = "2024-09-29T21:23:09.483Z" }, + { url = "https://files.pythonhosted.org/packages/48/39/07b0bf8355cb535ccdd58261a18fb6e786e175492363f5255b446fff6427/psycopg_binary-3.2.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:257c4aea6f70a9aef39b2a77d0658a41bf05c243e2bf41895eb02220ac6306f3", size = 3219579, upload-time = "2024-09-29T21:23:14.005Z" }, + { url = "https://files.pythonhosted.org/packages/64/ea/92c700989b5bdeb8e8e59732191547e32da732692d6c016830c82f9b4ac7/psycopg_binary-3.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:06b5cc915e57621eebf2393f4173793ed7e3387295f07fed93ed3fb6a6ccf585", size = 3257145, upload-time = "2024-09-29T21:23:20.458Z" }, + { url = "https://files.pythonhosted.org/packages/84/49/39f0875fd32a6d77cd22b44887df39eb470039b389c388cee4ba75c0bda7/psycopg_binary-3.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:09baa041856b35598d335b1a74e19a49da8500acedf78164600694c0ba8ce21b", size = 2924948, upload-time = "2024-09-29T21:23:25.591Z" }, + { url = "https://files.pythonhosted.org/packages/55/6b/9805a5c743c1d54dcd035bd5c069202fde21b4cf69857ca40c2a55e69f8c/psycopg_binary-3.2.3-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:48f8ca6ee8939bab760225b2ab82934d54330eec10afe4394a92d3f2a0c37dd6", size = 3363376, upload-time = "2024-09-29T21:23:30.049Z" }, + { url = "https://files.pythonhosted.org/packages/a8/82/45ac156b20e08e8f556a323c9568a011c71cf6e734e49667a398719ce0e4/psycopg_binary-3.2.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5361ea13c241d4f0ec3f95e0bf976c15e2e451e9cc7ef2e5ccfc9d170b197a40", size = 3506449, upload-time = "2024-09-29T21:23:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/e4/be/760cef50e1adfbc87dab2b05b30f544d7297040cce495835df9016556517/psycopg_binary-3.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb987f14af7da7c24f803111dbc7392f5070fd350146af3345103f76ea82e339", size = 4445757, upload-time = "2024-09-29T21:23:38.732Z" }, + { url = "https://files.pythonhosted.org/packages/b4/9c/bae6a9c6949aac577cc93f58705f649b50c62827038903bd75ff8956e63e/psycopg_binary-3.2.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0463a11b1cace5a6aeffaf167920707b912b8986a9c7920341c75e3686277920", size = 4248376, upload-time = "2024-09-29T21:23:43.951Z" }, + { url = "https://files.pythonhosted.org/packages/e5/0e/9db06ef94e4a156f3ed06043ee4f370e21866b0e3b7959691c8c4abfb698/psycopg_binary-3.2.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b7be9a6c06518967b641fb15032b1ed682fd3b0443f64078899c61034a0bca6", size = 4487765, upload-time = "2024-09-29T21:23:50.999Z" }, + { url = "https://files.pythonhosted.org/packages/9f/5f/8afc32b60ee8bc5c4af51e7cf6c42d93a989a09609524d0a393106e300cd/psycopg_binary-3.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64a607e630d9f4b2797f641884e52b9f8e239d35943f51bef817a384ec1678fe", size = 4188374, upload-time = "2024-09-29T21:24:00.191Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5d/210cb75aff0296dc5c09bcf67babf8679905412d7a11357b983f0d877360/psycopg_binary-3.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fa33ead69ed133210d96af0c63448b1385df48b9c0247eda735c5896b9e6dbbf", size = 3113180, upload-time = "2024-09-29T21:24:06.433Z" }, + { url = "https://files.pythonhosted.org/packages/40/ec/46b1a5cdb2fe995b8ec0376f0695003e97fed9ac077e090a3165ea15f735/psycopg_binary-3.2.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1f8b0d0e99d8e19923e6e07379fa00570be5182c201a8c0b5aaa9a4d4a4ea20b", size = 3099455, upload-time = "2024-09-29T21:24:10.933Z" }, + { url = "https://files.pythonhosted.org/packages/11/68/eaf85b3421b3f01b638dd6b16f4e9bc8de42eb1d000da62964fb29f8c823/psycopg_binary-3.2.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:709447bd7203b0b2debab1acec23123eb80b386f6c29e7604a5d4326a11e5bd6", size = 3189977, upload-time = "2024-09-29T21:24:15.708Z" }, + { url = "https://files.pythonhosted.org/packages/83/5a/cf94c3ba87ea6c8331aa0aba36a18a837a3231764457780661968804673e/psycopg_binary-3.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5e37d5027e297a627da3551a1e962316d0f88ee4ada74c768f6c9234e26346d9", size = 3232263, upload-time = "2024-09-29T21:24:20.237Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3a/9d912b16059e87b04e3eb4fca457f079d78d6468f627d5622fbda80e9378/psycopg_binary-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:261f0031ee6074765096a19b27ed0f75498a8338c3dcd7f4f0d831e38adf12d1", size = 2912530, upload-time = "2024-09-29T21:24:25.079Z" }, + { url = "https://files.pythonhosted.org/packages/c6/bf/717c5e51c68e2498b60a6e9f1476cc47953013275a54bf8e23fd5082a72d/psycopg_binary-3.2.3-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:41fdec0182efac66b27478ac15ef54c9ebcecf0e26ed467eb7d6f262a913318b", size = 3360874, upload-time = "2024-09-29T21:24:30.796Z" }, + { url = "https://files.pythonhosted.org/packages/31/d5/6f9ad6fe5ef80ca9172bc3d028ebae8e9a1ee8aebd917c95c747a5efd85f/psycopg_binary-3.2.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:07d019a786eb020c0f984691aa1b994cb79430061065a694cf6f94056c603d26", size = 3502320, upload-time = "2024-09-29T21:24:36.694Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7b/c58dd26c27fe7a491141ca765c103e702872ff1c174ebd669d73d7fb0b5d/psycopg_binary-3.2.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c57615791a337378fe5381143259a6c432cdcbb1d3e6428bfb7ce59fff3fb5c", size = 4446950, upload-time = "2024-09-29T21:24:43.028Z" }, + { url = "https://files.pythonhosted.org/packages/ed/75/acf6a81c788007b7bc0a43b02c22eff7cb19a6ace9e84c32838e86083a3f/psycopg_binary-3.2.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8eb9a4e394926b93ad919cad1b0a918e9b4c846609e8c1cfb6b743683f64da0", size = 4252409, upload-time = "2024-09-29T21:24:47.768Z" }, + { url = "https://files.pythonhosted.org/packages/83/a5/8a01b923fe42acd185d53f24fb98ead717725ede76a4cd183ff293daf1f1/psycopg_binary-3.2.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5905729668ef1418bd36fbe876322dcb0f90b46811bba96d505af89e6fbdce2f", size = 4488121, upload-time = "2024-09-29T21:24:54.244Z" }, + { url = "https://files.pythonhosted.org/packages/14/8f/b00e65e204340ab1259ecc8d4cc4c1f72c386be5ca7bfb90ae898a058d68/psycopg_binary-3.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd65774ed7d65101b314808b6893e1a75b7664f680c3ef18d2e5c84d570fa393", size = 4190653, upload-time = "2024-09-29T21:25:02.336Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fc/ba830fc6c9b02b66d1e2fb420736df4d78369760144169a9046f04d72ac6/psycopg_binary-3.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:700679c02f9348a0d0a2adcd33a0275717cd0d0aee9d4482b47d935023629505", size = 3118074, upload-time = "2024-09-29T21:25:07.755Z" }, + { url = "https://files.pythonhosted.org/packages/b8/75/b62d06930a615435e909e05de126aa3d49f6ec2993d1aa6a99e7faab5570/psycopg_binary-3.2.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:96334bb64d054e36fed346c50c4190bad9d7c586376204f50bede21a913bf942", size = 3100457, upload-time = "2024-09-29T21:25:13.002Z" }, + { url = "https://files.pythonhosted.org/packages/57/e5/32dc7518325d0010813853a87b19c784d8b11fdb17f5c0e0c148c5ac77af/psycopg_binary-3.2.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9099e443d4cc24ac6872e6a05f93205ba1a231b1a8917317b07c9ef2b955f1f4", size = 3192788, upload-time = "2024-09-29T21:25:18.815Z" }, + { url = "https://files.pythonhosted.org/packages/23/a3/d1aa04329253c024a2323051774446770d47b43073874a3de8cca797ed8e/psycopg_binary-3.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1985ab05e9abebfbdf3163a16ebb37fbc5d49aff2bf5b3d7375ff0920bbb54cd", size = 3234247, upload-time = "2024-09-29T21:25:24.005Z" }, + { url = "https://files.pythonhosted.org/packages/03/20/b675af723b9a61d48abd6a3d64cbb9797697d330255d1f8105713d54ed8e/psycopg_binary-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:e90352d7b610b4693fad0feea48549d4315d10f1eba5605421c92bb834e90170", size = 2913413, upload-time = "2024-09-29T21:25:28.151Z" }, + { url = "https://files.pythonhosted.org/packages/11/59/6f5763265b51e21abd6d63f529b68a0e60cc1df152316e79f1181a110e96/psycopg_binary-3.2.3-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:69320f05de8cdf4077ecd7fefdec223890eea232af0d58f2530cbda2871244a0", size = 3384410, upload-time = "2024-09-29T21:25:32.162Z" }, + { url = "https://files.pythonhosted.org/packages/92/be/50dcb479e75906b8cb003b0b57e9a8fda0b81bb0b2c738b52ccac0d32ab6/psycopg_binary-3.2.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4926ea5c46da30bec4a85907aa3f7e4ea6313145b2aa9469fdb861798daf1502", size = 4469507, upload-time = "2024-09-29T21:25:37.733Z" }, + { url = "https://files.pythonhosted.org/packages/ce/44/2b87aa5b2464367c18e007476032660554a5ba228508755f20e23e879039/psycopg_binary-3.2.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c64c4cd0d50d5b2288ab1bcb26c7126c772bbdebdfadcd77225a77df01c4a57e", size = 4270222, upload-time = "2024-09-29T21:25:42.48Z" }, + { url = "https://files.pythonhosted.org/packages/dd/cf/2011bbd6bdfc995087c24965cc1c59fddf6f7e27700febb124941dcf06a4/psycopg_binary-3.2.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05a1bdce30356e70a05428928717765f4a9229999421013f41338d9680d03a63", size = 4522305, upload-time = "2024-09-29T21:25:46.898Z" }, + { url = "https://files.pythonhosted.org/packages/7b/75/b82f43c38c4b7fbf0278d31165126acf889f69cdae7a3065c3bf935dba9a/psycopg_binary-3.2.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad357e426b0ea5c3043b8ec905546fa44b734bf11d33b3da3959f6e4447d350", size = 4215983, upload-time = "2024-09-29T21:25:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f4/a821440a62d0c4d6f5d76827c233a412f83e2d839e8e5589136d4e935ca4/psycopg_binary-3.2.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:967b47a0fd237aa17c2748fdb7425015c394a6fb57cdad1562e46a6eb070f96d", size = 3142095, upload-time = "2024-09-29T21:25:56.81Z" }, + { url = "https://files.pythonhosted.org/packages/3f/44/93841190fd09c76316f5d462508f1aeda7a98b57dfe7b78c622cadf99350/psycopg_binary-3.2.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:71db8896b942770ed7ab4efa59b22eee5203be2dfdee3c5258d60e57605d688c", size = 3120815, upload-time = "2024-09-29T21:26:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/757ddc8f76c624969a98c00f4774a55c5d4c6bf8674db4c7283b13a5673a/psycopg_binary-3.2.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2773f850a778575dd7158a6dd072f7925b67f3ba305e2003538e8831fec77a1d", size = 3228866, upload-time = "2024-09-29T21:26:09.041Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5d/d214ee8b84771695737737c433bdaa8ee37c378ead3e9123fe07bec08d1f/psycopg_binary-3.2.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aeddf7b3b3f6e24ccf7d0edfe2d94094ea76b40e831c16eff5230e040ce3b76b", size = 3264374, upload-time = "2024-09-29T21:26:15.202Z" }, + { url = "https://files.pythonhosted.org/packages/21/b2/350cda49155d95649c091e7618e7fea81d617b048df42bac5d80c6a3de9f/psycopg_binary-3.2.3-cp38-cp38-win_amd64.whl", hash = "sha256:824c867a38521d61d62b60aca7db7ca013a2b479e428a0db47d25d8ca5067410", size = 2929244, upload-time = "2024-09-29T21:26:20.625Z" }, + { url = "https://files.pythonhosted.org/packages/1c/21/71110c15aecf73176b4dfd9dccb7d5b48f7ad2b3ef845d30344a05096e96/psycopg_binary-3.2.3-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:9994f7db390c17fc2bd4c09dca722fd792ff8a49bb3bdace0c50a83f22f1767d", size = 3384458, upload-time = "2024-09-29T21:26:25.618Z" }, + { url = "https://files.pythonhosted.org/packages/0b/cb/c80fd6ba503434d538f71c271e189f32541ca0795a7ef6d11359dc621430/psycopg_binary-3.2.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1303bf8347d6be7ad26d1362af2c38b3a90b8293e8d56244296488ee8591058e", size = 4469330, upload-time = "2024-09-29T21:26:31.52Z" }, + { url = "https://files.pythonhosted.org/packages/6b/03/86c6b20c621cccc1a2eb74a01462036f6ff1bec45f9707c8ee8c35978f14/psycopg_binary-3.2.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:842da42a63ecb32612bb7f5b9e9f8617eab9bc23bd58679a441f4150fcc51c96", size = 4271605, upload-time = "2024-09-29T21:26:39.432Z" }, + { url = "https://files.pythonhosted.org/packages/37/47/fa357809bd873c90461ab41f71aa94a1fd43925adfd043c2aa6e56e0f615/psycopg_binary-3.2.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2bb342a01c76f38a12432848e6013c57eb630103e7556cf79b705b53814c3949", size = 4519938, upload-time = "2024-09-29T21:26:47.598Z" }, + { url = "https://files.pythonhosted.org/packages/d1/f4/2bdee9374313d224148a66f8d6f2f9726d1eb6b1919c37685d4d98d0d129/psycopg_binary-3.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd40af959173ea0d087b6b232b855cfeaa6738f47cb2a0fd10a7f4fa8b74293f", size = 4216398, upload-time = "2024-09-29T21:26:53.304Z" }, + { url = "https://files.pythonhosted.org/packages/1c/93/5ca69e54d331372aacc8ec1e63805c9e54c2f16d77a9156f5e0cb6c2bebe/psycopg_binary-3.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9b60b465773a52c7d4705b0a751f7f1cdccf81dd12aee3b921b31a6e76b07b0e", size = 3141385, upload-time = "2024-09-29T21:26:58.414Z" }, + { url = "https://files.pythonhosted.org/packages/18/ad/c28bd3661accde75df677af0ba6bc01089a332c31105ebddf5925d765374/psycopg_binary-3.2.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fc6d87a1c44df8d493ef44988a3ded751e284e02cdf785f746c2d357e99782a6", size = 3118288, upload-time = "2024-09-29T21:27:03.834Z" }, + { url = "https://files.pythonhosted.org/packages/63/d9/5dd0bdf7804ae2ab7326d27c94ea96f7c0dac2710f0603ef92ebc58ffb8c/psycopg_binary-3.2.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:f0b018e37608c3bfc6039a1dc4eb461e89334465a19916be0153c757a78ea426", size = 3226249, upload-time = "2024-09-29T21:27:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/56/d7/f1cf6447d1c45641cb8d860bb21c039fbe7f643f79b66dbd5d4b9201f068/psycopg_binary-3.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a29f5294b0b6360bfda69653697eff70aaf2908f58d1073b0acd6f6ab5b5a4f", size = 3260299, upload-time = "2024-09-29T21:27:18.006Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ab/f08f734154193af28ac02ab93fa0f1917ef75d4947277cbb87ccfa7ba1ac/psycopg_binary-3.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:e56b1fd529e5dde2d1452a7d72907b37ed1b4f07fdced5d8fb1e963acfff6749", size = 2923376, upload-time = "2024-09-29T21:27:23.149Z" }, +] + [[package]] name = "psycopg2-binary" version = "2.9.9" @@ -1416,7 +1492,7 @@ mypy = [ [[package]] name = "sqlalchemy-declarative-extensions" -version = "0.16.4" +version = "0.16.5" source = { editable = "." } dependencies = [ { name = "sqlalchemy" }, @@ -1437,7 +1513,7 @@ dev = [ { name = "coverage" }, { name = "fakesnow", marker = "python_full_version >= '3.9'" }, { name = "mypy" }, - { name = "psycopg" }, + { name = "psycopg", extra = ["binary"] }, { name = "psycopg2-binary" }, { name = "pymysql", extra = ["rsa"] }, { name = "pytest" }, @@ -1464,7 +1540,7 @@ dev = [ { name = "coverage", specifier = ">=5" }, { name = "fakesnow", marker = "python_full_version >= '3.9'", specifier = ">=0.9.21" }, { name = "mypy", specifier = "==1.8.0" }, - { name = "psycopg" }, + { name = "psycopg", extras = ["binary"] }, { name = "psycopg2-binary" }, { name = "pymysql", extras = ["rsa"] }, { name = "pytest", specifier = ">=7" },