first push

This commit is contained in:
annous246 2025-07-02 19:41:06 +01:00
commit 5fed665a3c
2646 changed files with 589783 additions and 0 deletions

19
app/__init__.py Normal file
View File

@ -0,0 +1,19 @@
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from app.config import Config
db = SQLAlchemy()
migrate = Migrate()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
migrate.init_app(app, db)
from app.routes import bp
app.register_blueprint(bp)
return app

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

6
app/config.py Normal file
View File

@ -0,0 +1,6 @@
import os
class Config:
SECRET_KEY = os.environ.get("SECRET_KEY") or "you-will-never-guess"
SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL") or "sqlite:///app.db"
SQLALCHEMY_TRACK_MODIFICATIONS = False

8
app/models.py Normal file
View File

@ -0,0 +1,8 @@
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, nullable=False)
def __repr__(self):
return f"<User {self.username}>"

47
app/routes.py Normal file
View File

@ -0,0 +1,47 @@
from flask import Blueprint, render_template,request,redirect,url_for
from app.models import User,db
bp = Blueprint('main', __name__)
@bp.route("/")
def home():
users = User.query.all()
return render_template("home.html", users=users)
@bp.route("/add_user",methods=['GET', 'POST'])
def add():
if request.method =='POST':
username=request.form.get('username')
oldUser = User.query.filter_by(username=username).first()
if oldUser is None:
newUser=User(username=username)
db.session.add(newUser)
db.session.commit()
return redirect('/')
return render_template("adder.html")
@bp.route('/delete_user/<int:id>')
def delete(id):
user = User.query.get(id)
if user:
db.session.delete(user)
db.session.commit()
return redirect("/")
@bp.route('/edit_user/<int:id>',methods=['GET', 'POST'])
def edit(id):
message=""
user=User.query.get(id)
if request.method=="POST":
newUsername=request.form.get('username')
oldUser=User.query.filter_by(username=newUsername).first()
if(oldUser is None):
user.username=newUsername
db.session.commit()
return redirect('/')
else:
message="Username Already taken"
return render_template("edit.html",user=user,message=message)

96
app/static/css/styles.css Normal file
View File

@ -0,0 +1,96 @@
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
background-color: #f9f9ff;
margin: 2em;
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
font-weight: 700;
font-size: 2.5em;
color: #2c3e50;
margin-bottom: 1em;
text-align: center;
width: 100%;
}
a.add-user {
display: inline-block;
margin-top: 2em;
font-weight: 600;
font-size: 1.2em;
color: #4a4af0;
text-decoration: none;
padding: 0.75em 2em;
border-radius: 1em;
background-color: #e8e8ff;
transition: background-color 0.3s ease;
text-align: center;
}
a.add-user:hover {
background-color: #c8c8ff;
}
.userContainer {
padding: 1em 2em;
margin: 1.5em 0;
background-color: #f5f5ff;
border-radius: 1em;
display: flex;
align-items: center;
gap: 2em;
flex-wrap: wrap;
}
.userContainer li {
font-size: 2em;
list-style-type: none;
flex: 1;
}
.userContainer a {
font-size: 1.2em;
text-decoration: none;
padding: 1em;
border-radius: 1em;
margin: 0;
display: inline-block;
transition: background-color 0.3s ease, color 0.3s ease;
}
.userContainer a.delete {
color: #4a4af0;
background-color: white;
}
.userContainer a.delete:hover {
background-color: #e0e0ff;
}
.userContainer a.edit {
color: white;
background-color: #4a4af0;
}
.userContainer a.edit:hover {
background-color: #3838c8;
}
a.add-user {
display: inline-block;
margin-top: 2em;
font-weight: 600;
font-size: 1.2em;
color: #4a4af0;
text-decoration: none;
padding: 0.75em 1.5em;
border-radius: 1em;
background-color: #e8e8ff;
transition: background-color 0.3s ease;
}
a.add-user:hover {
background-color: #c8c8ff;
}

72
app/templates/adder.html Normal file
View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Add New User</title>
<style>
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
background-color: #f9f9ff;
margin: 2em;
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
font-weight: 700;
font-size: 2.5em;
color: #2c3e50;
margin-bottom: 1em;
text-align: center;
width: 100%;
}
form {
display: flex;
flex-direction: column;
gap: 1em;
width: 100%;
max-width: 400px;
background-color: white;
padding: 2em;
border-radius: 1em;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
input[type="text"] {
padding: 0.75em 1em;
font-size: 1.2em;
border: 2px solid #4a4af0;
border-radius: 0.5em;
outline: none;
transition: border-color 0.3s ease;
}
input[type="text"]:focus {
border-color: #3838c8;
}
button {
padding: 0.75em 1.5em;
font-size: 1.2em;
color: white;
background-color: #4a4af0;
border: none;
border-radius: 1em;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #3838c8;
}
</style>
</head>
<body>
<h1>Add new User</h1>
<form method="POST">
<input
name="username"
type="text"
placeholder="Enter username"
required
/>
<button type="submit">Add New User</button>
</form>
</body>
</html>

112
app/templates/edit.html Normal file
View File

@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Edit Username</title>
<style>
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
background: #f9fafb;
color: #333;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
background: #fff;
padding: 2.5em 3em;
border-radius: 12px;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
max-width: 400px;
width: 100%;
text-align: center;
}
h1 {
margin-bottom: 1em;
font-weight: 700;
color: #2c3e50;
}
form p {
margin: 0.5em 0 0.2em;
font-weight: 600;
color: #555;
text-transform: uppercase;
font-size: 0.85rem;
letter-spacing: 1px;
}
h3 {
margin: 0 0 1.5em;
font-weight: 600;
color: #34495e;
}
input[type="text"] {
width: 50%;
padding: 0.6em 0.9em;
border: 2px solid #d1d5db;
border-radius: 8px;
font-size: 1rem;
transition: border-color 0.3s ease;
outline: none;
}
input[type="text"]:focus {
border-color: #4287f5;
box-shadow: 0 0 6px rgba(66, 135, 245, 0.4);
}
button {
margin-top: 1.5em;
background-color: #4287f5;
border: none;
color: white;
padding: 0.8em 1.6em;
font-size: 1rem;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
transition: background-color 0.3s ease;
width: 100%;
}
button:hover {
background-color: #2f62d0;
}
.message {
margin-top: 1.5em;
font-weight: 600;
color: #2ecc71;
font-size: 1rem;
}
</style>
</head>
<body>
<div class="container">
<h1>Edit Username</h1>
<form method="POST">
<p>From</p>
<h3>{{ user.username }}</h3>
<p>To</p>
<input
name="username"
type="text"
placeholder="Enter new username"
required
/>
<button type="submit">Save New Username</button>
</form>
{% if message %}
<div class="message">{{ message }}</div>
{% endif %}
</div>
</body>
</html>

25
app/templates/home.html Normal file
View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>Users List</title>
<link
rel="stylesheet"
href="{{ url_for('static', filename='css/styles.css') }}"
/>
</head>
<body>
<h1>Users</h1>
<ul style="width: 100%; max-width: 700px; padding: 0">
{% for user in users %}
<div class="userContainer">
<li>{{ user.username }}</li>
<a href="/delete_user/{{ user.id }}" class="delete">Delete this user</a>
<a href="/edit_user/{{ user.id }}" class="edit">Change Username</a>
</div>
{% else %}
<li>No users found.</li>
{% endfor %}
</ul>
<a href="add_user" class="add-user">Add New User</a>
</body>
</html>

BIN
instance/app.db Normal file

Binary file not shown.

1
migrations/README Normal file
View File

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

Binary file not shown.

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

