This commit is contained in:
2025-05-22 20:25:38 +02:00
parent 09f6750c2b
commit ce03fbf12f
529 changed files with 3353 additions and 3312 deletions

View File

@@ -11,6 +11,7 @@ WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install gunicorn
# Copy project files
COPY . .
@@ -22,5 +23,5 @@ EXPOSE 5000
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
# Run the application
CMD ["flask", "run", "--host=0.0.0.0"]
# Run the application with gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]

7
app.py
View File

@@ -1,5 +1,6 @@
from flask import Flask, render_template, request, redirect, url_for, flash, session
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename
@@ -16,6 +17,7 @@ app.config['MAX_CONTENT_LENGTH'] = 2 * 1024 * 1024 # 2MB max file size
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
# Database Models
class User(db.Model):
@@ -1142,4 +1144,7 @@ def seed_db():
description=plant['description'],
care_guide=plant['care_guide']
))
db.session.commit()
db.session.commit()
if __name__ == "__main__":
app.run(debug=True)

Binary file not shown.

1
migrations/README Normal file
View File

@@ -0,0 +1 @@
Single-database configuration for Flask.

50
migrations/alembic.ini Normal file
View File

@@ -0,0 +1,50 @@
# A generic, single database configuration.
[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic,flask_migrate
[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
[logger_flask_migrate]
level = INFO
handlers =
qualname = flask_migrate
[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

113
migrations/env.py Normal file
View File

@@ -0,0 +1,113 @@
import logging
from logging.config import fileConfig
from flask import current_app
from alembic import context
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
def get_engine():
try:
# this works with Flask-SQLAlchemy<3 and Alchemical
return current_app.extensions['migrate'].db.get_engine()
except (TypeError, AttributeError):
# this works with Flask-SQLAlchemy>=3
return current_app.extensions['migrate'].db.engine
def get_engine_url():
try:
return get_engine().url.render_as_string(hide_password=False).replace(
'%', '%%')
except AttributeError:
return str(get_engine().url).replace('%', '%%')
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
config.set_main_option('sqlalchemy.url', get_engine_url())
target_db = current_app.extensions['migrate'].db
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def get_metadata():
if hasattr(target_db, 'metadatas'):
return target_db.metadatas[None]
return target_db.metadata
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=get_metadata(), literal_binds=True
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
# this callback is used to prevent an auto-migration from being generated
# when there are no changes to the schema
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
logger.info('No changes in schema detected.')
conf_args = current_app.extensions['migrate'].configure_args
if conf_args.get("process_revision_directives") is None:
conf_args["process_revision_directives"] = process_revision_directives
connectable = get_engine()
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=get_metadata(),
**conf_args
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

24
migrations/script.py.mako Normal file
View File

@@ -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"}

View File

@@ -5,4 +5,6 @@ Flask-WTF==1.2.1
SQLAlchemy==2.0.23
Werkzeug==2.3.7
WTForms==3.1.1
python-dotenv==1.0.0
python-dotenv==1.0.0
psycopg2-binary==2.9.9
gunicorn==21.2.0

View File

@@ -1,28 +0,0 @@
Copyright 2010 WTForms
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,50 +0,0 @@
Metadata-Version: 2.1
Name: Flask-WTF
Version: 1.1.1
Summary: Form rendering, validation, and CSRF protection for Flask with WTForms.
Home-page: https://github.com/wtforms/flask-wtf/
Author: Dan Jacob
Author-email: danjac354@gmail.com
Maintainer: Hsiaoming Yang
Maintainer-email: me@lepture.com
License: BSD-3-Clause
Project-URL: Documentation, https://flask-wtf.readthedocs.io/
Project-URL: Changes, https://flask-wtf.readthedocs.io/changes/
Project-URL: Source Code, https://github.com/wtforms/flask-wtf/
Project-URL: Issue Tracker, https://github.com/wtforms/flask-wtf/issues/
Project-URL: Chat, https://discord.gg/pallets
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Flask
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE.rst
Requires-Dist: Flask
Requires-Dist: WTForms
Requires-Dist: itsdangerous
Provides-Extra: email
Requires-Dist: email-validator ; extra == 'email'
Flask-WTF
=========
Simple integration of Flask and WTForms, including CSRF, file upload,
and reCAPTCHA.
Links
-----
- Documentation: https://flask-wtf.readthedocs.io/
- Changes: https://flask-wtf.readthedocs.io/changes/
- PyPI Releases: https://pypi.org/project/Flask-WTF/
- Source Code: https://github.com/wtforms/flask-wtf/
- Issue Tracker: https://github.com/wtforms/flask-wtf/issues/
- Chat: https://discord.gg/pallets

View File

@@ -1,27 +0,0 @@
Flask_WTF-1.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
Flask_WTF-1.1.1.dist-info/LICENSE.rst,sha256=1fGQNkUVeMs27u8EyZ6_fXyi5w3PBDY2UZvEIOFafGI,1475
Flask_WTF-1.1.1.dist-info/METADATA,sha256=YR-t2rpU1ZnLGjB4H_LEm3ns3EPcs8VbAxASuoaWrgE,1868
Flask_WTF-1.1.1.dist-info/RECORD,,
Flask_WTF-1.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
Flask_WTF-1.1.1.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
Flask_WTF-1.1.1.dist-info/top_level.txt,sha256=zK3flQPSjYTkAMjB0V6Jhu3jyotC0biL1mMhzitYoog,10
flask_wtf/__init__.py,sha256=9N5z_8Nkzsla9cgqGKxlLmkgdHGuU3UI49_O2M1odr8,214
flask_wtf/__pycache__/__init__.cpython-310.pyc,,
flask_wtf/__pycache__/_compat.cpython-310.pyc,,
flask_wtf/__pycache__/csrf.cpython-310.pyc,,
flask_wtf/__pycache__/file.cpython-310.pyc,,
flask_wtf/__pycache__/form.cpython-310.pyc,,
flask_wtf/__pycache__/i18n.cpython-310.pyc,,
flask_wtf/_compat.py,sha256=N3sqC9yzFWY-3MZ7QazX1sidvkO3d5yy4NR6lkp0s94,248
flask_wtf/csrf.py,sha256=Z407bCLwNpqjmdh6vK162hG1dHxdrZ2kly4n-Hrbyhs,10156
flask_wtf/file.py,sha256=SKm-Tjk9mYrP94cMnIdEOab1vvQEjfKZ1PwPzXNhH6o,3644
flask_wtf/form.py,sha256=TmR7xCrxin2LHp6thn7fq1OeU8aLB7xsZzvv52nH7Ss,4049
flask_wtf/i18n.py,sha256=TyO8gqt9DocHMSaNhj0KKgxoUrPYs-G1nVW-jns0SOw,1166
flask_wtf/recaptcha/__init__.py,sha256=m4eNGoU3Q0Wnt_wP8VvOlA0mwWuoMtAcK9pYT7sPFp8,106
flask_wtf/recaptcha/__pycache__/__init__.cpython-310.pyc,,
flask_wtf/recaptcha/__pycache__/fields.cpython-310.pyc,,
flask_wtf/recaptcha/__pycache__/validators.cpython-310.pyc,,
flask_wtf/recaptcha/__pycache__/widgets.cpython-310.pyc,,
flask_wtf/recaptcha/fields.py,sha256=M1-RFuUKOsJAzsLm3xaaxuhX2bB9oRqS-HVSN-NpkmI,433
flask_wtf/recaptcha/validators.py,sha256=K4e_pvPoq0JBcSFXEB2XRzWbvi9LZef3ioNbS2jdNgU,2437
flask_wtf/recaptcha/widgets.py,sha256=OWSFCZDWaLBLkNJvzyqcIbRQVBD5tUyEOijfTv0Dpjo,1503

View File

@@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.38.4)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -1,19 +0,0 @@
Copyright 2005-2023 SQLAlchemy authors and contributors <see AUTHORS file>.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,238 +0,0 @@
Metadata-Version: 2.1
Name: SQLAlchemy
Version: 2.0.20
Summary: Database Abstraction Library
Home-page: https://www.sqlalchemy.org
Author: Mike Bayer
Author-email: mike_mp@zzzcomputing.com
License: MIT
Project-URL: Documentation, https://docs.sqlalchemy.org
Project-URL: Issue Tracker, https://github.com/sqlalchemy/sqlalchemy/
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Database :: Front-Ends
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: typing-extensions >=4.2.0
Requires-Dist: greenlet !=0.4.17 ; platform_machine == "aarch64" or (platform_machine == "ppc64le" or (platform_machine == "x86_64" or (platform_machine == "amd64" or (platform_machine == "AMD64" or (platform_machine == "win32" or platform_machine == "WIN32")))))
Requires-Dist: importlib-metadata ; python_version < "3.8"
Provides-Extra: aiomysql
Requires-Dist: greenlet !=0.4.17 ; extra == 'aiomysql'
Requires-Dist: aiomysql >=0.2.0 ; extra == 'aiomysql'
Provides-Extra: aiosqlite
Requires-Dist: greenlet !=0.4.17 ; extra == 'aiosqlite'
Requires-Dist: aiosqlite ; extra == 'aiosqlite'
Requires-Dist: typing-extensions !=3.10.0.1 ; extra == 'aiosqlite'
Provides-Extra: asyncio
Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncio'
Provides-Extra: asyncmy
Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncmy'
Requires-Dist: asyncmy !=0.2.4,!=0.2.6,>=0.2.3 ; extra == 'asyncmy'
Provides-Extra: mariadb_connector
Requires-Dist: mariadb !=1.1.2,!=1.1.5,>=1.0.1 ; extra == 'mariadb_connector'
Provides-Extra: mssql
Requires-Dist: pyodbc ; extra == 'mssql'
Provides-Extra: mssql_pymssql
Requires-Dist: pymssql ; extra == 'mssql_pymssql'
Provides-Extra: mssql_pyodbc
Requires-Dist: pyodbc ; extra == 'mssql_pyodbc'
Provides-Extra: mypy
Requires-Dist: mypy >=0.910 ; extra == 'mypy'
Provides-Extra: mysql
Requires-Dist: mysqlclient >=1.4.0 ; extra == 'mysql'
Provides-Extra: mysql_connector
Requires-Dist: mysql-connector-python ; extra == 'mysql_connector'
Provides-Extra: oracle
Requires-Dist: cx-oracle >=7 ; extra == 'oracle'
Provides-Extra: oracle_oracledb
Requires-Dist: oracledb >=1.0.1 ; extra == 'oracle_oracledb'
Provides-Extra: postgresql
Requires-Dist: psycopg2 >=2.7 ; extra == 'postgresql'
Provides-Extra: postgresql_asyncpg
Requires-Dist: greenlet !=0.4.17 ; extra == 'postgresql_asyncpg'
Requires-Dist: asyncpg ; extra == 'postgresql_asyncpg'
Provides-Extra: postgresql_pg8000
Requires-Dist: pg8000 >=1.29.1 ; extra == 'postgresql_pg8000'
Provides-Extra: postgresql_psycopg
Requires-Dist: psycopg >=3.0.7 ; extra == 'postgresql_psycopg'
Provides-Extra: postgresql_psycopg2binary
Requires-Dist: psycopg2-binary ; extra == 'postgresql_psycopg2binary'
Provides-Extra: postgresql_psycopg2cffi
Requires-Dist: psycopg2cffi ; extra == 'postgresql_psycopg2cffi'
Provides-Extra: postgresql_psycopgbinary
Requires-Dist: psycopg[binary] >=3.0.7 ; extra == 'postgresql_psycopgbinary'
Provides-Extra: pymysql
Requires-Dist: pymysql ; extra == 'pymysql'
Provides-Extra: sqlcipher
Requires-Dist: sqlcipher3-binary ; extra == 'sqlcipher'
SQLAlchemy
==========
|PyPI| |Python| |Downloads|
.. |PyPI| image:: https://img.shields.io/pypi/v/sqlalchemy
:target: https://pypi.org/project/sqlalchemy
:alt: PyPI
.. |Python| image:: https://img.shields.io/pypi/pyversions/sqlalchemy
:target: https://pypi.org/project/sqlalchemy
:alt: PyPI - Python Version
.. |Downloads| image:: https://static.pepy.tech/badge/sqlalchemy/month
:target: https://pepy.tech/project/sqlalchemy
:alt: PyPI - Downloads
The Python SQL Toolkit and Object Relational Mapper
Introduction
-------------
SQLAlchemy is the Python SQL toolkit and Object Relational Mapper
that gives application developers the full power and
flexibility of SQL. SQLAlchemy provides a full suite
of well known enterprise-level persistence patterns,
designed for efficient and high-performing database
access, adapted into a simple and Pythonic domain
language.
Major SQLAlchemy features include:
* An industrial strength ORM, built
from the core on the identity map, unit of work,
and data mapper patterns. These patterns
allow transparent persistence of objects
using a declarative configuration system.
Domain models
can be constructed and manipulated naturally,
and changes are synchronized with the
current transaction automatically.
* A relationally-oriented query system, exposing
the full range of SQL's capabilities
explicitly, including joins, subqueries,
correlation, and most everything else,
in terms of the object model.
Writing queries with the ORM uses the same
techniques of relational composition you use
when writing SQL. While you can drop into
literal SQL at any time, it's virtually never
needed.
* A comprehensive and flexible system
of eager loading for related collections and objects.
Collections are cached within a session,
and can be loaded on individual access, all
at once using joins, or by query per collection
across the full result set.
* A Core SQL construction system and DBAPI
interaction layer. The SQLAlchemy Core is
separate from the ORM and is a full database
abstraction layer in its own right, and includes
an extensible Python-based SQL expression
language, schema metadata, connection pooling,
type coercion, and custom types.
* All primary and foreign key constraints are
assumed to be composite and natural. Surrogate
integer primary keys are of course still the
norm, but SQLAlchemy never assumes or hardcodes
to this model.
* Database introspection and generation. Database
schemas can be "reflected" in one step into
Python structures representing database metadata;
those same structures can then generate
CREATE statements right back out - all within
the Core, independent of the ORM.
SQLAlchemy's philosophy:
* SQL databases behave less and less like object
collections the more size and performance start to
matter; object collections behave less and less like
tables and rows the more abstraction starts to matter.
SQLAlchemy aims to accommodate both of these
principles.
* An ORM doesn't need to hide the "R". A relational
database provides rich, set-based functionality
that should be fully exposed. SQLAlchemy's
ORM provides an open-ended set of patterns
that allow a developer to construct a custom
mediation layer between a domain model and
a relational schema, turning the so-called
"object relational impedance" issue into
a distant memory.
* The developer, in all cases, makes all decisions
regarding the design, structure, and naming conventions
of both the object model as well as the relational
schema. SQLAlchemy only provides the means
to automate the execution of these decisions.
* With SQLAlchemy, there's no such thing as
"the ORM generated a bad query" - you
retain full control over the structure of
queries, including how joins are organized,
how subqueries and correlation is used, what
columns are requested. Everything SQLAlchemy
does is ultimately the result of a developer-initiated
decision.
* Don't use an ORM if the problem doesn't need one.
SQLAlchemy consists of a Core and separate ORM
component. The Core offers a full SQL expression
language that allows Pythonic construction
of SQL constructs that render directly to SQL
strings for a target database, returning
result sets that are essentially enhanced DBAPI
cursors.
* Transactions should be the norm. With SQLAlchemy's
ORM, nothing goes to permanent storage until
commit() is called. SQLAlchemy encourages applications
to create a consistent means of delineating
the start and end of a series of operations.
* Never render a literal value in a SQL statement.
Bound parameters are used to the greatest degree
possible, allowing query optimizers to cache
query plans effectively and making SQL injection
attacks a non-issue.
Documentation
-------------
Latest documentation is at:
https://www.sqlalchemy.org/docs/
Installation / Requirements
---------------------------
Full documentation for installation is at
`Installation <https://www.sqlalchemy.org/docs/intro.html#installation>`_.
Getting Help / Development / Bug reporting
------------------------------------------
Please refer to the `SQLAlchemy Community Guide <https://www.sqlalchemy.org/support.html>`_.
Code of Conduct
---------------
Above all, SQLAlchemy places great emphasis on polite, thoughtful, and
constructive communication between users and developers.
Please see our current Code of Conduct at
`Code of Conduct <https://www.sqlalchemy.org/codeofconduct.html>`_.
License
-------
SQLAlchemy is distributed under the `MIT license
<https://www.opensource.org/licenses/mit-license.php>`_.

View File

@@ -1,524 +0,0 @@
SQLAlchemy-2.0.20.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
SQLAlchemy-2.0.20.dist-info/LICENSE,sha256=ZbcQGZNtpoLy8YjvH-nyoobTdOwtEgtXopPVzxy6pCo,1119
SQLAlchemy-2.0.20.dist-info/METADATA,sha256=nEFiWWNkJBol1Lb751ILyjQAvlSImwjdLuqoWTJ621A,9667
SQLAlchemy-2.0.20.dist-info/RECORD,,
SQLAlchemy-2.0.20.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
SQLAlchemy-2.0.20.dist-info/WHEEL,sha256=Wb1el1iP4ORW7FiLElw7HfxLpDiHzwvd2B382b2Idl0,102
SQLAlchemy-2.0.20.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
sqlalchemy/__init__.py,sha256=mnL2g2e81Pw9K-IxeLNmqYYpQ6ZMc4_0dOUVB3lpAyk,12993
sqlalchemy/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/__pycache__/events.cpython-310.pyc,,
sqlalchemy/__pycache__/exc.cpython-310.pyc,,
sqlalchemy/__pycache__/inspection.cpython-310.pyc,,
sqlalchemy/__pycache__/log.cpython-310.pyc,,
sqlalchemy/__pycache__/schema.cpython-310.pyc,,
sqlalchemy/__pycache__/types.cpython-310.pyc,,
sqlalchemy/connectors/__init__.py,sha256=sjPX1Mb2nWgRHLZY0mF350mGiqpi2CYBs2A1b8dh_wE,494
sqlalchemy/connectors/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/connectors/__pycache__/pyodbc.cpython-310.pyc,,
sqlalchemy/connectors/pyodbc.py,sha256=GFZ_OqBAwQpEprwEmVVCm0imUEqw36PGv1YFOsH2Lag,8730
sqlalchemy/cyextension/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
sqlalchemy/cyextension/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/cyextension/collections.cp310-win_amd64.pyd,sha256=7-Q6dQbt5KmaO6gtS17XhtPGx0yNiDFixx2G_Ovh9_g,173568
sqlalchemy/cyextension/collections.pyx,sha256=UY81HxvMAD4MOFR52SjzUbLHCGGcHZDSfK6gw6AYB8A,12726
sqlalchemy/cyextension/immutabledict.cp310-win_amd64.pyd,sha256=BLlAQZ1KNSPut7J14edvsMXDkbwSaGuNGei24P5ZfNM,72192
sqlalchemy/cyextension/immutabledict.pxd,sha256=JsNJYZIekkbtQQ2Tz6Bn1bO1g07yXztY9bb3rvH1e0Y,43
sqlalchemy/cyextension/immutabledict.pyx,sha256=VmhtF8aDXjEVVdA80LRY1iP85lNMwcz7vB6hZkAOGB0,3412
sqlalchemy/cyextension/processors.cp310-win_amd64.pyd,sha256=8IJwIPDE7K7JKHp5FshSv4X-w9fv4HMO9o8nPTJqrK0,58880
sqlalchemy/cyextension/processors.pyx,sha256=ZXuoi-hPRI9pVSbp6QbfJwy6S5kVCUZ8qj_h5-NvAFA,1607
sqlalchemy/cyextension/resultproxy.cp310-win_amd64.pyd,sha256=3HcHkSMTU4dksuzq0v2MD7Q_BJ-c9KidQIlTwyBO28E,60416
sqlalchemy/cyextension/resultproxy.pyx,sha256=qlk8eBpFo3UYbwQChdIWa3RqWXczuUL8ahulcLCL1bI,2573
sqlalchemy/cyextension/util.cp310-win_amd64.pyd,sha256=5PWlHJDSaHyempijzTE4GTrYKAGOqsQOZzZjoCz1fSg,72704
sqlalchemy/cyextension/util.pyx,sha256=H2FEg9uAAWO9UcNFyrfVuPhOznTq3h9UdjfmJ2BRD1Y,2374
sqlalchemy/dialects/__init__.py,sha256=vUDqtIsKolzjds0KK763SAnVCCF1SGQ64zY4WNIxbwM,1847
sqlalchemy/dialects/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/dialects/__pycache__/_typing.cpython-310.pyc,,
sqlalchemy/dialects/_typing.py,sha256=hA9jNttJjmVWDHKybSMSFWIlmRv2r5kuQPp8IeViifY,667
sqlalchemy/dialects/mssql/__init__.py,sha256=dvJCLhXDMkDvtAXT_26kBllhR7-geM9nrZOUxr2IG6Q,1928
sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/dialects/mssql/__pycache__/base.cpython-310.pyc,,
sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-310.pyc,,
sqlalchemy/dialects/mssql/__pycache__/json.cpython-310.pyc,,
sqlalchemy/dialects/mssql/__pycache__/provision.cpython-310.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-310.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-310.pyc,,
sqlalchemy/dialects/mssql/base.py,sha256=D8kqfvgGhZX98rfPlpluJmJBO72wx9QM3oirzj2CqO8,137310
sqlalchemy/dialects/mssql/information_schema.py,sha256=ufbEeGAFsciot3MmKPmFFhW95r57F_ORHULBS_UuUSw,8320
sqlalchemy/dialects/mssql/json.py,sha256=VOrBSxJWh7Fj-zIBA5aYZwx37DJq1OrWpJqc0xtWPhQ,4700
sqlalchemy/dialects/mssql/provision.py,sha256=hlKU-pYiCAMlLtEOjEhPr7ESgvQ4iLCAywtDXNQqZus,5142
sqlalchemy/dialects/mssql/pymssql.py,sha256=yA5NnGBs0YSzzjnGlqMrtHKdo4XHyJ6zKcySOvAw2ZA,4154
sqlalchemy/dialects/mssql/pyodbc.py,sha256=y4yayQokx8NWPvmod_JVI8BshoNpohfhclLxQlEhTIE,27444
sqlalchemy/dialects/mysql/__init__.py,sha256=060B9NtuQqUb8vNXDm8bdOGUUi6SUi_757-19DDhOQM,2245
sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/base.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/dml.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/expression.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/json.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/provision.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-310.pyc,,
sqlalchemy/dialects/mysql/__pycache__/types.cpython-310.pyc,,
sqlalchemy/dialects/mysql/aiomysql.py,sha256=xXvFyEjL613scXbU490iwK4fTegDGDojMUO565qo0iE,9831
sqlalchemy/dialects/mysql/asyncmy.py,sha256=Ilpd6rTcXFr40iepVerDuZmni8db4tGLiK2j0B9pnII,9966
sqlalchemy/dialects/mysql/base.py,sha256=JSYsIrsTQABPCE78llB7bYJyecJsAUlHkLBkPq4gUKw,122488
sqlalchemy/dialects/mysql/cymysql.py,sha256=RdwzBclxwN3uXWTT34YySR0rQfTjVzZDSadhzlOqhag,2375
sqlalchemy/dialects/mysql/dml.py,sha256=dDUvRalIG5l6_Iawkj0n-6p0NRfYfdP1wRl121qOYek,7855
sqlalchemy/dialects/mysql/enumerated.py,sha256=whCwVR5DmKh455d4EVg2XHItfvLtuzxA5bWOWzK6Cnw,8683
sqlalchemy/dialects/mysql/expression.py,sha256=-RmnmFCjWn16L_Nn82541wr6I3bUvvMk_6L7WhmeAK4,4206
sqlalchemy/dialects/mysql/json.py,sha256=hZr1MD4W6BaItKT5LRStDRQbr7wcer4YdWbkh47-RTA,2341
sqlalchemy/dialects/mysql/mariadb.py,sha256=SdFqsWMjIFwoC0bhlqksN4ju0Mnq3-iUDCKq7_idWk0,876
sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=yajW-43yKl94IxjCCFTzJ1Amr5RRDq0wkvBm_9IGBXU,7703
sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=8a2BZ_ChVR5HvTzonq0DtYoInD3iV4ihbA_9EbgtUxY,5845
sqlalchemy/dialects/mysql/mysqldb.py,sha256=4ZLfGACIMD5icglEqx6jTj3W4adT_ANCV1xo5kvAHYw,9962
sqlalchemy/dialects/mysql/provision.py,sha256=jdtfrsATv7hoMcepkMxHVG5QV2YkA92bg01CnOR9VMs,3327
sqlalchemy/dialects/mysql/pymysql.py,sha256=f09IxnUy9HFCEnNrW-RqxvByVUREYWRpbHsa1yIPDGk,3045
sqlalchemy/dialects/mysql/pyodbc.py,sha256=dfz0mekJDsOIvjs5utBRNltUr9YyhYuUH1rsUPb4NjI,4426
sqlalchemy/dialects/mysql/reflection.py,sha256=4-lXatfmSjcmv7YocofW3WUvLeQV8f0qrn73H40evpg,23198
sqlalchemy/dialects/mysql/reserved_words.py,sha256=KOR71_hBCzivGutG54Yq_K5t7dT6lgRBey0TcztWI3I,9712
sqlalchemy/dialects/mysql/types.py,sha256=vOi0kn2OLaWTjPKTNz5xPcS-jiHRLv_l1no_oA_jdYQ,25031
sqlalchemy/dialects/oracle/__init__.py,sha256=IghimaBtnKRrtkYdO5eFjJ5OUUGPJpYSZZJX0DIJb38,1368
sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/dialects/oracle/__pycache__/base.cpython-310.pyc,,
sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-310.pyc,,
sqlalchemy/dialects/oracle/__pycache__/dictionary.cpython-310.pyc,,
sqlalchemy/dialects/oracle/__pycache__/oracledb.cpython-310.pyc,,
sqlalchemy/dialects/oracle/__pycache__/provision.cpython-310.pyc,,
sqlalchemy/dialects/oracle/__pycache__/types.cpython-310.pyc,,
sqlalchemy/dialects/oracle/base.py,sha256=Ouc583EpWEzQ25yOTeaNCXNgvwM78PDSxoP6ETT82Zw,121073
sqlalchemy/dialects/oracle/cx_oracle.py,sha256=8HYHDRepG0X2sm2F_8VObomiaoRQzf8lnhlKNktruu8,56585
sqlalchemy/dialects/oracle/dictionary.py,sha256=yRmt5b218G1Q5pZR5kF1ocsusT4XAgl3v_t9WhKqlko,19993
sqlalchemy/dialects/oracle/oracledb.py,sha256=ZweQdl0ZKR3jaXowa14JBlWg1KXcabm_FWhOR4vqPsk,3566
sqlalchemy/dialects/oracle/provision.py,sha256=i4Ja1rCJLs0jIcpzt0PfcANHHoDOSWEpxqBvWYAdOcs,8269
sqlalchemy/dialects/oracle/types.py,sha256=vF5neW-vxJcl9nLL9x74zn05gmU-_wkCTNmF0diqqaw,7738
sqlalchemy/dialects/postgresql/__init__.py,sha256=kx5Iwob5j2_gYXMVF2qM6qIH5DCXxw1UYXVjuCgpvys,3897
sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/_psycopg_common.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/array.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/base.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/json.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/named_types.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/operators.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pg_catalog.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/types.cpython-310.pyc,,
sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=_DTODxy9UrjvqDk20-ZjEUAYhrzRFiosaH4GHp4mnNM,5841
sqlalchemy/dialects/postgresql/array.py,sha256=3C-g6RRWgB4Th_MV4UWpbxXo8UT2-GDRxmXowyyml5A,14108
sqlalchemy/dialects/postgresql/asyncpg.py,sha256=ooMOPDZEF4AnOJW9Mqz4zBpkXCUzZQGJwpxQGYXgvyA,40591
sqlalchemy/dialects/postgresql/base.py,sha256=3Zi_VZU801kJBsgrEzJC-GHG_rmCIp-v95S87uzBp48,180277
sqlalchemy/dialects/postgresql/dml.py,sha256=jESBXHjBdT-xzHSVADNz-2ljH-4I-rjjGuhKzrkGJII,11513
sqlalchemy/dialects/postgresql/ext.py,sha256=MdYxZshShFutBq657bDNyq68kzczovktkJeuGq-WqmM,16749
sqlalchemy/dialects/postgresql/hstore.py,sha256=x9kAVfXLHYDQfqg6IHPUhr4dvFSsBPoMKoBhqyaF_Vo,11929
sqlalchemy/dialects/postgresql/json.py,sha256=vrSiTBejjt54B9JSOixbh-oR8fg2OGIJLW2gOsmb0gI,11528
sqlalchemy/dialects/postgresql/named_types.py,sha256=fsAflctGz5teUyM7h2s0Z2Na14Dtt4vEj0rzf2VtRwU,17588
sqlalchemy/dialects/postgresql/operators.py,sha256=oe7NQRjOkJM46ISEnNIGxV4qOm2ANrqn3PLKHqj6bmY,2928
sqlalchemy/dialects/postgresql/pg8000.py,sha256=F6P7YT5iXWq3sYDIyiP1Qxoq3PrE7zFaUnivwMdRJSQ,19284
sqlalchemy/dialects/postgresql/pg_catalog.py,sha256=8imUP46LsmtnVozUZa2qEtDcDHwjrYg4c2tSJsbCpfo,9169
sqlalchemy/dialects/postgresql/provision.py,sha256=_sD_42mkypvAwDxwT6xeCGnHD5EMRVubIN5eonZ8MsQ,5678
sqlalchemy/dialects/postgresql/psycopg.py,sha256=vcqBE7Nr9mfSRGUeg9rSz94kfWDiCco_xabLu16qQyM,22984
sqlalchemy/dialects/postgresql/psycopg2.py,sha256=z2oVzGAfKnEIvJqEiJjb1nrsb84NaMjcHw6cWPC1PyU,32479
sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=gITk63w4Gi4wXQxBW22a0VGVUg-oBMyfg_YgIHc5kww,1800
sqlalchemy/dialects/postgresql/ranges.py,sha256=qrrFd9KWkaR83B69QhNQadnkQJe1u5hUf3nnCzQnf94,31206
sqlalchemy/dialects/postgresql/types.py,sha256=HuXQ2XabYNmWRkClpHTXCaB8vbI6tzwQI7y1aNZKNEo,7240
sqlalchemy/dialects/sqlite/__init__.py,sha256=CHJgBNgr7eufrgF-0l27xohu4N317u1IOy7Hyyrxx0o,1230
sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-310.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/base.cpython-310.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-310.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/json.cpython-310.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-310.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-310.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-310.pyc,,
sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=PiO8heyB6GXeTC4lxsptX6eKQlKvyy0s9UGeBEAdxog,11258
sqlalchemy/dialects/sqlite/base.py,sha256=ukLGX5LR8G6GlbmzG7bGDp8QmQ5-SzUzwAN9hTBWJl8,98978
sqlalchemy/dialects/sqlite/dml.py,sha256=KRBENBnUuZrraGSMmg2ohgQgPPcgCMCmEoKkuhn6Yq8,8674
sqlalchemy/dialects/sqlite/json.py,sha256=IZR_pBgC9sWLtP5SXm-o5FR6SScGLi4DEMGbLJzWN8E,2619
sqlalchemy/dialects/sqlite/provision.py,sha256=2LNwUT3zftd3WeGBJ9UsVKtwXn38KEdDxwZaJ2WXNjM,5575
sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=F8y3R0dILJcmqUzHotYF4tLUppe3PSU_C7Xdqm4YV0o,5502
sqlalchemy/dialects/sqlite/pysqlite.py,sha256=fKhH8ZGMW5XK1F_H9mqz7ofmR4nrsGrGdAgNMPizAEI,28644
sqlalchemy/dialects/type_migration_guidelines.txt,sha256=gyh3JCauAIFi_9XEfqm3vYv_jb2Eqcz2HjpmC9ZEPMM,8384
sqlalchemy/engine/__init__.py,sha256=ZlB1LVIV2GjvwyvKm2W0qVYQf51g8AiQvTkHGb1C8A0,2880
sqlalchemy/engine/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/_py_processors.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/_py_row.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/_py_util.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/base.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/characteristics.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/create.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/cursor.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/default.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/events.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/interfaces.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/mock.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/processors.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/reflection.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/result.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/row.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/strategies.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/url.cpython-310.pyc,,
sqlalchemy/engine/__pycache__/util.cpython-310.pyc,,
sqlalchemy/engine/_py_processors.py,sha256=XuNIr2kzSay7mAWy14Aq1q2H3ZcineWY-ERfy0yvWpw,3880
sqlalchemy/engine/_py_row.py,sha256=WASkBfwldq9LApfe1ILn93amhSc0ReihKic2lLO0gxI,3671
sqlalchemy/engine/_py_util.py,sha256=HB-18ta-qhMrA1oNIDeobt4TtyqLUTzbfRlkN6elRb0,2313
sqlalchemy/engine/base.py,sha256=3jNhTGA_esLDFLZOIkYWOovsyu1_yfmV-HiqKI8DoaQ,125509
sqlalchemy/engine/characteristics.py,sha256=P7JlS02X1DKRSpgqpQPwt2sFsatm1L1hVxdvvw3u95s,2419
sqlalchemy/engine/create.py,sha256=B1K2S_kTzfXrlOcOaPxJlxmKJWo9pbCpyAnODlJOul8,33489
sqlalchemy/engine/cursor.py,sha256=mNuHRp9SUS-_a_99y1Gj6Bx5jWnFbPf5mn0pa9EpjgQ,76545
sqlalchemy/engine/default.py,sha256=C0ooBrV9Tv1rsJgR3shVB1416-iL1Lg8ZlZ_GV15RAs,86235
sqlalchemy/engine/events.py,sha256=1ujDzJrUoanwgk5nlgldti-YukWleLS3wqmt4NFW68c,38375
sqlalchemy/engine/interfaces.py,sha256=-h2-AdHja9NLAUU7vynDBLa7HxgN6SCRMvl1XmviuOg,116234
sqlalchemy/engine/mock.py,sha256=y6-Magp0YKkuS0SsSihT8eYxDrt7aMgpY44rbhbxEDw,4326
sqlalchemy/engine/processors.py,sha256=GvY0nS06PrGMwgwk4HHYX8QGvIUA0vEaNAmuov08BiY,2444
sqlalchemy/engine/reflection.py,sha256=Ob-mKkHcKHtmxwIdYV3gjPHxaO_HGSyjg_3baOgoqTc,77365
sqlalchemy/engine/result.py,sha256=I-XjIh-TUcoN4AWk3qBpIN9fa6Tmn22lJUw2CFm26k8,80278
sqlalchemy/engine/row.py,sha256=dM3rJY3ASx_PzFKzu7CUGErJA_aIQYx1DibLAWnzY8M,12360
sqlalchemy/engine/strategies.py,sha256=Ryy15JovfbIMsF2sM23z0HYJ_7lXRBQlzpLacbn0mLg,461
sqlalchemy/engine/url.py,sha256=Lxdv29vz0l-FuWuZS7Gn5uLOxR75OPW1wxQpE58OEg4,31607
sqlalchemy/engine/util.py,sha256=YSuXV8ngYMaQy7mouAFJiin-rrtD6Pm04KZiIfM-sTs,5849
sqlalchemy/event/__init__.py,sha256=2QcWKXnqGRkl0lroK7ei8jT2Qbt8SRn_jqlTuYXGLu0,1022
sqlalchemy/event/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/event/__pycache__/api.cpython-310.pyc,,
sqlalchemy/event/__pycache__/attr.cpython-310.pyc,,
sqlalchemy/event/__pycache__/base.cpython-310.pyc,,
sqlalchemy/event/__pycache__/legacy.cpython-310.pyc,,
sqlalchemy/event/__pycache__/registry.cpython-310.pyc,,
sqlalchemy/event/api.py,sha256=_TjSW28so8R84M_s7gV6oMlgpvZ0MuM0fNxWEMj4BGM,8452
sqlalchemy/event/attr.py,sha256=o-Vr3RAGxE2mCXIvlMAGWmdb4My6q-JPkNTQdCW3IpI,21079
sqlalchemy/event/base.py,sha256=7R8MtAATdP9EybWyvlQDsnoHg5Biauxf13INBlEuLmg,15491
sqlalchemy/event/legacy.py,sha256=e_NtSjva3NmKLhM8kaUwLk2D705gjym2lYwrgQS0r9I,8457
sqlalchemy/event/registry.py,sha256=s7MlVn2cOPndD9pm4F7_NFE4sIP5fTGhDdSaRQV4xAU,11247
sqlalchemy/events.py,sha256=T8_TlVzRzd0Af9AAKUPXPxROwxeux7KuNhHTG0Cxamg,553
sqlalchemy/exc.py,sha256=qAEWjEGvoPvEdzLalZfqWSCr7D1OUh1LikZPie0Ld3s,24844
sqlalchemy/ext/__init__.py,sha256=2ow4CHEH4B_6wyAWKh1wqEbAUXG5ia2z2zASTa0Oqdk,333
sqlalchemy/ext/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/associationproxy.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/automap.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/baked.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/compiler.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/horizontal_shard.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/hybrid.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/indexable.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/instrumentation.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/mutable.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/orderinglist.cpython-310.pyc,,
sqlalchemy/ext/__pycache__/serializer.cpython-310.pyc,,
sqlalchemy/ext/associationproxy.py,sha256=Bgp1WjYLRhr8zTBv_1eHEj6eKZlTEiinqXofuzreH1A,68020
sqlalchemy/ext/asyncio/__init__.py,sha256=Qh5SCnlKUSkm1Ko5mzlNZ3_BUuU-aFg0vnlkhEOJdOE,1279
sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/ext/asyncio/__pycache__/base.cpython-310.pyc,,
sqlalchemy/ext/asyncio/__pycache__/engine.cpython-310.pyc,,
sqlalchemy/ext/asyncio/__pycache__/exc.cpython-310.pyc,,
sqlalchemy/ext/asyncio/__pycache__/result.cpython-310.pyc,,
sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-310.pyc,,
sqlalchemy/ext/asyncio/__pycache__/session.cpython-310.pyc,,
sqlalchemy/ext/asyncio/base.py,sha256=BRHwuXUqxmJDYBTgaOjRzvwwzG1qn9b3JOs_6opUs-g,9300
sqlalchemy/ext/asyncio/engine.py,sha256=lWn-w4VrZPJNvtMXOJnwKjPev6RTqygsn0sb5NBqktU,49188
sqlalchemy/ext/asyncio/exc.py,sha256=AeGYi7BtwZGHv4ZWaJl7wzE4u8dRlzi_06V_ipNPdfU,660
sqlalchemy/ext/asyncio/result.py,sha256=DrGcMICQThnvVH3YabnERYWONrQ3-E1oM-oVrSFT66E,31546
sqlalchemy/ext/asyncio/scoping.py,sha256=yVbP-AqxpYASySb5SerYIYMXj97jlauonMIYWr1idGA,52440
sqlalchemy/ext/asyncio/session.py,sha256=mdrkOinsEgiwFvyfYwad49aG_0iQTrtPMY1_V9bfUqk,63005
sqlalchemy/ext/automap.py,sha256=VC9p8sDu_EgWfqZ6aGAfVNBuUbnq4O2MjhUfgpY7keA,63089
sqlalchemy/ext/baked.py,sha256=vHWGGYyceArr5v-nGxgDfwVgnvUjcuGOllAZ5zb_PXI,18392
sqlalchemy/ext/compiler.py,sha256=pno-btbT4t16LEHUkRyVX5K6ct-MsPfixO41jhUI6R4,20946
sqlalchemy/ext/declarative/__init__.py,sha256=4a8Wl2P_BqYVYmx-HsPtt_U-NvwpVsAKtfWUSNbA2uY,1883
sqlalchemy/ext/declarative/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/ext/declarative/__pycache__/extensions.cpython-310.pyc,,
sqlalchemy/ext/declarative/extensions.py,sha256=GcAzNVSWKg6XXFbOIG6U4vXf0e__QB_Y7XWC8d23WhY,20095
sqlalchemy/ext/horizontal_shard.py,sha256=I8-KZiCgbtSbGww_vOD6OPzMXXRNUg9X4n1fF8EZTRo,17249
sqlalchemy/ext/hybrid.py,sha256=u7oR4DuriIuA2vNvde31fmtgMsRkBL667L7pqdzwGaE,54039
sqlalchemy/ext/indexable.py,sha256=F3NC4VaUkhrx4jDmaEuJLQ2AXatk9l4l_aVI5Uzazbs,11369
sqlalchemy/ext/instrumentation.py,sha256=biLs17X8UIGzisx-jC6JOphtldi-mlfR2bfumnlar70,16175
sqlalchemy/ext/mutable.py,sha256=3s_qKPt6It7A-7gdQxjL5p7kFE72raf0lgjimK__lFk,38471
sqlalchemy/ext/mypy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
sqlalchemy/ext/mypy/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/ext/mypy/__pycache__/apply.cpython-310.pyc,,
sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-310.pyc,,
sqlalchemy/ext/mypy/__pycache__/infer.cpython-310.pyc,,
sqlalchemy/ext/mypy/__pycache__/names.cpython-310.pyc,,
sqlalchemy/ext/mypy/__pycache__/plugin.cpython-310.pyc,,
sqlalchemy/ext/mypy/__pycache__/util.cpython-310.pyc,,
sqlalchemy/ext/mypy/apply.py,sha256=O3Rh-FCWiWJPeUT0dVsFD_fcL5oEJbrAkB0bAJ5I7Sg,10821
sqlalchemy/ext/mypy/decl_class.py,sha256=hfTpozOGwxeX9Vbkm12i8SawR91ngs1nfsiPC_f0PSg,17892
sqlalchemy/ext/mypy/infer.py,sha256=BsiKdH1IvbgRGpqGzDPt1bkDXCcBjzEhQcyHdaToohs,19954
sqlalchemy/ext/mypy/names.py,sha256=syJhY2UYcodS_NjuNdg-7ay_ZW8kf6xXMbTpMjHrdLQ,11310
sqlalchemy/ext/mypy/plugin.py,sha256=XF1E_XqZJA-RnI_d0_FWvwFIBTyBRA95sFUP0uqUygk,10053
sqlalchemy/ext/mypy/util.py,sha256=daH6X26-zYEBR1dnX3K5MJ946HHtgcYSH848LGT_uko,9762
sqlalchemy/ext/orderinglist.py,sha256=xeonIRL-m5Y4vB2n_1Nab8B61geRLHR0kyC_KnXTS7k,14800
sqlalchemy/ext/serializer.py,sha256=BhyC7ydKcKKz4vlxyU_8ranVigiGSO1hw_LieCxLCgM,6363
sqlalchemy/future/__init__.py,sha256=Iio4lD-SrIcuBq0gP7MRgVnU4v36gIMLHiQrSKy-sPM,532
sqlalchemy/future/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/future/__pycache__/engine.cpython-310.pyc,,
sqlalchemy/future/engine.py,sha256=4iO5THuQWIy3UGpOOMml23daun4wHdRZ6JVSwWykjJI,514
sqlalchemy/inspection.py,sha256=tJc_KriMGJ6kSRkKn5MvhxooFbPEAq3W5l-ggFw2sdE,5326
sqlalchemy/log.py,sha256=_31kfcLRj9dtaQs-VRMHqjPq2sGVYUeSUyO-OiafdlQ,8918
sqlalchemy/orm/__init__.py,sha256=M1pqaRU6NQuDcycnrbkKHcKjNAo3ZbGne3MOFPK0n1o,8633
sqlalchemy/orm/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/_orm_constructors.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/_typing.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/attributes.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/base.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/bulk_persistence.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/clsregistry.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/collections.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/context.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/decl_api.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/decl_base.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/dependency.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/descriptor_props.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/dynamic.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/evaluator.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/events.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/exc.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/identity.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/instrumentation.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/interfaces.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/loading.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/mapped_collection.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/mapper.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/path_registry.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/persistence.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/properties.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/query.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/relationships.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/scoping.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/session.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/state.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/state_changes.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/strategies.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/strategy_options.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/sync.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/unitofwork.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/util.cpython-310.pyc,,
sqlalchemy/orm/__pycache__/writeonly.cpython-310.pyc,,
sqlalchemy/orm/_orm_constructors.py,sha256=5CbxrInx1FYY40QxUNytbWfHRh3X0WH7WeUYXeYOiwQ,102250
sqlalchemy/orm/_typing.py,sha256=BmW1g-19ze_sbqF9uKlAqrPyp4jxVkmv-jjJmFpYhdg,5192
sqlalchemy/orm/attributes.py,sha256=sjCxIVm8nVjQnPt_MDgI3-DKZz4WYOsMuLKbEeIDJLk,95458
sqlalchemy/orm/base.py,sha256=KP34QXa9bLKPOMKHQ2Jf9bpEhTqqrbRG7uMxh7_2TLI,28459
sqlalchemy/orm/bulk_persistence.py,sha256=_OqUjW9yi3l7VYj1564CYvc8p4CjEXsFYGDt6wR0M0I,72044
sqlalchemy/orm/clsregistry.py,sha256=dgGogYd-ED3EUIgQkdUesequKBiGTs7csAB6Z5ROjrU,18521
sqlalchemy/orm/collections.py,sha256=HhJOKqeaDdeYFVyFyU4blvoD6tRsXF4X7TG1xUyhbdk,53778
sqlalchemy/orm/context.py,sha256=dmqih2qp7EkiIjp6ATihNOBTtAduYdjv8kLp2vah5EY,114925
sqlalchemy/orm/decl_api.py,sha256=w2FWGSAgq2_T7r3g1DaaLLX9Y_AQw6UCwrTxU23EcVo,65649
sqlalchemy/orm/decl_base.py,sha256=gGasl-rchnQxz9YcQESMe0zn201d5sFhx39Yph1trWc,83425
sqlalchemy/orm/dependency.py,sha256=vXDcvgrY8_9x5lLWIpoLvzwuN0b-2-DwhXux1nNf0E8,48885
sqlalchemy/orm/descriptor_props.py,sha256=fwsr88pQytZLQ8r0u-nXjgk1evU9MYtxmvgHs42gl_I,38502
sqlalchemy/orm/dynamic.py,sha256=MWkalEIrgW0p_CRr5Uclt6Ak-jUvcpyYHaTWidIuXdc,8898
sqlalchemy/orm/evaluator.py,sha256=lQ_uAoUBKJtQAyvhyFfHOf8gvxk0_-r4KpqQR53-COE,12293
sqlalchemy/orm/events.py,sha256=UguRQ343Q-rGxKjPQTgqOs5uVUkW1rcLBWoOSZUHirI,130506
sqlalchemy/orm/exc.py,sha256=k9K4M3zZvE7877TiTOI5gG4MOgnBEbKqvAsP43JU2Dk,7583
sqlalchemy/orm/identity.py,sha256=mVaoHHtniM1-wSqJ0VPu2v6LaSJfer4_vLsxFVw8aXc,9551
sqlalchemy/orm/instrumentation.py,sha256=VIWeAsEMveE2N-9W6mS5Lw40wwldHL_fGhln0EowEsY,25207
sqlalchemy/orm/interfaces.py,sha256=smpV-eYelH0Eswfav_kxOFRB4VE9_Nm0BPUNDMSqYSI,49878
sqlalchemy/orm/loading.py,sha256=qqr6wMPgP5qChbiaz4s4ea-fRNiqyc8Fu9_wj_UjfhQ,58032
sqlalchemy/orm/mapped_collection.py,sha256=kxy8_wQPjbjEMxCEe-l5lgH9wza_JedWzcksXXjLK9E,20260
sqlalchemy/orm/mapper.py,sha256=NGoiIjmTIK28MyvDnxgWkWdYv2e7fSUroqvoj0dPpOg,175446
sqlalchemy/orm/path_registry.py,sha256=hvTSOojMC31-nUJFHz2DynIh4_x85QQzemn7RFTdddc,26436
sqlalchemy/orm/persistence.py,sha256=flpr9E1BjytqAeijNJu13DiYcVf7rYUXCQce9-o4WCg,62271
sqlalchemy/orm/properties.py,sha256=unDpqioAz2DGH-ftJrJ_V9G0QcqVeD-PzTvqbTY6bj4,27622
sqlalchemy/orm/query.py,sha256=fp5Oe5J7_RMHBcw74ScgOLmrfEm-zkZFDiFJRTJVfP0,121046
sqlalchemy/orm/relationships.py,sha256=1ZN1WTkSCEICtl0cRpUyIRfeF0VxvAjtjiRfQ_lMrPE,131317
sqlalchemy/orm/scoping.py,sha256=O1-yguXL3W9Vtq8_wtC2B9Nc_0LtPB50NeOe8H6FrhA,77441
sqlalchemy/orm/session.py,sha256=FivieQ2V4UjKP08zJij8RbiwHDqao9fbfqoO7XdIb6U,193191
sqlalchemy/orm/state.py,sha256=UrAw3nTX9wlFoJbzKL1DXvD_KGvgfcD8xMnqzM5qO5U,38719
sqlalchemy/orm/state_changes.py,sha256=9jHui0oF4V7xIoL8hSUiFDhHrJVPmq3doX5zxY5dR3g,7013
sqlalchemy/orm/strategies.py,sha256=dUNlfzmWDzRb1JOG_xFisMtlmDTxeQLn4GkmBsN9b3o,117432
sqlalchemy/orm/strategy_options.py,sha256=cBic_Tm6rwmOiHncwUC8eM3SWPcVJkHM5T4YFwSto4U,84304
sqlalchemy/orm/sync.py,sha256=jLZWFRsn2iobdX23gQlthxhnFHQum3mWQHHcx8uj0t4,5912
sqlalchemy/orm/unitofwork.py,sha256=Jyri2dH5VmkX7oN-XWRzzKMCjQAzvBPWWem5XQL5SYY,27829
sqlalchemy/orm/util.py,sha256=4Mk2QOA-XjVybwEbdExpQ-9w9PKCPffYh1blvNl4CWU,82375
sqlalchemy/orm/writeonly.py,sha256=AKgGFWiOSAvYoqvAaKA9wiOJkZ6pvCR3AM-K1I51crY,20154
sqlalchemy/pool/__init__.py,sha256=rvWJtziqz1Yp_9NU7r-cKH1WKi8MwcjZX6kuBYu_s6s,1859
sqlalchemy/pool/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/pool/__pycache__/base.cpython-310.pyc,,
sqlalchemy/pool/__pycache__/events.cpython-310.pyc,,
sqlalchemy/pool/__pycache__/impl.cpython-310.pyc,,
sqlalchemy/pool/base.py,sha256=3-o9EcxLi_D6lrd5SDR6dhBv9oiMf-JX5SD-BekG7FE,53869
sqlalchemy/pool/events.py,sha256=dQDxP7Rwz3nDv2xkxQvkq3Eyj-00sBYIoF02lrKUHLM,13571
sqlalchemy/pool/impl.py,sha256=FpgcyklqBciEorQq5M0XxsbZG6HD-tqrXuqxA3wEkO8,18292
sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
sqlalchemy/schema.py,sha256=Liwt9G2PyOZZGfQkTGx7fFjTNH2o8t9kPcCAIHF9etw,3264
sqlalchemy/sql/__init__.py,sha256=9ffSt_Gl2TJYE4_PyWxtRNfBF8yGmIV2J_CaoAyx2QQ,5965
sqlalchemy/sql/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/_dml_constructors.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/_elements_constructors.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/_orm_types.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/_py_util.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/_typing.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/annotation.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/base.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/cache_key.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/coercions.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/compiler.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/crud.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/ddl.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/default_comparator.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/dml.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/elements.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/events.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/expression.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/functions.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/lambdas.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/naming.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/operators.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/roles.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/schema.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/selectable.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/sqltypes.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/traversals.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/type_api.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/util.cpython-310.pyc,,
sqlalchemy/sql/__pycache__/visitors.cpython-310.pyc,,
sqlalchemy/sql/_dml_constructors.py,sha256=CRI_cxOwcSBPUhouMuNoxBVb3EB0b6zQo6YSjI1d2Vo,4007
sqlalchemy/sql/_elements_constructors.py,sha256=gSshp_t_TteDk4mvekbPBDABWDW-umShMPmjCrUrLBs,64764
sqlalchemy/sql/_orm_types.py,sha256=WIdXTDALHCd3PuzpAPot2psv505T817wnQ3oXuGH7CU,640
sqlalchemy/sql/_py_util.py,sha256=YCkagVa5Ov1OjCfMhUbNa527zX2NsUGPuCLEWmbQQMA,2248
sqlalchemy/sql/_selectable_constructors.py,sha256=kHsJViBwydt3l-aiqtI2yLK4BgjEsVB9lpKPuWSWv60,19454
sqlalchemy/sql/_typing.py,sha256=WSEwGys86fVA_WAY6Nbu1Qn3Zam25Togi-OxM7Nlu2E,12724
sqlalchemy/sql/annotation.py,sha256=2c7wyGxH2gk9TIZELK3x8OSbQBKE4MGAFEWv3hefIbI,18881
sqlalchemy/sql/base.py,sha256=d7sIYp0XUH6FPqbehP5VC_8o31PuamwQgBILiotwE3o,76137
sqlalchemy/sql/cache_key.py,sha256=AaPpvFDwYMfGixEmEto02flVKyf4UifGdNbu2Z4rfMc,33773
sqlalchemy/sql/coercions.py,sha256=YXSGtgtUIJeDPPjtBpFA2PuZ0bP-x8E6N01OIDWJvxw,41959
sqlalchemy/sql/compiler.py,sha256=G1Vv0zq1RSjJe0fKE0ih8UXUMCKIljBHqbw-BSoBLqg,276204
sqlalchemy/sql/crud.py,sha256=rJ9U55a0oVH5cl4Su5mwCpS6UogZsz1ue6bNvEShrMc,57295
sqlalchemy/sql/ddl.py,sha256=J9wtcvAfh2E08Fg-bjr_TWQGmYWWn4vSr5VFJPxThk4,47069
sqlalchemy/sql/default_comparator.py,sha256=-sSDLcxHfL4jgIh5f_g2-8wOGazDHhTPyB0pA9Gr_Uk,17212
sqlalchemy/sql/dml.py,sha256=vzOpq8TqhdYN7jfla27c4pQITdO0IPcKdV5y92vjONM,67380
sqlalchemy/sql/elements.py,sha256=90qmYcYRdwEI_E4dREe98AXIUO7pAoyZ3D4pSq5KEko,176494
sqlalchemy/sql/events.py,sha256=ELMw8mkKUvVVeb354hfD_9Fk-KSX28hCfNdnMgnv2zI,18756
sqlalchemy/sql/expression.py,sha256=bD-nG9OxLmhgvKkCKFpMtycxq8szzUsxhDeH3E9WmkQ,7748
sqlalchemy/sql/functions.py,sha256=YMkPdiSBZnrA4ZrcolA58w1nsoEQbwrL38kMKwiLZP0,56289
sqlalchemy/sql/lambdas.py,sha256=lKggh8EyKJThwop8ulclAcMzJh4gydSTwEL8cyMCN10,50759
sqlalchemy/sql/naming.py,sha256=3J_KScJBe7dPY4Stj5veIzGfSnaWG9-f7IGbxhc7luE,7077
sqlalchemy/sql/operators.py,sha256=nqVC7ggPHfwF4QFMZz7uk0WvPUdxw7s92jjTGkgLJzs,78483
sqlalchemy/sql/roles.py,sha256=Rkbx36tMWkq6uu6bl1DMDkLQY8VFpKZtqfmYxooE8MQ,7952
sqlalchemy/sql/schema.py,sha256=5oaelPn8oUFB9b7XYJ4OjNqPhWQXvKie3UoReB0KfgY,233840
sqlalchemy/sql/selectable.py,sha256=QCeqrL6dU1-ZncSO3z0G5KksghMZyanAm6Egtj_5Xak,239710
sqlalchemy/sql/sqltypes.py,sha256=HsFU48oRsD-bLDSHim6LVcyZUpkPxK2eB-MUllRBZyg,129894
sqlalchemy/sql/traversals.py,sha256=3s9dNBNwsrpg57x0vxF5J-bdeZpzURGEbfpUhaCt1wU,34622
sqlalchemy/sql/type_api.py,sha256=QcUD2j49soLl07LQVHdDilZ1kUkRC_USwJx8xI6Qcyg,87359
sqlalchemy/sql/util.py,sha256=5UCudam-vT7lMoYKv2juS483vjdiY2kcFejfjk17KlI,49770
sqlalchemy/sql/visitors.py,sha256=1jPX8S1C6pFovW0HWkliVsI0BD0tmEUN8GYZ0F5Di6o,37534
sqlalchemy/testing/__init__.py,sha256=I_4C9vgF-GRODJ_IRNxIXXSQHgUDNVggGFFv6v0BJBQ,3221
sqlalchemy/testing/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/assertions.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/assertsql.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/asyncio.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/config.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/engines.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/entities.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/exclusions.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/pickleable.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/profiling.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/provision.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/requirements.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/schema.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/util.cpython-310.pyc,,
sqlalchemy/testing/__pycache__/warnings.cpython-310.pyc,,
sqlalchemy/testing/assertions.py,sha256=K_wIe570kI6xbtZmWMnLiuhBaCFyoQx-iaau9CR1PLI,32428
sqlalchemy/testing/assertsql.py,sha256=F8R_LgsUiZ31sgkCigFbxFcVOzt57EI1tRSHgk88CjQ,17290
sqlalchemy/testing/asyncio.py,sha256=7LatmcAk09IVuOMWcAdpVltLyhDWVG1cV7iOaR6soz0,3858
sqlalchemy/testing/config.py,sha256=L5Z7Inl6th23xpuVDo6Fhn3ItMca0yBT6YofkYRZiKI,11397
sqlalchemy/testing/engines.py,sha256=mTwCxPdb6K8S2y-O7dcJwO5kEE4qIAU8Kp6qawiP2TM,13824
sqlalchemy/testing/entities.py,sha256=E7IkhsQaziZSOZkKkFnfUvB0165SH5MP1q4QkGKdf98,3471
sqlalchemy/testing/exclusions.py,sha256=rWyo1SZpZ-EjNkvr-O085A3XSAqxSL5uqgOE4L5mzM0,12879
sqlalchemy/testing/fixtures/__init__.py,sha256=SHlEUIlUaqU2xjziZeBDL1Yd_URWou6dnZqG8s-Ay0g,1226
sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/testing/fixtures/__pycache__/base.cpython-310.pyc,,
sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-310.pyc,,
sqlalchemy/testing/fixtures/__pycache__/orm.cpython-310.pyc,,
sqlalchemy/testing/fixtures/__pycache__/sql.cpython-310.pyc,,
sqlalchemy/testing/fixtures/base.py,sha256=HQPgUuOnBVNUm9l7dcoT4AuggG0jXW4a23dVZhbSA9k,12622
sqlalchemy/testing/fixtures/mypy.py,sha256=TGTo8x02m9Qt10-iRjic_F66-uA2T82QbKYmpUyVzCw,12153
sqlalchemy/testing/fixtures/orm.py,sha256=VZBnFHHfQpVdLqV-F9myH886uq3clDIzixMwEe5TjMc,6322
sqlalchemy/testing/fixtures/sql.py,sha256=1c2omUuUSRlWNYtKr3ADJFaQeFnq1EnQtKTKn67B-2Q,16196
sqlalchemy/testing/pickleable.py,sha256=_E141_vPqtE-H_6cNBnWRDuMtvjeeBKovOI2_P9KJGQ,2988
sqlalchemy/testing/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
sqlalchemy/testing/plugin/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-310.pyc,,
sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-310.pyc,,
sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-310.pyc,,
sqlalchemy/testing/plugin/bootstrap.py,sha256=3WkvZXQad0oyxg6nJyLEthN30zBuueB33LFxWeMBdKc,1482
sqlalchemy/testing/plugin/plugin_base.py,sha256=300KMijfgPiKFbjj1F_6Dj6HAH8T-EuHuX3CQ6Z1m00,22110
sqlalchemy/testing/plugin/pytestplugin.py,sha256=IYmAr-FaAODCInWpnKJCNGw03cyRJK5YcZ07hrmtSNg,28151
sqlalchemy/testing/profiling.py,sha256=BLknvjemW8oDl0aCSUXQw_ESgoFfx0RoeQYclzhNv0Q,10472
sqlalchemy/testing/provision.py,sha256=fnmlxUacztdUcZOPTiSJ-rYe-tpIwyl8O97YRI24FLk,14686
sqlalchemy/testing/requirements.py,sha256=qc5zgxUJGmpZjKO4pVSDlKOYs_f8_W7KienkLgoJJZ4,52702
sqlalchemy/testing/schema.py,sha256=O76C-woOcc6qjk1csf9yljor-6wjRZzKQNrXdmLLtw8,6737
sqlalchemy/testing/suite/__init__.py,sha256=u3lEc0j47s7Dad_2SVWOZ6EU2aOMRWqE_WrQ17HmBsA,489
sqlalchemy/testing/suite/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_cte.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_insert.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_results.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_select.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_types.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-310.pyc,,
sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-310.pyc,,
sqlalchemy/testing/suite/test_cte.py,sha256=RFvWCSmmy3-JEIXIcZEkYpjt_bJQTe7SvfnpXeUFl9o,6410
sqlalchemy/testing/suite/test_ddl.py,sha256=VQuejaNUMN484DxwkYL57ZEnPY5UhNGWkQVgquPrWHA,12168
sqlalchemy/testing/suite/test_deprecations.py,sha256=8mhjZyrECXiVyE88BJLbyj4Jz_niXOs6ZOXd9rFAWsk,5229
sqlalchemy/testing/suite/test_dialect.py,sha256=C3aUUd16iS19WJrqZLGkG4DPhWMAADTCfTDkYYlUz5U,23407
sqlalchemy/testing/suite/test_insert.py,sha256=NVPw5lMxNSY99E7p_YZ2bBJG2C4SBCGv2nPyQtrxQnk,17948
sqlalchemy/testing/suite/test_reflection.py,sha256=si5nXSHcBWJRrcFC-0sto1m9jXf6hYwlYuQ43YqXiJo,107540
sqlalchemy/testing/suite/test_results.py,sha256=fl8qv9YdcEqSQFe1fDf7vrFk7Cam_MzJiETLzx5KYuU,16127
sqlalchemy/testing/suite/test_rowcount.py,sha256=ID2Y1jDZ1MjQyZldWQPt40qD2eu0haTaYB2AdWQ1Nnk,6356
sqlalchemy/testing/suite/test_select.py,sha256=3SCpfuy-D0q0l4elRNgMc0KlIjo3Nh1nGHo0uEZKAUo,60207
sqlalchemy/testing/suite/test_sequence.py,sha256=WqriMWJTPnmCBydP5vehSD2zXxwSyFGug_K2IyEIRSY,9983
sqlalchemy/testing/suite/test_types.py,sha256=NObEx25I9LQdMaW470ACm4w9fWRVYf9B0i1VafrOhGQ,63791
sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=1n0xf7EyGuLYIMiwc5lANGWvCrmoXf3gtQM93sMcd3c,6070
sqlalchemy/testing/suite/test_update_delete.py,sha256=cj9C7U8MFxMSfdckAdODQHsnqwOtU112zqk5U8iyCTM,1710
sqlalchemy/testing/util.py,sha256=QH355pEJeqUn4h2LPTleNGinD50hE40R1HRW2LEXF6U,14599
sqlalchemy/testing/warnings.py,sha256=ymXClxi_YtysQJZZQzgjT-d3tW63z4pOfKJsTqaBLMQ,1598
sqlalchemy/types.py,sha256=bV5WvXIjsG-bWRcwCVACJ6m3tlSMS5XNdtXVXGTMeI8,3244
sqlalchemy/util/__init__.py,sha256=NILyXDswLeG8lpxsPh2iIOs4BweaFz7tbn3wQ0-hK5c,8404
sqlalchemy/util/__pycache__/__init__.cpython-310.pyc,,
sqlalchemy/util/__pycache__/_collections.cpython-310.pyc,,
sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-310.pyc,,
sqlalchemy/util/__pycache__/_has_cy.cpython-310.pyc,,
sqlalchemy/util/__pycache__/_py_collections.cpython-310.pyc,,
sqlalchemy/util/__pycache__/compat.cpython-310.pyc,,
sqlalchemy/util/__pycache__/concurrency.cpython-310.pyc,,
sqlalchemy/util/__pycache__/deprecations.cpython-310.pyc,,
sqlalchemy/util/__pycache__/langhelpers.cpython-310.pyc,,
sqlalchemy/util/__pycache__/preloaded.cpython-310.pyc,,
sqlalchemy/util/__pycache__/queue.cpython-310.pyc,,
sqlalchemy/util/__pycache__/tool_support.cpython-310.pyc,,
sqlalchemy/util/__pycache__/topological.cpython-310.pyc,,
sqlalchemy/util/__pycache__/typing.cpython-310.pyc,,
sqlalchemy/util/_collections.py,sha256=yUsrZu5aQh0lK_ht3540KXsoaM-lz4nqK1gHXx29lBc,21124
sqlalchemy/util/_concurrency_py3k.py,sha256=LhS5ppk9UGJ2P36rbHaauV3hl57p1Zw_b3SzLkkOiTE,8544
sqlalchemy/util/_has_cy.py,sha256=7V8ZfMrlED0bIc6DWsBC_lTEP1JEihWKPdjyLJtxfaE,1268
sqlalchemy/util/_py_collections.py,sha256=lwf6V7hnvqP_88eVKZa6GqshDxyBkhPczaIhpfrXE-Y,17217
sqlalchemy/util/compat.py,sha256=xwPQl5QoV9Nrje-ZD_csX0penWXrhFi667v5WMdloB8,9416
sqlalchemy/util/concurrency.py,sha256=rLb4LbPSTnGaSb381_e3VwHbEjqiF10lkarFjihywTY,2353
sqlalchemy/util/deprecations.py,sha256=JMG1QpXb_HhCssZBkm0_vvCSWiCMIbAwIlULtP4DNgs,12514
sqlalchemy/util/langhelpers.py,sha256=iJO7hWDKfeAndgu6iDcWRx3tMmbNquzSnxDruMK6130,67147
sqlalchemy/util/preloaded.py,sha256=tMuj_6GELLQj1I8YRAKu--VLnxI9kH8Si_IwlDfre4M,6055
sqlalchemy/util/queue.py,sha256=-DPCfkQgtqP9znBvm3bRdYjAWs4dysflpL805IMf22A,10529
sqlalchemy/util/tool_support.py,sha256=SOfhWXzZXqx5RYX9WM_CeBJGGgV0eaxpv96VFb5KEKs,6167
sqlalchemy/util/topological.py,sha256=-i2pX8AD9hjaqpLDu5P3R5w6D8_t5nDWlwLI6xXYyyc,3578
sqlalchemy/util/typing.py,sha256=yFUquXzDGUuoyn4idoBkIsqjF_2noHM34DDInh2uERs,16207

View File

@@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.41.1)
Root-Is-Purelib: false
Tag: cp310-cp310-win_amd64

View File

@@ -1,28 +0,0 @@
Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,104 +0,0 @@
Metadata-Version: 2.1
Name: Flask-SQLAlchemy
Version: 3.0.5
Summary: Add SQLAlchemy support to your Flask application.
Maintainer-email: Pallets <contact@palletsprojects.com>
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Dist: flask>=2.2.5
Requires-Dist: sqlalchemy>=1.4.18
Project-URL: Changes, https://flask-sqlalchemy.palletsprojects.com/changes/
Project-URL: Chat, https://discord.gg/pallets
Project-URL: Documentation, https://flask-sqlalchemy.palletsprojects.com
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Issue Tracker, https://github.com/pallets-eco/flask-sqlalchemy/issues/
Project-URL: Source Code, https://github.com/pallets-eco/flask-sqlalchemy/
Flask-SQLAlchemy
================
Flask-SQLAlchemy is an extension for `Flask`_ that adds support for
`SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy
with Flask by providing useful defaults and extra helpers that make it
easier to accomplish common tasks.
.. _Flask: https://palletsprojects.com/p/flask/
.. _SQLAlchemy: https://www.sqlalchemy.org
Installing
----------
Install and update using `pip`_:
.. code-block:: text
$ pip install -U Flask-SQLAlchemy
.. _pip: https://pip.pypa.io/en/stable/getting-started/
A Simple Example
----------------
.. code-block:: python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite"
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, unique=True, nullable=False)
with app.app_context():
db.create_all()
db.session.add(User(username="example"))
db.session.commit()
users = db.session.execute(db.select(User)).scalars()
Contributing
------------
For guidance on setting up a development environment and how to make a
contribution to Flask-SQLAlchemy, see the `contributing guidelines`_.
.. _contributing guidelines: https://github.com/pallets-eco/flask-sqlalchemy/blob/main/CONTRIBUTING.rst
Donate
------
The Pallets organization develops and supports Flask-SQLAlchemy and
other popular packages. In order to grow the community of contributors
and users, and allow the maintainers to devote more time to the
projects, `please donate today`_.
.. _please donate today: https://palletsprojects.com/donate
Links
-----
- Documentation: https://flask-sqlalchemy.palletsprojects.com/
- Changes: https://flask-sqlalchemy.palletsprojects.com/changes/
- PyPI Releases: https://pypi.org/project/Flask-SQLAlchemy/
- Source Code: https://github.com/pallets-eco/flask-sqlalchemy/
- Issue Tracker: https://github.com/pallets-eco/flask-sqlalchemy/issues/
- Website: https://palletsprojects.com/
- Twitter: https://twitter.com/PalletsTeam
- Chat: https://discord.gg/pallets

View File

@@ -1,27 +0,0 @@
flask_sqlalchemy-3.0.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
flask_sqlalchemy-3.0.5.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475
flask_sqlalchemy-3.0.5.dist-info/METADATA,sha256=TP2sMyz9YuKRrcMbnrPjSC0A3NWysepDoNhKaPc_efI,3264
flask_sqlalchemy-3.0.5.dist-info/RECORD,,
flask_sqlalchemy-3.0.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
flask_sqlalchemy-3.0.5.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
flask_sqlalchemy/__init__.py,sha256=7toyeeZoZtSxWWqUTZGMiwIgyzTPhyDaCSMGSkUd-Vs,1250
flask_sqlalchemy/__pycache__/__init__.cpython-310.pyc,,
flask_sqlalchemy/__pycache__/cli.cpython-310.pyc,,
flask_sqlalchemy/__pycache__/extension.cpython-310.pyc,,
flask_sqlalchemy/__pycache__/model.cpython-310.pyc,,
flask_sqlalchemy/__pycache__/pagination.cpython-310.pyc,,
flask_sqlalchemy/__pycache__/query.cpython-310.pyc,,
flask_sqlalchemy/__pycache__/record_queries.cpython-310.pyc,,
flask_sqlalchemy/__pycache__/session.cpython-310.pyc,,
flask_sqlalchemy/__pycache__/table.cpython-310.pyc,,
flask_sqlalchemy/__pycache__/track_modifications.cpython-310.pyc,,
flask_sqlalchemy/cli.py,sha256=pg3QDxP36GW2qnwe_CpPtkRhPchyVSGM6zlBNWuNCFE,484
flask_sqlalchemy/extension.py,sha256=K_PMP0TlxSptgJqsK7Owq2DB20R0KkjgVolbZUbzwhA,38293
flask_sqlalchemy/model.py,sha256=LvR7sWR_9I5EYkKFs9U7r00W57hzg7wbXF-CXrYA9tw,7112
flask_sqlalchemy/pagination.py,sha256=JFpllrqkRkwacb8DAmQWaz9wsvQa0dypfSkhUDSC2ws,11119
flask_sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
flask_sqlalchemy/query.py,sha256=Uls9qbmnpb9Vba43EDfsRP17eHJ0X4VG7SE22tH5R3g,3748
flask_sqlalchemy/record_queries.py,sha256=DhEhgKRdTzT63Ti399BG_tzy9tLlyx9efA00FgPLFxc,4306
flask_sqlalchemy/session.py,sha256=e3oOf2gClNJrXJ5o5kHsTjTO5MQsah_uNFMtQHi_LjI,3189
flask_sqlalchemy/table.py,sha256=wAPOy8qwyAxpMwOIUJY4iMOultzz2W0D6xvBkQ7U2CE,859
flask_sqlalchemy/track_modifications.py,sha256=yieyozj7IiVzwnAGZ-ZrgqrzjrUfG0kPrXBfW_hStSU,2755

View File

@@ -1,4 +0,0 @@
Wheel-Version: 1.0
Generator: flit 3.9.0
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -4,43 +4,23 @@ import typing as t
from .extension import SQLAlchemy
__version__ = "3.0.5"
__all__ = [
"SQLAlchemy",
]
_deprecated_map = {
"Model": ".model.Model",
"DefaultMeta": ".model.DefaultMeta",
"Pagination": ".pagination.Pagination",
"BaseQuery": ".query.Query",
"get_debug_queries": ".record_queries.get_recorded_queries",
"SignallingSession": ".session.Session",
"before_models_committed": ".track_modifications.before_models_committed",
"models_committed": ".track_modifications.models_committed",
}
def __getattr__(name: str) -> t.Any:
import importlib
import warnings
if name in _deprecated_map:
path = _deprecated_map[name]
import_path, _, new_name = path.rpartition(".")
action = "moved and renamed"
if new_name == name:
action = "moved"
if name == "__version__":
import importlib.metadata
import warnings
warnings.warn(
f"'{name}' has been {action} to '{path[1:]}'. The top-level import is"
" deprecated and will be removed in Flask-SQLAlchemy 3.1.",
"The '__version__' attribute is deprecated and will be removed in"
" Flask-SQLAlchemy 3.2. Use feature detection or"
" 'importlib.metadata.version(\"flask-sqlalchemy\")' instead.",
DeprecationWarning,
stacklevel=2,
)
mod = importlib.import_module(import_path, __name__)
return getattr(mod, new_name)
return importlib.metadata.version("flask-sqlalchemy")
raise AttributeError(name)

View File

@@ -1,7 +1,9 @@
from __future__ import annotations
import os
import types
import typing as t
import warnings
from weakref import WeakKeyDictionary
import sqlalchemy as sa
@@ -14,8 +16,11 @@ from flask import Flask
from flask import has_app_context
from .model import _QueryProperty
from .model import BindMixin
from .model import DefaultMeta
from .model import DefaultMetaNoName
from .model import Model
from .model import NameMixin
from .pagination import Pagination
from .pagination import SelectPagination
from .query import Query
@@ -26,6 +31,33 @@ from .table import _Table
_O = t.TypeVar("_O", bound=object) # Based on sqlalchemy.orm._typing.py
# Type accepted for model_class argument
_FSA_MCT = t.TypeVar(
"_FSA_MCT",
bound=t.Union[
t.Type[Model],
sa_orm.DeclarativeMeta,
t.Type[sa_orm.DeclarativeBase],
t.Type[sa_orm.DeclarativeBaseNoMeta],
],
)
# Type returned by make_declarative_base
class _FSAModel(Model):
metadata: sa.MetaData
def _get_2x_declarative_bases(
model_class: _FSA_MCT,
) -> list[t.Type[t.Union[sa_orm.DeclarativeBase, sa_orm.DeclarativeBaseNoMeta]]]:
return [
b
for b in model_class.__bases__
if issubclass(b, (sa_orm.DeclarativeBase, sa_orm.DeclarativeBaseNoMeta))
]
class SQLAlchemy:
"""Integrates SQLAlchemy with Flask. This handles setting up one or more engines,
associating tables and models with specific engines, and cleaning up connections and
@@ -66,6 +98,18 @@ class SQLAlchemy:
:param add_models_to_shell: Add the ``db`` instance and all model classes to
``flask shell``.
.. versionchanged:: 3.1.0
The ``metadata`` parameter can still be used with SQLAlchemy 1.x classes,
but is ignored when using SQLAlchemy 2.x style of declarative classes.
Instead, specify metadata on your Base class.
.. versionchanged:: 3.1.0
Added the ``disable_autonaming`` parameter.
.. versionchanged:: 3.1.0
Changed ``model_class`` parameter to accepta SQLAlchemy 2.x
declarative base subclass.
.. versionchanged:: 3.0
An active Flask application context is always required to access ``session`` and
``engine``.
@@ -100,11 +144,6 @@ class SQLAlchemy:
.. versionchanged:: 3.0
Removed the ``use_native_unicode`` parameter and config.
.. versionchanged:: 3.0
The ``COMMIT_ON_TEARDOWN`` configuration is deprecated and will
be removed in Flask-SQLAlchemy 3.1. Call ``db.session.commit()``
directly instead.
.. versionchanged:: 2.4
Added the ``engine_options`` parameter.
@@ -129,9 +168,10 @@ class SQLAlchemy:
metadata: sa.MetaData | None = None,
session_options: dict[str, t.Any] | None = None,
query_class: type[Query] = Query,
model_class: type[Model] | sa_orm.DeclarativeMeta = Model,
model_class: _FSA_MCT = Model, # type: ignore[assignment]
engine_options: dict[str, t.Any] | None = None,
add_models_to_shell: bool = True,
disable_autonaming: bool = False,
):
if session_options is None:
session_options = {}
@@ -173,8 +213,17 @@ class SQLAlchemy:
"""
if metadata is not None:
metadata.info["bind_key"] = None
self.metadatas[None] = metadata
if len(_get_2x_declarative_bases(model_class)) > 0:
warnings.warn(
"When using SQLAlchemy 2.x style of declarative classes,"
" the `metadata` should be an attribute of the base class."
"The metadata passed into SQLAlchemy() is ignored.",
DeprecationWarning,
stacklevel=2,
)
else:
metadata.info["bind_key"] = None
self.metadatas[None] = metadata
self.Table = self._make_table_class()
"""A :class:`sqlalchemy.schema.Table` class that chooses a metadata
@@ -192,7 +241,9 @@ class SQLAlchemy:
This is a subclass of SQLAlchemy's ``Table`` rather than a function.
"""
self.Model = self._make_declarative_base(model_class)
self.Model = self._make_declarative_base(
model_class, disable_autonaming=disable_autonaming
)
"""A SQLAlchemy declarative model class. Subclass this to define database
models.
@@ -204,9 +255,15 @@ class SQLAlchemy:
database engine. Otherwise, it will use the default :attr:`metadata` and
:attr:`engine`. This is ignored if the model sets ``metadata`` or ``__table__``.
Customize this by subclassing :class:`.Model` and passing the ``model_class``
parameter to the extension. A fully created declarative model class can be
For code using the SQLAlchemy 1.x API, customize this model by subclassing
:class:`.Model` and passing the ``model_class`` parameter to the extension.
A fully created declarative model class can be
passed as well, to use a custom metaclass.
For code using the SQLAlchemy 2.x API, customize this model by subclassing
:class:`sqlalchemy.orm.DeclarativeBase` or
:class:`sqlalchemy.orm.DeclarativeBaseNoMeta`
and passing the ``model_class`` parameter to the extension.
"""
if engine_options is None:
@@ -258,25 +315,13 @@ class SQLAlchemy:
)
app.extensions["sqlalchemy"] = self
app.teardown_appcontext(self._teardown_session)
if self._add_models_to_shell:
from .cli import add_models_to_shell
app.shell_context_processor(add_models_to_shell)
if app.config.get("SQLALCHEMY_COMMIT_ON_TEARDOWN", False):
import warnings
warnings.warn(
"'SQLALCHEMY_COMMIT_ON_TEARDOWN' is deprecated and will be removed in"
" Flask-SQAlchemy 3.1. Call 'db.session.commit()'` directly instead.",
DeprecationWarning,
stacklevel=2,
)
app.teardown_appcontext(self._teardown_commit)
else:
app.teardown_appcontext(self._teardown_session)
basic_uri: str | sa.engine.URL | None = app.config.setdefault(
"SQLALCHEMY_DATABASE_URI", None
)
@@ -393,20 +438,6 @@ class SQLAlchemy:
options.setdefault("query_cls", self.Query)
return sa_orm.sessionmaker(db=self, **options)
def _teardown_commit(self, exc: BaseException | None) -> None:
"""Commit the session at the end of the request if there was not an unhandled
exception during the request.
:meta private:
.. deprecated:: 3.0
Will be removed in 3.1. Use ``db.session.commit()`` directly instead.
"""
if exc is None:
self.session.commit()
self.session.remove()
def _teardown_session(self, exc: BaseException | None) -> None:
"""Remove the current session at the end of the request.
@@ -464,29 +495,16 @@ class SQLAlchemy:
if not args or (len(args) >= 2 and isinstance(args[1], sa.MetaData)):
return super().__new__(cls, *args, **kwargs)
if (
bind_key is None
and "info" in kwargs
and "bind_key" in kwargs["info"]
):
import warnings
warnings.warn(
"'table.info['bind_key'] is deprecated and will not be used in"
" Flask-SQLAlchemy 3.1. Pass the 'bind_key' parameter instead.",
DeprecationWarning,
stacklevel=2,
)
bind_key = kwargs["info"].get("bind_key")
metadata = self._make_metadata(bind_key)
return super().__new__(cls, *[args[0], metadata, *args[1:]], **kwargs)
return Table
def _make_declarative_base(
self, model: type[Model] | sa_orm.DeclarativeMeta
) -> type[t.Any]:
self,
model_class: _FSA_MCT,
disable_autonaming: bool = False,
) -> t.Type[_FSAModel]:
"""Create a SQLAlchemy declarative model class. The result is available as
:attr:`Model`.
@@ -498,7 +516,14 @@ class SQLAlchemy:
:meta private:
:param model: A model base class, or an already created declarative model class.
:param model_class: A model base class, or an already created declarative model
class.
:param disable_autonaming: Turns off automatic tablename generation in models.
.. versionchanged:: 3.1.0
Added support for passing SQLAlchemy 2.x base class as model class.
Added optional ``disable_autonaming`` parameter.
.. versionchanged:: 3.0
Renamed with a leading underscore, this method is internal.
@@ -506,22 +531,45 @@ class SQLAlchemy:
.. versionchanged:: 2.3
``model`` can be an already created declarative model class.
"""
if not isinstance(model, sa_orm.DeclarativeMeta):
metadata = self._make_metadata(None)
model = sa_orm.declarative_base(
metadata=metadata, cls=model, name="Model", metaclass=DefaultMeta
model: t.Type[_FSAModel]
declarative_bases = _get_2x_declarative_bases(model_class)
if len(declarative_bases) > 1:
# raise error if more than one declarative base is found
raise ValueError(
"Only one declarative base can be passed to SQLAlchemy."
" Got: {}".format(model_class.__bases__)
)
elif len(declarative_bases) == 1:
body = dict(model_class.__dict__)
body["__fsa__"] = self
mixin_classes = [BindMixin, NameMixin, Model]
if disable_autonaming:
mixin_classes.remove(NameMixin)
model = types.new_class(
"FlaskSQLAlchemyBase",
(*mixin_classes, *model_class.__bases__),
{"metaclass": type(declarative_bases[0])},
lambda ns: ns.update(body),
)
elif not isinstance(model_class, sa_orm.DeclarativeMeta):
metadata = self._make_metadata(None)
metaclass = DefaultMetaNoName if disable_autonaming else DefaultMeta
model = sa_orm.declarative_base(
metadata=metadata, cls=model_class, name="Model", metaclass=metaclass
)
else:
model = model_class # type: ignore[assignment]
if None not in self.metadatas:
# Use the model's metadata as the default metadata.
model.metadata.info["bind_key"] = None # type: ignore[union-attr]
self.metadatas[None] = model.metadata # type: ignore[union-attr]
model.metadata.info["bind_key"] = None
self.metadatas[None] = model.metadata
else:
# Use the passed in default metadata as the model's metadata.
model.metadata = self.metadatas[None] # type: ignore[union-attr]
model.metadata = self.metadatas[None]
model.query_class = self.Query
model.query = _QueryProperty()
model.query = _QueryProperty() # type: ignore[assignment]
model.__fsa__ = self
return model
@@ -660,80 +708,41 @@ class SQLAlchemy:
"""
return self.engines[None]
def get_engine(self, bind_key: str | None = None) -> sa.engine.Engine:
def get_engine(
self, bind_key: str | None = None, **kwargs: t.Any
) -> sa.engine.Engine:
"""Get the engine for the given bind key for the current application.
This requires that a Flask application context is active.
:param bind_key: The name of the engine.
.. deprecated:: 3.0
Will be removed in Flask-SQLAlchemy 3.1. Use ``engines[key]`` instead.
Will be removed in Flask-SQLAlchemy 3.2. Use ``engines[key]`` instead.
.. versionchanged:: 3.0
Renamed the ``bind`` parameter to ``bind_key``. Removed the ``app``
parameter.
"""
import warnings
warnings.warn(
"'get_engine' is deprecated and will be removed in Flask-SQLAlchemy 3.1."
" Use 'engine' or 'engines[key]' instead.",
"'get_engine' is deprecated and will be removed in Flask-SQLAlchemy"
" 3.2. Use 'engine' or 'engines[key]' instead. If you're using"
" Flask-Migrate or Alembic, you'll need to update your 'env.py' file.",
DeprecationWarning,
stacklevel=2,
)
if "bind" in kwargs:
bind_key = kwargs.pop("bind")
return self.engines[bind_key]
def get_tables_for_bind(self, bind_key: str | None = None) -> list[sa.Table]:
"""Get all tables in the metadata for the given bind key.
:param bind_key: The bind key to get.
.. deprecated:: 3.0
Will be removed in Flask-SQLAlchemy 3.1. Use ``metadata.tables`` instead.
.. versionchanged:: 3.0
Renamed the ``bind`` parameter to ``bind_key``.
"""
import warnings
warnings.warn(
"'get_tables_for_bind' is deprecated and will be removed in"
" Flask-SQLAlchemy 3.1. Use 'metadata.tables' instead.",
DeprecationWarning,
stacklevel=2,
)
return list(self.metadatas[bind_key].tables.values())
def get_binds(self) -> dict[sa.Table, sa.engine.Engine]:
"""Map all tables to their engine based on their bind key, which can be used to
create a session with ``Session(binds=db.get_binds(app))``.
This requires that a Flask application context is active.
.. deprecated:: 3.0
Will be removed in Flask-SQLAlchemy 3.1. ``db.session`` supports multiple
binds directly.
.. versionchanged:: 3.0
Removed the ``app`` parameter.
"""
import warnings
warnings.warn(
"'get_binds' is deprecated and will be removed in Flask-SQLAlchemy 3.1."
" 'db.session' supports multiple binds directly.",
DeprecationWarning,
stacklevel=2,
)
return {
table: engine
for bind_key, engine in self.engines.items()
for table in self.metadatas[bind_key].tables.values()
}
def get_or_404(
self, entity: type[_O], ident: t.Any, *, description: str | None = None
self,
entity: type[_O],
ident: t.Any,
*,
description: str | None = None,
**kwargs: t.Any,
) -> _O:
"""Like :meth:`session.get() <sqlalchemy.orm.Session.get>` but aborts with a
``404 Not Found`` error instead of returning ``None``.
@@ -741,10 +750,14 @@ class SQLAlchemy:
:param entity: The model class to query.
:param ident: The primary key to query.
:param description: A custom message to show on the error page.
:param kwargs: Extra arguments passed to ``session.get()``.
.. versionchanged:: 3.1
Pass extra keyword arguments to ``session.get()``.
.. versionadded:: 3.0
"""
value = self.session.get(entity, ident)
value = self.session.get(entity, ident, **kwargs)
if value is None:
abort(404, description=description)
@@ -974,24 +987,11 @@ class SQLAlchemy:
.. versionchanged:: 3.0
The :attr:`Query` class is set on ``backref``.
"""
# Deprecated, removed in SQLAlchemy 2.0. Accessed through ``__getattr__``.
self._set_rel_query(kwargs)
f = sa_orm.relationship
return f(*args, **kwargs)
def __getattr__(self, name: str) -> t.Any:
if name == "db":
import warnings
warnings.warn(
"The 'db' attribute is deprecated and will be removed in"
" Flask-SQLAlchemy 3.1. The extension is registered directly as"
" 'app.extensions[\"sqlalchemy\"]'.",
DeprecationWarning,
stacklevel=2,
)
return self
if name == "relation":
return self._relation

View File

@@ -18,14 +18,6 @@ class _QueryProperty:
:meta private:
"""
@t.overload
def __get__(self, obj: None, cls: type[Model]) -> Query:
...
@t.overload
def __get__(self, obj: Model, cls: type[Model]) -> Query:
...
def __get__(self, obj: Model | None, cls: type[Model]) -> Query:
return cls.query_class(
cls, session=cls.__fsa__.session() # type: ignore[arg-type]
@@ -100,6 +92,38 @@ class BindMetaMixin(type):
super().__init__(name, bases, d, **kwargs)
class BindMixin:
"""DeclarativeBase mixin to set a model's ``metadata`` based on ``__bind_key__``.
If no ``__bind_key__`` is specified, the model will use the default metadata
provided by ``DeclarativeBase`` or ``DeclarativeBaseNoMeta``.
If the model doesn't set ``metadata`` or ``__table__`` directly
and does set ``__bind_key__``, the model will use the metadata
for the specified bind key.
If the ``metadata`` is the same as the parent model, it will not be set
directly on the child model.
.. versionchanged:: 3.1.0
"""
__fsa__: SQLAlchemy
metadata: sa.MetaData
@classmethod
def __init_subclass__(cls: t.Type[BindMixin], **kwargs: t.Dict[str, t.Any]) -> None:
if not ("metadata" in cls.__dict__ or "__table__" in cls.__dict__) and hasattr(
cls, "__bind_key__"
):
bind_key = getattr(cls, "__bind_key__", None)
parent_metadata = getattr(cls, "metadata", None)
metadata = cls.__fsa__._make_metadata(bind_key)
if metadata is not parent_metadata:
cls.metadata = metadata
super().__init_subclass__(**kwargs)
class NameMetaMixin(type):
"""Metaclass mixin that sets a model's ``__tablename__`` by converting the
``CamelCase`` class name to ``snake_case``. A name is set for non-abstract models
@@ -169,6 +193,77 @@ class NameMetaMixin(type):
return None
class NameMixin:
"""DeclarativeBase mixin that sets a model's ``__tablename__`` by converting the
``CamelCase`` class name to ``snake_case``. A name is set for non-abstract models
that do not otherwise define ``__tablename__``. If a model does not define a primary
key, it will not generate a name or ``__table__``, for single-table inheritance.
.. versionchanged:: 3.1.0
"""
metadata: sa.MetaData
__tablename__: str
__table__: sa.Table
@classmethod
def __init_subclass__(cls: t.Type[NameMixin], **kwargs: t.Dict[str, t.Any]) -> None:
if should_set_tablename(cls):
cls.__tablename__ = camel_to_snake_case(cls.__name__)
super().__init_subclass__(**kwargs)
# __table_cls__ has run. If no table was created, use the parent table.
if (
"__tablename__" not in cls.__dict__
and "__table__" in cls.__dict__
and cls.__dict__["__table__"] is None
):
del cls.__table__
@classmethod
def __table_cls__(cls, *args: t.Any, **kwargs: t.Any) -> sa.Table | None:
"""This is called by SQLAlchemy during mapper setup. It determines the final
table object that the model will use.
If no primary key is found, that indicates single-table inheritance, so no table
will be created and ``__tablename__`` will be unset.
"""
schema = kwargs.get("schema")
if schema is None:
key = args[0]
else:
key = f"{schema}.{args[0]}"
# Check if a table with this name already exists. Allows reflected tables to be
# applied to models by name.
if key in cls.metadata.tables:
return sa.Table(*args, **kwargs)
# If a primary key is found, create a table for joined-table inheritance.
for arg in args:
if (isinstance(arg, sa.Column) and arg.primary_key) or isinstance(
arg, sa.PrimaryKeyConstraint
):
return sa.Table(*args, **kwargs)
# If no base classes define a table, return one that's missing a primary key
# so SQLAlchemy shows the correct error.
for base in cls.__mro__[1:-1]:
if "__table__" in base.__dict__:
break
else:
return sa.Table(*args, **kwargs)
# Single-table inheritance, use the parent table name. __init__ will unset
# __table__ based on this.
if "__tablename__" in cls.__dict__:
del cls.__tablename__
return None
def should_set_tablename(cls: type) -> bool:
"""Determine whether ``__tablename__`` should be generated for a model.
@@ -181,8 +276,16 @@ def should_set_tablename(cls: type) -> bool:
Later, ``__table_cls__`` will determine if the model looks like single or
joined-table inheritance. If no primary key is found, the name will be unset.
"""
if cls.__dict__.get("__abstract__", False) or not any(
isinstance(b, sa_orm.DeclarativeMeta) for b in cls.__mro__[1:]
if (
cls.__dict__.get("__abstract__", False)
or (
not issubclass(cls, (sa_orm.DeclarativeBase, sa_orm.DeclarativeBaseNoMeta))
and not any(isinstance(b, sa_orm.DeclarativeMeta) for b in cls.__mro__[1:])
)
or any(
(b is sa_orm.DeclarativeBase or b is sa_orm.DeclarativeBaseNoMeta)
for b in cls.__bases__
)
):
return False
@@ -196,7 +299,14 @@ def should_set_tablename(cls: type) -> bool:
return not (
base is cls
or base.__dict__.get("__abstract__", False)
or not isinstance(base, sa_orm.DeclarativeMeta)
or not (
# SQLAlchemy 1.x
isinstance(base, sa_orm.DeclarativeMeta)
# 2.x: DeclarativeBas uses this as metaclass
or isinstance(base, sa_orm.decl_api.DeclarativeAttributeIntercept)
# 2.x: DeclarativeBaseNoMeta doesn't use a metaclass
or issubclass(base, sa_orm.DeclarativeBaseNoMeta)
)
)
return True
@@ -212,3 +322,9 @@ class DefaultMeta(BindMetaMixin, NameMetaMixin, sa_orm.DeclarativeMeta):
"""SQLAlchemy declarative metaclass that provides ``__bind_key__`` and
``__tablename__`` support.
"""
class DefaultMetaNoName(BindMetaMixin, sa_orm.DeclarativeMeta):
"""SQLAlchemy declarative metaclass that provides ``__bind_key__`` and
``__tablename__`` support.
"""

View File

@@ -70,30 +70,6 @@ class _QueryInfo:
def duration(self) -> float:
return self.end_time - self.start_time
@property
def context(self) -> str:
import warnings
warnings.warn(
"'context' is renamed to 'location'. The old name is deprecated and will be"
" removed in Flask-SQLAlchemy 3.1.",
DeprecationWarning,
stacklevel=2,
)
return self.location
def __getitem__(self, key: int) -> object:
import warnings
name = ("statement", "parameters", "start_time", "end_time", "location")[key]
warnings.warn(
"Query info is a dataclass, not a tuple. Lookup by index is deprecated and"
f" will be removed in Flask-SQLAlchemy 3.1. Use 'info.{name}' instead.",
DeprecationWarning,
stacklevel=2,
)
return getattr(self, name)
def _listen(engine: sa.engine.Engine) -> None:
sa_event.listen(engine, "before_cursor_execute", _record_start, named=True)

View File

@@ -79,13 +79,22 @@ class Session(sa_orm.Session):
def _clause_to_engine(
clause: t.Any | None, engines: t.Mapping[str | None, sa.engine.Engine]
clause: sa.ClauseElement | None,
engines: t.Mapping[str | None, sa.engine.Engine],
) -> sa.engine.Engine | None:
"""If the clause is a table, return the engine associated with the table's
metadata's bind key.
"""
if isinstance(clause, sa.Table) and "bind_key" in clause.metadata.info:
key = clause.metadata.info["bind_key"]
table = None
if clause is not None:
if isinstance(clause, sa.Table):
table = clause
elif isinstance(clause, sa.UpdateBase) and isinstance(clause.table, sa.Table):
table = clause.table
if table is not None and "bind_key" in table.metadata.info:
key = table.metadata.info["bind_key"]
if key not in engines:
raise sa_exc.UnboundExecutionError(

View File

@@ -5,4 +5,4 @@ from .recaptcha import Recaptcha
from .recaptcha import RecaptchaField
from .recaptcha import RecaptchaWidget
__version__ = "1.1.1"
__version__ = "1.2.1"

View File

@@ -217,7 +217,7 @@ class CSRFProtect:
if not request.endpoint:
return
if request.blueprint in self._exempt_blueprints:
if app.blueprints.get(request.blueprint) in self._exempt_blueprints:
return
view = app.view_functions.get(request.endpoint)
@@ -292,7 +292,7 @@ class CSRFProtect:
"""
if isinstance(view, Blueprint):
self._exempt_blueprints.add(view.name)
self._exempt_blueprints.add(view)
return view
if isinstance(view, str):

View File

@@ -2,6 +2,7 @@ from collections import abc
from werkzeug.datastructures import FileStorage
from wtforms import FileField as _FileField
from wtforms import MultipleFileField as _MultipleFileField
from wtforms.validators import DataRequired
from wtforms.validators import StopValidation
from wtforms.validators import ValidationError
@@ -20,8 +21,24 @@ class FileField(_FileField):
self.raw_data = ()
class MultipleFileField(_MultipleFileField):
"""Werkzeug-aware subclass of :class:`wtforms.fields.MultipleFileField`.
.. versionadded:: 1.2.0
"""
def process_formdata(self, valuelist):
valuelist = (x for x in valuelist if isinstance(x, FileStorage) and x)
data = list(valuelist) or None
if data is not None:
self.data = data
else:
self.raw_data = ()
class FileRequired(DataRequired):
"""Validates that the data is a Werkzeug
"""Validates that the uploaded files(s) is a Werkzeug
:class:`~werkzeug.datastructures.FileStorage` object.
:param message: error message
@@ -30,7 +47,10 @@ class FileRequired(DataRequired):
"""
def __call__(self, form, field):
if not (isinstance(field.data, FileStorage) and field.data):
field_data = [field.data] if not isinstance(field.data, list) else field.data
if not (
all(isinstance(x, FileStorage) and x for x in field_data) and field_data
):
raise StopValidation(
self.message or field.gettext("This field is required.")
)
@@ -40,7 +60,7 @@ file_required = FileRequired
class FileAllowed:
"""Validates that the uploaded file is allowed by a given list of
"""Validates that the uploaded file(s) is allowed by a given list of
extensions or a Flask-Uploads :class:`~flaskext.uploads.UploadSet`.
:param upload_set: A list of extensions or an
@@ -55,34 +75,38 @@ class FileAllowed:
self.message = message
def __call__(self, form, field):
if not (isinstance(field.data, FileStorage) and field.data):
field_data = [field.data] if not isinstance(field.data, list) else field.data
if not (
all(isinstance(x, FileStorage) and x for x in field_data) and field_data
):
return
filename = field.data.filename.lower()
filenames = [f.filename.lower() for f in field_data]
if isinstance(self.upload_set, abc.Iterable):
if any(filename.endswith("." + x) for x in self.upload_set):
return
for filename in filenames:
if isinstance(self.upload_set, abc.Iterable):
if any(filename.endswith("." + x) for x in self.upload_set):
continue
raise StopValidation(
self.message
or field.gettext(
"File does not have an approved extension: {extensions}"
).format(extensions=", ".join(self.upload_set))
)
raise StopValidation(
self.message
or field.gettext(
"File does not have an approved extension: {extensions}"
).format(extensions=", ".join(self.upload_set))
)
if not self.upload_set.file_allowed(field.data, filename):
raise StopValidation(
self.message
or field.gettext("File does not have an approved extension.")
)
if not self.upload_set.file_allowed(field_data, filename):
raise StopValidation(
self.message
or field.gettext("File does not have an approved extension.")
)
file_allowed = FileAllowed
class FileSize:
"""Validates that the uploaded file is within a minimum and maximum
"""Validates that the uploaded file(s) is within a minimum and maximum
file size (set in bytes).
:param min_size: minimum allowed file size (in bytes). Defaults to 0 bytes.
@@ -98,22 +122,26 @@ class FileSize:
self.message = message
def __call__(self, form, field):
if not (isinstance(field.data, FileStorage) and field.data):
field_data = [field.data] if not isinstance(field.data, list) else field.data
if not (
all(isinstance(x, FileStorage) and x for x in field_data) and field_data
):
return
file_size = len(field.data.read())
field.data.seek(0) # reset cursor position to beginning of file
for f in field_data:
file_size = len(f.read())
f.seek(0) # reset cursor position to beginning of file
if (file_size < self.min_size) or (file_size > self.max_size):
# the file is too small or too big => validation failure
raise ValidationError(
self.message
or field.gettext(
"File must be between {min_size} and {max_size} bytes.".format(
min_size=self.min_size, max_size=self.max_size
if (file_size < self.min_size) or (file_size > self.max_size):
# the file is too small or too big => validation failure
raise ValidationError(
self.message
or field.gettext(
"File must be between {min_size} and {max_size} bytes.".format(
min_size=self.min_size, max_size=self.max_size
)
)
)
)
file_size = FileSize

View File

@@ -1,9 +1,9 @@
import json
from urllib import request as http
from urllib.parse import urlencode
from flask import current_app
from flask import request
from werkzeug.urls import url_encode
from wtforms import ValidationError
RECAPTCHA_VERIFY_SERVER_DEFAULT = "https://www.google.com/recaptcha/api/siteverify"
@@ -54,7 +54,7 @@ class Recaptcha:
if not verify_server:
verify_server = RECAPTCHA_VERIFY_SERVER_DEFAULT
data = url_encode(
data = urlencode(
{"secret": private_key, "remoteip": remote_addr, "response": response}
)

View File

@@ -1,6 +1,7 @@
from urllib.parse import urlencode
from flask import current_app
from flask import Markup
from werkzeug.urls import url_encode
from markupsafe import Markup
RECAPTCHA_SCRIPT_DEFAULT = "https://www.google.com/recaptcha/api.js"
RECAPTCHA_DIV_CLASS_DEFAULT = "g-recaptcha"
@@ -22,10 +23,10 @@ class RecaptchaWidget:
if not script:
script = RECAPTCHA_SCRIPT_DEFAULT
if params:
script += "?" + url_encode(params)
script += "?" + urlencode(params)
attrs = current_app.config.get("RECAPTCHA_DATA_ATTRS", {})
attrs["sitekey"] = public_key
snippet = " ".join(f'data-{k}="{attrs[k]}"' for k in attrs) # noqa: B028
snippet = " ".join(f'data-{k}="{attrs[k]}"' for k in attrs) # noqa: B028, B907
div_class = current_app.config.get("RECAPTCHA_DIV_CLASS")
if not div_class:
div_class = RECAPTCHA_DIV_CLASS_DEFAULT

View File

@@ -269,7 +269,7 @@ from .types import Uuid as Uuid
from .types import VARBINARY as VARBINARY
from .types import VARCHAR as VARCHAR
__version__ = "2.0.20"
__version__ = "2.0.23"
def __go(lcls: Any) -> None:

View File

@@ -227,7 +227,7 @@ class PyODBCConnector(Connector):
def get_isolation_level_values(
self, dbapi_connection: interfaces.DBAPIConnection
) -> List[IsolationLevel]:
return super().get_isolation_level_values(dbapi_connection) + [ # type: ignore # noqa: E501
return super().get_isolation_level_values(dbapi_connection) + [
"AUTOCOMMIT"
]

View File

@@ -51,7 +51,7 @@ def _auto_fn(name: str) -> Optional[Callable[[], Type[Dialect]]]:
if hasattr(module, driver):
module = getattr(module, driver)
return lambda: module.dialect # type: ignore
return lambda: module.dialect
else:
return None

View File

@@ -6,6 +6,7 @@
# the MIT License: https://www.opensource.org/licenses/mit-license.php
# mypy: ignore-errors
from . import aioodbc # noqa
from . import base # noqa
from . import pymssql # noqa
from . import pyodbc # noqa

View File

@@ -1537,28 +1537,22 @@ class MSUUid(sqltypes.Uuid):
if self.native_uuid:
def process(value):
if value is not None:
value = f"""'{str(value).replace("''", "'")}'"""
return value
return f"""'{str(value).replace("''", "'")}'"""
return process
else:
if self.as_uuid:
def process(value):
if value is not None:
value = f"""'{value.hex}'"""
return value
return f"""'{value.hex}'"""
return process
else:
def process(value):
if value is not None:
value = f"""'{
return f"""'{
value.replace("-", "").replace("'", "''")
}'"""
return value
return process
@@ -1942,6 +1936,7 @@ class MSExecutionContext(default.DefaultExecutionContext):
row = self.cursor.fetchall()[0]
self._lastrowid = int(row[0])
self.cursor_fetch_strategy = _cursor._NO_CURSOR_DML
elif (
self.compiled is not None
and is_sql_compiler(self.compiled)
@@ -2057,6 +2052,12 @@ class MSSQLCompiler(compiler.SQLCompiler):
def visit_char_length_func(self, fn, **kw):
return "LEN%s" % self.function_argspec(fn, **kw)
def visit_aggregate_strings_func(self, fn, **kw):
expr = fn.clauses.clauses[0]._compiler_dispatch(self, **kw)
kw["literal_execute"] = True
delimeter = fn.clauses.clauses[1]._compiler_dispatch(self, **kw)
return f"string_agg({expr}, {delimeter})"
def visit_concat_op_expression_clauselist(
self, clauselist, operator, **kw
):
@@ -2119,6 +2120,7 @@ class MSSQLCompiler(compiler.SQLCompiler):
or (
# limit can use TOP with is by itself. fetch only uses TOP
# when it needs to because of PERCENT and/or WITH TIES
# TODO: Why? shouldn't we use TOP always ?
select._simple_int_clause(select._fetch_clause)
and (
select._fetch_clause_options["percent"]
@@ -2379,10 +2381,13 @@ class MSSQLCompiler(compiler.SQLCompiler):
return ""
def order_by_clause(self, select, **kw):
# MSSQL only allows ORDER BY in subqueries if there is a LIMIT
# MSSQL only allows ORDER BY in subqueries if there is a LIMIT:
# "The ORDER BY clause is invalid in views, inline functions,
# derived tables, subqueries, and common table expressions,
# unless TOP, OFFSET or FOR XML is also specified."
if (
self.is_subquery()
and not select._limit
and not self._use_top(select)
and (
select._offset is None
or not self.dialect._supports_offset_fetch

View File

@@ -211,7 +211,7 @@ class NumericSqlVariant(TypeDecorator):
cache_ok = True
def column_expression(self, colexpr):
return cast(colexpr, Numeric)
return cast(colexpr, Numeric(38, 0))
identity_columns = Table(

View File

@@ -26,7 +26,7 @@ def generate_driver_url(url, driver, query_str):
new_url = url.set(drivername="%s+%s" % (backend, driver))
if driver != "pyodbc":
if driver not in ("pyodbc", "aioodbc"):
new_url = new_url.set(query="")
if query_str:

View File

@@ -365,6 +365,7 @@ from ... import exc
from ... import types as sqltypes
from ... import util
from ...connectors.pyodbc import PyODBCConnector
from ...engine import cursor as _cursor
class _ms_numeric_pyodbc:
@@ -585,14 +586,22 @@ class MSExecutionContext_pyodbc(MSExecutionContext):
try:
# fetchall() ensures the cursor is consumed
# without closing it (FreeTDS particularly)
row = self.cursor.fetchall()[0]
break
rows = self.cursor.fetchall()
except self.dialect.dbapi.Error:
# no way around this - nextset() consumes the previous set
# so we need to just keep flipping
self.cursor.nextset()
else:
if not rows:
# async adapter drivers just return None here
self.cursor.nextset()
continue
row = rows[0]
break
self._lastrowid = int(row[0])
self.cursor_fetch_strategy = _cursor._NO_CURSOR_DML
else:
super().post_exec()

Some files were not shown because too many files have changed in this diff Show More