@ -0,0 +1,33 @@
"""Initial migration
Revision ID: 289e42693ec2
Revises:
Create Date: 2025-06-30 22:43:33.438674
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '289e42693ec2'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=64), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('username')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('user')
# ### end Alembic commands ###

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
Flask
Flask-SQLAlchemy
Flask-Migrate

8
run.py Normal file
View File

@ -0,0 +1,8 @@
from app import create_app, db
from flask_migrate import Migrate
app = create_app()
migrate = Migrate(app, db)
if __name__ == "__main__":
app.run(debug=True)

View File

@ -0,0 +1,164 @@
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
/* Greenlet object interface */
#ifndef Py_GREENLETOBJECT_H
#define Py_GREENLETOBJECT_H
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
/* This is deprecated and undocumented. It does not change. */
#define GREENLET_VERSION "1.0.0"
#ifndef GREENLET_MODULE
#define implementation_ptr_t void*
#endif
typedef struct _greenlet {
PyObject_HEAD
PyObject* weakreflist;
PyObject* dict;
implementation_ptr_t pimpl;
} PyGreenlet;
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
/* C API functions */
/* Total number of symbols that are exported */
#define PyGreenlet_API_pointers 12
#define PyGreenlet_Type_NUM 0
#define PyExc_GreenletError_NUM 1
#define PyExc_GreenletExit_NUM 2
#define PyGreenlet_New_NUM 3
#define PyGreenlet_GetCurrent_NUM 4
#define PyGreenlet_Throw_NUM 5
#define PyGreenlet_Switch_NUM 6
#define PyGreenlet_SetParent_NUM 7
#define PyGreenlet_MAIN_NUM 8
#define PyGreenlet_STARTED_NUM 9
#define PyGreenlet_ACTIVE_NUM 10
#define PyGreenlet_GET_PARENT_NUM 11
#ifndef GREENLET_MODULE
/* This section is used by modules that uses the greenlet C API */
static void** _PyGreenlet_API = NULL;
# define PyGreenlet_Type \
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
# define PyExc_GreenletError \
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
# define PyExc_GreenletExit \
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
/*
* PyGreenlet_New(PyObject *args)
*
* greenlet.greenlet(run, parent=None)
*/
# define PyGreenlet_New \
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
_PyGreenlet_API[PyGreenlet_New_NUM])
/*
* PyGreenlet_GetCurrent(void)
*
* greenlet.getcurrent()
*/
# define PyGreenlet_GetCurrent \
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
/*
* PyGreenlet_Throw(
* PyGreenlet *greenlet,
* PyObject *typ,
* PyObject *val,
* PyObject *tb)
*
* g.throw(...)
*/
# define PyGreenlet_Throw \
(*(PyObject * (*)(PyGreenlet * self, \
PyObject * typ, \
PyObject * val, \
PyObject * tb)) \
_PyGreenlet_API[PyGreenlet_Throw_NUM])
/*
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
*
* g.switch(*args, **kwargs)
*/
# define PyGreenlet_Switch \
(*(PyObject * \
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
_PyGreenlet_API[PyGreenlet_Switch_NUM])
/*
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
*
* g.parent = new_parent
*/
# define PyGreenlet_SetParent \
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
/*
* PyGreenlet_GetParent(PyObject* greenlet)
*
* return greenlet.parent;
*
* This could return NULL even if there is no exception active.
* If it does not return NULL, you are responsible for decrementing the
* reference count.
*/
# define PyGreenlet_GetParent \
(*(PyGreenlet* (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
/*
* deprecated, undocumented alias.
*/
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
# define PyGreenlet_MAIN \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_MAIN_NUM])
# define PyGreenlet_STARTED \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_STARTED_NUM])
# define PyGreenlet_ACTIVE \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
/* Macro that imports greenlet and initializes C API */
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
keep the older definition to be sure older code that might have a copy of
the header still works. */
# define PyGreenlet_Import() \
{ \
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
}
#endif /* GREENLET_MODULE */
#ifdef __cplusplus
}
#endif
#endif /* !Py_GREENLETOBJECT_H */

View File

@ -0,0 +1 @@
pip

View File

@ -0,0 +1,28 @@
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

@ -0,0 +1,123 @@
Metadata-Version: 2.1
Name: Flask
Version: 2.2.5
Summary: A simple framework for building complex web applications.
Home-page: https://palletsprojects.com/p/flask
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
Maintainer: Pallets
Maintainer-email: contact@palletsprojects.com
License: BSD-3-Clause
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://flask.palletsprojects.com/
Project-URL: Changes, https://flask.palletsprojects.com/changes/
Project-URL: Source Code, https://github.com/pallets/flask/
Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/
Project-URL: Twitter, https://twitter.com/PalletsTeam
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: Werkzeug (>=2.2.2)
Requires-Dist: Jinja2 (>=3.0)
Requires-Dist: itsdangerous (>=2.0)
Requires-Dist: click (>=8.0)
Requires-Dist: importlib-metadata (>=3.6.0) ; python_version < "3.10"
Provides-Extra: async
Requires-Dist: asgiref (>=3.2) ; extra == 'async'
Provides-Extra: dotenv
Requires-Dist: python-dotenv ; extra == 'dotenv'
Flask
=====
Flask is a lightweight `WSGI`_ web application framework. It is designed
to make getting started quick and easy, with the ability to scale up to
complex applications. It began as a simple wrapper around `Werkzeug`_
and `Jinja`_ and has become one of the most popular Python web
application frameworks.
Flask offers suggestions, but doesn't enforce any dependencies or
project layout. It is up to the developer to choose the tools and
libraries they want to use. There are many extensions provided by the
community that make adding new functionality easy.
.. _WSGI: https://wsgi.readthedocs.io/
.. _Werkzeug: https://werkzeug.palletsprojects.com/
.. _Jinja: https://jinja.palletsprojects.com/
Installing
----------
Install and update using `pip`_:
.. code-block:: text
$ pip install -U Flask
.. _pip: https://pip.pypa.io/en/stable/getting-started/
A Simple Example
----------------
.. code-block:: python
# save this as app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
.. code-block:: text
$ flask run
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Contributing
------------
For guidance on setting up a development environment and how to make a
contribution to Flask, see the `contributing guidelines`_.
.. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst
Donate
------
The Pallets organization develops and supports Flask and the libraries
it uses. 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.palletsprojects.com/
- Changes: https://flask.palletsprojects.com/changes/
- PyPI Releases: https://pypi.org/project/Flask/
- Source Code: https://github.com/pallets/flask/
- Issue Tracker: https://github.com/pallets/flask/issues/
- Website: https://palletsprojects.com/p/flask/
- Twitter: https://twitter.com/PalletsTeam
- Chat: https://discord.gg/pallets

View File

@ -0,0 +1,53 @@
flask/__init__.py,sha256=GJgAILDWhW_DQljuoJ4pk9zBUy70zPPu-VZ6kLyiVI4,2890
flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
flask/app.py,sha256=ue4tEeDnr3m-eSEwz7OJ1_wafSYl3fl6eo-NLFgNNJQ,99141
flask/blueprints.py,sha256=fenhKP_Sh5eU6qtWeHacg1GVeun4pQzK2vq8sNDd1hY,27266
flask/cli.py,sha256=pLmnWObe_G4_ZAFQdh7kgwqPMxRXm4oUhaUSBpJMeq4,33532
flask/config.py,sha256=Ubo_juzSYsAKqD2vD3vm6mjsPo3EOJDdSEzYq8lKTJI,12585
flask/ctx.py,sha256=bGEQQuF2_cHqZ3ZNMeMeEG8HOLJkDlL88u2BBxCrRao,14829
flask/debughelpers.py,sha256=_RvAL3TW5lqMJeCVWtTU6rSDJC7jnRaBL6OEkVmooyU,5511
flask/globals.py,sha256=EX0XdX73BTWdVF0UHDSNet2ER3kI6sKveo3_o5IOs98,3187
flask/helpers.py,sha256=XTHRgLlyxeEzR988q63-4OY8RswTscR-5exFxK10CLU,25280
flask/logging.py,sha256=WYng0bLTRS_CJrocGcCLJpibHf1lygHE_pg-KoUIQ4w,2293
flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
flask/scaffold.py,sha256=EKx-Tr5BXLzeKKvq3ZAi_2oUQVZuC4OJSJTocyDXsSo,35958
flask/sessions.py,sha256=adWCRnJYETJcjjhlcvUgZR5S0DMqKQctS0nzkY9g9Us,15927
flask/signals.py,sha256=H7QwDciK-dtBxinjKpexpglP0E6k0MJILiFWTItfmqU,2136
flask/templating.py,sha256=1P4OzvSnA2fsJTYgQT3G4owVKsuOz8XddCiR6jMHGJ0,7419
flask/testing.py,sha256=JtHRQY7mIH39SM4S51svAr8e7Xk87dqMb30Z6Dyv9TA,10706
flask/typing.py,sha256=KgxegTF9v9WvuongeF8LooIvpZPauzGrq9ZXf3gBlYc,2969
flask/views.py,sha256=LulttWL4owVFlgwrJi8GCNM4inC3xbs2IBlY31bdCS4,6765
flask/wrappers.py,sha256=el3tn1LgSUV0eNGgYMjKICT5I7qGJgbpIhvci4nrwQ8,5702
flask/json/__init__.py,sha256=TOwldHT3_kFaXHlORKi9yCWt7dbPNB0ovdHHQWlSRzY,11175
flask/json/provider.py,sha256=jXCNypf11PN4ngQjEt6LnSdCWQ1yHIAkNLHlXQlCB-A,10674
flask/json/tag.py,sha256=fys3HBLssWHuMAIJuTcf2K0bCtosePBKXIWASZEEjnU,8857
Flask-2.2.5.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475
Flask-2.2.5.dist-info/METADATA,sha256=rZTjr5v4M7HB-zC-w2Y0ZU96OYSGBb-Hm15jlLJhs3g,3889
Flask-2.2.5.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
Flask-2.2.5.dist-info/entry_points.txt,sha256=s3MqQpduU25y4dq3ftBYD6bMVdVnbMpZP-sUNw0zw0k,41
Flask-2.2.5.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6
Flask-2.2.5.dist-info/RECORD,,
../../Scripts/flask.exe,sha256=qGij7Kv23Lo2VCcabqGpWK2dXueIiluNyQgVpPoqVx0,102783
Flask-2.2.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
flask/json/__pycache__/provider.cpython-37.pyc,,
flask/json/__pycache__/tag.cpython-37.pyc,,
flask/json/__pycache__/__init__.cpython-37.pyc,,
flask/__pycache__/app.cpython-37.pyc,,
flask/__pycache__/blueprints.cpython-37.pyc,,
flask/__pycache__/cli.cpython-37.pyc,,
flask/__pycache__/config.cpython-37.pyc,,
flask/__pycache__/ctx.cpython-37.pyc,,
flask/__pycache__/debughelpers.cpython-37.pyc,,
flask/__pycache__/globals.cpython-37.pyc,,
flask/__pycache__/helpers.cpython-37.pyc,,
flask/__pycache__/logging.cpython-37.pyc,,
flask/__pycache__/scaffold.cpython-37.pyc,,
flask/__pycache__/sessions.cpython-37.pyc,,
flask/__pycache__/signals.cpython-37.pyc,,
flask/__pycache__/templating.cpython-37.pyc,,
flask/__pycache__/testing.cpython-37.pyc,,
flask/__pycache__/typing.cpython-37.pyc,,
flask/__pycache__/views.cpython-37.pyc,,
flask/__pycache__/wrappers.cpython-37.pyc,,
flask/__pycache__/__init__.cpython-37.pyc,,
flask/__pycache__/__main__.cpython-37.pyc,,

View File

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

View File

@ -0,0 +1,2 @@
[console_scripts]
flask = flask.cli:main

View File

@ -0,0 +1 @@
flask

View File

@ -0,0 +1 @@
pip

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Miguel Grinberg
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

@ -0,0 +1,91 @@
Metadata-Version: 2.2
Name: Flask-Migrate
Version: 4.1.0
Summary: SQLAlchemy database migrations for Flask applications using Alembic.
Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/miguelgrinberg/flask-migrate
Project-URL: Bug Tracker, https://github.com/miguelgrinberg/flask-migrate/issues
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Flask>=0.9
Requires-Dist: Flask-SQLAlchemy>=1.0
Requires-Dist: alembic>=1.9.0
Provides-Extra: dev
Requires-Dist: tox; extra == "dev"
Requires-Dist: flake8; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx; extra == "docs"
Flask-Migrate
=============
[![Build status](https://github.com/miguelgrinberg/flask-migrate/workflows/build/badge.svg)](https://github.com/miguelgrinberg/flask-migrate/actions)
Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are provided as command-line arguments under the `flask db` command.
Installation
------------
Install Flask-Migrate with `pip`:
pip install Flask-Migrate
Example
-------
This is an example application that handles database migrations through Flask-Migrate:
```python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
```
With the above application you can create the database or enable migrations if the database already exists with the following command:
$ flask db init
Note that the `FLASK_APP` environment variable must be set according to the Flask documentation for this command to work. This will add a `migrations` folder to your application. The contents of this folder need to be added to version control along with your other source files.
You can then generate an initial migration:
$ flask db migrate
The migration script needs to be reviewed and edited, as Alembic currently does not detect every change you make to your models. In particular, Alembic is currently unable to detect indexes. Once finalized, the migration script also needs to be added to version control.
Then you can apply the migration to the database:
$ flask db upgrade
Then each time the database models change repeat the `migrate` and `upgrade` commands.
To sync the database in another system just refresh the `migrations` folder from source control and run the `upgrade` command.
To see all the commands that are available run this command:
$ flask db --help
Resources
---------
- [Documentation](http://flask-migrate.readthedocs.io/en/latest/)
- [pypi](https://pypi.python.org/pypi/Flask-Migrate)
- [Change Log](https://github.com/miguelgrinberg/Flask-Migrate/blob/master/CHANGES.md)

View File

@ -0,0 +1,31 @@
Flask_Migrate-4.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
Flask_Migrate-4.1.0.dist-info/LICENSE,sha256=kfkXGlJQvKy3Y__6tAJ8ynIp1HQfeROXhL8jZU1d-DI,1082
Flask_Migrate-4.1.0.dist-info/METADATA,sha256=jifIy8PzfDzjuCEeKLDKRJA8O56KOgLfj3s2lmzZA-8,3289
Flask_Migrate-4.1.0.dist-info/RECORD,,
Flask_Migrate-4.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
Flask_Migrate-4.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
Flask_Migrate-4.1.0.dist-info/top_level.txt,sha256=jLoPgiMG6oR4ugNteXn3IHskVVIyIXVStZOVq-AWLdU,14
flask_migrate/__init__.py,sha256=JMySGA55Y8Gxy3HviWu7qq5rPUNQBWc2NID2OicpDyw,10082
flask_migrate/__pycache__/__init__.cpython-37.pyc,,
flask_migrate/__pycache__/cli.cpython-37.pyc,,
flask_migrate/cli.py,sha256=IxrxBSC82S5sPfWac8Qg83_FVsRvqTYtCG7HRyMW8RU,11097
flask_migrate/templates/aioflask-multidb/README,sha256=Ek4cJqTaxneVjtkue--BXMlfpfp3MmJRjqoZvnSizww,43
flask_migrate/templates/aioflask-multidb/__pycache__/env.cpython-37.pyc,,
flask_migrate/templates/aioflask-multidb/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857
flask_migrate/templates/aioflask-multidb/env.py,sha256=UcjeqkAbyUjTkuQFmCFPG7QOvqhco8-uGp8QEbto0T8,6573
flask_migrate/templates/aioflask-multidb/script.py.mako,sha256=198VPxVEN3NZ3vHcRuCxSoI4XnOYirGWt01qkbPKoJw,1246
flask_migrate/templates/aioflask/README,sha256=KKqWGl4YC2RqdOdq-y6quTDW0b7D_UZNHuM8glM1L-c,44
flask_migrate/templates/aioflask/__pycache__/env.cpython-37.pyc,,
flask_migrate/templates/aioflask/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857
flask_migrate/templates/aioflask/env.py,sha256=m6ZtBhdpwuq89vVeLTWmNT-1NfJZqarC_hsquCdR9bw,3478
flask_migrate/templates/aioflask/script.py.mako,sha256=8_xgA-gm_OhehnO7CiIijWgnm00ZlszEHtIHrAYFJl0,494
flask_migrate/templates/flask-multidb/README,sha256=AfiP5foaV2odZxXxuUuSIS6YhkIpR7CsOo2mpuxwHdc,40
flask_migrate/templates/flask-multidb/__pycache__/env.cpython-37.pyc,,
flask_migrate/templates/flask-multidb/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857
flask_migrate/templates/flask-multidb/env.py,sha256=F44iqsAxLTVBN_zD8CMUkdE7Aub4niHMmo5wl9mY4Uw,6190
flask_migrate/templates/flask-multidb/script.py.mako,sha256=198VPxVEN3NZ3vHcRuCxSoI4XnOYirGWt01qkbPKoJw,1246
flask_migrate/templates/flask/README,sha256=JL0NrjOrscPcKgRmQh1R3hlv1_rohDot0TvpmdM27Jk,41
flask_migrate/templates/flask/__pycache__/env.cpython-37.pyc,,
flask_migrate/templates/flask/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857
flask_migrate/templates/flask/env.py,sha256=ibK1hsdOsOBzXNU2yQoAIza7f_EFzaVSWwON_NSpNzQ,3344
flask_migrate/templates/flask/script.py.mako,sha256=8_xgA-gm_OhehnO7CiIijWgnm00ZlszEHtIHrAYFJl0,494

View File

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

View File

@ -0,0 +1 @@
flask_migrate

View File

@ -0,0 +1 @@
pip

View File

@ -0,0 +1,19 @@
Copyright 2006-2022 the Mako 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

@ -0,0 +1,87 @@
Metadata-Version: 2.1
Name: Mako
Version: 1.2.4
Summary: A super-fast templating language that borrows the best ideas from the existing templating languages.
Home-page: https://www.makotemplates.org/
Author: Mike Bayer
Author-email: mike@zzzcomputing.com
License: MIT
Project-URL: Documentation, https://docs.makotemplates.org
Project-URL: Issue Tracker, https://github.com/sqlalchemy/mako
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved :: MIT License
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
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 :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: MarkupSafe (>=0.9.2)
Requires-Dist: importlib-metadata ; python_version < "3.8"
Provides-Extra: babel
Requires-Dist: Babel ; extra == 'babel'
Provides-Extra: lingua
Requires-Dist: lingua ; extra == 'lingua'
Provides-Extra: testing
Requires-Dist: pytest ; extra == 'testing'
=========================
Mako Templates for Python
=========================
Mako is a template library written in Python. It provides a familiar, non-XML
syntax which compiles into Python modules for maximum performance. Mako's
syntax and API borrows from the best ideas of many others, including Django
templates, Cheetah, Myghty, and Genshi. Conceptually, Mako is an embedded
Python (i.e. Python Server Page) language, which refines the familiar ideas
of componentized layout and inheritance to produce one of the most
straightforward and flexible models available, while also maintaining close
ties to Python calling and scoping semantics.
Nutshell
========
::
<%inherit file="base.html"/>
<%
rows = [[v for v in range(0,10)] for row in range(0,10)]
%>
<table>
% for row in rows:
${makerow(row)}
% endfor
</table>
<%def name="makerow(row)">
<tr>
% for name in row:
<td>${name}</td>\
% endfor
</tr>
</%def>
Philosophy
===========
Python is a great scripting language. Don't reinvent the wheel...your templates can handle it !
Documentation
==============
See documentation for Mako at https://docs.makotemplates.org/en/latest/
License
========
Mako is licensed under an MIT-style license (see LICENSE).
Other incorporated projects may be licensed under different licenses.
All licenses allow for non-commercial and commercial use.

View File

@ -0,0 +1,74 @@
../../Scripts/mako-render.exe,sha256=hvjiILflMgFkhHX-uZ2KIdyfl7G5uUMZiErfi9nrvRE,108417
Mako-1.2.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
Mako-1.2.4.dist-info/LICENSE,sha256=dg8is-nqSlDrmSAb2N0RiGnygQjPtkzM5tGzBc-a6fo,1098
Mako-1.2.4.dist-info/METADATA,sha256=MlPkZcQ5bASEMtzkRaH8aRSQE6gmLH3KTnASUawz6eA,2909
Mako-1.2.4.dist-info/RECORD,,
Mako-1.2.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
Mako-1.2.4.dist-info/entry_points.txt,sha256=LsKkUsOsJQYbJ2M72hZCm968wi5K8Ywb5uFxCuN8Obk,512
Mako-1.2.4.dist-info/top_level.txt,sha256=LItdH8cDPetpUu8rUyBG3DObS6h9Gcpr9j_WLj2S-R0,5
mako/__init__.py,sha256=R1cQoVGhYA-fl43kNSPKm6kzdJOs28e8sq8WYMHctMQ,242
mako/__pycache__/__init__.cpython-37.pyc,,
mako/__pycache__/_ast_util.cpython-37.pyc,,
mako/__pycache__/ast.cpython-37.pyc,,
mako/__pycache__/cache.cpython-37.pyc,,
mako/__pycache__/cmd.cpython-37.pyc,,
mako/__pycache__/codegen.cpython-37.pyc,,
mako/__pycache__/compat.cpython-37.pyc,,
mako/__pycache__/exceptions.cpython-37.pyc,,
mako/__pycache__/filters.cpython-37.pyc,,
mako/__pycache__/lexer.cpython-37.pyc,,
mako/__pycache__/lookup.cpython-37.pyc,,
mako/__pycache__/parsetree.cpython-37.pyc,,
mako/__pycache__/pygen.cpython-37.pyc,,
mako/__pycache__/pyparser.cpython-37.pyc,,
mako/__pycache__/runtime.cpython-37.pyc,,
mako/__pycache__/template.cpython-37.pyc,,
mako/__pycache__/util.cpython-37.pyc,,
mako/_ast_util.py,sha256=BcwJLuE4E-aiFXi_fanO378Cn3Ou03bJxc6Incjse4Y,20247
mako/ast.py,sha256=h07xBpz2l19RSwpejrhkhgB4r5efpwGmsYOy_L8xvUc,6642
mako/cache.py,sha256=jkspun9tLgu0IVKSmo_fkL_DAbSTl2P5a5zkMBkjZvk,7680
mako/cmd.py,sha256=vQg9ip89KMsuZEGamCRAPg7UyDNlpMmnG3XHDNLHS5o,2814
mako/codegen.py,sha256=h1z8DGLkB92nbUz2OZGVmUKqPr9kVNbnNL8KnLizYAk,47309
mako/compat.py,sha256=Sa3Rzrjl44xo25nXUHbhfIrEoMgceq5-Ohl0FO6cCHk,1913
mako/exceptions.py,sha256=xQZKYdb-4d8rcrNFsFzjGSEuNG4upFqGNPErtSCDqfI,12530
mako/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
mako/ext/__pycache__/__init__.cpython-37.pyc,,
mako/ext/__pycache__/autohandler.cpython-37.pyc,,
mako/ext/__pycache__/babelplugin.cpython-37.pyc,,
mako/ext/__pycache__/beaker_cache.cpython-37.pyc,,
mako/ext/__pycache__/extract.cpython-37.pyc,,
mako/ext/__pycache__/linguaplugin.cpython-37.pyc,,
mako/ext/__pycache__/preprocessors.cpython-37.pyc,,
mako/ext/__pycache__/pygmentplugin.cpython-37.pyc,,
mako/ext/__pycache__/turbogears.cpython-37.pyc,,
mako/ext/autohandler.py,sha256=-hNv4VHbQplLGDt5e4mFsBC-QpfWMjKokOe0axDP308,1885
mako/ext/babelplugin.py,sha256=s6ZIAh1hUhsJIiF3j4soVHrFN_1cRJ_e3sEbz7ein7k,2091
mako/ext/beaker_cache.py,sha256=D6gh_ke7QOKiSJtq9v67RvmqCRMDJx-IwTcd-NDjKvk,2578
mako/ext/extract.py,sha256=EhXglj2eW5u80T3xWWB7jMgL8oNDfAQaD5E5IRiL9N0,4659
mako/ext/linguaplugin.py,sha256=iLip2gZ0ya5pooHrxwZrP8VFQfJidXmgPZ5h1j30Kow,1935
mako/ext/preprocessors.py,sha256=pEUbmfSO2zb4DuCt_-_oYnWypWiXs4MnJHxjTMiks5A,576
mako/ext/pygmentplugin.py,sha256=GuOd93TjetzpTfW5oUEtuPS7jKDHgJIH3Faiaq76S0c,4753
mako/ext/turbogears.py,sha256=mxFDF59NFK6cm__3qwGjZ1VAW0qdjJWNj23l6dcwqEg,2141
mako/filters.py,sha256=rlHJ2L5RFr5Gf-MyOJKZI7TSJpM5oBXH58niJWCp2-4,4658
mako/lexer.py,sha256=GOHNLeSlTIEa_yV8W5Qr27SjaPlJcO0Kij7Z2rpUkCA,15982
mako/lookup.py,sha256=_2VPSA2CgCiT0Vd9GnSIjyY5wlpXiB2C5luXJP7gym8,12429
mako/parsetree.py,sha256=pXbZP0orsT3iBIgWa9yD1TEfvytsCaXu2Ttws8RTMGM,19007
mako/pygen.py,sha256=K-l_hsvXfWdMTunfHyVxvA5EG4Uzr4Qaw6IUc3hw8zI,10416
mako/pyparser.py,sha256=diSXgo-ZwdZxbRsNZ1DmARQKVnlOFc6Qgx9Dc3wZB_U,7032
mako/runtime.py,sha256=MwO5T1rGy0yLeJiFh2hh5cO_kfd5_9fJq_vfBzLFe_0,27806
mako/template.py,sha256=gEhMPjKZ1Q_sYWWg6PLnRX-KBeTF0kBnyRZimlmgQks,23858
mako/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
mako/testing/__pycache__/__init__.cpython-37.pyc,,
mako/testing/__pycache__/_config.cpython-37.pyc,,
mako/testing/__pycache__/assertions.cpython-37.pyc,,
mako/testing/__pycache__/config.cpython-37.pyc,,
mako/testing/__pycache__/exclusions.cpython-37.pyc,,
mako/testing/__pycache__/fixtures.cpython-37.pyc,,
mako/testing/__pycache__/helpers.cpython-37.pyc,,
mako/testing/_config.py,sha256=k-qpnsnbXUoN-ykMN5BRpg84i1x0p6UsAddKQnrIytU,3566
mako/testing/assertions.py,sha256=XnYDPSnDFiEX9eO95OZ5LndZrUpJ6_xGofe6qDzJxqU,5162
mako/testing/config.py,sha256=wmYVZfzGvOK3mJUZpzmgO8-iIgvaCH41Woi4yDpxq6E,323
mako/testing/exclusions.py,sha256=_t6ADKdatk3f18tOfHV_ZY6u_ZwQsKphZ2MXJVSAOcI,1553
mako/testing/fixtures.py,sha256=nEp7wTusf7E0n3Q-BHJW2s_t1vx0KB9poadQ1BmIJzE,3044
mako/testing/helpers.py,sha256=kTaIg8OL1uvcuLptbRA_aJtGndIDDaxAzacYbv_Km1Q,1521
mako/util.py,sha256=XmYQmq6WfMAt-BPM7zhT9lybEqHVIWCM9wF1ukzqpew,10638

View File

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

View File

@ -0,0 +1,18 @@
[babel.extractors]
mako = mako.ext.babelplugin:extract [babel]
[console_scripts]
mako-render = mako.cmd:cmdline
[lingua.extractors]
mako = mako.ext.linguaplugin:LinguaMakoExtractor [lingua]
[pygments.lexers]
css+mako = mako.ext.pygmentplugin:MakoCssLexer
html+mako = mako.ext.pygmentplugin:MakoHtmlLexer
js+mako = mako.ext.pygmentplugin:MakoJavascriptLexer
mako = mako.ext.pygmentplugin:MakoLexer
xml+mako = mako.ext.pygmentplugin:MakoXmlLexer
[python.templating.engines]
mako = mako.ext.turbogears:TGPlugin

View File

@ -0,0 +1 @@
mako

View File

@ -0,0 +1 @@
pip

View File

@ -0,0 +1,28 @@
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

@ -0,0 +1,93 @@
Metadata-Version: 2.1
Name: MarkupSafe
Version: 2.1.5
Summary: Safely add untrusted strings to HTML/XML markup.
Home-page: https://palletsprojects.com/p/markupsafe/
Maintainer: Pallets
Maintainer-email: contact@palletsprojects.com
License: BSD-3-Clause
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://markupsafe.palletsprojects.com/
Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
Project-URL: Source Code, https://github.com/pallets/markupsafe/
Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/
Project-URL: Chat, https://discord.gg/pallets
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
Classifier: Topic :: Text Processing :: Markup :: HTML
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE.rst
MarkupSafe
==========
MarkupSafe implements a text object that escapes characters so it is
safe to use in HTML and XML. Characters that have special meanings are
replaced so that they display as the actual characters. This mitigates
injection attacks, meaning untrusted user input can safely be displayed
on a page.
Installing
----------
Install and update using `pip`_:
.. code-block:: text
pip install -U MarkupSafe
.. _pip: https://pip.pypa.io/en/stable/getting-started/
Examples
--------
.. code-block:: pycon
>>> from markupsafe import Markup, escape
>>> # escape replaces special characters and wraps in Markup
>>> escape("<script>alert(document.cookie);</script>")
Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
>>> # wrap in Markup to mark text "safe" and prevent escaping
>>> Markup("<strong>Hello</strong>")
Markup('<strong>hello</strong>')
>>> escape(Markup("<strong>Hello</strong>"))
Markup('<strong>hello</strong>')
>>> # Markup is a str subclass
>>> # methods and operators escape their arguments
>>> template = Markup("Hello <em>{name}</em>")
>>> template.format(name='"World"')
Markup('Hello <em>&#34;World&#34;</em>')
Donate
------
The Pallets organization develops and supports MarkupSafe 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://markupsafe.palletsprojects.com/
- Changes: https://markupsafe.palletsprojects.com/changes/
- PyPI Releases: https://pypi.org/project/MarkupSafe/
- Source Code: https://github.com/pallets/markupsafe/
- Issue Tracker: https://github.com/pallets/markupsafe/issues/
- Chat: https://discord.gg/pallets

View File

@ -0,0 +1,14 @@
markupsafe/__init__.py,sha256=m1ysNeqf55zbEoJtaovca40ivrkEFolPlw5bGoC5Gi4,11290
markupsafe/_native.py,sha256=_Q7UsXCOvgdonCgqG3l5asANI6eo50EKnDM-mlwEC5M,1776
markupsafe/_speedups.c,sha256=n3jzzaJwXcoN8nTFyA53f3vSqsWK2vujI-v6QYifjhQ,7403
markupsafe/_speedups.cp37-win_amd64.pyd,sha256=k3EXotF4ZpaL3vGiqsU1KBjvNWTXbO3VEEaPcLEhlN0,15872
markupsafe/_speedups.pyi,sha256=f5QtwIOP0eLrxh2v5p6SmaYmlcHIGIfmz0DovaqL0OU,238
markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
MarkupSafe-2.1.5.dist-info/LICENSE.rst,sha256=RjHsDbX9kKVH4zaBcmTGeYIUM4FG-KyUtKV_lu6MnsQ,1503
MarkupSafe-2.1.5.dist-info/METADATA,sha256=icNlaniV7YIQZ1BScCVqNaRtm7MAgfw8d3OBmoSVyAY,3096
MarkupSafe-2.1.5.dist-info/WHEEL,sha256=slqBGdqRnxanDn00BSYHhryEsWH_8CUurgRUvoMtK_Y,101
MarkupSafe-2.1.5.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
MarkupSafe-2.1.5.dist-info/RECORD,,
MarkupSafe-2.1.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
markupsafe/__pycache__/_native.cpython-37.pyc,,
markupsafe/__pycache__/__init__.cpython-37.pyc,,

View File

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

View File

@ -0,0 +1 @@
markupsafe

View File

@ -0,0 +1 @@
pip

View File

@ -0,0 +1,19 @@
Copyright 2005-2025 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

@ -0,0 +1,242 @@
Metadata-Version: 2.1
Name: SQLAlchemy
Version: 2.0.41
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: 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 :: 3.12
Classifier: Programming Language :: Python :: 3.13
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.6.0
Requires-Dist: greenlet >=1 ; python_version < "3.14" and (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 >=1 ; extra == 'aiomysql'
Requires-Dist: aiomysql >=0.2.0 ; extra == 'aiomysql'
Provides-Extra: aioodbc
Requires-Dist: greenlet >=1 ; extra == 'aioodbc'
Requires-Dist: aioodbc ; extra == 'aioodbc'
Provides-Extra: aiosqlite
Requires-Dist: greenlet >=1 ; extra == 'aiosqlite'
Requires-Dist: aiosqlite ; extra == 'aiosqlite'
Requires-Dist: typing-extensions !=3.10.0.1 ; extra == 'aiosqlite'
Provides-Extra: asyncio
Requires-Dist: greenlet >=1 ; extra == 'asyncio'
Provides-Extra: asyncmy
Requires-Dist: greenlet >=1 ; 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.10,!=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 >=8 ; 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 >=1 ; 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

@ -0,0 +1,531 @@
SQLAlchemy-2.0.41.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
SQLAlchemy-2.0.41.dist-info/LICENSE,sha256=EaDEEc4Kj89UgMeGJS1_hW8v_-Ozo7Z1Vsc0AX892Ko,1119
SQLAlchemy-2.0.41.dist-info/METADATA,sha256=-k8Key3C1yxoTXjNYrZlorD4jBcYFVyUJ-BT140Qs_8,9848
SQLAlchemy-2.0.41.dist-info/RECORD,,
SQLAlchemy-2.0.41.dist-info/WHEEL,sha256=slqBGdqRnxanDn00BSYHhryEsWH_8CUurgRUvoMtK_Y,101
SQLAlchemy-2.0.41.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
sqlalchemy/__init__.py,sha256=mlkGBLStUEQPy0ey8O9AKjH7xjqkiuYs0-mHk05pJy0,12942
sqlalchemy/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/__pycache__/events.cpython-37.pyc,,
sqlalchemy/__pycache__/exc.cpython-37.pyc,,
sqlalchemy/__pycache__/inspection.cpython-37.pyc,,
sqlalchemy/__pycache__/log.cpython-37.pyc,,
sqlalchemy/__pycache__/schema.cpython-37.pyc,,
sqlalchemy/__pycache__/types.cpython-37.pyc,,
sqlalchemy/connectors/__init__.py,sha256=28v5l6FpQmo62VSX0ry0ZykOLoH2BPGyAStaXaarfVo,494
sqlalchemy/connectors/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/connectors/__pycache__/aioodbc.cpython-37.pyc,,
sqlalchemy/connectors/__pycache__/asyncio.cpython-37.pyc,,
sqlalchemy/connectors/__pycache__/pyodbc.cpython-37.pyc,,
sqlalchemy/connectors/aioodbc.py,sha256=I28-DgGDz1FIUgRJsEpZxdd05jMmZUdE3YBpoMYbyBA,5462
sqlalchemy/connectors/asyncio.py,sha256=E0Y7T4bwfkxMZQQfODyRBgWhb8kLMkRSo6ON6vrfJPo,6351
sqlalchemy/connectors/pyodbc.py,sha256=Qv0fWBPIHuirljSjyq8JQp59sAHx6OrP_lO87vQoss4,8714
sqlalchemy/cyextension/__init__.py,sha256=zfsKIVdRE5w2P4Qe9p_xcTCfyStODRDV9_iIBs-SdCM,250
sqlalchemy/cyextension/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/cyextension/collections.cp37-win_amd64.pyd,sha256=XflzRXpXViOjroVNSI1lENhq_P2VxbKRITp5LWJlXwM,179712
sqlalchemy/cyextension/collections.pyx,sha256=GXPkr9cHRLW3Vcu-ik3dVBZMR-zf0Q5_K4J-_8yV-gk,12980
sqlalchemy/cyextension/immutabledict.cp37-win_amd64.pyd,sha256=M8ySdc4o209HZkszMYRJld-yLR1p3MNG21FyynQUmVQ,75776
sqlalchemy/cyextension/immutabledict.pxd,sha256=5iGndSbJCgCkNmRbJ_z14RANs2dSSnAzyiRPUTBk58Y,299
sqlalchemy/cyextension/immutabledict.pyx,sha256=IhB2pR49CrORXQ3LXMFpuCIRc6I08QNvIylE1cPQA5o,3668
sqlalchemy/cyextension/processors.cp37-win_amd64.pyd,sha256=1NHfucoRC8t_IKRYgmAho1xCBkOeaXeAOEvgjNLWink,61440
sqlalchemy/cyextension/processors.pyx,sha256=V9gzqXiNHWsa5DBgYl-3KzclFHY8kXGF_TD1xHFE7eM,1860
sqlalchemy/cyextension/resultproxy.cp37-win_amd64.pyd,sha256=zm_WlD26SEXUSQO2rfjVFkox0LlxGKDPw5U5AiCLGfY,64000
sqlalchemy/cyextension/resultproxy.pyx,sha256=h_RrKasbLtKK3LqUh6UiWtkumBlKtcN5eeB_1bZROMA,2827
sqlalchemy/cyextension/util.cp37-win_amd64.pyd,sha256=e2ytDB2cpAqyI8VSFCSBcP4KD1Y67Qt9FhpOJZVieFQ,76800
sqlalchemy/cyextension/util.pyx,sha256=50QYpSAKgLSUfhFEQgSN2e1qHWCMh_b6ZNlErDUS7ec,2621
sqlalchemy/dialects/__init__.py,sha256=6dkwhXOEYaEwFHlZWpa6Oh4Oht1XKUOLSGmj1QXdRP8,1831
sqlalchemy/dialects/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/dialects/__pycache__/_typing.cpython-37.pyc,,
sqlalchemy/dialects/_typing.py,sha256=dU8B2aZcBxM9zq7tfi4ZI-o13doagfgL-Is2XDrKXes,1001
sqlalchemy/dialects/mssql/__init__.py,sha256=5zGb8Oxnm5_Fa39MRj22hCG4HH22lzbJOCaCyeYHu7M,1968
sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/dialects/mssql/__pycache__/aioodbc.cpython-37.pyc,,
sqlalchemy/dialects/mssql/__pycache__/base.cpython-37.pyc,,
sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-37.pyc,,
sqlalchemy/dialects/mssql/__pycache__/json.cpython-37.pyc,,
sqlalchemy/dialects/mssql/__pycache__/provision.cpython-37.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-37.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-37.pyc,,
sqlalchemy/dialects/mssql/aioodbc.py,sha256=n8l0wTVfFShm0SMEfjys371tGTzyUJn4oIivtvBpFNc,2084
sqlalchemy/dialects/mssql/base.py,sha256=ZWLYRoYqkDbJbAMjLdPeXACKG4Y-VoO5oASj1mFmr5s,136725
sqlalchemy/dialects/mssql/information_schema.py,sha256=EYOuxhCII5kWBeEEuVH-1UWZhwJ7EgO9OFXL37zh-o0,8338
sqlalchemy/dialects/mssql/json.py,sha256=FNUpbyEH-X6sax97fpEnDZTkd5pkXj64Bk0SCGsaDfo,4885
sqlalchemy/dialects/mssql/provision.py,sha256=udeC0uRg9sz4hwUhM7vCJYBxOzE1DkxayYD6SGqz6Zc,5755
sqlalchemy/dialects/mssql/pymssql.py,sha256=XgC9NbmKHsCV729BnQboHdg1T901zQzcs8kjOiyxylQ,4223
sqlalchemy/dialects/mssql/pyodbc.py,sha256=n4MdPmr40CeWqDb53QufVHaPJpxWB7bwrgmhMjW-AQc,27933
sqlalchemy/dialects/mysql/__init__.py,sha256=sFKzurRfOGPJ3nMUTitceeHGg6fqTtzoijwzIGicqYg,2310
sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/base.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/dml.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/expression.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/json.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/provision.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-37.pyc,,
sqlalchemy/dialects/mysql/__pycache__/types.cpython-37.pyc,,
sqlalchemy/dialects/mysql/aiomysql.py,sha256=EdDaSRQ1Tjkc4mW04rbTJjVa5AdPUTOv0PlQqSLsmmg,10348
sqlalchemy/dialects/mysql/asyncmy.py,sha256=mawnMfeYixG0fkLREhNmsMwC_RtoWrVOfEa_efIsqk4,10420
sqlalchemy/dialects/mysql/base.py,sha256=Y2RvbWzeotfh4OpErvq5XBN76P_MvtEWFVqr-7Gtnnk,128357
sqlalchemy/dialects/mysql/cymysql.py,sha256=hq1eBnluo4V_2TXUIbmyzKWU8LBAzuqGElnZpMUoC7A,2384
sqlalchemy/dialects/mysql/dml.py,sha256=5Twbxc25IRehCJjCmLoDmSUoBChoz-UQ_jM0dV-CrFk,7993
sqlalchemy/dialects/mysql/enumerated.py,sha256=i8JV1FvCFfEfwPBBraDUkpV9NsMKEY_mII3J9GZmXp8,8690
sqlalchemy/dialects/mysql/expression.py,sha256=dOiqor_NYeEXW0K31dsNaXWGySrVIhnc7zSBCcV1Cr8,4264
sqlalchemy/dialects/mysql/json.py,sha256=E1oYKCYuK0UfA3lj60tf49f7JLwdgJjruMKu3mlUiuE,2350
sqlalchemy/dialects/mysql/mariadb.py,sha256=PW0_ZelBPyOlpzwQ4Te2hFdWwx6-44O8tGgztwToe_Q,1715
sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=mArMXLTwy-6q0grXK6wKSbnbUFjM9PzS56nQOuXJAvY,8900
sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=mnO9fAR2bv16DRX508Wez7T5XbbVpr_JdlsVZNMRoTo,8439
sqlalchemy/dialects/mysql/mysqldb.py,sha256=7A7CNxcY5MA-9JIgspaW4i2K48c-_UQiqc7xtlq-kgY,9831
sqlalchemy/dialects/mysql/provision.py,sha256=daMaDfKa131baX4AA2I7oOJKNZaRJFhF55KgCAFAiqQ,3832
sqlalchemy/dialects/mysql/pymysql.py,sha256=FgNr2hkQFFA32mx4iUZZ2dcAx8Yx3NirMrEqe3D8rmU,4218
sqlalchemy/dialects/mysql/pyodbc.py,sha256=Kvw-CK6FXdvkM7vR018F6VpKJr6sl3BlFjNSiFNtAI8,4437
sqlalchemy/dialects/mysql/reflection.py,sha256=y_kBy1UDduTBWwESl_sfDPWLstgTuRP_1HRKjtzYi8s,23519
sqlalchemy/dialects/mysql/reserved_words.py,sha256=tgAoz0SMyEf2O5QbXdx8QUNgVjdnCTv3bU5ogyFyxHs,9829
sqlalchemy/dialects/mysql/types.py,sha256=U8B6tMqLVdUh64mm7o9joQXN8fueGt7Vs0TnBARSvkY,25028
sqlalchemy/dialects/oracle/__init__.py,sha256=ZPTSdSlRSku4YPfSB9c5umBGnJGUE5SOfXii7yxEcVg,1859
sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/dialects/oracle/__pycache__/base.cpython-37.pyc,,
sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-37.pyc,,
sqlalchemy/dialects/oracle/__pycache__/dictionary.cpython-37.pyc,,
sqlalchemy/dialects/oracle/__pycache__/oracledb.cpython-37.pyc,,
sqlalchemy/dialects/oracle/__pycache__/provision.cpython-37.pyc,,
sqlalchemy/dialects/oracle/__pycache__/types.cpython-37.pyc,,
sqlalchemy/dialects/oracle/__pycache__/vector.cpython-37.pyc,,
sqlalchemy/dialects/oracle/base.py,sha256=buwYk9CakXfVAtWmcueN2RxRo1MWe-9fgwX-_kP5qvA,140804
sqlalchemy/dialects/oracle/cx_oracle.py,sha256=RRyBBdNLHsp3jT38UnCFndTjHmzBgjLp0kXdTkhykc4,58164
sqlalchemy/dialects/oracle/dictionary.py,sha256=cpMXbspWRmWClLac87lvBO7KMglz_tsF4NMQYHt_brc,20026
sqlalchemy/dialects/oracle/oracledb.py,sha256=htpTfSqkxrA1PeNXir2TIaIjND8E8IavsmJMLPXFmH0,34718
sqlalchemy/dialects/oracle/provision.py,sha256=ePX5ae92TOkcB0rKsb6jACGNtSgqd7RplYA1fpEkqUQ,8533
sqlalchemy/dialects/oracle/types.py,sha256=xuslK0g_5_LjZ-vlV6kNcUD-a3WfYtJle3cGGzGmud4,9374
sqlalchemy/dialects/oracle/vector.py,sha256=AtGAlI_AYvri5TFoHsKuZyZWBep1TVBsgD62pTzKThQ,8127
sqlalchemy/dialects/postgresql/__init__.py,sha256=b8c1eYvTvP3J8FFb6e9Deaw5raHeAfGqj1WQLOOaQ4E,4059
sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/_psycopg_common.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/array.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/base.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/json.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/named_types.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/operators.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pg_catalog.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/types.cpython-37.pyc,,
sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=Y7DYPQC1ETUHZ9NBn52mpnoai2nQmcRawN89cOHQiaQ,5860
sqlalchemy/dialects/postgresql/array.py,sha256=wrIkXmf0_XVd_CiweQSoJkegP018niFuUzQSAQsyuyI,17495
sqlalchemy/dialects/postgresql/asyncpg.py,sha256=ijwHuqbhm8LsPIjPXe1ux09ZelB_j5XEUc2xCIemUdU,42574
sqlalchemy/dialects/postgresql/base.py,sha256=ceLRq-l60TiOWXsyUPQmeH4FUYsXmByq4qmnEbPaP5o,189193
sqlalchemy/dialects/postgresql/dml.py,sha256=NwSlxWQH2IG_DVGvFha9z2TVVRiDEez5sf2yqjBrOK8,12465
sqlalchemy/dialects/postgresql/ext.py,sha256=1PNXGkIvPYPuVVujpKro73s8DiauXtjiGdV6Ngu4k4U,17883
sqlalchemy/dialects/postgresql/hstore.py,sha256=-dYcZeW4N6grdHIt31cjDkNuXk-rFUATXH1z7cImklY,12340
sqlalchemy/dialects/postgresql/json.py,sha256=kRDNFHCQmCrhmX_5Ug4ULtZmfZIXH9NWvTSnlu86Ah8,13209
sqlalchemy/dialects/postgresql/named_types.py,sha256=IHCjrPFqtXngwcSeHvoqe2xNxxw8afmwrin-uq4yrmk,18818
sqlalchemy/dialects/postgresql/operators.py,sha256=U2bri8df1IumpuB3PGrDE9k2N__yX2EJtPmKf1F7-bU,2937
sqlalchemy/dialects/postgresql/pg8000.py,sha256=TZOJKfVmPQrBUyDkTbgSenm7e5dyZdwRff58OGQRRAM,19304
sqlalchemy/dialects/postgresql/pg_catalog.py,sha256=piAIGRBByBC0a2LKrnvcs4kqGfroYhKaEj0Mi823hdc,9944
sqlalchemy/dialects/postgresql/provision.py,sha256=mVbELvHcXOQDAyXa3KLQxANyMy8ET0Bkhg8A_KN9_Fs,5945
sqlalchemy/dialects/postgresql/psycopg.py,sha256=wUODBYhaKgauqQm9tUWR8A9gSvsqpO0bNlVXRIedvAc,24109
sqlalchemy/dialects/postgresql/psycopg2.py,sha256=8owflXJl8HAVc1-qJNvR7X1SlPII4Sc3A-e-TS5B7s4,32924
sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=-r1exhBFvKWnzxqa9km5cXAwlsEppJiF_2t2V-bM_6U,1817
sqlalchemy/dialects/postgresql/ranges.py,sha256=ywBw2Iq-LBc8muVdtR8NTfPfAtnu7IBb99lHCNVhOIo,34009
sqlalchemy/dialects/postgresql/types.py,sha256=jXYuEf7DNtv7nl1OzlVEI5nJgDA423_kl6SbDdXnhbU,7942
sqlalchemy/dialects/sqlite/__init__.py,sha256=ScDazYTucj7D5CntecmIw36pcLG4Q6jP1HCxc_uOaCU,1239
sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-37.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/base.cpython-37.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-37.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/json.cpython-37.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-37.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-37.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-37.pyc,,
sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=jyfU_O2GjVq0tJq2ycucmjxGUO-t1SdLEP2TlPz4K5k,12656
sqlalchemy/dialects/sqlite/base.py,sha256=P2inS000YCN8HigPkceIE7jgo8wog_J9AeE20o4bPM8,105514
sqlalchemy/dialects/sqlite/dml.py,sha256=8HDXVO-BYD4MnLM3e4X5RWUeZOWr8JqveoRidrSAUN8,9401
sqlalchemy/dialects/sqlite/json.py,sha256=kue76-HGin7nqtDye6l512qixbCweMON72qINT531jE,2869
sqlalchemy/dialects/sqlite/provision.py,sha256=poguIVUc5uMMvUdXKQvaTYZCgzPZRbcndU06tXyQ7uM,5792
sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=RIGIUq6NeB7wnj_5_STxv7KPoSpey01_782d1VEk1Yg,5528
sqlalchemy/dialects/sqlite/pysqlite.py,sha256=m0Q97Q6K8V2kt1T0RxOPx_fdRF2g7d5MV8RX9DNpNLk,25984
sqlalchemy/dialects/type_migration_guidelines.txt,sha256=gyh3JCauAIFi_9XEfqm3vYv_jb2Eqcz2HjpmC9ZEPMM,8384
sqlalchemy/engine/__init__.py,sha256=QCVJfSmacMwrT3uKOjGEggw2nP6eoaYeCPPhh_vAZeI,2880
sqlalchemy/engine/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/_py_processors.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/_py_row.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/_py_util.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/base.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/characteristics.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/create.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/cursor.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/default.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/events.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/interfaces.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/mock.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/processors.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/reflection.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/result.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/row.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/strategies.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/url.cpython-37.pyc,,
sqlalchemy/engine/__pycache__/util.cpython-37.pyc,,
sqlalchemy/engine/_py_processors.py,sha256=DXgQhVD_KvSRTEG5fn44voI6X3_qUc7CuVLKTS8SPLY,3880
sqlalchemy/engine/_py_row.py,sha256=ylRDk1zEsS7XgRuVo4I2kNArKebr_1N3wGcbDLbH-xE,3915
sqlalchemy/engine/_py_util.py,sha256=gkXD7uZ2gwK1Qckr0pXSyegWvW8nAzfJKybWMxEJkmA,2558
sqlalchemy/engine/base.py,sha256=rTKKIZl5wRQyXELq8YEylht_v9uPp3IwJ2nZr4JHxmU,126181
sqlalchemy/engine/characteristics.py,sha256=mVV980KnAyV_2_CL_Wd-UjV9KAENY4b4Nl7puq5VVzg,4920
sqlalchemy/engine/create.py,sha256=bMfIIOKkpnWKOseiLuM7GDxDaIaL1Tptap_DuSnCW5Q,34095
sqlalchemy/engine/cursor.py,sha256=hGN1PfLvyvko1W0qFDEAHEY3rkXQR-6s94ewW-Kh0pk,78670
sqlalchemy/engine/default.py,sha256=V2SlqSDemj4yVPO248m4uTvhqi9HZxhRfqjMQpgQiDM,87686
sqlalchemy/engine/events.py,sha256=fix6y0u2stLXiUruOzswbH79hV72Dn7R8UyCQmVlMEY,38365
sqlalchemy/engine/interfaces.py,sha256=9CFmu4mVbqzccyWhzbbZsfWz0Uz5ArXaZwpflMBl1gg,116869
sqlalchemy/engine/mock.py,sha256=_Pixj3kZMA2mThtgnlxU-DLtCrPel7fNF1Y-wJZvTNI,4290
sqlalchemy/engine/processors.py,sha256=RWNjfb3YfAeNJbEsvM3NPvFGgc51fWsStzv5F-vJXqA,2440
sqlalchemy/engine/reflection.py,sha256=ABp0-ErZYNvNWDS9IhAIdN8lZ-Ejr_Na2ZzXZxoyrnc,77667
sqlalchemy/engine/result.py,sha256=X7_ZO5sWkq6Dwg931_4kNjhIDKYNy07Y9NCGYSniWqE,80193
sqlalchemy/engine/row.py,sha256=lOOvrGphIFJGBXRFkIiD_5cWvdyTSsOlyDFjTH5Isgc,12431
sqlalchemy/engine/strategies.py,sha256=yiyjnbLH0n4GoDY01jKYZN46kJPzKGtdcLMoAqJb5N0,461
sqlalchemy/engine/url.py,sha256=Q8kDWI4Y-e9NFZwzodCiTDaV9wKTO3uv-ADsakG_yWw,31991
sqlalchemy/engine/util.py,sha256=RKYAvUBtIvN7bFKmWv67we6QWNmNFsKyY9-QFoCo6TI,5849
sqlalchemy/event/__init__.py,sha256=lBGB1sQY9xMqfkokpSgB5DJeWvvNEjwUGVefosnlEBw,1022
sqlalchemy/event/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/event/__pycache__/api.cpython-37.pyc,,
sqlalchemy/event/__pycache__/attr.cpython-37.pyc,,
sqlalchemy/event/__pycache__/base.cpython-37.pyc,,
sqlalchemy/event/__pycache__/legacy.cpython-37.pyc,,
sqlalchemy/event/__pycache__/registry.cpython-37.pyc,,
sqlalchemy/event/api.py,sha256=yVUDVUtwmcCVFOK-b1Wwe486VpynykFnRzdFOxRZips,8333
sqlalchemy/event/attr.py,sha256=OJNDkrfnMN_zVG5nndCbMLQdjHcAWUkyh63BSCsUQO4,21406
sqlalchemy/event/base.py,sha256=-ASiV5Put9nTtWertuYN-zlcggXy9cTHisHquxsw1xM,15726
sqlalchemy/event/legacy.py,sha256=66l-Nd4atuCAtfegOv8l65qEL81ZV8mc0nY_OWnCRtU,8473
sqlalchemy/event/registry.py,sha256=ex3hwR-Q0hw9BInvjdQztvC68PjH1kjKPZALhe09Re4,11534
sqlalchemy/events.py,sha256=dljlE94Q8_sLFDniTWiL3w6kt17yPsl4cPV383rHvGc,542
sqlalchemy/exc.py,sha256=WJ-pOBKlfS37uBz4dWa_MYHMAi0NpoSTTqqpK1_iC-s,24810
sqlalchemy/ext/__init__.py,sha256=oZ15qCNcsI6TNS7GOr1BTg0ke5XvuKBBbwxDpbUBZfI,333
sqlalchemy/ext/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/associationproxy.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/automap.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/baked.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/compiler.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/horizontal_shard.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/hybrid.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/indexable.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/instrumentation.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/mutable.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/orderinglist.cpython-37.pyc,,
sqlalchemy/ext/__pycache__/serializer.cpython-37.pyc,,
sqlalchemy/ext/associationproxy.py,sha256=MvB7oDhuXpUynFt77pL5oP_WMKZfY2W_QFNscBg085Y,68075
sqlalchemy/ext/asyncio/__init__.py,sha256=q8_gBx_2IJTDh8pGhNw2RWRwSdabwqQAK7Ydi8K6fds,1342
sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/ext/asyncio/__pycache__/base.cpython-37.pyc,,
sqlalchemy/ext/asyncio/__pycache__/engine.cpython-37.pyc,,
sqlalchemy/ext/asyncio/__pycache__/exc.cpython-37.pyc,,
sqlalchemy/ext/asyncio/__pycache__/result.cpython-37.pyc,,
sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-37.pyc,,
sqlalchemy/ext/asyncio/__pycache__/session.cpython-37.pyc,,
sqlalchemy/ext/asyncio/base.py,sha256=bWTnXrH7EWD9i2o7Ry1oT3ucdLBQYvC2UTvOSfIQ2Jo,9313
sqlalchemy/ext/asyncio/engine.py,sha256=ZsipaCv3jaEnrx4YlX9dk2PhbYRAykpjkR01k-wyaj0,49789
sqlalchemy/ext/asyncio/exc.py,sha256=wCc5msrUy8ultaTaQoiI9neVnaeqzgyzkGjo6Lv4BSA,660
sqlalchemy/ext/asyncio/result.py,sha256=AZSgj5XSUs4efinJ7kC5CS-E7ZAW9apvLt1WWu7xsbo,31516
sqlalchemy/ext/asyncio/scoping.py,sha256=5rMnD5C3sLK_dkFjBVGagRwqXem_bOVxgA19EYVCTIU,54183
sqlalchemy/ext/asyncio/session.py,sha256=_gSnYN-kGQAEfgty-fHSE0OsaJWWD1WBF0KiehyfK2g,65704
sqlalchemy/ext/automap.py,sha256=J-erzP37JGlRSXYRN82Q0gVd24QUwBtcy-tK5Jjc5DA,63376
sqlalchemy/ext/baked.py,sha256=bS0SwosDjo9uj3268QlhkMvMbBrlEnejLPv0SiA8k2U,18323
sqlalchemy/ext/compiler.py,sha256=J2ggO_IQtsOKVBaURRgHHILUQuagb4cMqYFXAhzfMBs,21489
sqlalchemy/ext/declarative/__init__.py,sha256=itYJRCCslk1dx9cVsdypGxrS7i4Uj0FL9ZFiVox-SGM,1883
sqlalchemy/ext/declarative/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/ext/declarative/__pycache__/extensions.cpython-37.pyc,,
sqlalchemy/ext/declarative/extensions.py,sha256=m4SYzAaybQECU58j8NU-l2weCNFyDv_KLh8RVf_FApI,20095
sqlalchemy/ext/horizontal_shard.py,sha256=oqyQXWknES7bcVO-evE7fLaLb5asZKDGXureoIkFol8,17169
sqlalchemy/ext/hybrid.py,sha256=ZNEnWXr2XcsTWj-Jb4DwIjzUvLaDKIblcnV8NJSsO0I,54064
sqlalchemy/ext/indexable.py,sha256=VFmB1yvHJ4kI3zl-hPvRpMKyjIXndtuGUOfaredO6ik,11410
sqlalchemy/ext/instrumentation.py,sha256=HR8Ebk_pW3yzVDEIwtEvs3vESh-wsZgQik2whVTGB-M,16157
sqlalchemy/ext/mutable.py,sha256=8jV9eWaLUvy2YqGZzP571-2j56rAmyvblHHt3Jev5YM,38655
sqlalchemy/ext/mypy/__init__.py,sha256=_SefzxOkJ9pt8-V-OdC_l-FC2hKUY-zRopCT61jD6lk,247
sqlalchemy/ext/mypy/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/ext/mypy/__pycache__/apply.cpython-37.pyc,,
sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-37.pyc,,
sqlalchemy/ext/mypy/__pycache__/infer.cpython-37.pyc,,
sqlalchemy/ext/mypy/__pycache__/names.cpython-37.pyc,,
sqlalchemy/ext/mypy/__pycache__/plugin.cpython-37.pyc,,
sqlalchemy/ext/mypy/__pycache__/util.cpython-37.pyc,,
sqlalchemy/ext/mypy/apply.py,sha256=aLB8sIdkYT2y-VUcLzZG_TQFTiLS-OCvQvatxXsm58w,10915
sqlalchemy/ext/mypy/decl_class.py,sha256=bGAl5Pliq0dgfTnn-9TEzQJPLJbluLxqWpPa94Fdsig,17899
sqlalchemy/ext/mypy/infer.py,sha256=wvvjmBBvT0pNDzQZk-DwBSzsYMKK3cyPmaVtSp0sCzM,19957
sqlalchemy/ext/mypy/names.py,sha256=2K1etoLj3o7ntHeZYf5oIDX6cO4Vp56qs4WMBDixF7s,10814
sqlalchemy/ext/mypy/plugin.py,sha256=JjimTZbP5I7EbfSUGJm3htTRFgi8JZD2306GrU3bM3M,10053
sqlalchemy/ext/mypy/util.py,sha256=qlvEHUFWQIex-mQcBhvjdCK5-tgRCwaP1Pbt8ENv21k,10317
sqlalchemy/ext/orderinglist.py,sha256=3IIFjEqTT6VkZ6ny_taytbbmTgHKaoPgGZpfZ8lWPW4,14858
sqlalchemy/ext/serializer.py,sha256=Jaj99JFxeMmYEL1sDG2_qskT8_1beQY3BoXKU0VhyGY,6354
sqlalchemy/future/__init__.py,sha256=bRMk4Ib05mCxDBZfJnhTZk241rRKgBO1C5REMKnyD4M,528
sqlalchemy/future/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/future/__pycache__/engine.cpython-37.pyc,,
sqlalchemy/future/engine.py,sha256=ABOf5TMdGBV1Nr8BwFttsg15umImWZ4lMUnSKnQCc3o,510
sqlalchemy/inspection.py,sha256=ikV5Kx2RB1tv7_fmsdmbgAvg1SMV3AmcmvfEyJELtFg,5237
sqlalchemy/log.py,sha256=jy7isZDjgejMYW-LFO-F-wdse2LgPMi8UQMUOoPFApg,8895
sqlalchemy/orm/__init__.py,sha256=T0wrInkfQEJc83lG3RGlGKAJ7WCSFh8ej7hVOG912XU,8633
sqlalchemy/orm/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/_orm_constructors.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/_typing.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/attributes.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/base.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/bulk_persistence.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/clsregistry.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/collections.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/context.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/decl_api.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/decl_base.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/dependency.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/descriptor_props.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/dynamic.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/evaluator.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/events.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/exc.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/identity.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/instrumentation.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/interfaces.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/loading.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/mapped_collection.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/mapper.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/path_registry.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/persistence.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/properties.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/query.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/relationships.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/scoping.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/session.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/state.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/state_changes.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/strategies.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/strategy_options.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/sync.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/unitofwork.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/util.cpython-37.pyc,,
sqlalchemy/orm/__pycache__/writeonly.cpython-37.pyc,,
sqlalchemy/orm/_orm_constructors.py,sha256=PWGKa1m7ygq8v9RE5t_J8KBYp5zwmdQISy69Vac2uFA,106216
sqlalchemy/orm/_typing.py,sha256=m9CPK7mmf7W541gmXyAolA8p69mppDWjUagI9mQYS0s,5152
sqlalchemy/orm/attributes.py,sha256=eaLtbMlvk5dFLScHyCaVKlLK_D32D-Xd9DzjJTSrceY,95369
sqlalchemy/orm/base.py,sha256=zXrk9x7bkVvA6eJj_GAJjRRfxKS1JJRsoRvSB4iwQLs,28474
sqlalchemy/orm/bulk_persistence.py,sha256=U2Z1SIkU-Deto4OMV3psQZY3eKOAdLgc0-utQlYwavI,74786
sqlalchemy/orm/clsregistry.py,sha256=4GG7hpqALDdSWeXk6Pt-nIouakARhxKj660Xu6laIaE,18523
sqlalchemy/orm/collections.py,sha256=ydulkaeKgTZG_o8PRWTRpzMrQD6O2ZGqIKj5cGj3FX4,53879
sqlalchemy/orm/context.py,sha256=iphHqd6oUEvDizN-gXZjcsgDB_C1_Jk1LguAhow2wls,118414
sqlalchemy/orm/decl_api.py,sha256=y7nL89F5UVMpMendtYNbZz6joej3GPUS03ugNMrTLmE,66847
sqlalchemy/orm/decl_base.py,sha256=yBEm3C1N8kDxcByfiMaSLTUjJxWhgcsBimZedDizmE0,85433
sqlalchemy/orm/dependency.py,sha256=KEj5YjAV1w4w6LOkCdv8uPoCMPSK-mQJd8vNhwfqjuY,48925
sqlalchemy/orm/descriptor_props.py,sha256=7B-Ced89zBJi2gI1jFp2aPBBazB2-xWsdHSQKYwbKq4,38308
sqlalchemy/orm/dynamic.py,sha256=bY6ka1kKWB9s8_OFgCPrmPR5K-UTOFai5XBAxcRwd18,10116
sqlalchemy/orm/evaluator.py,sha256=zOP-8qaqI9PZ7v93BESv5SK91EwDg4NOe2G9z--4lo8,12732
sqlalchemy/orm/events.py,sha256=U5JpN0QrmqJS7J3510gB_AKxfR0cccXATYj4u6vbq_M,131052
sqlalchemy/orm/exc.py,sha256=GiNtriH9uWZi0uQ27KmuGM_83AXZ3fAuhgY7KUwLyuE,7873
sqlalchemy/orm/identity.py,sha256=PeO9wsd7omERGC2GmiUgcCHmpotCJUbZ3O2g23JGnME,9551
sqlalchemy/orm/instrumentation.py,sha256=wcTXkRTty_DjF0em2D5DZhqgdxZC1VhcnASiZ1ZE36w,25075
sqlalchemy/orm/interfaces.py,sha256=U2781Q6C7dcXRB05Fy1K2KtLLcdC2eCkOfbogs3F3N4,50287
sqlalchemy/orm/loading.py,sha256=Ny4Uo-6BJBKU7pE7icmTM8hF2DNP2MPSvR4nYxxCxrw,59959
sqlalchemy/orm/mapped_collection.py,sha256=wV5K1m0L3UnC-EHI9BviUL5Vpb-kYL66S-UUK3PfnQc,20239
sqlalchemy/orm/mapper.py,sha256=zKyxi3GMZmR6vc6GdSQfYVqgkhBA3gGnfAw31Jn3Hto,176128
sqlalchemy/orm/path_registry.py,sha256=LZ1l3tQAAjb_6OeM1bmhWHZzUzOuRPfRxDJTwoqiPsU,26727
sqlalchemy/orm/persistence.py,sha256=ce8d3BCQEPxJqXfoz4ul8w2hayzROxsFHen0ISk0lOI,63483
sqlalchemy/orm/properties.py,sha256=oTcrey56S0zq2ZJUo-4HBsUkmXw4jxGKttNHH-GY8Eg,30385
sqlalchemy/orm/query.py,sha256=7VB21pssrMAIeU1dKK7RrZs4OfVySB8CvUTcGqzFTxM,122177
sqlalchemy/orm/relationships.py,sha256=1cTw-RdKm--SL-WoQvataTJv-OVD9QWrTptDtkds-Ww,132333
sqlalchemy/orm/scoping.py,sha256=1vCxaZLnnSZbc3qg4yJW1dYiBwwlR1Ey9__4idvV5jY,80762
sqlalchemy/orm/session.py,sha256=bsz0Kl7qH2ZE7Ud0acM-dko2UqukrEuyHEHFIqPBSqc,201172
sqlalchemy/orm/state.py,sha256=Gip6hxecWRUHmIkExuvV0PO16kgEKBJDHBL4obz8uHU,38813
sqlalchemy/orm/state_changes.py,sha256=uyOAglDQS2eFeyHJVO-hWBnSaNHJQ9Voz7dmbCtCBoQ,7013
sqlalchemy/orm/strategies.py,sha256=MVLYXBcCXlsjyfLUcbF28g1JKgfG8j2FftjSb6XFOjQ,123276
sqlalchemy/orm/strategy_options.py,sha256=s7LdjDLfbamI74hqCM185Vqh4_2F4e5pol-B9meN9mo,87571
sqlalchemy/orm/sync.py,sha256=rGQsKGPor2saMCBUnudZsZGU0TKbGQdIqYiRgs8FhjI,5943
sqlalchemy/orm/unitofwork.py,sha256=_5rRqoPerq_KcMcf7srsEld9XuRD-fOVodYs81w-e9I,27829
sqlalchemy/orm/util.py,sha256=wMzIyZzDnDDxe8hgcduh_EoSbAHG2XK4-PLSRitIbKg,83308
sqlalchemy/orm/writeonly.py,sha256=rztFcabTQFKAfrtdd3sawTGEGjyPBlg9NxJGZcU6MtY,22983
sqlalchemy/pool/__init__.py,sha256=VqloraQaP2yt2MMfc0hJO51sM7KuHEHyApuDvh2FREI,1848
sqlalchemy/pool/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/pool/__pycache__/base.cpython-37.pyc,,
sqlalchemy/pool/__pycache__/events.cpython-37.pyc,,
sqlalchemy/pool/__pycache__/impl.cpython-37.pyc,,
sqlalchemy/pool/base.py,sha256=DAQF9d1L01X73dGZlS2Osjk12C9p14Skw-ReaG6BT0I,53848
sqlalchemy/pool/events.py,sha256=xlmNZCCEKmtPR3d3cT3oQ-DqbuphNr7ahPk5OV2ZTYQ,13521
sqlalchemy/pool/impl.py,sha256=YRadtSaTRtdexMJfoZIy8hThHj1q5IKeJ5JLZBaS-jc,19525
sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
sqlalchemy/schema.py,sha256=iPmSU1tudFs6dC11fatstMQPmk1It5QFvcO-xUAp9kw,3324
sqlalchemy/sql/__init__.py,sha256=8-2pW4PssFcOM50bW8u3bmsVEjEA6zSbOI_viThuLhs,5965
sqlalchemy/sql/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/_dml_constructors.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/_elements_constructors.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/_orm_types.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/_py_util.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/_typing.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/annotation.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/base.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/cache_key.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/coercions.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/compiler.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/crud.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/ddl.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/default_comparator.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/dml.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/elements.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/events.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/expression.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/functions.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/lambdas.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/naming.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/operators.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/roles.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/schema.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/selectable.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/sqltypes.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/traversals.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/type_api.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/util.cpython-37.pyc,,
sqlalchemy/sql/__pycache__/visitors.cpython-37.pyc,,
sqlalchemy/sql/_dml_constructors.py,sha256=0yFc_rMvnSuj7bIBH54IyYfWM2QEKmJBgKryUIRKy-M,3927
sqlalchemy/sql/_elements_constructors.py,sha256=usT81rfJnE4YWyQ3-l6vL13TSCTwU7WjWWpSIcVO6vQ,64968
sqlalchemy/sql/_orm_types.py,sha256=LRQgGBiB-Pejqjnu57QKej6bjLBfFsv8NJKaIDEArWc,645
sqlalchemy/sql/_py_util.py,sha256=WUT5MIpoD6XByDQ9M_ArPycWUdZO4PVg3qKTOfTkSKs,2248
sqlalchemy/sql/_selectable_constructors.py,sha256=renGwNcVdXtr1NLjFP5fiyLJyTpQmjvxLhwLKuM2Hts,21166
sqlalchemy/sql/_typing.py,sha256=KEPKrRS6ci-qTku78GZ7_qMcbeBt-BM4hsmB1u_G-WQ,13495
sqlalchemy/sql/annotation.py,sha256=1lFoOA6iKiY_YxqYBsJ2rjYc30Gm_D0FEKumMpmNGu8,18830
sqlalchemy/sql/base.py,sha256=LxLLlASqMsRenFc0NDe6Vj055GJp4uf_B31nbjp_EVU,76338
sqlalchemy/sql/cache_key.py,sha256=DzMm_m9T0XZ34bBJg9YlLgcTm5aC9gC57li1_VxKHDg,34710
sqlalchemy/sql/coercions.py,sha256=da7m0oXD0yx4oc89NunhhRy_ug55P7_MFj4M8T-SK6I,42069
sqlalchemy/sql/compiler.py,sha256=BzItiz5LUkOdG9Vs4NUhueDyVJ08OrSNzk-KPbX47vo,288311
sqlalchemy/sql/crud.py,sha256=nOWEWkEPDeDtZ9eW_yKniIMhRkv2pLyOArq-lE1WX-M,58526
sqlalchemy/sql/ddl.py,sha256=Up7IhXppABA7-YWLI5IoPde3rDmU3Cufs59jQ5G_jz4,49391
sqlalchemy/sql/default_comparator.py,sha256=fV8WRXlUuDfqKHckcRkA1x8nRJx5Xt_5KlWBpLCVgwo,17259
sqlalchemy/sql/dml.py,sha256=YRUvuNuvjZD0rHRebW9ITe92_k0FrpBvzhLNmyxiA1g,68069
sqlalchemy/sql/elements.py,sha256=yNTKl194fjTSP9DRm6jXVhcPGdw4GbX_j5dyYNvsMgs,183430
sqlalchemy/sql/events.py,sha256=V_PFYjVRlOCcTZdgGaKkQRStaF7aLfiLUJD954qjG0I,18770
sqlalchemy/sql/expression.py,sha256=5iKfdbH78Kqw4r67VKyTLLMycJMZVfAXV_HW0glE1fc,7748
sqlalchemy/sql/functions.py,sha256=M0vcB93SgL-7vlcHU2YkB_S30tUXLKmVhT75zryWiI8,66947
sqlalchemy/sql/lambdas.py,sha256=4Nk0uD-LhgHgiSTEUGiXoSaY4dTZJDKcSaWv6ciHf3c,50544
sqlalchemy/sql/naming.py,sha256=ujwzVnEMAI3XBpNZUJjkvYIuxP2Je3MpK1a4oN8WpJ8,7070
sqlalchemy/sql/operators.py,sha256=hlDdhKXY1mkWPBjEumPF4DDl4PC9dkSg8YqgdGPWFPY,79415
sqlalchemy/sql/roles.py,sha256=GEDH55xG83_EuEioW5XcKwfdyloX58cu0VnrAHHsrok,7985
sqlalchemy/sql/schema.py,sha256=xKZPqrNRoSs2va1UqIL4gfLQCbUhqoTMIvFVtSjaVNg,236616
sqlalchemy/sql/selectable.py,sha256=hdNulF4P_gjeCWgdCQ26JjdqRyBzVnUSU0uwpkZ-LDQ,248672
sqlalchemy/sql/sqltypes.py,sha256=8NUfIbgGPXHyyltklaWQsYeiu8pALL7K_lys8TMY5Fc,135722
sqlalchemy/sql/traversals.py,sha256=15BjlLsxWm7-S_ZCOtnAybo2x938EH7ThDbNUE3pLcE,34688
sqlalchemy/sql/type_api.py,sha256=p63dFjSbacFdQn8QcE9b2NxGQBOBpezJ4s5gdrO9arg,87277
sqlalchemy/sql/util.py,sha256=GHEZEz76TBn7c-dUm6qiykDWkcIT724u0X7SkgRhW-k,49615
sqlalchemy/sql/visitors.py,sha256=9RyCpmzkkzDCxBoEpMjZHGJGxxNidWrqW4sjLm_L_fI,37486
sqlalchemy/testing/__init__.py,sha256=U0gCwvaiU7zPRdExE9IqoT0JrS2MQCA215c3EeyN14A,3256
sqlalchemy/testing/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/assertions.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/assertsql.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/asyncio.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/config.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/engines.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/entities.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/exclusions.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/pickleable.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/profiling.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/provision.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/requirements.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/schema.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/util.cpython-37.pyc,,
sqlalchemy/testing/__pycache__/warnings.cpython-37.pyc,,
sqlalchemy/testing/assertions.py,sha256=3pg7WFQlyoEEAXBOZk4prY1T55jrNb3c_inikJilyEM,32444
sqlalchemy/testing/assertsql.py,sha256=eYp5X6p4IhPK_xcDNtm5rVBv-4km9UYiNxMLdvuPJlQ,17333
sqlalchemy/testing/asyncio.py,sha256=_gUdSw0onXWOe7lIdNGZ2vvlsK-zu_KznQ3extBr8v4,3965
sqlalchemy/testing/config.py,sha256=smPmR2_MM9rzvjOs_ZVuEI28cb8l7NSP_ADehr6RFgA,12481
sqlalchemy/testing/engines.py,sha256=lLSIGDhEG2YH0VM8l_J-5INSS7Q0m055qIclzwxi9oo,13888
sqlalchemy/testing/entities.py,sha256=JfrkjtAS_JWKGL-yyYtOkiyEru4yrFBT_4gYma-Clqo,3471
sqlalchemy/testing/exclusions.py,sha256=TjWrXtSNtrCQxmkF2nWm3ueBWBzg46KBpCC_bRp3hQA,12895
sqlalchemy/testing/fixtures/__init__.py,sha256=AKttorBSiaYwg3m_cR2TJzRFgN1YJMiTcth_GfHn1K8,1226
sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/testing/fixtures/__pycache__/base.cpython-37.pyc,,
sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-37.pyc,,
sqlalchemy/testing/fixtures/__pycache__/orm.cpython-37.pyc,,
sqlalchemy/testing/fixtures/__pycache__/sql.cpython-37.pyc,,
sqlalchemy/testing/fixtures/base.py,sha256=TvyZt7p9-re_6eLPO2HGkGsCNYIAsHgrGR5LGRFS1sw,12622
sqlalchemy/testing/fixtures/mypy.py,sha256=2HauwkIOIvFWmO6jgq44OJJ_17f21DJWvBzzraM8aJk,13087
sqlalchemy/testing/fixtures/orm.py,sha256=gYURL-1kdNZsDP2tq48Xbm5LsZA4TSR4rStZFy59UUY,6322
sqlalchemy/testing/fixtures/sql.py,sha256=QkSV5BPYoSmHjo904bhgJzgmyJGmgV3RfJu8xrLyW-g,16403
sqlalchemy/testing/pickleable.py,sha256=sE5abXG6sjAbQ67thkhd45PisuJwalESSWSI4Zjsn64,2988
sqlalchemy/testing/plugin/__init__.py,sha256=cG2c4xiyW6CL9hwdBXRKC1v0_SEckcZZW5rfalPPWCY,253
sqlalchemy/testing/plugin/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-37.pyc,,
sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-37.pyc,,
sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-37.pyc,,
sqlalchemy/testing/plugin/bootstrap.py,sha256=kzZvNqgAES8Q2Y0ScQ1CYKBZT2JGaoTpbXUL0qn_XJg,1736
sqlalchemy/testing/plugin/plugin_base.py,sha256=79OfIX8aeS7PJgry8wuvMMHtBvkCC5miNkHULN1RWjA,22357
sqlalchemy/testing/plugin/pytestplugin.py,sha256=WkDE508u_rTaCMH9t27gNsNFwYKBEQYyuS0fMfiTwk8,28491
sqlalchemy/testing/profiling.py,sha256=DVcy2RvIXvf1f6L1OX7IkZHZdxURqlZG91wiML0Y6hk,10472
sqlalchemy/testing/provision.py,sha256=-SG3P4bp-t28Ms6qzfAL03rPqlXhjwxbqPqtoe4uOmU,15204
sqlalchemy/testing/requirements.py,sha256=hNWs_JOvlbXJTabol7n6qFYQnO5YSCC260Iv_cWuKZ8,56884
sqlalchemy/testing/schema.py,sha256=UCYSoN-xYbXMDtK1nhNdcL0IGY-pQy6BBIdD7RUYKYY,6737
sqlalchemy/testing/suite/__init__.py,sha256=w-m10jFbq5pEg9a1UxRO46mPtfe5SBeuyGV-yHIbuls,741
sqlalchemy/testing/suite/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_cte.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_insert.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_results.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_select.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_types.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-37.pyc,,
sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-37.pyc,,
sqlalchemy/testing/suite/test_cte.py,sha256=foAV0X6LQ2Q-7I6VkfSpJiv2Pq82wMRI1jAMnhViRno,6662
sqlalchemy/testing/suite/test_ddl.py,sha256=rkHgQvdt4SH6w5CR6DzQFtj2C_QiN1DJ9FRokcnXD7k,12420
sqlalchemy/testing/suite/test_deprecations.py,sha256=DhzweNn4y8M6ZHnQEsO17z0ntZHpAQP9VPkz_KKX8JQ,5490
sqlalchemy/testing/suite/test_dialect.py,sha256=tbyrFBPi9vjfhUkDrTHg5n1WNiTGZCsV5KXir0WWOOY,23648
sqlalchemy/testing/suite/test_insert.py,sha256=9rgFol6F3vR-gbLDR_B7dsPM6OJvB6bO_6veoiR2cjA,19454
sqlalchemy/testing/suite/test_reflection.py,sha256=zFf8G6e28BsILZk_Bt0bHBkAVOHbMB6xb7ufLreg9JQ,114580
sqlalchemy/testing/suite/test_results.py,sha256=9X2WhQTkslm0lQsMaPziPkNi-dWBo-Ohs5K7QrhQKp8,17546
sqlalchemy/testing/suite/test_rowcount.py,sha256=0cjWNS4CsfZmBJukn88KtIi6C-KVIyd64shwwkg9Oc4,8158
sqlalchemy/testing/suite/test_select.py,sha256=_LSuUhkIM9PxsWHmmjpAKHdGPiuUAoA_aYjpkxBqbG0,64049
sqlalchemy/testing/suite/test_sequence.py,sha256=Ksp7o88PTLxgWwJHJFqHe8O006dixljRjzwHRwO_jXs,10240
sqlalchemy/testing/suite/test_types.py,sha256=zXTJP0rMAlYIHrUwESfvS_23TTo-BzL8VHFTXBAS1pw,70158
sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=Zlvu4l574cRTfMYXoEec-ALgIQucYpGLQXvgityo4Rc,6330
sqlalchemy/testing/suite/test_update_delete.py,sha256=8SnJlTOJhA8AQ_UwVqAumRIjvpMml1mFSt00vMpSnu8,4133
sqlalchemy/testing/util.py,sha256=zPMDNeMh1tcXgWCLK8A5qH5ZGkLPA8Vqxy_rIOHMPI0,15109
sqlalchemy/testing/warnings.py,sha256=mC8iK0YXuuYo6fpmfYopF6VJoa12o7_gsgbEKunPYQ4,1598
sqlalchemy/types.py,sha256=TaiynMoDCeLbr-ZEkEcNSS7bqhmz5xEmUaDzLkaAe8o,3244
sqlalchemy/util/__init__.py,sha256=DfcWdJ-qjP_K0zyk6rfnuxMhg9zylODiV6b6HPmR90I,8474
sqlalchemy/util/__pycache__/__init__.cpython-37.pyc,,
sqlalchemy/util/__pycache__/_collections.cpython-37.pyc,,
sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-37.pyc,,
sqlalchemy/util/__pycache__/_has_cy.cpython-37.pyc,,
sqlalchemy/util/__pycache__/_py_collections.cpython-37.pyc,,
sqlalchemy/util/__pycache__/compat.cpython-37.pyc,,
sqlalchemy/util/__pycache__/concurrency.cpython-37.pyc,,
sqlalchemy/util/__pycache__/deprecations.cpython-37.pyc,,
sqlalchemy/util/__pycache__/langhelpers.cpython-37.pyc,,
sqlalchemy/util/__pycache__/preloaded.cpython-37.pyc,,
sqlalchemy/util/__pycache__/queue.cpython-37.pyc,,
sqlalchemy/util/__pycache__/tool_support.cpython-37.pyc,,
sqlalchemy/util/__pycache__/topological.cpython-37.pyc,,
sqlalchemy/util/__pycache__/typing.cpython-37.pyc,,
sqlalchemy/util/_collections.py,sha256=MTetNkdk3299jkROxC6eEINaRu4gH6dcvG3mXG8hris,20868
sqlalchemy/util/_concurrency_py3k.py,sha256=NZfK7tXncTiceFJ4Jm2diV1z3ZauDBWUZlqf2qfXmcA,9458
sqlalchemy/util/_has_cy.py,sha256=-azchXDDoNCPGLFKQCTa16D6zC3EOztvNzf2jnQtwD8,1287
sqlalchemy/util/_py_collections.py,sha256=zCCcVqzspCm_k5QrGC7ysoLYuFDeiiuspioHE0NTxjs,17255
sqlalchemy/util/compat.py,sha256=WyFRFaRqX9s2JB_0yNfe5Wc1NUtalc08FFzS7KVBBiQ,9151
sqlalchemy/util/concurrency.py,sha256=bcYwD5hjzi9lsHH0wR1gTkVRFT621z-82W99jEuMzx0,3412
sqlalchemy/util/deprecations.py,sha256=fsbt7RWhTOGmgKo2zZoFYSaag3m9HN9r1H4vELFfcv4,12413
sqlalchemy/util/langhelpers.py,sha256=0uKRWtelP_VQArS5a6Lei9iWXSLRaFO_VJg_Wmfffik,70674
sqlalchemy/util/preloaded.py,sha256=v03avtAWRviCHUVg90Mu_mLJCmeXydNotORPpJkjWsc,6054
sqlalchemy/util/queue.py,sha256=Ne8VFlS1b4ArZi1siBB8HeqUDHu7vqvacbhMVT4VeI8,10507
sqlalchemy/util/tool_support.py,sha256=8I8lTYOQ-EEPYOdw0ghvuiSfKXhaqWDUrC795NBQOCw,6336
sqlalchemy/util/topological.py,sha256=lZH3zvwzIQAJYlQCijX7sGbbvoKQssaBjbdOa-320v4,3571
sqlalchemy/util/typing.py,sha256=H8jx79EbveEjY392zHizwglMfn1DUy_P11LJp4PJ5v4,23199

View File

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

View File

@ -0,0 +1 @@
sqlalchemy

View File

@ -0,0 +1 @@
pip

View File

@ -0,0 +1,28 @@
Copyright 2007 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

@ -0,0 +1,126 @@
Metadata-Version: 2.1
Name: Werkzeug
Version: 2.2.3
Summary: The comprehensive WSGI web application library.
Home-page: https://palletsprojects.com/p/werkzeug/
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
Maintainer: Pallets
Maintainer-email: contact@palletsprojects.com
License: BSD-3-Clause
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://werkzeug.palletsprojects.com/
Project-URL: Changes, https://werkzeug.palletsprojects.com/changes/
Project-URL: Source Code, https://github.com/pallets/werkzeug/
Project-URL: Issue Tracker, https://github.com/pallets/werkzeug/issues/
Project-URL: Twitter, https://twitter.com/PalletsTeam
Project-URL: Chat, https://discord.gg/pallets
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
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE.rst
Requires-Dist: MarkupSafe (>=2.1.1)
Provides-Extra: watchdog
Requires-Dist: watchdog ; extra == 'watchdog'
Werkzeug
========
*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff")
Werkzeug is a comprehensive `WSGI`_ web application library. It began as
a simple collection of various utilities for WSGI applications and has
become one of the most advanced WSGI utility libraries.
It includes:
- An interactive debugger that allows inspecting stack traces and
source code in the browser with an interactive interpreter for any
frame in the stack.
- A full-featured request object with objects to interact with
headers, query args, form data, files, and cookies.
- A response object that can wrap other WSGI applications and handle
streaming data.
- A routing system for matching URLs to endpoints and generating URLs
for endpoints, with an extensible system for capturing variables
from URLs.
- HTTP utilities to handle entity tags, cache control, dates, user
agents, cookies, files, and more.
- A threaded WSGI server for use while developing applications
locally.
- A test client for simulating HTTP requests during testing without
requiring running a server.
Werkzeug doesn't enforce any dependencies. It is up to the developer to
choose a template engine, database adapter, and even how to handle
requests. It can be used to build all sorts of end user applications
such as blogs, wikis, or bulletin boards.
`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
providing more structure and patterns for defining powerful
applications.
.. _WSGI: https://wsgi.readthedocs.io/en/latest/
.. _Flask: https://www.palletsprojects.com/p/flask/
Installing
----------
Install and update using `pip`_:
.. code-block:: text
pip install -U Werkzeug
.. _pip: https://pip.pypa.io/en/stable/getting-started/
A Simple Example
----------------
.. code-block:: python
from werkzeug.wrappers import Request, Response
@Request.application
def application(request):
return Response('Hello, World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, application)
Donate
------
The Pallets organization develops and supports Werkzeug 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://werkzeug.palletsprojects.com/
- Changes: https://werkzeug.palletsprojects.com/changes/
- PyPI Releases: https://pypi.org/project/Werkzeug/
- Source Code: https://github.com/pallets/werkzeug/
- Issue Tracker: https://github.com/pallets/werkzeug/issues/
- Website: https://palletsprojects.com/p/werkzeug/
- Twitter: https://twitter.com/PalletsTeam
- Chat: https://discord.gg/pallets

View File

@ -0,0 +1,98 @@
werkzeug/__init__.py,sha256=Hr0lQweC21HXPVBemSpBJUIzrbq2mn8h70J1h30QcqY,188
werkzeug/_internal.py,sha256=4lwshe63pFlCo0h2IMcmvhbugA50QXQvfLD5VoY5c4Q,16271
werkzeug/_reloader.py,sha256=hiP0z4bi6p_8UIJOtq7K0BV2dqCik5yztWLsDXeI_WE,14285
werkzeug/datastructures.py,sha256=v2WYfs1rb1OuQgXyLripHQFwgodrfTNCd5P5f8n3ueA,97081
werkzeug/datastructures.pyi,sha256=HRzDLc7A6qnwluhNqn6AT76CsLZIkAbVVqxn0AbfV-s,34506
werkzeug/exceptions.py,sha256=8-KOXguQkOLoBUdN-7x_WyHT92TcAmjTWNwG4t8QYIg,26527
werkzeug/formparser.py,sha256=DBRbbAnzspYUBzgfxPaZC7MjGAK_m5QTvdWoyvrhw4o,16516
werkzeug/http.py,sha256=NqJjYCt8tKn2XOEKPApq4L3q8zb8YFq3GFOe5gsonI4,42776
werkzeug/local.py,sha256=v-HEqr4bLpLHl4upCj97MOfUyCjW10Tp6mcNaFRFyew,22288
werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
werkzeug/security.py,sha256=7TVI0L62emBHAh-1RHB_KlwGYcE08pPCyU674Ho4aNE,4653
werkzeug/serving.py,sha256=XCiHFbMCFCgecKycgajhF4rFsGoemeN0xW1eTQqNt-g,37558
werkzeug/test.py,sha256=uMahfM81RqEN3d3Sp4SkN36Pi8oZpV6dTgFY0cW1_2c,48126
werkzeug/testapp.py,sha256=RJhT_2JweNiMKe304N3bF1zaIeMpRx-CIMERdeydfTY,9404
werkzeug/urls.py,sha256=Q9Si-eVh7yxk3rwkzrwGRm146FXVXgg9lBP3k0HUfVM,36600
werkzeug/user_agent.py,sha256=WclZhpvgLurMF45hsioSbS75H1Zb4iMQGKN3_yZ2oKo,1420
werkzeug/utils.py,sha256=BDX5_7OCMVgl-ib84bCEdBG5MVvrxaSlfdg7Cxh4ND0,25174
werkzeug/wsgi.py,sha256=-VKI2iwCgLb-VToIZeBpdutkTETxy9HkIwgcFC5orkU,36060
werkzeug/debug/__init__.py,sha256=wfJ2OmljsO5C0e0sXJpTUiG6bwGU6uHtFDDDMfJfQJk,18877
werkzeug/debug/console.py,sha256=dechqiCtHfs0AQZWZofUC1S97tCuvwDgT0gdha5KwWM,6208
werkzeug/debug/repr.py,sha256=vF3TLnYBohYr8V6Gz13PTJspQs42uv3gUJSzSbmHJBo,9472
werkzeug/debug/tbtools.py,sha256=6iohJovtBSFRAcgX7_aRY4r3e19PLj3FavYB3RM4CmA,13263
werkzeug/debug/shared/ICON_LICENSE.md,sha256=DhA6Y1gUl5Jwfg0NFN9Rj4VWITt8tUx0IvdGf0ux9-s,222
werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507
werkzeug/debug/shared/debugger.js,sha256=tg42SZs1SVmYWZ-_Fj5ELK5-FLHnGNQrei0K2By8Bw8,10521
werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191
werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200
werkzeug/debug/shared/style.css,sha256=-xSxzUEZGw_IqlDR5iZxitNl8LQUjBM-_Y4UAvXVH8g,6078
werkzeug/middleware/__init__.py,sha256=qfqgdT5npwG9ses3-FXQJf3aB95JYP1zchetH_T3PUw,500
werkzeug/middleware/dispatcher.py,sha256=Fh_w-KyWnTSYF-Lfv5dimQ7THSS7afPAZMmvc4zF1gg,2580
werkzeug/middleware/http_proxy.py,sha256=HE8VyhS7CR-E1O6_9b68huv8FLgGGR1DLYqkS3Xcp3Q,7558
werkzeug/middleware/lint.py,sha256=1w_UVKkAwq5wjjtCcDCDZwhAhWzPSZ0aDyUmbjAEeXw,13952
werkzeug/middleware/profiler.py,sha256=7pWYDYPC774S0-HYLkG3Uge58PGUMX7tWp_Cor3etvo,4883
werkzeug/middleware/proxy_fix.py,sha256=l7LC_LDu0Yd4SvUxS5SFigAJMzcIOGm6LNKl9IXJBSU,6974
werkzeug/middleware/shared_data.py,sha256=fXjrEkuqxUVLG1DLrOdQLc96QQdjftCBZ1oM5oK89h4,9528
werkzeug/routing/__init__.py,sha256=HpvahY7WwkLdV4Cq3Bsc3GrqNon4u6t8-vhbb9E5o00,4819
werkzeug/routing/converters.py,sha256=05bkekg64vLC6mqqK4ddBh589WH9yBsjtW8IJhdUBvw,6968
werkzeug/routing/exceptions.py,sha256=RklUDL9ajOv2fTcRNj4pb18Bs4Y-GKk4rIeTSfsqkks,4737
werkzeug/routing/map.py,sha256=XN4ZjzEF1SfMxtdov89SDE-1_U78KVnnobTfnHzqbmE,36757
werkzeug/routing/matcher.py,sha256=6VvQYCCOjyj1JKUZKuAiVA_U1nXtvvJ70pSbBUdL_1k,7509
werkzeug/routing/rules.py,sha256=3YsPpI9ZGcNmFiV2Go2Td1DvZ9ZdaMMnvGP1o17aMfc,31836
werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
werkzeug/sansio/http.py,sha256=k3nREBfU-r8fXCfSTKQenys25q9bzUOvdY-OVGrqztA,5107
werkzeug/sansio/multipart.py,sha256=vMZ85cvLD55clUTcTin2DtBv2GQRGh0_fExklnXKHoI,10055
werkzeug/sansio/request.py,sha256=SiGcx2cz-l81TlCCrKrT2fePqC64hN8fSg5Ig6J6vRs,20175
werkzeug/sansio/response.py,sha256=UTl-teQDDjovrZMkjj3ZQsHw-JtiFak5JfKEk1_vBYU,26026
werkzeug/sansio/utils.py,sha256=EjbqdHdT-JZWgjUQaaWSgBUIRprXUkrsMQQqJlJHpVU,4847
werkzeug/wrappers/__init__.py,sha256=kGyK7rOud3qCxll_jFyW15YarJhj1xtdf3ocx9ZheB8,120
werkzeug/wrappers/request.py,sha256=XmpTThXytTdznbPJnIsfdoIAvdi-THzTJQ9DsvARhn4,24026
werkzeug/wrappers/response.py,sha256=ii1IaN2eUfoB-tBqbn_46fCB_SVVL8Fu4qd6cu0AlEY,34963
Werkzeug-2.2.3.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475
Werkzeug-2.2.3.dist-info/METADATA,sha256=TIyameVEp5p52N9E1mTWWabY6g1sB0Dm25vznZQeXPQ,4416
Werkzeug-2.2.3.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
Werkzeug-2.2.3.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9
Werkzeug-2.2.3.dist-info/RECORD,,
Werkzeug-2.2.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
werkzeug/debug/__pycache__/console.cpython-37.pyc,,
werkzeug/debug/__pycache__/repr.cpython-37.pyc,,
werkzeug/debug/__pycache__/tbtools.cpython-37.pyc,,
werkzeug/debug/__pycache__/__init__.cpython-37.pyc,,
werkzeug/middleware/__pycache__/dispatcher.cpython-37.pyc,,
werkzeug/middleware/__pycache__/http_proxy.cpython-37.pyc,,
werkzeug/middleware/__pycache__/lint.cpython-37.pyc,,
werkzeug/middleware/__pycache__/profiler.cpython-37.pyc,,
werkzeug/middleware/__pycache__/proxy_fix.cpython-37.pyc,,
werkzeug/middleware/__pycache__/shared_data.cpython-37.pyc,,
werkzeug/middleware/__pycache__/__init__.cpython-37.pyc,,
werkzeug/routing/__pycache__/converters.cpython-37.pyc,,
werkzeug/routing/__pycache__/exceptions.cpython-37.pyc,,
werkzeug/routing/__pycache__/map.cpython-37.pyc,,
werkzeug/routing/__pycache__/matcher.cpython-37.pyc,,
werkzeug/routing/__pycache__/rules.cpython-37.pyc,,
werkzeug/routing/__pycache__/__init__.cpython-37.pyc,,
werkzeug/sansio/__pycache__/http.cpython-37.pyc,,
werkzeug/sansio/__pycache__/multipart.cpython-37.pyc,,
werkzeug/sansio/__pycache__/request.cpython-37.pyc,,
werkzeug/sansio/__pycache__/response.cpython-37.pyc,,
werkzeug/sansio/__pycache__/utils.cpython-37.pyc,,
werkzeug/sansio/__pycache__/__init__.cpython-37.pyc,,
werkzeug/wrappers/__pycache__/request.cpython-37.pyc,,
werkzeug/wrappers/__pycache__/response.cpython-37.pyc,,
werkzeug/wrappers/__pycache__/__init__.cpython-37.pyc,,
werkzeug/__pycache__/datastructures.cpython-37.pyc,,
werkzeug/__pycache__/exceptions.cpython-37.pyc,,
werkzeug/__pycache__/formparser.cpython-37.pyc,,
werkzeug/__pycache__/http.cpython-37.pyc,,
werkzeug/__pycache__/local.cpython-37.pyc,,
werkzeug/__pycache__/security.cpython-37.pyc,,
werkzeug/__pycache__/serving.cpython-37.pyc,,
werkzeug/__pycache__/test.cpython-37.pyc,,
werkzeug/__pycache__/testapp.cpython-37.pyc,,
werkzeug/__pycache__/urls.cpython-37.pyc,,
werkzeug/__pycache__/user_agent.cpython-37.pyc,,
werkzeug/__pycache__/utils.cpython-37.pyc,,
werkzeug/__pycache__/wsgi.cpython-37.pyc,,
werkzeug/__pycache__/_internal.cpython-37.pyc,,
werkzeug/__pycache__/_reloader.cpython-37.pyc,,
werkzeug/__pycache__/__init__.cpython-37.pyc,,

View File

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

View File

@ -0,0 +1 @@
werkzeug

View File

@ -0,0 +1 @@
pip

View File

@ -0,0 +1,19 @@
Copyright 2009-2023 Michael Bayer.
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

@ -0,0 +1,141 @@
Metadata-Version: 2.1
Name: alembic
Version: 1.12.1
Summary: A database migration tool for SQLAlchemy.
Home-page: https://alembic.sqlalchemy.org
Author: Mike Bayer
Author-email: mike_mp@zzzcomputing.com
License: MIT
Project-URL: Documentation, https://alembic.sqlalchemy.org/en/latest/
Project-URL: Changelog, https://alembic.sqlalchemy.org/en/latest/changelog.html
Project-URL: Source, https://github.com/sqlalchemy/alembic/
Project-URL: Issue Tracker, https://github.com/sqlalchemy/alembic/issues/
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Environment :: Console
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 :: 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: SQLAlchemy >=1.3.0
Requires-Dist: Mako
Requires-Dist: typing-extensions >=4
Requires-Dist: importlib-metadata ; python_version < "3.9"
Requires-Dist: importlib-resources ; python_version < "3.9"
Provides-Extra: tz
Requires-Dist: python-dateutil ; extra == 'tz'
Alembic is a database migrations tool written by the author
of `SQLAlchemy <http://www.sqlalchemy.org>`_. A migrations tool
offers the following functionality:
* Can emit ALTER statements to a database in order to change
the structure of tables and other constructs
* Provides a system whereby "migration scripts" may be constructed;
each script indicates a particular series of steps that can "upgrade" a
target database to a new version, and optionally a series of steps that can
"downgrade" similarly, doing the same steps in reverse.
* Allows the scripts to execute in some sequential manner.
The goals of Alembic are:
* Very open ended and transparent configuration and operation. A new
Alembic environment is generated from a set of templates which is selected
among a set of options when setup first occurs. The templates then deposit a
series of scripts that define fully how database connectivity is established
and how migration scripts are invoked; the migration scripts themselves are
generated from a template within that series of scripts. The scripts can
then be further customized to define exactly how databases will be
interacted with and what structure new migration files should take.
* Full support for transactional DDL. The default scripts ensure that all
migrations occur within a transaction - for those databases which support
this (Postgresql, Microsoft SQL Server), migrations can be tested with no
need to manually undo changes upon failure.
* Minimalist script construction. Basic operations like renaming
tables/columns, adding/removing columns, changing column attributes can be
performed through one line commands like alter_column(), rename_table(),
add_constraint(). There is no need to recreate full SQLAlchemy Table
structures for simple operations like these - the functions themselves
generate minimalist schema structures behind the scenes to achieve the given
DDL sequence.
* "auto generation" of migrations. While real world migrations are far more
complex than what can be automatically determined, Alembic can still
eliminate the initial grunt work in generating new migration directives
from an altered schema. The ``--autogenerate`` feature will inspect the
current status of a database using SQLAlchemy's schema inspection
capabilities, compare it to the current state of the database model as
specified in Python, and generate a series of "candidate" migrations,
rendering them into a new migration script as Python directives. The
developer then edits the new file, adding additional directives and data
migrations as needed, to produce a finished migration. Table and column
level changes can be detected, with constraints and indexes to follow as
well.
* Full support for migrations generated as SQL scripts. Those of us who
work in corporate environments know that direct access to DDL commands on a
production database is a rare privilege, and DBAs want textual SQL scripts.
Alembic's usage model and commands are oriented towards being able to run a
series of migrations into a textual output file as easily as it runs them
directly to a database. Care must be taken in this mode to not invoke other
operations that rely upon in-memory SELECTs of rows - Alembic tries to
provide helper constructs like bulk_insert() to help with data-oriented
operations that are compatible with script-based DDL.
* Non-linear, dependency-graph versioning. Scripts are given UUID
identifiers similarly to a DVCS, and the linkage of one script to the next
is achieved via human-editable markers within the scripts themselves.
The structure of a set of migration files is considered as a
directed-acyclic graph, meaning any migration file can be dependent
on any other arbitrary set of migration files, or none at
all. Through this open-ended system, migration files can be organized
into branches, multiple roots, and mergepoints, without restriction.
Commands are provided to produce new branches, roots, and merges of
branches automatically.
* Provide a library of ALTER constructs that can be used by any SQLAlchemy
application. The DDL constructs build upon SQLAlchemy's own DDLElement base
and can be used standalone by any application or script.
* At long last, bring SQLite and its inability to ALTER things into the fold,
but in such a way that SQLite's very special workflow needs are accommodated
in an explicit way that makes the most of a bad situation, through the
concept of a "batch" migration, where multiple changes to a table can
be batched together to form a series of instructions for a single, subsequent
"move-and-copy" workflow. You can even use "move-and-copy" workflow for
other databases, if you want to recreate a table in the background
on a busy system.
Documentation and status of Alembic is at https://alembic.sqlalchemy.org/
The SQLAlchemy Project
======================
Alembic is part of the `SQLAlchemy Project <https://www.sqlalchemy.org>`_ and
adheres to the same standards and conventions as the core project.
Development / Bug reporting / Pull requests
___________________________________________
Please refer to the
`SQLAlchemy Community Guide <https://www.sqlalchemy.org/develop.html>`_ for
guidelines on coding and participating in this project.
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
=======
Alembic is distributed under the `MIT license
<https://opensource.org/licenses/MIT>`_.

View File

@ -0,0 +1,148 @@
../../Scripts/alembic.exe,sha256=2S_vn6fxm7klAP1BCNip1TfE0NH8d61p4N_hb77tHTo,108417
alembic-1.12.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
alembic-1.12.1.dist-info/LICENSE,sha256=soUmiob0QW6vTQWyrjiAwVb3xZqPk1pAK8BW6vszrwg,1058
alembic-1.12.1.dist-info/METADATA,sha256=D9-LeKL0unLPg2JKmlFMB5NAxt9N9y-8oVEGOUHbQnU,7306
alembic-1.12.1.dist-info/RECORD,,
alembic-1.12.1.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
alembic-1.12.1.dist-info/entry_points.txt,sha256=aykM30soxwGN0pB7etLc1q0cHJbL9dy46RnK9VX4LLw,48
alembic-1.12.1.dist-info/top_level.txt,sha256=FwKWd5VsPFC8iQjpu1u9Cn-JnK3-V1RhUCmWqz1cl-s,8
alembic/__init__.py,sha256=gczqgDgBRw3aV70aNeH6WGu0WdASQf_YiChV12qCRRI,75
alembic/__main__.py,sha256=373m7-TBh72JqrSMYviGrxCHZo-cnweM8AGF8A22PmY,78
alembic/__pycache__/__init__.cpython-37.pyc,,
alembic/__pycache__/__main__.cpython-37.pyc,,
alembic/__pycache__/command.cpython-37.pyc,,
alembic/__pycache__/config.cpython-37.pyc,,
alembic/__pycache__/context.cpython-37.pyc,,
alembic/__pycache__/environment.cpython-37.pyc,,
alembic/__pycache__/migration.cpython-37.pyc,,
alembic/__pycache__/op.cpython-37.pyc,,
alembic/autogenerate/__init__.py,sha256=4IHgWH89pForRq-yCDZhGjjVtsfGX5ECWNPuUs8nGUk,351
alembic/autogenerate/__pycache__/__init__.cpython-37.pyc,,
alembic/autogenerate/__pycache__/api.cpython-37.pyc,,
alembic/autogenerate/__pycache__/compare.cpython-37.pyc,,
alembic/autogenerate/__pycache__/render.cpython-37.pyc,,
alembic/autogenerate/__pycache__/rewriter.cpython-37.pyc,,
alembic/autogenerate/api.py,sha256=MNn0Xtmj44aMFjfiR0LMkbxOynHyiyaRBnrj5EkImm4,21967
alembic/autogenerate/compare.py,sha256=gSCjxrkQAl0rJD6o9Ln8wNxGVNU6FrWzKZYVkH5Tmac,47042
alembic/autogenerate/render.py,sha256=Fik2aPZEIxOlTCrBd0UiPxnX5SFG__CvfXqMWoJr6lw,34475
alembic/autogenerate/rewriter.py,sha256=Osba8GFVeqiX1ypGJW7Axt0ui2EROlaFtVZdMFbhzZ0,7384
alembic/command.py,sha256=ze4pYvKpB-FtF8rduY6F6n3XHqeA-15iXaaEDeNHVzI,21588
alembic/config.py,sha256=68e1nmYU5Nfh0bNRqRWUygSilDl1p0G_U1zZ8ifgmD8,21931
alembic/context.py,sha256=hK1AJOQXJ29Bhn276GYcosxeG7pC5aZRT5E8c4bMJ4Q,195
alembic/context.pyi,sha256=FLsT0be_vO_ozlC05EJkWR5olDPoTVq-7tgtoM5wSAw,31463
alembic/ddl/__init__.py,sha256=xXr1W6PePe0gCLwR42ude0E6iru9miUFc1fCeQN4YP8,137
alembic/ddl/__pycache__/__init__.cpython-37.pyc,,
alembic/ddl/__pycache__/base.cpython-37.pyc,,
alembic/ddl/__pycache__/impl.cpython-37.pyc,,
alembic/ddl/__pycache__/mssql.cpython-37.pyc,,
alembic/ddl/__pycache__/mysql.cpython-37.pyc,,
alembic/ddl/__pycache__/oracle.cpython-37.pyc,,
alembic/ddl/__pycache__/postgresql.cpython-37.pyc,,
alembic/ddl/__pycache__/sqlite.cpython-37.pyc,,
alembic/ddl/base.py,sha256=cCY3NldMRggrKd9bZ0mFRBE9GNDaAy0UJcM3ey4Utgw,9638
alembic/ddl/impl.py,sha256=Z3GpNM2KwBpfl1UCam1YsYbSd0mQzRigOKQhUCLIPgE,25564
alembic/ddl/mssql.py,sha256=0k26xnUSZNj3qCHEMzRFbaWgUzKcV07I3_-Ns47VhO0,14105
alembic/ddl/mysql.py,sha256=ff8OE0zQ8YYjAgltBbtjQkDR-g9z65DNeFjEMm4sX6c,16675
alembic/ddl/oracle.py,sha256=E0VaZaUM_5mwqNiJVA3zOAK-cuHVVIv_-NmUbH1JuGQ,6097
alembic/ddl/postgresql.py,sha256=aO8pcVN5ycw1wG2m1RRt8dQUD1KgRa6T4rSzg9FPCkU,26457
alembic/ddl/sqlite.py,sha256=9q7NAxyeFwn9kWwQSc9RLeMFSos8waM7x9lnXdByh44,7613
alembic/environment.py,sha256=MM5lPayGT04H3aeng1H7GQ8HEAs3VGX5yy6mDLCPLT4,43
alembic/migration.py,sha256=MV6Fju6rZtn2fTREKzXrCZM6aIBGII4OMZFix0X-GLs,41
alembic/op.py,sha256=flHtcsVqOD-ZgZKK2pv-CJ5Cwh-KJ7puMUNXzishxLw,167
alembic/op.pyi,sha256=ldQBwAfzm_-ZsC3nizMuGoD34hjMKb4V_-Q1rR8q8LI,48591
alembic/operations/__init__.py,sha256=e0KQSZAgLpTWvyvreB7DWg7RJV_MWSOPVDgCqsd2FzY,318
alembic/operations/__pycache__/__init__.cpython-37.pyc,,
alembic/operations/__pycache__/base.cpython-37.pyc,,
alembic/operations/__pycache__/batch.cpython-37.pyc,,
alembic/operations/__pycache__/ops.cpython-37.pyc,,
alembic/operations/__pycache__/schemaobj.cpython-37.pyc,,
alembic/operations/__pycache__/toimpl.cpython-37.pyc,,
alembic/operations/base.py,sha256=2so4KisDNuOLw0CRiZqorIHrhuenpVoFbn3B0sNvDic,72471
alembic/operations/batch.py,sha256=uMvGJDlcTs0GSHasg4Gsdv1YcXeLOK_1lkRl3jk1ezY,26954
alembic/operations/ops.py,sha256=aP9Uz36k98O_Y-njKIAifyvyhi0g2zU6_igKMos91_s,93539
alembic/operations/schemaobj.py,sha256=-tWad8pgWUNWucbpTnPuFK_EEl913C0RADJhlBnrjhc,9393
alembic/operations/toimpl.py,sha256=K8nUmojtL94tyLSWdDD-e94IbghZ19k55iBIMvzMm5E,6993
alembic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
alembic/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
alembic/runtime/__pycache__/__init__.cpython-37.pyc,,
alembic/runtime/__pycache__/environment.cpython-37.pyc,,
alembic/runtime/__pycache__/migration.cpython-37.pyc,,
alembic/runtime/environment.py,sha256=qaerrw5jB7zYliNnCvIziaju4-tvQ451MuGW8PHnfvw,41019
alembic/runtime/migration.py,sha256=5UtTI_T0JtYzt6ZpeUhannMZOvXWiEymKFOpeCefaPY,49407
alembic/script/__init__.py,sha256=lSj06O391Iy5avWAiq8SPs6N8RBgxkSPjP8wpXcNDGg,100
alembic/script/__pycache__/__init__.cpython-37.pyc,,
alembic/script/__pycache__/base.cpython-37.pyc,,
alembic/script/__pycache__/revision.cpython-37.pyc,,
alembic/script/__pycache__/write_hooks.cpython-37.pyc,,
alembic/script/base.py,sha256=90SpT8wyTMTUuS0Svsy5YIoqJSrR-6CtYSzStmRvFT0,37174
alembic/script/revision.py,sha256=DE0nwvDOzdFo843brvnhs1DfP0jRC5EVQHrNihC7PUQ,61471
alembic/script/write_hooks.py,sha256=Nqj4zz3sm97kAPOpK1m-i2znJchiybO_TWT50oljlJw,4917
alembic/templates/async/README,sha256=ISVtAOvqvKk_5ThM5ioJE-lMkvf9IbknFUFVU_vPma4,58
alembic/templates/async/__pycache__/env.cpython-37.pyc,,
alembic/templates/async/alembic.ini.mako,sha256=k3IyGDG15Rp1JDweC0TiDauaKYNvj3clrGfhw6oV6MI,3505
alembic/templates/async/env.py,sha256=zbOCf3Y7w2lg92hxSwmG1MM_7y56i_oRH4AKp0pQBYo,2389
alembic/templates/async/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
alembic/templates/generic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
alembic/templates/generic/__pycache__/env.cpython-37.pyc,,
alembic/templates/generic/alembic.ini.mako,sha256=gZWFmH2A9sP0i7cxEDhJFkjGtTKUXaVna8QAbIaRqxk,3614
alembic/templates/generic/env.py,sha256=TLRWOVW3Xpt_Tpf8JFzlnoPn_qoUu8UV77Y4o9XD6yI,2103
alembic/templates/generic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
alembic/templates/multidb/README,sha256=dWLDhnBgphA4Nzb7sNlMfCS3_06YqVbHhz-9O5JNqyI,606
alembic/templates/multidb/__pycache__/env.cpython-37.pyc,,
alembic/templates/multidb/alembic.ini.mako,sha256=j_Y0yuZVoHy7sTPgSPd8DmbT2ItvAdWs7trYZSOmFnw,3708
alembic/templates/multidb/env.py,sha256=6zNjnW8mXGUk7erTsAvrfhvqoczJ-gagjVq1Ypg2YIQ,4230
alembic/templates/multidb/script.py.mako,sha256=N06nMtNSwHkgl0EBXDyMt8njp9tlOesR583gfq21nbY,1090
alembic/testing/__init__.py,sha256=kOxOh5nwmui9d-_CCq9WA4Udwy7ITjm453w74CTLZDo,1159
alembic/testing/__pycache__/__init__.cpython-37.pyc,,
alembic/testing/__pycache__/assertions.cpython-37.pyc,,
alembic/testing/__pycache__/env.cpython-37.pyc,,
alembic/testing/__pycache__/fixtures.cpython-37.pyc,,
alembic/testing/__pycache__/requirements.cpython-37.pyc,,
alembic/testing/__pycache__/schemacompare.cpython-37.pyc,,
alembic/testing/__pycache__/util.cpython-37.pyc,,
alembic/testing/__pycache__/warnings.cpython-37.pyc,,
alembic/testing/assertions.py,sha256=1CbJk8c8-WO9eJ0XJ0jJvMsNRLUrXV41NOeIJUAlOBk,5015
alembic/testing/env.py,sha256=zJacVb_z6uLs2U1TtkmnFH9P3_F-3IfYbVv4UEPOvfo,10754
alembic/testing/fixtures.py,sha256=NyP4wE_dFN9ZzSGiBagRu1cdzkka03nwJYJYHYrrkSY,9112
alembic/testing/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
alembic/testing/plugin/__pycache__/__init__.cpython-37.pyc,,
alembic/testing/plugin/__pycache__/bootstrap.cpython-37.pyc,,
alembic/testing/plugin/bootstrap.py,sha256=9C6wtjGrIVztZ928w27hsQE0KcjDLIUtUN3dvZKsMVk,50
alembic/testing/requirements.py,sha256=WByOiJxn2crazIXPq6-0cfqV95cfd9vP_ZQ1Cf2l8hY,4841
alembic/testing/schemacompare.py,sha256=7_4_0Y4UvuMiZ66pz1RC_P8Z1kYOP-R4Y5qUcNmcMKA,4535
alembic/testing/suite/__init__.py,sha256=MvE7-hwbaVN1q3NM-ztGxORU9dnIelUCINKqNxewn7Y,288
alembic/testing/suite/__pycache__/__init__.cpython-37.pyc,,
alembic/testing/suite/__pycache__/_autogen_fixtures.cpython-37.pyc,,
alembic/testing/suite/__pycache__/test_autogen_comments.cpython-37.pyc,,
alembic/testing/suite/__pycache__/test_autogen_computed.cpython-37.pyc,,
alembic/testing/suite/__pycache__/test_autogen_diffs.cpython-37.pyc,,
alembic/testing/suite/__pycache__/test_autogen_fks.cpython-37.pyc,,
alembic/testing/suite/__pycache__/test_autogen_identity.cpython-37.pyc,,
alembic/testing/suite/__pycache__/test_environment.cpython-37.pyc,,
alembic/testing/suite/__pycache__/test_op.cpython-37.pyc,,
alembic/testing/suite/_autogen_fixtures.py,sha256=cDq1pmzHe15S6dZPGNC6sqFaCQ3hLT_oPV2IDigUGQ0,9880
alembic/testing/suite/test_autogen_comments.py,sha256=aEGqKUDw4kHjnDk298aoGcQvXJWmZXcIX_2FxH4cJK8,6283
alembic/testing/suite/test_autogen_computed.py,sha256=qJeBpc8urnwTFvbwWrSTIbHVkRUuCXP-dKaNbUK2U2U,6077
alembic/testing/suite/test_autogen_diffs.py,sha256=T4SR1n_kmcOKYhR4W1-dA0e5sddJ69DSVL2HW96kAkE,8394
alembic/testing/suite/test_autogen_fks.py,sha256=AqFmb26Buex167HYa9dZWOk8x-JlB1OK3bwcvvjDFaU,32927
alembic/testing/suite/test_autogen_identity.py,sha256=kcuqngG7qXAKPJDX4U8sRzPKHEJECHuZ0DtuaS6tVkk,5824
alembic/testing/suite/test_environment.py,sha256=w9F0xnLEbALeR8k6_-Tz6JHvy91IqiTSypNasVzXfZQ,11877
alembic/testing/suite/test_op.py,sha256=2XQCdm_NmnPxHGuGj7hmxMzIhKxXNotUsKdACXzE1mM,1343
alembic/testing/util.py,sha256=CQrcQDA8fs_7ME85z5ydb-Bt70soIIID-qNY1vbR2dg,3350
alembic/testing/warnings.py,sha256=RxA7x_8GseANgw07Us8JN_1iGbANxaw6_VitX2ZGQH4,1078
alembic/util/__init__.py,sha256=cPF_jjFx7YRBByHHDqW3wxCIHsqnGfncEr_i238aduY,1202
alembic/util/__pycache__/__init__.cpython-37.pyc,,
alembic/util/__pycache__/compat.cpython-37.pyc,,
alembic/util/__pycache__/editor.cpython-37.pyc,,
alembic/util/__pycache__/exc.cpython-37.pyc,,
alembic/util/__pycache__/langhelpers.cpython-37.pyc,,
alembic/util/__pycache__/messaging.cpython-37.pyc,,
alembic/util/__pycache__/pyfiles.cpython-37.pyc,,
alembic/util/__pycache__/sqla_compat.cpython-37.pyc,,
alembic/util/compat.py,sha256=WN8jPPFB9ri_uuEM1HEaN1ak3RJc_H3x8NqvtFkoXuM,2279
alembic/util/editor.py,sha256=JIz6_BdgV8_oKtnheR6DZoB7qnrHrlRgWjx09AsTsUw,2546
alembic/util/exc.py,sha256=KQTru4zcgAmN4IxLMwLFS56XToUewaXB7oOLcPNjPwg,98
alembic/util/langhelpers.py,sha256=ZFGyGygHRbztOeajpajppyhd-Gp4PB5slMuvCFVrnmg,8591
alembic/util/messaging.py,sha256=B6T-loMhIOY3OTbG47Ywp1Df9LZn18PgjwpwLrD1VNg,3042
alembic/util/pyfiles.py,sha256=95J01FChN0j2uP3p72mjaOQvh5wC6XbdGtTDK8oEzsQ,3373
alembic/util/sqla_compat.py,sha256=94MHlkj43y-QQySz5dCUiJUNOPr3BF9TQ_BrP6ey-8w,18906

View File

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

View File

@ -0,0 +1,2 @@
[console_scripts]
alembic = alembic.config:main

View File

@ -0,0 +1 @@
alembic

View File

@ -0,0 +1,6 @@
import sys
from . import context
from . import op
__version__ = "1.12.1"

View File

@ -0,0 +1,4 @@
from .config import main
if __name__ == "__main__":
main(prog="alembic")

View File

@ -0,0 +1,10 @@
from .api import _render_migration_diffs
from .api import compare_metadata
from .api import produce_migrations
from .api import render_python_code
from .api import RevisionContext
from .compare import _produce_net_changes
from .compare import comparators
from .render import render_op_text
from .render import renderers
from .rewriter import Rewriter

View File

@ -0,0 +1,647 @@
from __future__ import annotations
import contextlib
from typing import Any
from typing import Dict
from typing import Iterator
from typing import List
from typing import Optional
from typing import Sequence
from typing import Set
from typing import TYPE_CHECKING
from typing import Union
from sqlalchemy import inspect
from . import compare
from . import render
from .. import util
from ..operations import ops
"""Provide the 'autogenerate' feature which can produce migration operations
automatically."""
if TYPE_CHECKING:
from sqlalchemy.engine import Connection
from sqlalchemy.engine import Dialect
from sqlalchemy.engine import Inspector
from sqlalchemy.sql.schema import MetaData
from sqlalchemy.sql.schema import SchemaItem
from ..config import Config
from ..operations.ops import DowngradeOps
from ..operations.ops import MigrationScript
from ..operations.ops import UpgradeOps
from ..runtime.environment import NameFilterParentNames
from ..runtime.environment import NameFilterType
from ..runtime.environment import ProcessRevisionDirectiveFn
from ..runtime.environment import RenderItemFn
from ..runtime.migration import MigrationContext
from ..script.base import Script
from ..script.base import ScriptDirectory
from ..script.revision import _GetRevArg
def compare_metadata(context: MigrationContext, metadata: MetaData) -> Any:
"""Compare a database schema to that given in a
:class:`~sqlalchemy.schema.MetaData` instance.
The database connection is presented in the context
of a :class:`.MigrationContext` object, which
provides database connectivity as well as optional
comparison functions to use for datatypes and
server defaults - see the "autogenerate" arguments
at :meth:`.EnvironmentContext.configure`
for details on these.
The return format is a list of "diff" directives,
each representing individual differences::
from alembic.migration import MigrationContext
from alembic.autogenerate import compare_metadata
from sqlalchemy import (
create_engine,
MetaData,
Column,
Integer,
String,
Table,
text,
)
import pprint
engine = create_engine("sqlite://")
with engine.begin() as conn:
conn.execute(
text(
'''
create table foo (
id integer not null primary key,
old_data varchar,
x integer
)
'''
)
)
conn.execute(text("create table bar (data varchar)"))
metadata = MetaData()
Table(
"foo",
metadata,
Column("id", Integer, primary_key=True),
Column("data", Integer),
Column("x", Integer, nullable=False),
)
Table("bat", metadata, Column("info", String))
mc = MigrationContext.configure(engine.connect())
diff = compare_metadata(mc, metadata)
pprint.pprint(diff, indent=2, width=20)
Output::
[
(
"add_table",
Table(
"bat",
MetaData(),
Column("info", String(), table=<bat>),
schema=None,
),
),
(
"remove_table",
Table(
"bar",
MetaData(),
Column("data", VARCHAR(), table=<bar>),
schema=None,
),
),
(
"add_column",
None,
"foo",
Column("data", Integer(), table=<foo>),
),
[
(
"modify_nullable",
None,
"foo",
"x",
{
"existing_comment": None,
"existing_server_default": False,
"existing_type": INTEGER(),
},
True,
False,
)
],
(
"remove_column",
None,
"foo",
Column("old_data", VARCHAR(), table=<foo>),
),
]
:param context: a :class:`.MigrationContext`
instance.
:param metadata: a :class:`~sqlalchemy.schema.MetaData`
instance.
.. seealso::
:func:`.produce_migrations` - produces a :class:`.MigrationScript`
structure based on metadata comparison.
"""
migration_script = produce_migrations(context, metadata)
return migration_script.upgrade_ops.as_diffs()
def produce_migrations(
context: MigrationContext, metadata: MetaData
) -> MigrationScript:
"""Produce a :class:`.MigrationScript` structure based on schema
comparison.
This function does essentially what :func:`.compare_metadata` does,
but then runs the resulting list of diffs to produce the full
:class:`.MigrationScript` object. For an example of what this looks like,
see the example in :ref:`customizing_revision`.
.. seealso::
:func:`.compare_metadata` - returns more fundamental "diff"
data from comparing a schema.
"""
autogen_context = AutogenContext(context, metadata=metadata)
migration_script = ops.MigrationScript(
rev_id=None,
upgrade_ops=ops.UpgradeOps([]),
downgrade_ops=ops.DowngradeOps([]),
)
compare._populate_migration_script(autogen_context, migration_script)
return migration_script
def render_python_code(
up_or_down_op: Union[UpgradeOps, DowngradeOps],
sqlalchemy_module_prefix: str = "sa.",
alembic_module_prefix: str = "op.",
render_as_batch: bool = False,
imports: Sequence[str] = (),
render_item: Optional[RenderItemFn] = None,
migration_context: Optional[MigrationContext] = None,
user_module_prefix: Optional[str] = None,
) -> str:
"""Render Python code given an :class:`.UpgradeOps` or
:class:`.DowngradeOps` object.
This is a convenience function that can be used to test the
autogenerate output of a user-defined :class:`.MigrationScript` structure.
:param up_or_down_op: :class:`.UpgradeOps` or :class:`.DowngradeOps` object
:param sqlalchemy_module_prefix: module prefix for SQLAlchemy objects
:param alembic_module_prefix: module prefix for Alembic constructs
:param render_as_batch: use "batch operations" style for rendering
:param imports: sequence of import symbols to add
:param render_item: callable to render items
:param migration_context: optional :class:`.MigrationContext`
:param user_module_prefix: optional string prefix for user-defined types
.. versionadded:: 1.11.0
"""
opts = {
"sqlalchemy_module_prefix": sqlalchemy_module_prefix,
"alembic_module_prefix": alembic_module_prefix,
"render_item": render_item,
"render_as_batch": render_as_batch,
"user_module_prefix": user_module_prefix,
}
if migration_context is None:
from ..runtime.migration import MigrationContext
from sqlalchemy.engine.default import DefaultDialect
migration_context = MigrationContext.configure(
dialect=DefaultDialect()
)
autogen_context = AutogenContext(migration_context, opts=opts)
autogen_context.imports = set(imports)
return render._indent(
render._render_cmd_body(up_or_down_op, autogen_context)
)
def _render_migration_diffs(
context: MigrationContext, template_args: Dict[Any, Any]
) -> None:
"""legacy, used by test_autogen_composition at the moment"""
autogen_context = AutogenContext(context)
upgrade_ops = ops.UpgradeOps([])
compare._produce_net_changes(autogen_context, upgrade_ops)
migration_script = ops.MigrationScript(
rev_id=None,
upgrade_ops=upgrade_ops,
downgrade_ops=upgrade_ops.reverse(),
)
render._render_python_into_templatevars(
autogen_context, migration_script, template_args
)
class AutogenContext:
"""Maintains configuration and state that's specific to an
autogenerate operation."""
metadata: Optional[MetaData] = None
"""The :class:`~sqlalchemy.schema.MetaData` object
representing the destination.
This object is the one that is passed within ``env.py``
to the :paramref:`.EnvironmentContext.configure.target_metadata`
parameter. It represents the structure of :class:`.Table` and other
objects as stated in the current database model, and represents the
destination structure for the database being examined.
While the :class:`~sqlalchemy.schema.MetaData` object is primarily
known as a collection of :class:`~sqlalchemy.schema.Table` objects,
it also has an :attr:`~sqlalchemy.schema.MetaData.info` dictionary
that may be used by end-user schemes to store additional schema-level
objects that are to be compared in custom autogeneration schemes.
"""
connection: Optional[Connection] = None
"""The :class:`~sqlalchemy.engine.base.Connection` object currently
connected to the database backend being compared.
This is obtained from the :attr:`.MigrationContext.bind` and is
ultimately set up in the ``env.py`` script.
"""
dialect: Optional[Dialect] = None
"""The :class:`~sqlalchemy.engine.Dialect` object currently in use.
This is normally obtained from the
:attr:`~sqlalchemy.engine.base.Connection.dialect` attribute.
"""
imports: Set[str] = None # type: ignore[assignment]
"""A ``set()`` which contains string Python import directives.
The directives are to be rendered into the ``${imports}`` section
of a script template. The set is normally empty and can be modified
within hooks such as the
:paramref:`.EnvironmentContext.configure.render_item` hook.
.. seealso::
:ref:`autogen_render_types`
"""
migration_context: MigrationContext = None # type: ignore[assignment]
"""The :class:`.MigrationContext` established by the ``env.py`` script."""
def __init__(
self,
migration_context: MigrationContext,
metadata: Optional[MetaData] = None,
opts: Optional[dict] = None,
autogenerate: bool = True,
) -> None:
if (
autogenerate
and migration_context is not None
and migration_context.as_sql
):
raise util.CommandError(
"autogenerate can't use as_sql=True as it prevents querying "
"the database for schema information"
)
if opts is None:
opts = migration_context.opts
self.metadata = metadata = (
opts.get("target_metadata", None) if metadata is None else metadata
)
if (
autogenerate
and metadata is None
and migration_context is not None
and migration_context.script is not None
):
raise util.CommandError(
"Can't proceed with --autogenerate option; environment "
"script %s does not provide "
"a MetaData object or sequence of objects to the context."
% (migration_context.script.env_py_location)
)
include_object = opts.get("include_object", None)
include_name = opts.get("include_name", None)
object_filters = []
name_filters = []
if include_object:
object_filters.append(include_object)
if include_name:
name_filters.append(include_name)
self._object_filters = object_filters
self._name_filters = name_filters
self.migration_context = migration_context
if self.migration_context is not None:
self.connection = self.migration_context.bind
self.dialect = self.migration_context.dialect
self.imports = set()
self.opts: Dict[str, Any] = opts
self._has_batch: bool = False
@util.memoized_property
def inspector(self) -> Inspector:
if self.connection is None:
raise TypeError(
"can't return inspector as this "
"AutogenContext has no database connection"
)
return inspect(self.connection)
@contextlib.contextmanager
def _within_batch(self) -> Iterator[None]:
self._has_batch = True
yield
self._has_batch = False
def run_name_filters(
self,
name: Optional[str],
type_: NameFilterType,
parent_names: NameFilterParentNames,
) -> bool:
"""Run the context's name filters and return True if the targets
should be part of the autogenerate operation.
This method should be run for every kind of name encountered within the
reflection side of an autogenerate operation, giving the environment
the chance to filter what names should be reflected as database
objects. The filters here are produced directly via the
:paramref:`.EnvironmentContext.configure.include_name` parameter.
"""
if "schema_name" in parent_names:
if type_ == "table":
table_name = name
else:
table_name = parent_names.get("table_name", None)
if table_name:
schema_name = parent_names["schema_name"]
if schema_name:
parent_names["schema_qualified_table_name"] = "%s.%s" % (
schema_name,
table_name,
)
else:
parent_names["schema_qualified_table_name"] = table_name
for fn in self._name_filters:
if not fn(name, type_, parent_names):
return False
else:
return True
def run_object_filters(
self,
object_: SchemaItem,
name: Optional[str],
type_: NameFilterType,
reflected: bool,
compare_to: Optional[SchemaItem],
) -> bool:
"""Run the context's object filters and return True if the targets
should be part of the autogenerate operation.
This method should be run for every kind of object encountered within
an autogenerate operation, giving the environment the chance
to filter what objects should be included in the comparison.
The filters here are produced directly via the
:paramref:`.EnvironmentContext.configure.include_object` parameter.
"""
for fn in self._object_filters:
if not fn(object_, name, type_, reflected, compare_to):
return False
else:
return True
run_filters = run_object_filters
@util.memoized_property
def sorted_tables(self):
"""Return an aggregate of the :attr:`.MetaData.sorted_tables`
collection(s).
For a sequence of :class:`.MetaData` objects, this
concatenates the :attr:`.MetaData.sorted_tables` collection
for each individual :class:`.MetaData` in the order of the
sequence. It does **not** collate the sorted tables collections.
"""
result = []
for m in util.to_list(self.metadata):
result.extend(m.sorted_tables)
return result
@util.memoized_property
def table_key_to_table(self):
"""Return an aggregate of the :attr:`.MetaData.tables` dictionaries.
The :attr:`.MetaData.tables` collection is a dictionary of table key
to :class:`.Table`; this method aggregates the dictionary across
multiple :class:`.MetaData` objects into one dictionary.
Duplicate table keys are **not** supported; if two :class:`.MetaData`
objects contain the same table key, an exception is raised.
"""
result = {}
for m in util.to_list(self.metadata):
intersect = set(result).intersection(set(m.tables))
if intersect:
raise ValueError(
"Duplicate table keys across multiple "
"MetaData objects: %s"
% (", ".join('"%s"' % key for key in sorted(intersect)))
)
result.update(m.tables)
return result
class RevisionContext:
"""Maintains configuration and state that's specific to a revision
file generation operation."""
generated_revisions: List[MigrationScript]
process_revision_directives: Optional[ProcessRevisionDirectiveFn]
def __init__(
self,
config: Config,
script_directory: ScriptDirectory,
command_args: Dict[str, Any],
process_revision_directives: Optional[
ProcessRevisionDirectiveFn
] = None,
) -> None:
self.config = config
self.script_directory = script_directory
self.command_args = command_args
self.process_revision_directives = process_revision_directives
self.template_args = {
"config": config # Let templates use config for
# e.g. multiple databases
}
self.generated_revisions = [self._default_revision()]
def _to_script(
self, migration_script: MigrationScript
) -> Optional[Script]:
template_args: Dict[str, Any] = self.template_args.copy()
if getattr(migration_script, "_needs_render", False):
autogen_context = self._last_autogen_context
# clear out existing imports if we are doing multiple
# renders
autogen_context.imports = set()
if migration_script.imports:
autogen_context.imports.update(migration_script.imports)
render._render_python_into_templatevars(
autogen_context, migration_script, template_args
)
assert migration_script.rev_id is not None
return self.script_directory.generate_revision(
migration_script.rev_id,
migration_script.message,
refresh=True,
head=migration_script.head,
splice=migration_script.splice,
branch_labels=migration_script.branch_label,
version_path=migration_script.version_path,
depends_on=migration_script.depends_on,
**template_args,
)
def run_autogenerate(
self, rev: _GetRevArg, migration_context: MigrationContext
) -> None:
self._run_environment(rev, migration_context, True)
def run_no_autogenerate(
self, rev: _GetRevArg, migration_context: MigrationContext
) -> None:
self._run_environment(rev, migration_context, False)
def _run_environment(
self,
rev: _GetRevArg,
migration_context: MigrationContext,
autogenerate: bool,
) -> None:
if autogenerate:
if self.command_args["sql"]:
raise util.CommandError(
"Using --sql with --autogenerate does not make any sense"
)
if set(self.script_directory.get_revisions(rev)) != set(
self.script_directory.get_revisions("heads")
):
raise util.CommandError("Target database is not up to date.")
upgrade_token = migration_context.opts["upgrade_token"]
downgrade_token = migration_context.opts["downgrade_token"]
migration_script = self.generated_revisions[-1]
if not getattr(migration_script, "_needs_render", False):
migration_script.upgrade_ops_list[-1].upgrade_token = upgrade_token
migration_script.downgrade_ops_list[
-1
].downgrade_token = downgrade_token
migration_script._needs_render = True
else:
migration_script._upgrade_ops.append(
ops.UpgradeOps([], upgrade_token=upgrade_token)
)
migration_script._downgrade_ops.append(
ops.DowngradeOps([], downgrade_token=downgrade_token)
)
autogen_context = AutogenContext(
migration_context, autogenerate=autogenerate
)
self._last_autogen_context: AutogenContext = autogen_context
if autogenerate:
compare._populate_migration_script(
autogen_context, migration_script
)
if self.process_revision_directives:
self.process_revision_directives(
migration_context, rev, self.generated_revisions
)
hook = migration_context.opts["process_revision_directives"]
if hook:
hook(migration_context, rev, self.generated_revisions)
for migration_script in self.generated_revisions:
migration_script._needs_render = True
def _default_revision(self) -> MigrationScript:
command_args: Dict[str, Any] = self.command_args
op = ops.MigrationScript(
rev_id=command_args["rev_id"] or util.rev_id(),
message=command_args["message"],
upgrade_ops=ops.UpgradeOps([]),
downgrade_ops=ops.DowngradeOps([]),
head=command_args["head"],
splice=command_args["splice"],
branch_label=command_args["branch_label"],
version_path=command_args["version_path"],
depends_on=command_args["depends_on"],
)
return op
def generate_scripts(self) -> Iterator[Optional[Script]]:
for generated_revision in self.generated_revisions:
yield self._to_script(generated_revision)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,227 @@
from __future__ import annotations
from typing import Any
from typing import Callable
from typing import Iterator
from typing import List
from typing import Optional
from typing import Type
from typing import TYPE_CHECKING
from typing import Union
from .. import util
from ..operations import ops
if TYPE_CHECKING:
from ..operations.ops import AddColumnOp
from ..operations.ops import AlterColumnOp
from ..operations.ops import CreateTableOp
from ..operations.ops import MigrateOperation
from ..operations.ops import MigrationScript
from ..operations.ops import ModifyTableOps
from ..operations.ops import OpContainer
from ..runtime.environment import _GetRevArg
from ..runtime.migration import MigrationContext
class Rewriter:
"""A helper object that allows easy 'rewriting' of ops streams.
The :class:`.Rewriter` object is intended to be passed along
to the
:paramref:`.EnvironmentContext.configure.process_revision_directives`
parameter in an ``env.py`` script. Once constructed, any number
of "rewrites" functions can be associated with it, which will be given
the opportunity to modify the structure without having to have explicit
knowledge of the overall structure.
The function is passed the :class:`.MigrationContext` object and
``revision`` tuple that are passed to the :paramref:`.Environment
Context.configure.process_revision_directives` function normally,
and the third argument is an individual directive of the type
noted in the decorator. The function has the choice of returning
a single op directive, which normally can be the directive that
was actually passed, or a new directive to replace it, or a list
of zero or more directives to replace it.
.. seealso::
:ref:`autogen_rewriter` - usage example
"""
_traverse = util.Dispatcher()
_chained: Optional[Rewriter] = None
def __init__(self) -> None:
self.dispatch = util.Dispatcher()
def chain(self, other: Rewriter) -> Rewriter:
"""Produce a "chain" of this :class:`.Rewriter` to another.
This allows two rewriters to operate serially on a stream,
e.g.::
writer1 = autogenerate.Rewriter()
writer2 = autogenerate.Rewriter()
@writer1.rewrites(ops.AddColumnOp)
def add_column_nullable(context, revision, op):
op.column.nullable = True
return op
@writer2.rewrites(ops.AddColumnOp)
def add_column_idx(context, revision, op):
idx_op = ops.CreateIndexOp(
"ixc", op.table_name, [op.column.name]
)
return [op, idx_op]
writer = writer1.chain(writer2)
:param other: a :class:`.Rewriter` instance
:return: a new :class:`.Rewriter` that will run the operations
of this writer, then the "other" writer, in succession.
"""
wr = self.__class__.__new__(self.__class__)
wr.__dict__.update(self.__dict__)
wr._chained = other
return wr
def rewrites(
self,
operator: Union[
Type[AddColumnOp],
Type[MigrateOperation],
Type[AlterColumnOp],
Type[CreateTableOp],
Type[ModifyTableOps],
],
) -> Callable:
"""Register a function as rewriter for a given type.
The function should receive three arguments, which are
the :class:`.MigrationContext`, a ``revision`` tuple, and
an op directive of the type indicated. E.g.::
@writer1.rewrites(ops.AddColumnOp)
def add_column_nullable(context, revision, op):
op.column.nullable = True
return op
"""
return self.dispatch.dispatch_for(operator)
def _rewrite(
self,
context: MigrationContext,
revision: _GetRevArg,
directive: MigrateOperation,
) -> Iterator[MigrateOperation]:
try:
_rewriter = self.dispatch.dispatch(directive)
except ValueError:
_rewriter = None
yield directive
else:
if self in directive._mutations:
yield directive
else:
for r_directive in util.to_list(
_rewriter(context, revision, directive), []
):
r_directive._mutations = r_directive._mutations.union(
[self]
)
yield r_directive
def __call__(
self,
context: MigrationContext,
revision: _GetRevArg,
directives: List[MigrationScript],
) -> None:
self.process_revision_directives(context, revision, directives)
if self._chained:
self._chained(context, revision, directives)
@_traverse.dispatch_for(ops.MigrationScript)
def _traverse_script(
self,
context: MigrationContext,
revision: _GetRevArg,
directive: MigrationScript,
) -> None:
upgrade_ops_list = []
for upgrade_ops in directive.upgrade_ops_list:
ret = self._traverse_for(context, revision, upgrade_ops)
if len(ret) != 1:
raise ValueError(
"Can only return single object for UpgradeOps traverse"
)
upgrade_ops_list.append(ret[0])
directive.upgrade_ops = upgrade_ops_list
downgrade_ops_list = []
for downgrade_ops in directive.downgrade_ops_list:
ret = self._traverse_for(context, revision, downgrade_ops)
if len(ret) != 1:
raise ValueError(
"Can only return single object for DowngradeOps traverse"
)
downgrade_ops_list.append(ret[0])
directive.downgrade_ops = downgrade_ops_list
@_traverse.dispatch_for(ops.OpContainer)
def _traverse_op_container(
self,
context: MigrationContext,
revision: _GetRevArg,
directive: OpContainer,
) -> None:
self._traverse_list(context, revision, directive.ops)
@_traverse.dispatch_for(ops.MigrateOperation)
def _traverse_any_directive(
self,
context: MigrationContext,
revision: _GetRevArg,
directive: MigrateOperation,
) -> None:
pass
def _traverse_for(
self,
context: MigrationContext,
revision: _GetRevArg,
directive: MigrateOperation,
) -> Any:
directives = list(self._rewrite(context, revision, directive))
for directive in directives:
traverser = self._traverse.dispatch(directive)
traverser(self, context, revision, directive)
return directives
def _traverse_list(
self,
context: MigrationContext,
revision: _GetRevArg,
directives: Any,
) -> None:
dest = []
for directive in directives:
dest.extend(self._traverse_for(context, revision, directive))
directives[:] = dest
def process_revision_directives(
self,
context: MigrationContext,
revision: _GetRevArg,
directives: List[MigrationScript],
) -> None:
self._traverse_list(context, revision, directives)

View File

@ -0,0 +1,744 @@
from __future__ import annotations
import os
from typing import List
from typing import Optional
from typing import TYPE_CHECKING
from typing import Union
from . import autogenerate as autogen
from . import util
from .runtime.environment import EnvironmentContext
from .script import ScriptDirectory
if TYPE_CHECKING:
from alembic.config import Config
from alembic.script.base import Script
from alembic.script.revision import _RevIdType
from .runtime.environment import ProcessRevisionDirectiveFn
def list_templates(config: Config):
"""List available templates.
:param config: a :class:`.Config` object.
"""
config.print_stdout("Available templates:\n")
for tempname in os.listdir(config.get_template_directory()):
with open(
os.path.join(config.get_template_directory(), tempname, "README")
) as readme:
synopsis = next(readme).rstrip()
config.print_stdout("%s - %s", tempname, synopsis)
config.print_stdout("\nTemplates are used via the 'init' command, e.g.:")
config.print_stdout("\n alembic init --template generic ./scripts")
def init(
config: Config,
directory: str,
template: str = "generic",
package: bool = False,
) -> None:
"""Initialize a new scripts directory.
:param config: a :class:`.Config` object.
:param directory: string path of the target directory
:param template: string name of the migration environment template to
use.
:param package: when True, write ``__init__.py`` files into the
environment location as well as the versions/ location.
"""
if os.access(directory, os.F_OK) and os.listdir(directory):
raise util.CommandError(
"Directory %s already exists and is not empty" % directory
)
template_dir = os.path.join(config.get_template_directory(), template)
if not os.access(template_dir, os.F_OK):
raise util.CommandError("No such template %r" % template)
if not os.access(directory, os.F_OK):
with util.status(
f"Creating directory {os.path.abspath(directory)!r}",
**config.messaging_opts,
):
os.makedirs(directory)
versions = os.path.join(directory, "versions")
with util.status(
f"Creating directory {os.path.abspath(versions)!r}",
**config.messaging_opts,
):
os.makedirs(versions)
script = ScriptDirectory(directory)
config_file: str | None = None
for file_ in os.listdir(template_dir):
file_path = os.path.join(template_dir, file_)
if file_ == "alembic.ini.mako":
assert config.config_file_name is not None
config_file = os.path.abspath(config.config_file_name)
if os.access(config_file, os.F_OK):
util.msg(
f"File {config_file!r} already exists, skipping",
**config.messaging_opts,
)
else:
script._generate_template(
file_path, config_file, script_location=directory
)
elif os.path.isfile(file_path):
output_file = os.path.join(directory, file_)
script._copy_file(file_path, output_file)
if package:
for path in [
os.path.join(os.path.abspath(directory), "__init__.py"),
os.path.join(os.path.abspath(versions), "__init__.py"),
]:
with util.status(f"Adding {path!r}", **config.messaging_opts):
with open(path, "w"):
pass
assert config_file is not None
util.msg(
"Please edit configuration/connection/logging "
f"settings in {config_file!r} before proceeding.",
**config.messaging_opts,
)
def revision(
config: Config,
message: Optional[str] = None,
autogenerate: bool = False,
sql: bool = False,
head: str = "head",
splice: bool = False,
branch_label: Optional[_RevIdType] = None,
version_path: Optional[str] = None,
rev_id: Optional[str] = None,
depends_on: Optional[str] = None,
process_revision_directives: Optional[ProcessRevisionDirectiveFn] = None,
) -> Union[Optional[Script], List[Optional[Script]]]:
"""Create a new revision file.
:param config: a :class:`.Config` object.
:param message: string message to apply to the revision; this is the
``-m`` option to ``alembic revision``.
:param autogenerate: whether or not to autogenerate the script from
the database; this is the ``--autogenerate`` option to
``alembic revision``.
:param sql: whether to dump the script out as a SQL string; when specified,
the script is dumped to stdout. This is the ``--sql`` option to
``alembic revision``.
:param head: head revision to build the new revision upon as a parent;
this is the ``--head`` option to ``alembic revision``.
:param splice: whether or not the new revision should be made into a
new head of its own; is required when the given ``head`` is not itself
a head. This is the ``--splice`` option to ``alembic revision``.
:param branch_label: string label to apply to the branch; this is the
``--branch-label`` option to ``alembic revision``.
:param version_path: string symbol identifying a specific version path
from the configuration; this is the ``--version-path`` option to
``alembic revision``.
:param rev_id: optional revision identifier to use instead of having
one generated; this is the ``--rev-id`` option to ``alembic revision``.
:param depends_on: optional list of "depends on" identifiers; this is the
``--depends-on`` option to ``alembic revision``.
:param process_revision_directives: this is a callable that takes the
same form as the callable described at
:paramref:`.EnvironmentContext.configure.process_revision_directives`;
will be applied to the structure generated by the revision process
where it can be altered programmatically. Note that unlike all
the other parameters, this option is only available via programmatic
use of :func:`.command.revision`
"""
script_directory = ScriptDirectory.from_config(config)
command_args = dict(
message=message,
autogenerate=autogenerate,
sql=sql,
head=head,
splice=splice,
branch_label=branch_label,
version_path=version_path,
rev_id=rev_id,
depends_on=depends_on,
)
revision_context = autogen.RevisionContext(
config,
script_directory,
command_args,
process_revision_directives=process_revision_directives,
)
environment = util.asbool(config.get_main_option("revision_environment"))
if autogenerate:
environment = True
if sql:
raise util.CommandError(
"Using --sql with --autogenerate does not make any sense"
)
def retrieve_migrations(rev, context):
revision_context.run_autogenerate(rev, context)
return []
elif environment:
def retrieve_migrations(rev, context):
revision_context.run_no_autogenerate(rev, context)
return []
elif sql:
raise util.CommandError(
"Using --sql with the revision command when "
"revision_environment is not configured does not make any sense"
)
if environment:
with EnvironmentContext(
config,
script_directory,
fn=retrieve_migrations,
as_sql=sql,
template_args=revision_context.template_args,
revision_context=revision_context,
):
script_directory.run_env()
# the revision_context now has MigrationScript structure(s) present.
# these could theoretically be further processed / rewritten *here*,
# in addition to the hooks present within each run_migrations() call,
# or at the end of env.py run_migrations_online().
scripts = [script for script in revision_context.generate_scripts()]
if len(scripts) == 1:
return scripts[0]
else:
return scripts
def check(config: "Config") -> None:
"""Check if revision command with autogenerate has pending upgrade ops.
:param config: a :class:`.Config` object.
.. versionadded:: 1.9.0
"""
script_directory = ScriptDirectory.from_config(config)
command_args = dict(
message=None,
autogenerate=True,
sql=False,
head="head",
splice=False,
branch_label=None,
version_path=None,
rev_id=None,
depends_on=None,
)
revision_context = autogen.RevisionContext(
config,
script_directory,
command_args,
)
def retrieve_migrations(rev, context):
revision_context.run_autogenerate(rev, context)
return []
with EnvironmentContext(
config,
script_directory,
fn=retrieve_migrations,
as_sql=False,
template_args=revision_context.template_args,
revision_context=revision_context,
):
script_directory.run_env()
# the revision_context now has MigrationScript structure(s) present.
migration_script = revision_context.generated_revisions[-1]
diffs = migration_script.upgrade_ops.as_diffs()
if diffs:
raise util.AutogenerateDiffsDetected(
f"New upgrade operations detected: {diffs}"
)
else:
config.print_stdout("No new upgrade operations detected.")
def merge(
config: Config,
revisions: _RevIdType,
message: Optional[str] = None,
branch_label: Optional[_RevIdType] = None,
rev_id: Optional[str] = None,
) -> Optional[Script]:
"""Merge two revisions together. Creates a new migration file.
:param config: a :class:`.Config` instance
:param message: string message to apply to the revision
:param branch_label: string label name to apply to the new revision
:param rev_id: hardcoded revision identifier instead of generating a new
one.
.. seealso::
:ref:`branches`
"""
script = ScriptDirectory.from_config(config)
template_args = {
"config": config # Let templates use config for
# e.g. multiple databases
}
environment = util.asbool(config.get_main_option("revision_environment"))
if environment:
def nothing(rev, context):
return []
with EnvironmentContext(
config,
script,
fn=nothing,
as_sql=False,
template_args=template_args,
):
script.run_env()
return script.generate_revision(
rev_id or util.rev_id(),
message,
refresh=True,
head=revisions,
branch_labels=branch_label,
**template_args, # type:ignore[arg-type]
)
def upgrade(
config: Config,
revision: str,
sql: bool = False,
tag: Optional[str] = None,
) -> None:
"""Upgrade to a later version.
:param config: a :class:`.Config` instance.
:param revision: string revision target or range for --sql mode
:param sql: if True, use ``--sql`` mode
:param tag: an arbitrary "tag" that can be intercepted by custom
``env.py`` scripts via the :meth:`.EnvironmentContext.get_tag_argument`
method.
"""
script = ScriptDirectory.from_config(config)
starting_rev = None
if ":" in revision:
if not sql:
raise util.CommandError("Range revision not allowed")
starting_rev, revision = revision.split(":", 2)
def upgrade(rev, context):
return script._upgrade_revs(revision, rev)
with EnvironmentContext(
config,
script,
fn=upgrade,
as_sql=sql,
starting_rev=starting_rev,
destination_rev=revision,
tag=tag,
):
script.run_env()
def downgrade(
config: Config,
revision: str,
sql: bool = False,
tag: Optional[str] = None,
) -> None:
"""Revert to a previous version.
:param config: a :class:`.Config` instance.
:param revision: string revision target or range for --sql mode
:param sql: if True, use ``--sql`` mode
:param tag: an arbitrary "tag" that can be intercepted by custom
``env.py`` scripts via the :meth:`.EnvironmentContext.get_tag_argument`
method.
"""
script = ScriptDirectory.from_config(config)
starting_rev = None
if ":" in revision:
if not sql:
raise util.CommandError("Range revision not allowed")
starting_rev, revision = revision.split(":", 2)
elif sql:
raise util.CommandError(
"downgrade with --sql requires <fromrev>:<torev>"
)
def downgrade(rev, context):
return script._downgrade_revs(revision, rev)
with EnvironmentContext(
config,
script,
fn=downgrade,
as_sql=sql,
starting_rev=starting_rev,
destination_rev=revision,
tag=tag,
):
script.run_env()
def show(config, rev):
"""Show the revision(s) denoted by the given symbol.
:param config: a :class:`.Config` instance.
:param revision: string revision target
"""
script = ScriptDirectory.from_config(config)
if rev == "current":
def show_current(rev, context):
for sc in script.get_revisions(rev):
config.print_stdout(sc.log_entry)
return []
with EnvironmentContext(config, script, fn=show_current):
script.run_env()
else:
for sc in script.get_revisions(rev):
config.print_stdout(sc.log_entry)
def history(
config: Config,
rev_range: Optional[str] = None,
verbose: bool = False,
indicate_current: bool = False,
) -> None:
"""List changeset scripts in chronological order.
:param config: a :class:`.Config` instance.
:param rev_range: string revision range
:param verbose: output in verbose mode.
:param indicate_current: indicate current revision.
"""
base: Optional[str]
head: Optional[str]
script = ScriptDirectory.from_config(config)
if rev_range is not None:
if ":" not in rev_range:
raise util.CommandError(
"History range requires [start]:[end], " "[start]:, or :[end]"
)
base, head = rev_range.strip().split(":")
else:
base = head = None
environment = (
util.asbool(config.get_main_option("revision_environment"))
or indicate_current
)
def _display_history(config, script, base, head, currents=()):
for sc in script.walk_revisions(
base=base or "base", head=head or "heads"
):
if indicate_current:
sc._db_current_indicator = sc.revision in currents
config.print_stdout(
sc.cmd_format(
verbose=verbose,
include_branches=True,
include_doc=True,
include_parents=True,
)
)
def _display_history_w_current(config, script, base, head):
def _display_current_history(rev, context):
if head == "current":
_display_history(config, script, base, rev, rev)
elif base == "current":
_display_history(config, script, rev, head, rev)
else:
_display_history(config, script, base, head, rev)
return []
with EnvironmentContext(config, script, fn=_display_current_history):
script.run_env()
if base == "current" or head == "current" or environment:
_display_history_w_current(config, script, base, head)
else:
_display_history(config, script, base, head)
def heads(config, verbose=False, resolve_dependencies=False):
"""Show current available heads in the script directory.
:param config: a :class:`.Config` instance.
:param verbose: output in verbose mode.
:param resolve_dependencies: treat dependency version as down revisions.
"""
script = ScriptDirectory.from_config(config)
if resolve_dependencies:
heads = script.get_revisions("heads")
else:
heads = script.get_revisions(script.get_heads())
for rev in heads:
config.print_stdout(
rev.cmd_format(
verbose, include_branches=True, tree_indicators=False
)
)
def branches(config, verbose=False):
"""Show current branch points.
:param config: a :class:`.Config` instance.
:param verbose: output in verbose mode.
"""
script = ScriptDirectory.from_config(config)
for sc in script.walk_revisions():
if sc.is_branch_point:
config.print_stdout(
"%s\n%s\n",
sc.cmd_format(verbose, include_branches=True),
"\n".join(
"%s -> %s"
% (
" " * len(str(sc.revision)),
rev_obj.cmd_format(
False, include_branches=True, include_doc=verbose
),
)
for rev_obj in (
script.get_revision(rev) for rev in sc.nextrev
)
),
)
def current(config: Config, verbose: bool = False) -> None:
"""Display the current revision for a database.
:param config: a :class:`.Config` instance.
:param verbose: output in verbose mode.
"""
script = ScriptDirectory.from_config(config)
def display_version(rev, context):
if verbose:
config.print_stdout(
"Current revision(s) for %s:",
util.obfuscate_url_pw(context.connection.engine.url),
)
for rev in script.get_all_current(rev):
config.print_stdout(rev.cmd_format(verbose))
return []
with EnvironmentContext(
config, script, fn=display_version, dont_mutate=True
):
script.run_env()
def stamp(
config: Config,
revision: _RevIdType,
sql: bool = False,
tag: Optional[str] = None,
purge: bool = False,
) -> None:
"""'stamp' the revision table with the given revision; don't
run any migrations.
:param config: a :class:`.Config` instance.
:param revision: target revision or list of revisions. May be a list
to indicate stamping of multiple branch heads.
.. note:: this parameter is called "revisions" in the command line
interface.
:param sql: use ``--sql`` mode
:param tag: an arbitrary "tag" that can be intercepted by custom
``env.py`` scripts via the :class:`.EnvironmentContext.get_tag_argument`
method.
:param purge: delete all entries in the version table before stamping.
"""
script = ScriptDirectory.from_config(config)
if sql:
destination_revs = []
starting_rev = None
for _revision in util.to_list(revision):
if ":" in _revision:
srev, _revision = _revision.split(":", 2)
if starting_rev != srev:
if starting_rev is None:
starting_rev = srev
else:
raise util.CommandError(
"Stamp operation with --sql only supports a "
"single starting revision at a time"
)
destination_revs.append(_revision)
else:
destination_revs = util.to_list(revision)
def do_stamp(rev, context):
return script._stamp_revs(util.to_tuple(destination_revs), rev)
with EnvironmentContext(
config,
script,
fn=do_stamp,
as_sql=sql,
starting_rev=starting_rev if sql else None,
destination_rev=util.to_tuple(destination_revs),
tag=tag,
purge=purge,
):
script.run_env()
def edit(config: Config, rev: str) -> None:
"""Edit revision script(s) using $EDITOR.
:param config: a :class:`.Config` instance.
:param rev: target revision.
"""
script = ScriptDirectory.from_config(config)
if rev == "current":
def edit_current(rev, context):
if not rev:
raise util.CommandError("No current revisions")
for sc in script.get_revisions(rev):
util.open_in_editor(sc.path)
return []
with EnvironmentContext(config, script, fn=edit_current):
script.run_env()
else:
revs = script.get_revisions(rev)
if not revs:
raise util.CommandError(
"No revision files indicated by symbol '%s'" % rev
)
for sc in revs:
assert sc
util.open_in_editor(sc.path)
def ensure_version(config: Config, sql: bool = False) -> None:
"""Create the alembic version table if it doesn't exist already .
:param config: a :class:`.Config` instance.
:param sql: use ``--sql`` mode
.. versionadded:: 1.7.6
"""
script = ScriptDirectory.from_config(config)
def do_ensure_version(rev, context):
context._ensure_version_table()
return []
with EnvironmentContext(
config,
script,
fn=do_ensure_version,
as_sql=sql,
):
script.run_env()

View File

@ -0,0 +1,634 @@
from __future__ import annotations
from argparse import ArgumentParser
from argparse import Namespace
from configparser import ConfigParser
import inspect
import os
import sys
from typing import Any
from typing import cast
from typing import Dict
from typing import Mapping
from typing import Optional
from typing import overload
from typing import TextIO
from typing import Union
from typing_extensions import TypedDict
from . import __version__
from . import command
from . import util
from .util import compat
class Config:
r"""Represent an Alembic configuration.
Within an ``env.py`` script, this is available
via the :attr:`.EnvironmentContext.config` attribute,
which in turn is available at ``alembic.context``::
from alembic import context
some_param = context.config.get_main_option("my option")
When invoking Alembic programmatically, a new
:class:`.Config` can be created by passing
the name of an .ini file to the constructor::
from alembic.config import Config
alembic_cfg = Config("/path/to/yourapp/alembic.ini")
With a :class:`.Config` object, you can then
run Alembic commands programmatically using the directives
in :mod:`alembic.command`.
The :class:`.Config` object can also be constructed without
a filename. Values can be set programmatically, and
new sections will be created as needed::
from alembic.config import Config
alembic_cfg = Config()
alembic_cfg.set_main_option("script_location", "myapp:migrations")
alembic_cfg.set_main_option("sqlalchemy.url", "postgresql://foo/bar")
alembic_cfg.set_section_option("mysection", "foo", "bar")
.. warning::
When using programmatic configuration, make sure the
``env.py`` file in use is compatible with the target configuration;
including that the call to Python ``logging.fileConfig()`` is
omitted if the programmatic configuration doesn't actually include
logging directives.
For passing non-string values to environments, such as connections and
engines, use the :attr:`.Config.attributes` dictionary::
with engine.begin() as connection:
alembic_cfg.attributes['connection'] = connection
command.upgrade(alembic_cfg, "head")
:param file\_: name of the .ini file to open.
:param ini_section: name of the main Alembic section within the
.ini file
:param output_buffer: optional file-like input buffer which
will be passed to the :class:`.MigrationContext` - used to redirect
the output of "offline generation" when using Alembic programmatically.
:param stdout: buffer where the "print" output of commands will be sent.
Defaults to ``sys.stdout``.
:param config_args: A dictionary of keys and values that will be used
for substitution in the alembic config file. The dictionary as given
is **copied** to a new one, stored locally as the attribute
``.config_args``. When the :attr:`.Config.file_config` attribute is
first invoked, the replacement variable ``here`` will be added to this
dictionary before the dictionary is passed to ``ConfigParser()``
to parse the .ini file.
:param attributes: optional dictionary of arbitrary Python keys/values,
which will be populated into the :attr:`.Config.attributes` dictionary.
.. seealso::
:ref:`connection_sharing`
"""
def __init__(
self,
file_: Union[str, os.PathLike[str], None] = None,
ini_section: str = "alembic",
output_buffer: Optional[TextIO] = None,
stdout: TextIO = sys.stdout,
cmd_opts: Optional[Namespace] = None,
config_args: Mapping[str, Any] = util.immutabledict(),
attributes: Optional[dict] = None,
) -> None:
"""Construct a new :class:`.Config`"""
self.config_file_name = file_
self.config_ini_section = ini_section
self.output_buffer = output_buffer
self.stdout = stdout
self.cmd_opts = cmd_opts
self.config_args = dict(config_args)
if attributes:
self.attributes.update(attributes)
cmd_opts: Optional[Namespace] = None
"""The command-line options passed to the ``alembic`` script.
Within an ``env.py`` script this can be accessed via the
:attr:`.EnvironmentContext.config` attribute.
.. seealso::
:meth:`.EnvironmentContext.get_x_argument`
"""
config_file_name: Union[str, os.PathLike[str], None] = None
"""Filesystem path to the .ini file in use."""
config_ini_section: str = None # type:ignore[assignment]
"""Name of the config file section to read basic configuration
from. Defaults to ``alembic``, that is the ``[alembic]`` section
of the .ini file. This value is modified using the ``-n/--name``
option to the Alembic runner.
"""
@util.memoized_property
def attributes(self):
"""A Python dictionary for storage of additional state.
This is a utility dictionary which can include not just strings but
engines, connections, schema objects, or anything else.
Use this to pass objects into an env.py script, such as passing
a :class:`sqlalchemy.engine.base.Connection` when calling
commands from :mod:`alembic.command` programmatically.
.. seealso::
:ref:`connection_sharing`
:paramref:`.Config.attributes`
"""
return {}
def print_stdout(self, text: str, *arg) -> None:
"""Render a message to standard out.
When :meth:`.Config.print_stdout` is called with additional args
those arguments will formatted against the provided text,
otherwise we simply output the provided text verbatim.
This is a no-op when the``quiet`` messaging option is enabled.
e.g.::
>>> config.print_stdout('Some text %s', 'arg')
Some Text arg
"""
if arg:
output = str(text) % arg
else:
output = str(text)
util.write_outstream(self.stdout, output, "\n", **self.messaging_opts)
@util.memoized_property
def file_config(self):
"""Return the underlying ``ConfigParser`` object.
Direct access to the .ini file is available here,
though the :meth:`.Config.get_section` and
:meth:`.Config.get_main_option`
methods provide a possibly simpler interface.
"""
if self.config_file_name:
here = os.path.abspath(os.path.dirname(self.config_file_name))
else:
here = ""
self.config_args["here"] = here
file_config = ConfigParser(self.config_args)
if self.config_file_name:
compat.read_config_parser(file_config, [self.config_file_name])
else:
file_config.add_section(self.config_ini_section)
return file_config
def get_template_directory(self) -> str:
"""Return the directory where Alembic setup templates are found.
This method is used by the alembic ``init`` and ``list_templates``
commands.
"""
import alembic
package_dir = os.path.abspath(os.path.dirname(alembic.__file__))
return os.path.join(package_dir, "templates")
@overload
def get_section(
self, name: str, default: None = ...
) -> Optional[Dict[str, str]]:
...
# "default" here could also be a TypeVar
# _MT = TypeVar("_MT", bound=Mapping[str, str]),
# however mypy wasn't handling that correctly (pyright was)
@overload
def get_section(
self, name: str, default: Dict[str, str]
) -> Dict[str, str]:
...
@overload
def get_section(
self, name: str, default: Mapping[str, str]
) -> Union[Dict[str, str], Mapping[str, str]]:
...
def get_section(
self, name: str, default: Optional[Mapping[str, str]] = None
) -> Optional[Mapping[str, str]]:
"""Return all the configuration options from a given .ini file section
as a dictionary.
If the given section does not exist, the value of ``default``
is returned, which is expected to be a dictionary or other mapping.
"""
if not self.file_config.has_section(name):
return default
return dict(self.file_config.items(name))
def set_main_option(self, name: str, value: str) -> None:
"""Set an option programmatically within the 'main' section.
This overrides whatever was in the .ini file.
:param name: name of the value
:param value: the value. Note that this value is passed to
``ConfigParser.set``, which supports variable interpolation using
pyformat (e.g. ``%(some_value)s``). A raw percent sign not part of
an interpolation symbol must therefore be escaped, e.g. ``%%``.
The given value may refer to another value already in the file
using the interpolation format.
"""
self.set_section_option(self.config_ini_section, name, value)
def remove_main_option(self, name: str) -> None:
self.file_config.remove_option(self.config_ini_section, name)
def set_section_option(self, section: str, name: str, value: str) -> None:
"""Set an option programmatically within the given section.
The section is created if it doesn't exist already.
The value here will override whatever was in the .ini
file.
:param section: name of the section
:param name: name of the value
:param value: the value. Note that this value is passed to
``ConfigParser.set``, which supports variable interpolation using
pyformat (e.g. ``%(some_value)s``). A raw percent sign not part of
an interpolation symbol must therefore be escaped, e.g. ``%%``.
The given value may refer to another value already in the file
using the interpolation format.
"""
if not self.file_config.has_section(section):
self.file_config.add_section(section)
self.file_config.set(section, name, value)
def get_section_option(
self, section: str, name: str, default: Optional[str] = None
) -> Optional[str]:
"""Return an option from the given section of the .ini file."""
if not self.file_config.has_section(section):
raise util.CommandError(
"No config file %r found, or file has no "
"'[%s]' section" % (self.config_file_name, section)
)
if self.file_config.has_option(section, name):
return self.file_config.get(section, name)
else:
return default
@overload
def get_main_option(self, name: str, default: str) -> str:
...
@overload
def get_main_option(
self, name: str, default: Optional[str] = None
) -> Optional[str]:
...
def get_main_option(self, name, default=None):
"""Return an option from the 'main' section of the .ini file.
This defaults to being a key from the ``[alembic]``
section, unless the ``-n/--name`` flag were used to
indicate a different section.
"""
return self.get_section_option(self.config_ini_section, name, default)
@util.memoized_property
def messaging_opts(self) -> MessagingOptions:
"""The messaging options."""
return cast(
MessagingOptions,
util.immutabledict(
{"quiet": getattr(self.cmd_opts, "quiet", False)}
),
)
class MessagingOptions(TypedDict, total=False):
quiet: bool
class CommandLine:
def __init__(self, prog: Optional[str] = None) -> None:
self._generate_args(prog)
def _generate_args(self, prog: Optional[str]) -> None:
def add_options(fn, parser, positional, kwargs):
kwargs_opts = {
"template": (
"-t",
"--template",
dict(
default="generic",
type=str,
help="Setup template for use with 'init'",
),
),
"message": (
"-m",
"--message",
dict(
type=str, help="Message string to use with 'revision'"
),
),
"sql": (
"--sql",
dict(
action="store_true",
help="Don't emit SQL to database - dump to "
"standard output/file instead. See docs on "
"offline mode.",
),
),
"tag": (
"--tag",
dict(
type=str,
help="Arbitrary 'tag' name - can be used by "
"custom env.py scripts.",
),
),
"head": (
"--head",
dict(
type=str,
help="Specify head revision or <branchname>@head "
"to base new revision on.",
),
),
"splice": (
"--splice",
dict(
action="store_true",
help="Allow a non-head revision as the "
"'head' to splice onto",
),
),
"depends_on": (
"--depends-on",
dict(
action="append",
help="Specify one or more revision identifiers "
"which this revision should depend on.",
),
),
"rev_id": (
"--rev-id",
dict(
type=str,
help="Specify a hardcoded revision id instead of "
"generating one",
),
),
"version_path": (
"--version-path",
dict(
type=str,
help="Specify specific path from config for "
"version file",
),
),
"branch_label": (
"--branch-label",
dict(
type=str,
help="Specify a branch label to apply to the "
"new revision",
),
),
"verbose": (
"-v",
"--verbose",
dict(action="store_true", help="Use more verbose output"),
),
"resolve_dependencies": (
"--resolve-dependencies",
dict(
action="store_true",
help="Treat dependency versions as down revisions",
),
),
"autogenerate": (
"--autogenerate",
dict(
action="store_true",
help="Populate revision script with candidate "
"migration operations, based on comparison "
"of database to model.",
),
),
"rev_range": (
"-r",
"--rev-range",
dict(
action="store",
help="Specify a revision range; "
"format is [start]:[end]",
),
),
"indicate_current": (
"-i",
"--indicate-current",
dict(
action="store_true",
help="Indicate the current revision",
),
),
"purge": (
"--purge",
dict(
action="store_true",
help="Unconditionally erase the version table "
"before stamping",
),
),
"package": (
"--package",
dict(
action="store_true",
help="Write empty __init__.py files to the "
"environment and version locations",
),
),
}
positional_help = {
"directory": "location of scripts directory",
"revision": "revision identifier",
"revisions": "one or more revisions, or 'heads' for all heads",
}
for arg in kwargs:
if arg in kwargs_opts:
args = kwargs_opts[arg]
args, kw = args[0:-1], args[-1]
parser.add_argument(*args, **kw)
for arg in positional:
if (
arg == "revisions"
or fn in positional_translations
and positional_translations[fn][arg] == "revisions"
):
subparser.add_argument(
"revisions",
nargs="+",
help=positional_help.get("revisions"),
)
else:
subparser.add_argument(arg, help=positional_help.get(arg))
parser = ArgumentParser(prog=prog)
parser.add_argument(
"--version", action="version", version="%%(prog)s %s" % __version__
)
parser.add_argument(
"-c",
"--config",
type=str,
default=os.environ.get("ALEMBIC_CONFIG", "alembic.ini"),
help="Alternate config file; defaults to value of "
'ALEMBIC_CONFIG environment variable, or "alembic.ini"',
)
parser.add_argument(
"-n",
"--name",
type=str,
default="alembic",
help="Name of section in .ini file to " "use for Alembic config",
)
parser.add_argument(
"-x",
action="append",
help="Additional arguments consumed by "
"custom env.py scripts, e.g. -x "
"setting1=somesetting -x setting2=somesetting",
)
parser.add_argument(
"--raiseerr",
action="store_true",
help="Raise a full stack trace on error",
)
parser.add_argument(
"-q",
"--quiet",
action="store_true",
help="Do not log to std output.",
)
subparsers = parser.add_subparsers()
positional_translations = {command.stamp: {"revision": "revisions"}}
for fn in [getattr(command, n) for n in dir(command)]:
if (
inspect.isfunction(fn)
and fn.__name__[0] != "_"
and fn.__module__ == "alembic.command"
):
spec = compat.inspect_getfullargspec(fn)
if spec[3] is not None:
positional = spec[0][1 : -len(spec[3])]
kwarg = spec[0][-len(spec[3]) :]
else:
positional = spec[0][1:]
kwarg = []
if fn in positional_translations:
positional = [
positional_translations[fn].get(name, name)
for name in positional
]
# parse first line(s) of helptext without a line break
help_ = fn.__doc__
if help_:
help_text = []
for line in help_.split("\n"):
if not line.strip():
break
else:
help_text.append(line.strip())
else:
help_text = []
subparser = subparsers.add_parser(
fn.__name__, help=" ".join(help_text)
)
add_options(fn, subparser, positional, kwarg)
subparser.set_defaults(cmd=(fn, positional, kwarg))
self.parser = parser
def run_cmd(self, config: Config, options: Namespace) -> None:
fn, positional, kwarg = options.cmd
try:
fn(
config,
*[getattr(options, k, None) for k in positional],
**{k: getattr(options, k, None) for k in kwarg},
)
except util.CommandError as e:
if options.raiseerr:
raise
else:
util.err(str(e), **config.messaging_opts)
def main(self, argv=None):
options = self.parser.parse_args(argv)
if not hasattr(options, "cmd"):
# see http://bugs.python.org/issue9253, argparse
# behavior changed incompatibly in py3.3
self.parser.error("too few arguments")
else:
cfg = Config(
file_=options.config,
ini_section=options.name,
cmd_opts=options,
)
self.run_cmd(cfg, options)
def main(argv=None, prog=None, **kwargs):
"""The console runner function for Alembic."""
CommandLine(prog=prog).main(argv=argv)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,5 @@
from .runtime.environment import EnvironmentContext
# create proxy functions for
# each method on the EnvironmentContext class.
EnvironmentContext.create_module_class_proxy(globals(), locals())

View File

@ -0,0 +1,847 @@
# ### this file stubs are generated by tools/write_pyi.py - do not edit ###
# ### imports are manually managed
from __future__ import annotations
from typing import Any
from typing import Callable
from typing import Collection
from typing import ContextManager
from typing import Dict
from typing import Iterable
from typing import List
from typing import Literal
from typing import Mapping
from typing import MutableMapping
from typing import Optional
from typing import overload
from typing import TextIO
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
if TYPE_CHECKING:
from sqlalchemy.engine.base import Connection
from sqlalchemy.engine.url import URL
from sqlalchemy.sql import Executable
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.schema import FetchedValue
from sqlalchemy.sql.schema import MetaData
from sqlalchemy.sql.schema import SchemaItem
from sqlalchemy.sql.type_api import TypeEngine
from .autogenerate.api import AutogenContext
from .config import Config
from .operations.ops import MigrationScript
from .runtime.migration import _ProxyTransaction
from .runtime.migration import MigrationContext
from .runtime.migration import MigrationInfo
from .script import ScriptDirectory
### end imports ###
def begin_transaction() -> Union[_ProxyTransaction, ContextManager[None]]:
"""Return a context manager that will
enclose an operation within a "transaction",
as defined by the environment's offline
and transactional DDL settings.
e.g.::
with context.begin_transaction():
context.run_migrations()
:meth:`.begin_transaction` is intended to
"do the right thing" regardless of
calling context:
* If :meth:`.is_transactional_ddl` is ``False``,
returns a "do nothing" context manager
which otherwise produces no transactional
state or directives.
* If :meth:`.is_offline_mode` is ``True``,
returns a context manager that will
invoke the :meth:`.DefaultImpl.emit_begin`
and :meth:`.DefaultImpl.emit_commit`
methods, which will produce the string
directives ``BEGIN`` and ``COMMIT`` on
the output stream, as rendered by the
target backend (e.g. SQL Server would
emit ``BEGIN TRANSACTION``).
* Otherwise, calls :meth:`sqlalchemy.engine.Connection.begin`
on the current online connection, which
returns a :class:`sqlalchemy.engine.Transaction`
object. This object demarcates a real
transaction and is itself a context manager,
which will roll back if an exception
is raised.
Note that a custom ``env.py`` script which
has more specific transactional needs can of course
manipulate the :class:`~sqlalchemy.engine.Connection`
directly to produce transactional state in "online"
mode.
"""
config: Config
def configure(
connection: Optional[Connection] = None,
url: Union[str, URL, None] = None,
dialect_name: Optional[str] = None,
dialect_opts: Optional[Dict[str, Any]] = None,
transactional_ddl: Optional[bool] = None,
transaction_per_migration: bool = False,
output_buffer: Optional[TextIO] = None,
starting_rev: Optional[str] = None,
tag: Optional[str] = None,
template_args: Optional[Dict[str, Any]] = None,
render_as_batch: bool = False,
target_metadata: Optional[MetaData] = None,
include_name: Optional[
Callable[
[
Optional[str],
Literal[
"schema",
"table",
"column",
"index",
"unique_constraint",
"foreign_key_constraint",
],
MutableMapping[
Literal[
"schema_name",
"table_name",
"schema_qualified_table_name",
],
Optional[str],
],
],
bool,
]
] = None,
include_object: Optional[
Callable[
[
SchemaItem,
Optional[str],
Literal[
"schema",
"table",
"column",
"index",
"unique_constraint",
"foreign_key_constraint",
],
bool,
Optional[SchemaItem],
],
bool,
]
] = None,
include_schemas: bool = False,
process_revision_directives: Optional[
Callable[
[
MigrationContext,
Union[str, Iterable[Optional[str]], Iterable[str]],
List[MigrationScript],
],
None,
]
] = None,
compare_type: Union[
bool,
Callable[
[
MigrationContext,
Column[Any],
Column[Any],
TypeEngine,
TypeEngine,
],
Optional[bool],
],
] = True,
compare_server_default: Union[
bool,
Callable[
[
MigrationContext,
Column[Any],
Column[Any],
Optional[str],
Optional[FetchedValue],
Optional[str],
],
Optional[bool],
],
] = False,
render_item: Optional[
Callable[[str, Any, AutogenContext], Union[str, Literal[False]]]
] = None,
literal_binds: bool = False,
upgrade_token: str = "upgrades",
downgrade_token: str = "downgrades",
alembic_module_prefix: str = "op.",
sqlalchemy_module_prefix: str = "sa.",
user_module_prefix: Optional[str] = None,
on_version_apply: Optional[
Callable[
[
MigrationContext,
MigrationInfo,
Collection[Any],
Mapping[str, Any],
],
None,
]
] = None,
**kw: Any,
) -> None:
"""Configure a :class:`.MigrationContext` within this
:class:`.EnvironmentContext` which will provide database
connectivity and other configuration to a series of
migration scripts.
Many methods on :class:`.EnvironmentContext` require that
this method has been called in order to function, as they
ultimately need to have database access or at least access
to the dialect in use. Those which do are documented as such.
The important thing needed by :meth:`.configure` is a
means to determine what kind of database dialect is in use.
An actual connection to that database is needed only if
the :class:`.MigrationContext` is to be used in
"online" mode.
If the :meth:`.is_offline_mode` function returns ``True``,
then no connection is needed here. Otherwise, the
``connection`` parameter should be present as an
instance of :class:`sqlalchemy.engine.Connection`.
This function is typically called from the ``env.py``
script within a migration environment. It can be called
multiple times for an invocation. The most recent
:class:`~sqlalchemy.engine.Connection`
for which it was called is the one that will be operated upon
by the next call to :meth:`.run_migrations`.
General parameters:
:param connection: a :class:`~sqlalchemy.engine.Connection`
to use
for SQL execution in "online" mode. When present, is also
used to determine the type of dialect in use.
:param url: a string database url, or a
:class:`sqlalchemy.engine.url.URL` object.
The type of dialect to be used will be derived from this if
``connection`` is not passed.
:param dialect_name: string name of a dialect, such as
"postgresql", "mssql", etc.
The type of dialect to be used will be derived from this if
``connection`` and ``url`` are not passed.
:param dialect_opts: dictionary of options to be passed to dialect
constructor.
:param transactional_ddl: Force the usage of "transactional"
DDL on or off;
this otherwise defaults to whether or not the dialect in
use supports it.
:param transaction_per_migration: if True, nest each migration script
in a transaction rather than the full series of migrations to
run.
:param output_buffer: a file-like object that will be used
for textual output
when the ``--sql`` option is used to generate SQL scripts.
Defaults to
``sys.stdout`` if not passed here and also not present on
the :class:`.Config`
object. The value here overrides that of the :class:`.Config`
object.
:param output_encoding: when using ``--sql`` to generate SQL
scripts, apply this encoding to the string output.
:param literal_binds: when using ``--sql`` to generate SQL
scripts, pass through the ``literal_binds`` flag to the compiler
so that any literal values that would ordinarily be bound
parameters are converted to plain strings.
.. warning:: Dialects can typically only handle simple datatypes
like strings and numbers for auto-literal generation. Datatypes
like dates, intervals, and others may still require manual
formatting, typically using :meth:`.Operations.inline_literal`.
.. note:: the ``literal_binds`` flag is ignored on SQLAlchemy
versions prior to 0.8 where this feature is not supported.
.. seealso::
:meth:`.Operations.inline_literal`
:param starting_rev: Override the "starting revision" argument
when using ``--sql`` mode.
:param tag: a string tag for usage by custom ``env.py`` scripts.
Set via the ``--tag`` option, can be overridden here.
:param template_args: dictionary of template arguments which
will be added to the template argument environment when
running the "revision" command. Note that the script environment
is only run within the "revision" command if the --autogenerate
option is used, or if the option "revision_environment=true"
is present in the alembic.ini file.
:param version_table: The name of the Alembic version table.
The default is ``'alembic_version'``.
:param version_table_schema: Optional schema to place version
table within.
:param version_table_pk: boolean, whether the Alembic version table
should use a primary key constraint for the "value" column; this
only takes effect when the table is first created.
Defaults to True; setting to False should not be necessary and is
here for backwards compatibility reasons.
:param on_version_apply: a callable or collection of callables to be
run for each migration step.
The callables will be run in the order they are given, once for
each migration step, after the respective operation has been
applied but before its transaction is finalized.
Each callable accepts no positional arguments and the following
keyword arguments:
* ``ctx``: the :class:`.MigrationContext` running the migration,
* ``step``: a :class:`.MigrationInfo` representing the
step currently being applied,
* ``heads``: a collection of version strings representing the
current heads,
* ``run_args``: the ``**kwargs`` passed to :meth:`.run_migrations`.
Parameters specific to the autogenerate feature, when
``alembic revision`` is run with the ``--autogenerate`` feature:
:param target_metadata: a :class:`sqlalchemy.schema.MetaData`
object, or a sequence of :class:`~sqlalchemy.schema.MetaData`
objects, that will be consulted during autogeneration.
The tables present in each :class:`~sqlalchemy.schema.MetaData`
will be compared against
what is locally available on the target
:class:`~sqlalchemy.engine.Connection`
to produce candidate upgrade/downgrade operations.
:param compare_type: Indicates type comparison behavior during
an autogenerate
operation. Defaults to ``True`` turning on type comparison, which
has good accuracy on most backends. See :ref:`compare_types`
for an example as well as information on other type
comparison options. Set to ``False`` which disables type
comparison. A callable can also be passed to provide custom type
comparison, see :ref:`compare_types` for additional details.
.. versionchanged:: 1.12.0 The default value of
:paramref:`.EnvironmentContext.configure.compare_type` has been
changed to ``True``.
.. seealso::
:ref:`compare_types`
:paramref:`.EnvironmentContext.configure.compare_server_default`
:param compare_server_default: Indicates server default comparison
behavior during
an autogenerate operation. Defaults to ``False`` which disables
server default
comparison. Set to ``True`` to turn on server default comparison,
which has
varied accuracy depending on backend.
To customize server default comparison behavior, a callable may
be specified
which can filter server default comparisons during an
autogenerate operation.
defaults during an autogenerate operation. The format of this
callable is::
def my_compare_server_default(context, inspected_column,
metadata_column, inspected_default, metadata_default,
rendered_metadata_default):
# return True if the defaults are different,
# False if not, or None to allow the default implementation
# to compare these defaults
return None
context.configure(
# ...
compare_server_default = my_compare_server_default
)
``inspected_column`` is a dictionary structure as returned by
:meth:`sqlalchemy.engine.reflection.Inspector.get_columns`, whereas
``metadata_column`` is a :class:`sqlalchemy.schema.Column` from
the local model environment.
A return value of ``None`` indicates to allow default server default
comparison
to proceed. Note that some backends such as Postgresql actually
execute
the two defaults on the database side to compare for equivalence.
.. seealso::
:paramref:`.EnvironmentContext.configure.compare_type`
:param include_name: A callable function which is given
the chance to return ``True`` or ``False`` for any database reflected
object based on its name, including database schema names when
the :paramref:`.EnvironmentContext.configure.include_schemas` flag
is set to ``True``.
The function accepts the following positional arguments:
* ``name``: the name of the object, such as schema name or table name.
Will be ``None`` when indicating the default schema name of the
database connection.
* ``type``: a string describing the type of object; currently
``"schema"``, ``"table"``, ``"column"``, ``"index"``,
``"unique_constraint"``, or ``"foreign_key_constraint"``
* ``parent_names``: a dictionary of "parent" object names, that are
relative to the name being given. Keys in this dictionary may
include: ``"schema_name"``, ``"table_name"`` or
``"schema_qualified_table_name"``.
E.g.::
def include_name(name, type_, parent_names):
if type_ == "schema":
return name in ["schema_one", "schema_two"]
else:
return True
context.configure(
# ...
include_schemas = True,
include_name = include_name
)
.. seealso::
:ref:`autogenerate_include_hooks`
:paramref:`.EnvironmentContext.configure.include_object`
:paramref:`.EnvironmentContext.configure.include_schemas`
:param include_object: A callable function which is given
the chance to return ``True`` or ``False`` for any object,
indicating if the given object should be considered in the
autogenerate sweep.
The function accepts the following positional arguments:
* ``object``: a :class:`~sqlalchemy.schema.SchemaItem` object such
as a :class:`~sqlalchemy.schema.Table`,
:class:`~sqlalchemy.schema.Column`,
:class:`~sqlalchemy.schema.Index`
:class:`~sqlalchemy.schema.UniqueConstraint`,
or :class:`~sqlalchemy.schema.ForeignKeyConstraint` object
* ``name``: the name of the object. This is typically available
via ``object.name``.
* ``type``: a string describing the type of object; currently
``"table"``, ``"column"``, ``"index"``, ``"unique_constraint"``,
or ``"foreign_key_constraint"``
* ``reflected``: ``True`` if the given object was produced based on
table reflection, ``False`` if it's from a local :class:`.MetaData`
object.
* ``compare_to``: the object being compared against, if available,
else ``None``.
E.g.::
def include_object(object, name, type_, reflected, compare_to):
if (type_ == "column" and
not reflected and
object.info.get("skip_autogenerate", False)):
return False
else:
return True
context.configure(
# ...
include_object = include_object
)
For the use case of omitting specific schemas from a target database
when :paramref:`.EnvironmentContext.configure.include_schemas` is
set to ``True``, the :attr:`~sqlalchemy.schema.Table.schema`
attribute can be checked for each :class:`~sqlalchemy.schema.Table`
object passed to the hook, however it is much more efficient
to filter on schemas before reflection of objects takes place
using the :paramref:`.EnvironmentContext.configure.include_name`
hook.
.. seealso::
:ref:`autogenerate_include_hooks`
:paramref:`.EnvironmentContext.configure.include_name`
:paramref:`.EnvironmentContext.configure.include_schemas`
:param render_as_batch: if True, commands which alter elements
within a table will be placed under a ``with batch_alter_table():``
directive, so that batch migrations will take place.
.. seealso::
:ref:`batch_migrations`
:param include_schemas: If True, autogenerate will scan across
all schemas located by the SQLAlchemy
:meth:`~sqlalchemy.engine.reflection.Inspector.get_schema_names`
method, and include all differences in tables found across all
those schemas. When using this option, you may want to also
use the :paramref:`.EnvironmentContext.configure.include_name`
parameter to specify a callable which
can filter the tables/schemas that get included.
.. seealso::
:ref:`autogenerate_include_hooks`
:paramref:`.EnvironmentContext.configure.include_name`
:paramref:`.EnvironmentContext.configure.include_object`
:param render_item: Callable that can be used to override how
any schema item, i.e. column, constraint, type,
etc., is rendered for autogenerate. The callable receives a
string describing the type of object, the object, and
the autogen context. If it returns False, the
default rendering method will be used. If it returns None,
the item will not be rendered in the context of a Table
construct, that is, can be used to skip columns or constraints
within op.create_table()::
def my_render_column(type_, col, autogen_context):
if type_ == "column" and isinstance(col, MySpecialCol):
return repr(col)
else:
return False
context.configure(
# ...
render_item = my_render_column
)
Available values for the type string include: ``"column"``,
``"primary_key"``, ``"foreign_key"``, ``"unique"``, ``"check"``,
``"type"``, ``"server_default"``.
.. seealso::
:ref:`autogen_render_types`
:param upgrade_token: When autogenerate completes, the text of the
candidate upgrade operations will be present in this template
variable when ``script.py.mako`` is rendered. Defaults to
``upgrades``.
:param downgrade_token: When autogenerate completes, the text of the
candidate downgrade operations will be present in this
template variable when ``script.py.mako`` is rendered. Defaults to
``downgrades``.
:param alembic_module_prefix: When autogenerate refers to Alembic
:mod:`alembic.operations` constructs, this prefix will be used
(i.e. ``op.create_table``) Defaults to "``op.``".
Can be ``None`` to indicate no prefix.
:param sqlalchemy_module_prefix: When autogenerate refers to
SQLAlchemy
:class:`~sqlalchemy.schema.Column` or type classes, this prefix
will be used
(i.e. ``sa.Column("somename", sa.Integer)``) Defaults to "``sa.``".
Can be ``None`` to indicate no prefix.
Note that when dialect-specific types are rendered, autogenerate
will render them using the dialect module name, i.e. ``mssql.BIT()``,
``postgresql.UUID()``.
:param user_module_prefix: When autogenerate refers to a SQLAlchemy
type (e.g. :class:`.TypeEngine`) where the module name is not
under the ``sqlalchemy`` namespace, this prefix will be used
within autogenerate. If left at its default of
``None``, the ``__module__`` attribute of the type is used to
render the import module. It's a good practice to set this
and to have all custom types be available from a fixed module space,
in order to future-proof migration files against reorganizations
in modules.
.. seealso::
:ref:`autogen_module_prefix`
:param process_revision_directives: a callable function that will
be passed a structure representing the end result of an autogenerate
or plain "revision" operation, which can be manipulated to affect
how the ``alembic revision`` command ultimately outputs new
revision scripts. The structure of the callable is::
def process_revision_directives(context, revision, directives):
pass
The ``directives`` parameter is a Python list containing
a single :class:`.MigrationScript` directive, which represents
the revision file to be generated. This list as well as its
contents may be freely modified to produce any set of commands.
The section :ref:`customizing_revision` shows an example of
doing this. The ``context`` parameter is the
:class:`.MigrationContext` in use,
and ``revision`` is a tuple of revision identifiers representing the
current revision of the database.
The callable is invoked at all times when the ``--autogenerate``
option is passed to ``alembic revision``. If ``--autogenerate``
is not passed, the callable is invoked only if the
``revision_environment`` variable is set to True in the Alembic
configuration, in which case the given ``directives`` collection
will contain empty :class:`.UpgradeOps` and :class:`.DowngradeOps`
collections for ``.upgrade_ops`` and ``.downgrade_ops``. The
``--autogenerate`` option itself can be inferred by inspecting
``context.config.cmd_opts.autogenerate``.
The callable function may optionally be an instance of
a :class:`.Rewriter` object. This is a helper object that
assists in the production of autogenerate-stream rewriter functions.
.. seealso::
:ref:`customizing_revision`
:ref:`autogen_rewriter`
:paramref:`.command.revision.process_revision_directives`
Parameters specific to individual backends:
:param mssql_batch_separator: The "batch separator" which will
be placed between each statement when generating offline SQL Server
migrations. Defaults to ``GO``. Note this is in addition to the
customary semicolon ``;`` at the end of each statement; SQL Server
considers the "batch separator" to denote the end of an
individual statement execution, and cannot group certain
dependent operations in one step.
:param oracle_batch_separator: The "batch separator" which will
be placed between each statement when generating offline
Oracle migrations. Defaults to ``/``. Oracle doesn't add a
semicolon between statements like most other backends.
"""
def execute(
sql: Union[Executable, str], execution_options: Optional[dict] = None
) -> None:
"""Execute the given SQL using the current change context.
The behavior of :meth:`.execute` is the same
as that of :meth:`.Operations.execute`. Please see that
function's documentation for full detail including
caveats and limitations.
This function requires that a :class:`.MigrationContext` has
first been made available via :meth:`.configure`.
"""
def get_bind() -> Connection:
"""Return the current 'bind'.
In "online" mode, this is the
:class:`sqlalchemy.engine.Connection` currently being used
to emit SQL to the database.
This function requires that a :class:`.MigrationContext`
has first been made available via :meth:`.configure`.
"""
def get_context() -> MigrationContext:
"""Return the current :class:`.MigrationContext` object.
If :meth:`.EnvironmentContext.configure` has not been
called yet, raises an exception.
"""
def get_head_revision() -> Union[str, Tuple[str, ...], None]:
"""Return the hex identifier of the 'head' script revision.
If the script directory has multiple heads, this
method raises a :class:`.CommandError`;
:meth:`.EnvironmentContext.get_head_revisions` should be preferred.
This function does not require that the :class:`.MigrationContext`
has been configured.
.. seealso:: :meth:`.EnvironmentContext.get_head_revisions`
"""
def get_head_revisions() -> Union[str, Tuple[str, ...], None]:
"""Return the hex identifier of the 'heads' script revision(s).
This returns a tuple containing the version number of all
heads in the script directory.
This function does not require that the :class:`.MigrationContext`
has been configured.
"""
def get_revision_argument() -> Union[str, Tuple[str, ...], None]:
"""Get the 'destination' revision argument.
This is typically the argument passed to the
``upgrade`` or ``downgrade`` command.
If it was specified as ``head``, the actual
version number is returned; if specified
as ``base``, ``None`` is returned.
This function does not require that the :class:`.MigrationContext`
has been configured.
"""
def get_starting_revision_argument() -> Union[str, Tuple[str, ...], None]:
"""Return the 'starting revision' argument,
if the revision was passed using ``start:end``.
This is only meaningful in "offline" mode.
Returns ``None`` if no value is available
or was configured.
This function does not require that the :class:`.MigrationContext`
has been configured.
"""
def get_tag_argument() -> Optional[str]:
"""Return the value passed for the ``--tag`` argument, if any.
The ``--tag`` argument is not used directly by Alembic,
but is available for custom ``env.py`` configurations that
wish to use it; particularly for offline generation scripts
that wish to generate tagged filenames.
This function does not require that the :class:`.MigrationContext`
has been configured.
.. seealso::
:meth:`.EnvironmentContext.get_x_argument` - a newer and more
open ended system of extending ``env.py`` scripts via the command
line.
"""
@overload
def get_x_argument(as_dictionary: Literal[False]) -> List[str]: ...
@overload
def get_x_argument(as_dictionary: Literal[True]) -> Dict[str, str]: ...
@overload
def get_x_argument(
as_dictionary: bool = ...,
) -> Union[List[str], Dict[str, str]]:
"""Return the value(s) passed for the ``-x`` argument, if any.
The ``-x`` argument is an open ended flag that allows any user-defined
value or values to be passed on the command line, then available
here for consumption by a custom ``env.py`` script.
The return value is a list, returned directly from the ``argparse``
structure. If ``as_dictionary=True`` is passed, the ``x`` arguments
are parsed using ``key=value`` format into a dictionary that is
then returned.
For example, to support passing a database URL on the command line,
the standard ``env.py`` script can be modified like this::
cmd_line_url = context.get_x_argument(
as_dictionary=True).get('dbname')
if cmd_line_url:
engine = create_engine(cmd_line_url)
else:
engine = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool)
This then takes effect by running the ``alembic`` script as::
alembic -x dbname=postgresql://user:pass@host/dbname upgrade head
This function does not require that the :class:`.MigrationContext`
has been configured.
.. seealso::
:meth:`.EnvironmentContext.get_tag_argument`
:attr:`.Config.cmd_opts`
"""
def is_offline_mode() -> bool:
"""Return True if the current migrations environment
is running in "offline mode".
This is ``True`` or ``False`` depending
on the ``--sql`` flag passed.
This function does not require that the :class:`.MigrationContext`
has been configured.
"""
def is_transactional_ddl():
"""Return True if the context is configured to expect a
transactional DDL capable backend.
This defaults to the type of database in use, and
can be overridden by the ``transactional_ddl`` argument
to :meth:`.configure`
This function requires that a :class:`.MigrationContext`
has first been made available via :meth:`.configure`.
"""
def run_migrations(**kw: Any) -> None:
"""Run migrations as determined by the current command line
configuration
as well as versioning information present (or not) in the current
database connection (if one is present).
The function accepts optional ``**kw`` arguments. If these are
passed, they are sent directly to the ``upgrade()`` and
``downgrade()``
functions within each target revision file. By modifying the
``script.py.mako`` file so that the ``upgrade()`` and ``downgrade()``
functions accept arguments, parameters can be passed here so that
contextual information, usually information to identify a particular
database in use, can be passed from a custom ``env.py`` script
to the migration functions.
This function requires that a :class:`.MigrationContext` has
first been made available via :meth:`.configure`.
"""
script: ScriptDirectory
def static_output(text: str) -> None:
"""Emit text directly to the "offline" SQL stream.
Typically this is for emitting comments that
start with --. The statement is not treated
as a SQL execution, no ; or batch separator
is added, etc.
"""

View File

@ -0,0 +1,6 @@
from . import mssql
from . import mysql
from . import oracle
from . import postgresql
from . import sqlite
from .impl import DefaultImpl

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