Compare commits
	
		
			39 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 768747140a | ||
|  | 1fde3cd04e | ||
|  | d0ce545ff5 | ||
|  | 09b89ed7d0 | ||
|  | 86c8382593 | ||
|  | 48e3ff48a3 | ||
|  | 1bf6d45bb0 | ||
|  | 342f4cdd3b | ||
|  | 8cace21fde | ||
|  | 9889d9492b | ||
|  | 823368aea8 | ||
|  | 6b1ad46cf1 | ||
|  | ce8c0b1f06 | ||
|  | 43922d3734 | ||
|  | 48c5318737 | ||
|  | 002221e557 | ||
|  | 141d7205bf | ||
|  | af4d4be19a | ||
|  | 3b4d9b47ce | ||
|  | 4b0c4ae7d0 | ||
|  | dc821d8a02 | ||
|  | d18a6b5be0 | ||
|  | 1e56a70f21 | ||
|  | ab1e1aab75 | ||
|  | 00dd04f97d | ||
|  | fc914acc80 | ||
|  | ac03ecb002 | ||
|  | 235ef3f7ea | ||
|  | e00eb7f3d9 | ||
|  | d6c8941676 | ||
|  | cf062c9310 | ||
|  | 309adec8c9 | ||
|  | 8674142ba8 | ||
|  | cda9bd1c47 | ||
|  | 198e4e0032 | ||
|  | 1b440477a2 | ||
|  | 1263c6f735 | ||
|  | 6504384879 | ||
|  | 17ab0a1421 | 
							
								
								
									
										102
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | # ChangeLog | ||||||
|  |  | ||||||
|  | ## 0.3 | ||||||
|  |  | ||||||
|  | ### 0.3.1 | ||||||
|  |  | ||||||
|  | - Fix first version error. | ||||||
|  | - Fix init error. (#61) | ||||||
|  |  | ||||||
|  | ### 0.3.0 | ||||||
|  |  | ||||||
|  | - Refactoring migrate logic, and this version is not compatible with previous version. | ||||||
|  | - Now there don't need `old_models.py` and it store in database. | ||||||
|  | - Upgrade steps: | ||||||
|  |   1. Upgrade aerich version. | ||||||
|  |   2. Drop aerich table in database. | ||||||
|  |   3. Delete `migrations/{app}` folder and rerun `aerich init-db`. | ||||||
|  |   4. Update model and `aerich migrate` normally. | ||||||
|  |  | ||||||
|  | ## 0.2 | ||||||
|  |  | ||||||
|  | ### 0.2.5 | ||||||
|  |  | ||||||
|  | - Fix windows support. (#46) | ||||||
|  | - Support `db_constraint` in fk, m2m should manual define table with fk. (#52) | ||||||
|  |  | ||||||
|  | ### 0.2.4 | ||||||
|  |  | ||||||
|  | - Raise error with SQLite unsupported features. | ||||||
|  | - Fix Postgres alter table. (#48) | ||||||
|  | - Add `Rename` support. | ||||||
|  |  | ||||||
|  | ### 0.2.3 | ||||||
|  |  | ||||||
|  | - Fix tortoise ssl config. | ||||||
|  | - PostgreSQL add/drop index/unique. | ||||||
|  |  | ||||||
|  | ### 0.2.2 | ||||||
|  |  | ||||||
|  | - Fix postgres drop fk. | ||||||
|  | - Fix version sort. | ||||||
|  |  | ||||||
|  | ### 0.2.1 | ||||||
|  |  | ||||||
|  | - Fix bug in windows. | ||||||
|  | - Enhance PostgreSQL support. | ||||||
|  |  | ||||||
|  | ### 0.2.0 | ||||||
|  |  | ||||||
|  | - Update model file find method. | ||||||
|  | - Set `--safe` bool. | ||||||
|  |  | ||||||
|  | ## 0.1 | ||||||
|  |  | ||||||
|  | ### 0.1.9 | ||||||
|  |  | ||||||
|  | - Fix default_connection when upgrade | ||||||
|  | - Find default app instead of default. | ||||||
|  | - Diff MySQL ddl. | ||||||
|  | - Check tortoise config. | ||||||
|  |  | ||||||
|  | ### 0.1.8 | ||||||
|  |  | ||||||
|  | - Fix upgrade error when migrate. | ||||||
|  | - Fix init db sql error. | ||||||
|  | - Support change column. | ||||||
|  |  | ||||||
|  | ### 0.1.7 | ||||||
|  |  | ||||||
|  | - Exclude models.Aerich. | ||||||
|  | - Add init record when init-db. | ||||||
|  | - Fix version num str. | ||||||
|  |  | ||||||
|  | ### 0.1.6 | ||||||
|  |  | ||||||
|  | - update dependency_links | ||||||
|  |  | ||||||
|  | ### 0.1.5 | ||||||
|  |  | ||||||
|  | - Add sqlite and postgres support. | ||||||
|  | - Fix dependency import. | ||||||
|  | - Store versions in db. | ||||||
|  |  | ||||||
|  | ### 0.1.4 | ||||||
|  |  | ||||||
|  | - Fix transaction and fields import. | ||||||
|  | - Make unique index worked. | ||||||
|  | - Add cli --version. | ||||||
|  |  | ||||||
|  | ### 0.1.3 | ||||||
|  |  | ||||||
|  | - Support indexes and unique_together. | ||||||
|  |  | ||||||
|  | ### 0.1.2 | ||||||
|  |  | ||||||
|  | - Now aerich support m2m. | ||||||
|  | - Add cli cmd init-db. | ||||||
|  | - Change cli options. | ||||||
|  |  | ||||||
|  | ### 0.1.1 | ||||||
|  |  | ||||||
|  | - Now aerich is basic worked. | ||||||
| @@ -1,71 +0,0 @@ | |||||||
| ========= |  | ||||||
| ChangeLog |  | ||||||
| ========= |  | ||||||
| 0.2 |  | ||||||
| === |  | ||||||
|  |  | ||||||
| 0.2.2 |  | ||||||
| ----- |  | ||||||
| - Fix postgres drop fk. |  | ||||||
| - Fix version sort. |  | ||||||
|  |  | ||||||
| 0.2.1 |  | ||||||
| ----- |  | ||||||
| - Fix bug in windows. |  | ||||||
| - Enhance PostgreSQL support. |  | ||||||
|  |  | ||||||
| 0.2.0 |  | ||||||
| ----- |  | ||||||
| - Update model file find method. |  | ||||||
| - Set ``--safe`` bool. |  | ||||||
|  |  | ||||||
| 0.1 |  | ||||||
| === |  | ||||||
| 0.1.9 |  | ||||||
| ----- |  | ||||||
| - Fix default_connection when upgrade |  | ||||||
| - Find default app instead of default. |  | ||||||
| - Diff MySQL ddl. |  | ||||||
| - Check tortoise config. |  | ||||||
|  |  | ||||||
| 0.1.8 |  | ||||||
| ----- |  | ||||||
| - Fix upgrade error when migrate. |  | ||||||
| - Fix init db sql error. |  | ||||||
| - Support change column. |  | ||||||
|  |  | ||||||
| 0.1.7 |  | ||||||
| ----- |  | ||||||
| - Exclude models.Aerich. |  | ||||||
| - Add init record when init-db. |  | ||||||
| - Fix version num str. |  | ||||||
|  |  | ||||||
| 0.1.6 |  | ||||||
| ----- |  | ||||||
| - update dependency_links |  | ||||||
|  |  | ||||||
| 0.1.5 |  | ||||||
| ----- |  | ||||||
| - Add sqlite and postgres support. |  | ||||||
| - Fix dependency import. |  | ||||||
| - Store versions in db. |  | ||||||
|  |  | ||||||
| 0.1.4 |  | ||||||
| ----- |  | ||||||
| - Fix transaction and fields import. |  | ||||||
| - Make unique index worked. |  | ||||||
| - Add cli --version. |  | ||||||
|  |  | ||||||
| 0.1.3 |  | ||||||
| ----- |  | ||||||
| - Support indexes and unique_together. |  | ||||||
|  |  | ||||||
| 0.1.2 |  | ||||||
| ----- |  | ||||||
| - Now aerich support m2m. |  | ||||||
| - Add cli cmd init-db. |  | ||||||
| - Change cli options. |  | ||||||
|  |  | ||||||
| 0.1.1 |  | ||||||
| ----- |  | ||||||
| - Now aerich is basic worked. |  | ||||||
							
								
								
									
										7
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								Makefile
									
									
									
									
									
								
							| @@ -40,17 +40,14 @@ test_sqlite: | |||||||
| 	$(py_warn) TEST_DB=sqlite://:memory: py.test | 	$(py_warn) TEST_DB=sqlite://:memory: py.test | ||||||
|  |  | ||||||
| test_mysql: | test_mysql: | ||||||
| 	$(py_warn) TEST_DB="mysql://root:$(MYSQL_PASS)@$(MYSQL_HOST):$(MYSQL_PORT)/test_\{\}" py.test | 	$(py_warn) TEST_DB="mysql://root:$(MYSQL_PASS)@$(MYSQL_HOST):$(MYSQL_PORT)/test_\{\}" pytest -vv -s | ||||||
|  |  | ||||||
| test_postgres: | test_postgres: | ||||||
| 	$(py_warn) TEST_DB="postgres://postgres:$(POSTGRES_PASS)@$(POSTGRES_HOST):$(POSTGRES_PORT)/test_\{\}" py.test | 	$(py_warn) TEST_DB="postgres://postgres:$(POSTGRES_PASS)@$(POSTGRES_HOST):$(POSTGRES_PORT)/test_\{\}" pytest | ||||||
|  |  | ||||||
| testall: deps test_sqlite test_postgres test_mysql | testall: deps test_sqlite test_postgres test_mysql | ||||||
|  |  | ||||||
| build: deps | build: deps | ||||||
| 	@poetry build | 	@poetry build | ||||||
|  |  | ||||||
| publish: deps |  | ||||||
| 	@poetry publish --build |  | ||||||
|  |  | ||||||
| ci: check testall | ci: check testall | ||||||
							
								
								
									
										78
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								README.md
									
									
									
									
									
								
							| @@ -5,16 +5,12 @@ | |||||||
| [](https://github.com/long2ice/aerich/actions?query=workflow:pypi) | [](https://github.com/long2ice/aerich/actions?query=workflow:pypi) | ||||||
| [](https://github.com/long2ice/aerich/actions?query=workflow:test) | [](https://github.com/long2ice/aerich/actions?query=workflow:test) | ||||||
|  |  | ||||||
| <a href="https://tracking.gitads.io/?repo=aerich"><img src="https://images.gitads.io/aerich" alt="GitAds"/></a> |  | ||||||
|  |  | ||||||
| ## Introduction | ## Introduction | ||||||
|  |  | ||||||
| Tortoise-ORM is the best asyncio ORM now, but it lacks a database | Aerich is a database migrations tool for Tortoise-ORM, which like alembic for SQLAlchemy, or Django ORM with it\'s | ||||||
| migrations tool like alembic for SQLAlchemy, or Django ORM with it\'s | own migrations solution. | ||||||
| own migrations tool. |  | ||||||
|  |  | ||||||
| This project aim to be a best migrations tool for Tortoise-ORM and which | **If you upgrade aerich from <= 0.2.5 to >= 0.3.0, see [changelog](https://github.com/tortoise/aerich/blob/dev/CHANGELOG.md) for upgrade steps.** | ||||||
| written by one of contributors of Tortoise-ORM. |  | ||||||
|  |  | ||||||
| ## Install | ## Install | ||||||
|  |  | ||||||
| @@ -27,7 +23,7 @@ Just install from pypi: | |||||||
| ## Quick Start | ## Quick Start | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| $ aerich -h | > aerich -h | ||||||
|  |  | ||||||
| Usage: aerich [OPTIONS] COMMAND [ARGS]... | Usage: aerich [OPTIONS] COMMAND [ARGS]... | ||||||
|  |  | ||||||
| @@ -39,7 +35,7 @@ Options: | |||||||
|   -h, --help         Show this message and exit. |   -h, --help         Show this message and exit. | ||||||
|  |  | ||||||
| Commands: | Commands: | ||||||
|   downgrade  Downgrade to previous version. |   downgrade  Downgrade to specified version. | ||||||
|   heads      Show current available heads in migrate location. |   heads      Show current available heads in migrate location. | ||||||
|   history    List all migrate items. |   history    List all migrate items. | ||||||
|   init       Init config file and generate root migrate location. |   init       Init config file and generate root migrate location. | ||||||
| @@ -68,7 +64,7 @@ TORTOISE_ORM = { | |||||||
| ### Initialization | ### Initialization | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| $ aerich init -h | > aerich init -h | ||||||
|  |  | ||||||
| Usage: aerich init [OPTIONS] | Usage: aerich init [OPTIONS] | ||||||
|  |  | ||||||
| @@ -84,7 +80,7 @@ Options: | |||||||
| Init config file and location: | Init config file and location: | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| $ aerich init -t tests.backends.mysql.TORTOISE_ORM | > aerich init -t tests.backends.mysql.TORTOISE_ORM | ||||||
|  |  | ||||||
| Success create migrate location ./migrations | Success create migrate location ./migrations | ||||||
| Success generate config file aerich.ini | Success generate config file aerich.ini | ||||||
| @@ -93,56 +89,66 @@ Success generate config file aerich.ini | |||||||
| ### Init db | ### Init db | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| $ aerich init-db | > aerich init-db | ||||||
|  |  | ||||||
| Success create app migrate location ./migrations/models | Success create app migrate location ./migrations/models | ||||||
| Success generate schema for app "models" | Success generate schema for app "models" | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ::: {.note} |  | ||||||
| ::: {.title} |  | ||||||
| Note |  | ||||||
| ::: |  | ||||||
|  |  | ||||||
| If your Tortoise-ORM app is not default `models`, you must specify | If your Tortoise-ORM app is not default `models`, you must specify | ||||||
| `--app` like `aerich --app other_models init-db`. | `--app` like `aerich --app other_models init-db`. | ||||||
| ::: |  | ||||||
|  |  | ||||||
| ### Update models and make migrate | ### Update models and make migrate | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| $ aerich migrate --name drop_column | > aerich migrate --name drop_column | ||||||
|  |  | ||||||
| Success migrate 1_202029051520102929_drop_column.json | Success migrate 1_202029051520102929_drop_column.json | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Format of migrate filename is | Format of migrate filename is | ||||||
| `{version_num}_{datetime}_{name|update}.json` | `{version_num}_{datetime}_{name|update}.json`. | ||||||
|  |  | ||||||
|  | And if `aerich` guess you are renaming a column, it will ask `Rename {old_column} to {new_column} [True]`, you can choice `True` to rename column without column drop, or choice `False` to drop column then create. | ||||||
|  |  | ||||||
|  | If you use `MySQL`, only MySQL8.0+ support `rename..to` syntax. | ||||||
|  |  | ||||||
| ### Upgrade to latest version | ### Upgrade to latest version | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| $ aerich upgrade | > aerich upgrade | ||||||
|  |  | ||||||
| Success upgrade 1_202029051520102929_drop_column.json | Success upgrade 1_202029051520102929_drop_column.json | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Now your db is migrated to latest. | Now your db is migrated to latest. | ||||||
|  |  | ||||||
| ### Downgrade to previous version | ### Downgrade to specified version | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| $ aerich downgrade | > aerich init -h | ||||||
|  |  | ||||||
|  | Usage: aerich downgrade [OPTIONS] | ||||||
|  |  | ||||||
|  |   Downgrade to specified version. | ||||||
|  |  | ||||||
|  | Options: | ||||||
|  |   -v, --version INTEGER  Specified version, default to last.  [default: -1] | ||||||
|  |   -h, --help             Show this message and exit. | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ```shell | ||||||
|  | > aerich downgrade | ||||||
|  |  | ||||||
| Success downgrade 1_202029051520102929_drop_column.json | Success downgrade 1_202029051520102929_drop_column.json | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Now your db rollback to previous version. | Now your db rollback to specified version. | ||||||
|  |  | ||||||
| ### Show history | ### Show history | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| $ aerich history | > aerich history | ||||||
|  |  | ||||||
| 1_202029051520102929_drop_column.json | 1_202029051520102929_drop_column.json | ||||||
| ``` | ``` | ||||||
| @@ -150,33 +156,19 @@ $ aerich history | |||||||
| ### Show heads to be migrated | ### Show heads to be migrated | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| $ aerich heads | > aerich heads | ||||||
|  |  | ||||||
| 1_202029051520102929_drop_column.json | 1_202029051520102929_drop_column.json | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ## Limitations |  | ||||||
|  |  | ||||||
| - Not support `rename column` now. |  | ||||||
| - `Sqlite` and `Postgres` may not work as expected because I don\'t |  | ||||||
|   use those in my work. |  | ||||||
|  |  | ||||||
| ## Support this project | ## Support this project | ||||||
|  |  | ||||||
| - Just give a star! | - Just give a star! | ||||||
| - Donation. | - Donation. | ||||||
|  |  | ||||||
| ### AliPay | | AliPay                                                                                 | WeChatPay                                                                                 | PayPal                                                           | | ||||||
|  | | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | | ||||||
| <img width="200" src="https://github.com/long2ice/aerich/raw/dev/images/alipay.jpeg"/> | | <img width="200" src="https://github.com/long2ice/aerich/raw/dev/images/alipay.jpeg"/> | <img width="200" src="https://github.com/long2ice/aerich/raw/dev/images/wechatpay.jpeg"/> | [PayPal](https://www.paypal.me/long2ice) to my account long2ice. | | ||||||
|  |  | ||||||
| ### WeChat Pay |  | ||||||
|  |  | ||||||
| <img width="200" src="https://github.com/long2ice/aerich/raw/dev/images/wechatpay.jpeg"/> |  | ||||||
|  |  | ||||||
| ### PayPal |  | ||||||
|  |  | ||||||
| Donate money by [paypal](https://www.paypal.me/long2ice) to my account long2ice. |  | ||||||
|  |  | ||||||
| ## License | ## License | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| __version__ = "0.2.2" | __version__ = "0.3.1" | ||||||
|   | |||||||
							
								
								
									
										114
									
								
								aerich/cli.py
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								aerich/cli.py
									
									
									
									
									
								
							| @@ -1,12 +1,12 @@ | |||||||
| import functools | import asyncio | ||||||
| import json | import json | ||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
| from configparser import ConfigParser | from configparser import ConfigParser | ||||||
| from enum import Enum | from functools import wraps | ||||||
|  |  | ||||||
| import asyncclick as click | import click | ||||||
| from asyncclick import Context, UsageError | from click import Context, UsageError | ||||||
| from tortoise import Tortoise, generate_schema_for_client | from tortoise import Tortoise, generate_schema_for_client | ||||||
| from tortoise.exceptions import OperationalError | from tortoise.exceptions import OperationalError | ||||||
| from tortoise.transactions import in_transaction | from tortoise.transactions import in_transaction | ||||||
| @@ -16,26 +16,23 @@ from aerich.migrate import Migrate | |||||||
| from aerich.utils import get_app_connection, get_app_connection_name, get_tortoise_config | from aerich.utils import get_app_connection, get_app_connection_name, get_tortoise_config | ||||||
|  |  | ||||||
| from . import __version__ | from . import __version__ | ||||||
|  | from .enums import Color | ||||||
| from .models import Aerich | from .models import Aerich | ||||||
|  |  | ||||||
|  |  | ||||||
| class Color(str, Enum): |  | ||||||
|     green = "green" |  | ||||||
|     red = "red" |  | ||||||
|     yellow = "yellow" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| parser = ConfigParser() | parser = ConfigParser() | ||||||
|  |  | ||||||
|  |  | ||||||
| def close_db(func): | def coro(f): | ||||||
|     @functools.wraps(func) |     @wraps(f) | ||||||
|     async def close_db_inner(*args, **kwargs): |     def wrapper(*args, **kwargs): | ||||||
|         result = await func(*args, **kwargs) |         loop = asyncio.get_event_loop() | ||||||
|         await Tortoise.close_connections() |         ctx = args[0] | ||||||
|         return result |         loop.run_until_complete(f(*args, **kwargs)) | ||||||
|  |         app = ctx.obj.get("app") | ||||||
|  |         if app: | ||||||
|  |             Migrate.remove_old_model_file(app, ctx.obj["location"]) | ||||||
|  |  | ||||||
|     return close_db_inner |     return wrapper | ||||||
|  |  | ||||||
|  |  | ||||||
| @click.group(context_settings={"help_option_names": ["-h", "--help"]}) | @click.group(context_settings={"help_option_names": ["-h", "--help"]}) | ||||||
| @@ -52,6 +49,7 @@ def close_db(func): | |||||||
|     help="Name of section in .ini file to use for aerich config.", |     help="Name of section in .ini file to use for aerich config.", | ||||||
| ) | ) | ||||||
| @click.pass_context | @click.pass_context | ||||||
|  | @coro | ||||||
| async def cli(ctx: Context, config, app, name): | async def cli(ctx: Context, config, app, name): | ||||||
|     ctx.ensure_object(dict) |     ctx.ensure_object(dict) | ||||||
|     ctx.obj["config_file"] = config |     ctx.obj["config_file"] = config | ||||||
| @@ -73,7 +71,7 @@ async def cli(ctx: Context, config, app, name): | |||||||
|         ctx.obj["config"] = tortoise_config |         ctx.obj["config"] = tortoise_config | ||||||
|         ctx.obj["location"] = location |         ctx.obj["location"] = location | ||||||
|         ctx.obj["app"] = app |         ctx.obj["app"] = app | ||||||
|  |         Migrate.app = app | ||||||
|         if invoked_subcommand != "init-db": |         if invoked_subcommand != "init-db": | ||||||
|             await Migrate.init_with_old_models(tortoise_config, app, location) |             await Migrate.init_with_old_models(tortoise_config, app, location) | ||||||
|  |  | ||||||
| @@ -81,56 +79,72 @@ async def cli(ctx: Context, config, app, name): | |||||||
| @cli.command(help="Generate migrate changes file.") | @cli.command(help="Generate migrate changes file.") | ||||||
| @click.option("--name", default="update", show_default=True, help="Migrate name.") | @click.option("--name", default="update", show_default=True, help="Migrate name.") | ||||||
| @click.pass_context | @click.pass_context | ||||||
| @close_db | @coro | ||||||
| async def migrate(ctx: Context, name): | async def migrate(ctx: Context, name): | ||||||
|     config = ctx.obj["config"] |  | ||||||
|     location = ctx.obj["location"] |  | ||||||
|     app = ctx.obj["app"] |  | ||||||
|  |  | ||||||
|     ret = await Migrate.migrate(name) |     ret = await Migrate.migrate(name) | ||||||
|     if not ret: |     if not ret: | ||||||
|         return click.secho("No changes detected", fg=Color.yellow) |         return click.secho("No changes detected", fg=Color.yellow) | ||||||
|     Migrate.write_old_models(config, app, location) |  | ||||||
|     click.secho(f"Success migrate {ret}", fg=Color.green) |     click.secho(f"Success migrate {ret}", fg=Color.green) | ||||||
|  |  | ||||||
|  |  | ||||||
| @cli.command(help="Upgrade to latest version.") | @cli.command(help="Upgrade to specified version.") | ||||||
| @click.pass_context | @click.pass_context | ||||||
| @close_db | @coro | ||||||
| async def upgrade(ctx: Context): | async def upgrade(ctx: Context): | ||||||
|     config = ctx.obj["config"] |     config = ctx.obj["config"] | ||||||
|     app = ctx.obj["app"] |     app = ctx.obj["app"] | ||||||
|  |     location = ctx.obj["location"] | ||||||
|     migrated = False |     migrated = False | ||||||
|     for version in Migrate.get_all_version_files(): |     for version_file in Migrate.get_all_version_files(): | ||||||
|         try: |         try: | ||||||
|             exists = await Aerich.exists(version=version, app=app) |             exists = await Aerich.exists(version=version_file, app=app) | ||||||
|         except OperationalError: |         except OperationalError: | ||||||
|             exists = False |             exists = False | ||||||
|         if not exists: |         if not exists: | ||||||
|             async with in_transaction(get_app_connection_name(config, app)) as conn: |             async with in_transaction(get_app_connection_name(config, app)) as conn: | ||||||
|                 file_path = os.path.join(Migrate.migrate_location, version) |                 file_path = os.path.join(Migrate.migrate_location, version_file) | ||||||
|                 with open(file_path, "r", encoding="utf-8") as f: |                 with open(file_path, "r", encoding="utf-8") as f: | ||||||
|                     content = json.load(f) |                     content = json.load(f) | ||||||
|                     upgrade_query_list = content.get("upgrade") |                     upgrade_query_list = content.get("upgrade") | ||||||
|                     for upgrade_query in upgrade_query_list: |                     for upgrade_query in upgrade_query_list: | ||||||
|                         await conn.execute_script(upgrade_query) |                         await conn.execute_script(upgrade_query) | ||||||
|                 await Aerich.create(version=version, app=app) |                 await Aerich.create( | ||||||
|             click.secho(f"Success upgrade {version}", fg=Color.green) |                     version=version_file, | ||||||
|  |                     app=app, | ||||||
|  |                     content=Migrate.get_models_content(config, app, location), | ||||||
|  |                 ) | ||||||
|  |             click.secho(f"Success upgrade {version_file}", fg=Color.green) | ||||||
|             migrated = True |             migrated = True | ||||||
|     if not migrated: |     if not migrated: | ||||||
|         click.secho("No migrate items", fg=Color.yellow) |         click.secho("No migrate items", fg=Color.yellow) | ||||||
|  |  | ||||||
|  |  | ||||||
| @cli.command(help="Downgrade to previous version.") | @cli.command(help="Downgrade to specified version.") | ||||||
|  | @click.option( | ||||||
|  |     "-v", | ||||||
|  |     "--version", | ||||||
|  |     default=-1, | ||||||
|  |     type=int, | ||||||
|  |     show_default=True, | ||||||
|  |     help="Specified version, default to last.", | ||||||
|  | ) | ||||||
| @click.pass_context | @click.pass_context | ||||||
| @close_db | @coro | ||||||
| async def downgrade(ctx: Context): | async def downgrade(ctx: Context, version: int): | ||||||
|     app = ctx.obj["app"] |     app = ctx.obj["app"] | ||||||
|     config = ctx.obj["config"] |     config = ctx.obj["config"] | ||||||
|     last_version = await Migrate.get_last_version() |     if version == -1: | ||||||
|     if not last_version: |         specified_version = await Migrate.get_last_version() | ||||||
|         return click.secho("No last version found", fg=Color.yellow) |     else: | ||||||
|     file = last_version.version |         specified_version = await Aerich.filter(app=app, version__startswith=f"{version}_").first() | ||||||
|  |     if not specified_version: | ||||||
|  |         return click.secho("No specified version found", fg=Color.yellow) | ||||||
|  |     if version == -1: | ||||||
|  |         versions = [specified_version] | ||||||
|  |     else: | ||||||
|  |         versions = await Aerich.filter(app=app, pk__gte=specified_version.pk) | ||||||
|  |     for version in versions: | ||||||
|  |         file = version.version | ||||||
|         async with in_transaction(get_app_connection_name(config, app)) as conn: |         async with in_transaction(get_app_connection_name(config, app)) as conn: | ||||||
|             file_path = os.path.join(Migrate.migrate_location, file) |             file_path = os.path.join(Migrate.migrate_location, file) | ||||||
|             with open(file_path, "r", encoding="utf-8") as f: |             with open(file_path, "r", encoding="utf-8") as f: | ||||||
| @@ -140,13 +154,14 @@ async def downgrade(ctx: Context): | |||||||
|                     return click.secho("No downgrade item found", fg=Color.yellow) |                     return click.secho("No downgrade item found", fg=Color.yellow) | ||||||
|                 for downgrade_query in downgrade_query_list: |                 for downgrade_query in downgrade_query_list: | ||||||
|                     await conn.execute_query(downgrade_query) |                     await conn.execute_query(downgrade_query) | ||||||
|             await last_version.delete() |                 await version.delete() | ||||||
|         return click.secho(f"Success downgrade {file}", fg=Color.green) |             os.unlink(file_path) | ||||||
|  |             click.secho(f"Success downgrade {file}", fg=Color.green) | ||||||
|  |  | ||||||
|  |  | ||||||
| @cli.command(help="Show current available heads in migrate location.") | @cli.command(help="Show current available heads in migrate location.") | ||||||
| @click.pass_context | @click.pass_context | ||||||
| @close_db | @coro | ||||||
| async def heads(ctx: Context): | async def heads(ctx: Context): | ||||||
|     app = ctx.obj["app"] |     app = ctx.obj["app"] | ||||||
|     versions = Migrate.get_all_version_files() |     versions = Migrate.get_all_version_files() | ||||||
| @@ -156,12 +171,12 @@ async def heads(ctx: Context): | |||||||
|             click.secho(version, fg=Color.green) |             click.secho(version, fg=Color.green) | ||||||
|             is_heads = True |             is_heads = True | ||||||
|     if not is_heads: |     if not is_heads: | ||||||
|         click.secho("No available heads,try migrate", fg=Color.green) |         click.secho("No available heads,try migrate first", fg=Color.green) | ||||||
|  |  | ||||||
|  |  | ||||||
| @cli.command(help="List all migrate items.") | @cli.command(help="List all migrate items.") | ||||||
| @click.pass_context | @click.pass_context | ||||||
| @close_db | @coro | ||||||
| async def history(ctx: Context): | async def history(ctx: Context): | ||||||
|     versions = Migrate.get_all_version_files() |     versions = Migrate.get_all_version_files() | ||||||
|     for version in versions: |     for version in versions: | ||||||
| @@ -181,6 +196,7 @@ async def history(ctx: Context): | |||||||
|     "--location", default="./migrations", show_default=True, help="Migrate store location." |     "--location", default="./migrations", show_default=True, help="Migrate store location." | ||||||
| ) | ) | ||||||
| @click.pass_context | @click.pass_context | ||||||
|  | @coro | ||||||
| async def init( | async def init( | ||||||
|     ctx: Context, tortoise_orm, location, |     ctx: Context, tortoise_orm, location, | ||||||
| ): | ): | ||||||
| @@ -212,7 +228,7 @@ async def init( | |||||||
|     show_default=True, |     show_default=True, | ||||||
| ) | ) | ||||||
| @click.pass_context | @click.pass_context | ||||||
| @close_db | @coro | ||||||
| async def init_db(ctx: Context, safe): | async def init_db(ctx: Context, safe): | ||||||
|     config = ctx.obj["config"] |     config = ctx.obj["config"] | ||||||
|     location = ctx.obj["location"] |     location = ctx.obj["location"] | ||||||
| @@ -225,8 +241,6 @@ async def init_db(ctx: Context, safe): | |||||||
|     else: |     else: | ||||||
|         return click.secho(f"Inited {app} already", fg=Color.yellow) |         return click.secho(f"Inited {app} already", fg=Color.yellow) | ||||||
|  |  | ||||||
|     Migrate.write_old_models(config, app, location) |  | ||||||
|  |  | ||||||
|     await Tortoise.init(config=config) |     await Tortoise.init(config=config) | ||||||
|     connection = get_app_connection(config, app) |     connection = get_app_connection(config, app) | ||||||
|     await generate_schema_for_client(connection, safe) |     await generate_schema_for_client(connection, safe) | ||||||
| @@ -234,7 +248,9 @@ async def init_db(ctx: Context, safe): | |||||||
|     schema = get_schema_sql(connection, safe) |     schema = get_schema_sql(connection, safe) | ||||||
|  |  | ||||||
|     version = await Migrate.generate_version() |     version = await Migrate.generate_version() | ||||||
|     await Aerich.create(version=version, app=app) |     await Aerich.create( | ||||||
|  |         version=version, app=app, content=Migrate.get_models_content(config, app, location) | ||||||
|  |     ) | ||||||
|     with open(os.path.join(dirname, version), "w", encoding="utf-8") as f: |     with open(os.path.join(dirname, version), "w", encoding="utf-8") as f: | ||||||
|         content = { |         content = { | ||||||
|             "upgrade": [schema], |             "upgrade": [schema], | ||||||
| @@ -245,4 +261,4 @@ async def init_db(ctx: Context, safe): | |||||||
|  |  | ||||||
| def main(): | def main(): | ||||||
|     sys.path.insert(0, ".") |     sys.path.insert(0, ".") | ||||||
|     cli(_anyio_backend="asyncio") |     cli() | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ from typing import List, Type | |||||||
|  |  | ||||||
| from tortoise import BaseDBAsyncClient, ForeignKeyFieldInstance, ManyToManyFieldInstance, Model | from tortoise import BaseDBAsyncClient, ForeignKeyFieldInstance, ManyToManyFieldInstance, Model | ||||||
| from tortoise.backends.base.schema_generator import BaseSchemaGenerator | from tortoise.backends.base.schema_generator import BaseSchemaGenerator | ||||||
| from tortoise.fields import Field, JSONField, TextField, UUIDField | from tortoise.fields import CASCADE, Field, JSONField, TextField, UUIDField | ||||||
|  |  | ||||||
|  |  | ||||||
| class BaseDDL: | class BaseDDL: | ||||||
| @@ -11,13 +11,16 @@ class BaseDDL: | |||||||
|     _DROP_TABLE_TEMPLATE = 'DROP TABLE IF EXISTS "{table_name}"' |     _DROP_TABLE_TEMPLATE = 'DROP TABLE IF EXISTS "{table_name}"' | ||||||
|     _ADD_COLUMN_TEMPLATE = 'ALTER TABLE "{table_name}" ADD {column}' |     _ADD_COLUMN_TEMPLATE = 'ALTER TABLE "{table_name}" ADD {column}' | ||||||
|     _DROP_COLUMN_TEMPLATE = 'ALTER TABLE "{table_name}" DROP COLUMN "{column_name}"' |     _DROP_COLUMN_TEMPLATE = 'ALTER TABLE "{table_name}" DROP COLUMN "{column_name}"' | ||||||
|  |     _RENAME_COLUMN_TEMPLATE = ( | ||||||
|  |         'ALTER TABLE "{table_name}" RENAME COLUMN "{old_column_name}" TO "{new_column_name}"' | ||||||
|  |     ) | ||||||
|     _ADD_INDEX_TEMPLATE = ( |     _ADD_INDEX_TEMPLATE = ( | ||||||
|         'ALTER TABLE "{table_name}" ADD {unique} INDEX "{index_name}" ({column_names})' |         'ALTER TABLE "{table_name}" ADD {unique} INDEX "{index_name}" ({column_names})' | ||||||
|     ) |     ) | ||||||
|     _DROP_INDEX_TEMPLATE = 'ALTER TABLE "{table_name}" DROP INDEX "{index_name}"' |     _DROP_INDEX_TEMPLATE = 'ALTER TABLE "{table_name}" DROP INDEX "{index_name}"' | ||||||
|     _ADD_FK_TEMPLATE = 'ALTER TABLE "{table_name}" ADD CONSTRAINT "{fk_name}" FOREIGN KEY ("{db_column}") REFERENCES "{table}" ("{field}") ON DELETE {on_delete}' |     _ADD_FK_TEMPLATE = 'ALTER TABLE "{table_name}" ADD CONSTRAINT "{fk_name}" FOREIGN KEY ("{db_column}") REFERENCES "{table}" ("{field}") ON DELETE {on_delete}' | ||||||
|     _DROP_FK_TEMPLATE = 'ALTER TABLE "{table_name}" DROP FOREIGN KEY "{fk_name}"' |     _DROP_FK_TEMPLATE = 'ALTER TABLE "{table_name}" DROP FOREIGN KEY "{fk_name}"' | ||||||
|     _M2M_TABLE_TEMPLATE = 'CREATE TABLE "{table_name}" ("{backward_key}" {backward_type} NOT NULL REFERENCES "{backward_table}" ("{backward_field}") ON DELETE CASCADE,"{forward_key}" {forward_type} NOT NULL REFERENCES "{forward_table}" ("{forward_field}") ON DELETE CASCADE){extra}{comment};' |     _M2M_TABLE_TEMPLATE = 'CREATE TABLE "{table_name}" ("{backward_key}" {backward_type} NOT NULL REFERENCES "{backward_table}" ("{backward_field}") ON DELETE CASCADE,"{forward_key}" {forward_type} NOT NULL REFERENCES "{forward_table}" ("{forward_field}") ON DELETE {on_delete}){extra}{comment};' | ||||||
|     _MODIFY_COLUMN_TEMPLATE = 'ALTER TABLE "{table_name}" MODIFY COLUMN {column}' |     _MODIFY_COLUMN_TEMPLATE = 'ALTER TABLE "{table_name}" MODIFY COLUMN {column}' | ||||||
|  |  | ||||||
|     def __init__(self, client: "BaseDBAsyncClient"): |     def __init__(self, client: "BaseDBAsyncClient"): | ||||||
| @@ -41,6 +44,7 @@ class BaseDDL: | |||||||
|             backward_type=model._meta.pk.get_for_dialect(self.DIALECT, "SQL_TYPE"), |             backward_type=model._meta.pk.get_for_dialect(self.DIALECT, "SQL_TYPE"), | ||||||
|             forward_key=field.forward_key, |             forward_key=field.forward_key, | ||||||
|             forward_type=field.related_model._meta.pk.get_for_dialect(self.DIALECT, "SQL_TYPE"), |             forward_type=field.related_model._meta.pk.get_for_dialect(self.DIALECT, "SQL_TYPE"), | ||||||
|  |             on_delete=CASCADE, | ||||||
|             extra=self.schema_generator._table_generate_extra(table=field.through), |             extra=self.schema_generator._table_generate_extra(table=field.through), | ||||||
|             comment=self.schema_generator._table_comment_generator( |             comment=self.schema_generator._table_comment_generator( | ||||||
|                 table=field.through, comment=field.description |                 table=field.through, comment=field.description | ||||||
| @@ -125,6 +129,13 @@ class BaseDDL: | |||||||
|             ), |             ), | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     def rename_column(self, model: "Type[Model]", old_column_name: str, new_column_name: str): | ||||||
|  |         return self._RENAME_COLUMN_TEMPLATE.format( | ||||||
|  |             table_name=model._meta.db_table, | ||||||
|  |             old_column_name=old_column_name, | ||||||
|  |             new_column_name=new_column_name, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     def add_index(self, model: "Type[Model]", field_names: List[str], unique=False): |     def add_index(self, model: "Type[Model]", field_names: List[str], unique=False): | ||||||
|         return self._ADD_INDEX_TEMPLATE.format( |         return self._ADD_INDEX_TEMPLATE.format( | ||||||
|             unique="UNIQUE" if unique else "", |             unique="UNIQUE" if unique else "", | ||||||
|   | |||||||
| @@ -9,6 +9,9 @@ class MysqlDDL(BaseDDL): | |||||||
|     _DROP_TABLE_TEMPLATE = "DROP TABLE IF EXISTS `{table_name}`" |     _DROP_TABLE_TEMPLATE = "DROP TABLE IF EXISTS `{table_name}`" | ||||||
|     _ADD_COLUMN_TEMPLATE = "ALTER TABLE `{table_name}` ADD {column}" |     _ADD_COLUMN_TEMPLATE = "ALTER TABLE `{table_name}` ADD {column}" | ||||||
|     _DROP_COLUMN_TEMPLATE = "ALTER TABLE `{table_name}` DROP COLUMN `{column_name}`" |     _DROP_COLUMN_TEMPLATE = "ALTER TABLE `{table_name}` DROP COLUMN `{column_name}`" | ||||||
|  |     _RENAME_COLUMN_TEMPLATE = ( | ||||||
|  |         "ALTER TABLE `{table_name}` RENAME COLUMN `{old_column_name}` TO `{new_column_name}`" | ||||||
|  |     ) | ||||||
|     _ADD_INDEX_TEMPLATE = ( |     _ADD_INDEX_TEMPLATE = ( | ||||||
|         "ALTER TABLE `{table_name}` ADD {unique} INDEX `{index_name}` ({column_names})" |         "ALTER TABLE `{table_name}` ADD {unique} INDEX `{index_name}` ({column_names})" | ||||||
|     ) |     ) | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| from typing import Type | from typing import List, Type | ||||||
|  |  | ||||||
| from tortoise import Model | from tortoise import Model | ||||||
| from tortoise.backends.asyncpg.schema_generator import AsyncpgSchemaGenerator | from tortoise.backends.asyncpg.schema_generator import AsyncpgSchemaGenerator | ||||||
| @@ -10,6 +10,12 @@ from aerich.ddl import BaseDDL | |||||||
| class PostgresDDL(BaseDDL): | class PostgresDDL(BaseDDL): | ||||||
|     schema_generator_cls = AsyncpgSchemaGenerator |     schema_generator_cls = AsyncpgSchemaGenerator | ||||||
|     DIALECT = AsyncpgSchemaGenerator.DIALECT |     DIALECT = AsyncpgSchemaGenerator.DIALECT | ||||||
|  |     _ADD_INDEX_TEMPLATE = 'CREATE INDEX "{index_name}" ON "{table_name}" ({column_names})' | ||||||
|  |     _ADD_UNIQUE_TEMPLATE = ( | ||||||
|  |         'ALTER TABLE "{table_name}" ADD CONSTRAINT "{index_name}" UNIQUE ({column_names})' | ||||||
|  |     ) | ||||||
|  |     _DROP_INDEX_TEMPLATE = 'DROP INDEX "{index_name}"' | ||||||
|  |     _DROP_UNIQUE_TEMPLATE = 'ALTER TABLE "{table_name}" DROP CONSTRAINT "{index_name}"' | ||||||
|     _ALTER_DEFAULT_TEMPLATE = 'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" {default}' |     _ALTER_DEFAULT_TEMPLATE = 'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" {default}' | ||||||
|     _ALTER_NULL_TEMPLATE = 'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" {set_drop} NOT NULL' |     _ALTER_NULL_TEMPLATE = 'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" {set_drop} NOT NULL' | ||||||
|     _MODIFY_COLUMN_TEMPLATE = 'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" TYPE {datatype}' |     _MODIFY_COLUMN_TEMPLATE = 'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" TYPE {datatype}' | ||||||
| @@ -41,6 +47,25 @@ class PostgresDDL(BaseDDL): | |||||||
|             datatype=field_object.get_for_dialect(self.DIALECT, "SQL_TYPE"), |             datatype=field_object.get_for_dialect(self.DIALECT, "SQL_TYPE"), | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     def add_index(self, model: "Type[Model]", field_names: List[str], unique=False): | ||||||
|  |         template = self._ADD_UNIQUE_TEMPLATE if unique else self._ADD_INDEX_TEMPLATE | ||||||
|  |         return template.format( | ||||||
|  |             index_name=self.schema_generator._generate_index_name( | ||||||
|  |                 "uid" if unique else "idx", model, field_names | ||||||
|  |             ), | ||||||
|  |             table_name=model._meta.db_table, | ||||||
|  |             column_names=", ".join([self.schema_generator.quote(f) for f in field_names]), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     def drop_index(self, model: "Type[Model]", field_names: List[str], unique=False): | ||||||
|  |         template = self._DROP_UNIQUE_TEMPLATE if unique else self._DROP_INDEX_TEMPLATE | ||||||
|  |         return template.format( | ||||||
|  |             index_name=self.schema_generator._generate_index_name( | ||||||
|  |                 "uid" if unique else "idx", model, field_names | ||||||
|  |             ), | ||||||
|  |             table_name=model._meta.db_table, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     def set_comment(self, model: "Type[Model]", field_object: Field): |     def set_comment(self, model: "Type[Model]", field_object: Field): | ||||||
|         db_table = model._meta.db_table |         db_table = model._meta.db_table | ||||||
|         return self._SET_COMMENT_TEMPLATE.format( |         return self._SET_COMMENT_TEMPLATE.format( | ||||||
|   | |||||||
| @@ -1,8 +1,19 @@ | |||||||
|  | from typing import Type | ||||||
|  |  | ||||||
|  | from tortoise import Model | ||||||
| from tortoise.backends.sqlite.schema_generator import SqliteSchemaGenerator | from tortoise.backends.sqlite.schema_generator import SqliteSchemaGenerator | ||||||
|  | from tortoise.fields import Field | ||||||
|  |  | ||||||
| from aerich.ddl import BaseDDL | from aerich.ddl import BaseDDL | ||||||
|  | from aerich.exceptions import NotSupportError | ||||||
|  |  | ||||||
|  |  | ||||||
| class SqliteDDL(BaseDDL): | class SqliteDDL(BaseDDL): | ||||||
|     schema_generator_cls = SqliteSchemaGenerator |     schema_generator_cls = SqliteSchemaGenerator | ||||||
|     DIALECT = SqliteSchemaGenerator.DIALECT |     DIALECT = SqliteSchemaGenerator.DIALECT | ||||||
|  |  | ||||||
|  |     def drop_column(self, model: "Type[Model]", column_name: str): | ||||||
|  |         raise NotSupportError("Drop column is unsupported in SQLite.") | ||||||
|  |  | ||||||
|  |     def modify_column(self, model: "Type[Model]", field_object: Field): | ||||||
|  |         raise NotSupportError("Modify column is unsupported in SQLite.") | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								aerich/enums.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								aerich/enums.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | from enum import Enum | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Color(str, Enum): | ||||||
|  |     green = "green" | ||||||
|  |     red = "red" | ||||||
|  |     yellow = "yellow" | ||||||
| @@ -1,6 +1,4 @@ | |||||||
| class ConfigurationError(Exception): | class NotSupportError(Exception): | ||||||
|     """ |     """ | ||||||
|     config error |     raise when features not support | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     pass |  | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| import json | import json | ||||||
| import os | import os | ||||||
| import re | import re | ||||||
| from copy import deepcopy |  | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| from importlib import import_module | from importlib import import_module | ||||||
|  | from io import StringIO | ||||||
| from typing import Dict, List, Tuple, Type | from typing import Dict, List, Tuple, Type | ||||||
|  |  | ||||||
|  | import click | ||||||
| from tortoise import ( | from tortoise import ( | ||||||
|     BackwardFKRelation, |     BackwardFKRelation, | ||||||
|     BackwardOneToOneRelation, |     BackwardOneToOneRelation, | ||||||
| @@ -29,6 +30,8 @@ class Migrate: | |||||||
|     _upgrade_m2m: List[str] = [] |     _upgrade_m2m: List[str] = [] | ||||||
|     _downgrade_m2m: List[str] = [] |     _downgrade_m2m: List[str] = [] | ||||||
|     _aerich = Aerich.__name__ |     _aerich = Aerich.__name__ | ||||||
|  |     _rename_old = [] | ||||||
|  |     _rename_new = [] | ||||||
|  |  | ||||||
|     ddl: BaseDDL |     ddl: BaseDDL | ||||||
|     migrate_config: dict |     migrate_config: dict | ||||||
| @@ -39,14 +42,14 @@ class Migrate: | |||||||
|     dialect: str |     dialect: str | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def get_old_model_file(cls): |     def get_old_model_file(cls, app: str, location: str): | ||||||
|         return cls.old_models + ".py" |         return os.path.join(location, app, cls.old_models + ".py") | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def get_all_version_files(cls) -> List[str]: |     def get_all_version_files(cls) -> List[str]: | ||||||
|         return sorted( |         return sorted( | ||||||
|             filter(lambda x: x.endswith("json"), os.listdir(cls.migrate_location)), |             filter(lambda x: x.endswith("json"), os.listdir(cls.migrate_location)), | ||||||
|             key=lambda x: x.split("_")[0], |             key=lambda x: int(x.split("_")[0]), | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
| @@ -54,9 +57,22 @@ class Migrate: | |||||||
|         return await Aerich.filter(app=cls.app).first() |         return await Aerich.filter(app=cls.app).first() | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     async def init_with_old_models(cls, config: dict, app: str, location: str): |     def remove_old_model_file(cls, app: str, location: str): | ||||||
|         migrate_config = cls._get_migrate_config(config, app, location) |         try: | ||||||
|  |             os.unlink(cls.get_old_model_file(app, location)) | ||||||
|  |         except FileNotFoundError: | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     async def init_with_old_models(cls, config: dict, app: str, location: str): | ||||||
|  |         await Tortoise.init(config=config) | ||||||
|  |         last_version = await cls.get_last_version() | ||||||
|  |         if last_version: | ||||||
|  |             content = last_version.content | ||||||
|  |             with open(cls.get_old_model_file(app, location), "w") as f: | ||||||
|  |                 f.write(content) | ||||||
|  |  | ||||||
|  |         migrate_config = cls._get_migrate_config(config, app, location) | ||||||
|         cls.app = app |         cls.app = app | ||||||
|         cls.migrate_config = migrate_config |         cls.migrate_config = migrate_config | ||||||
|         cls.migrate_location = os.path.join(location, app) |         cls.migrate_location = os.path.join(location, app) | ||||||
| @@ -88,7 +104,7 @@ class Migrate: | |||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     async def generate_version(cls, name=None): |     async def generate_version(cls, name=None): | ||||||
|         now = datetime.now().strftime("%Y%M%D%H%M%S").replace("/", "") |         now = datetime.now().strftime("%Y%m%d%H%M%S").replace("/", "") | ||||||
|         last_version_num = await cls._get_last_version_num() |         last_version_num = await cls._get_last_version_num() | ||||||
|         if last_version_num is None: |         if last_version_num is None: | ||||||
|             return f"0_{now}_init.json" |             return f"0_{now}_init.json" | ||||||
| @@ -100,6 +116,10 @@ class Migrate: | |||||||
|     @classmethod |     @classmethod | ||||||
|     async def _generate_diff_sql(cls, name): |     async def _generate_diff_sql(cls, name): | ||||||
|         version = await cls.generate_version(name) |         version = await cls.generate_version(name) | ||||||
|  |         # delete if same version exists | ||||||
|  |         for version_file in cls.get_all_version_files(): | ||||||
|  |             if version_file.startswith(version.split("_")[0]): | ||||||
|  |                 os.unlink(os.path.join(cls.migrate_location, version_file)) | ||||||
|         content = { |         content = { | ||||||
|             "upgrade": cls.upgrade_operators, |             "upgrade": cls.upgrade_operators, | ||||||
|             "downgrade": cls.downgrade_operators, |             "downgrade": cls.downgrade_operators, | ||||||
| @@ -130,7 +150,7 @@ class Migrate: | |||||||
|         return await cls._generate_diff_sql(name) |         return await cls._generate_diff_sql(name) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _add_operator(cls, operator: str, upgrade=True, fk=False): |     def _add_operator(cls, operator: str, upgrade=True, fk_m2m=False): | ||||||
|         """ |         """ | ||||||
|         add operator,differentiate fk because fk is order limit |         add operator,differentiate fk because fk is order limit | ||||||
|         :param operator: |         :param operator: | ||||||
| @@ -139,36 +159,16 @@ class Migrate: | |||||||
|         :return: |         :return: | ||||||
|         """ |         """ | ||||||
|         if upgrade: |         if upgrade: | ||||||
|             if fk: |             if fk_m2m: | ||||||
|                 cls._upgrade_fk_m2m_index_operators.append(operator) |                 cls._upgrade_fk_m2m_index_operators.append(operator) | ||||||
|             else: |             else: | ||||||
|                 cls.upgrade_operators.append(operator) |                 cls.upgrade_operators.append(operator) | ||||||
|         else: |         else: | ||||||
|             if fk: |             if fk_m2m: | ||||||
|                 cls._downgrade_fk_m2m_index_operators.append(operator) |                 cls._downgrade_fk_m2m_index_operators.append(operator) | ||||||
|             else: |             else: | ||||||
|                 cls.downgrade_operators.append(operator) |                 cls.downgrade_operators.append(operator) | ||||||
|  |  | ||||||
|     @classmethod |  | ||||||
|     def cp_models( |  | ||||||
|         cls, app: str, model_files: List[str], old_model_file, |  | ||||||
|     ): |  | ||||||
|         """ |  | ||||||
|         cp currents models to old_model_files |  | ||||||
|         :param app: |  | ||||||
|         :param model_files: |  | ||||||
|         :param old_model_file: |  | ||||||
|         :return: |  | ||||||
|         """ |  | ||||||
|         pattern = rf"(\n)?('|\")({app})(.\w+)('|\")" |  | ||||||
|         for i, model_file in enumerate(model_files): |  | ||||||
|             with open(model_file, "r", encoding="utf-8") as f: |  | ||||||
|                 content = f.read() |  | ||||||
|             ret = re.sub(pattern, rf"\2{cls.diff_app}\4\5", content) |  | ||||||
|             mode = "w" if i == 0 else "a" |  | ||||||
|             with open(old_model_file, mode, encoding="utf-8") as f: |  | ||||||
|                 f.write(ret) |  | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _get_migrate_config(cls, config: dict, app: str, location: str): |     def _get_migrate_config(cls, config: dict, app: str, location: str): | ||||||
|         """ |         """ | ||||||
| @@ -178,17 +178,16 @@ class Migrate: | |||||||
|         :param location: |         :param location: | ||||||
|         :return: |         :return: | ||||||
|         """ |         """ | ||||||
|         temp_config = deepcopy(config) |  | ||||||
|         path = os.path.join(location, app, cls.old_models) |         path = os.path.join(location, app, cls.old_models) | ||||||
|         path = path.replace(os.sep, ".").lstrip(".") |         path = path.replace(os.sep, ".").lstrip(".") | ||||||
|         temp_config["apps"][cls.diff_app] = { |         config["apps"][cls.diff_app] = { | ||||||
|             "models": [path], |             "models": [path], | ||||||
|             "default_connection": config.get("apps").get(app).get("default_connection", "default"), |             "default_connection": config.get("apps").get(app).get("default_connection", "default"), | ||||||
|         } |         } | ||||||
|         return temp_config |         return config | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def write_old_models(cls, config: dict, app: str, location: str): |     def get_models_content(cls, config: dict, app: str, location: str): | ||||||
|         """ |         """ | ||||||
|         write new models to old models |         write new models to old models | ||||||
|         :param config: |         :param config: | ||||||
| @@ -196,14 +195,18 @@ class Migrate: | |||||||
|         :param location: |         :param location: | ||||||
|         :return: |         :return: | ||||||
|         """ |         """ | ||||||
|         cls.app = app |  | ||||||
|  |  | ||||||
|         old_model_files = [] |         old_model_files = [] | ||||||
|         models = config.get("apps").get(app).get("models") |         models = config.get("apps").get(app).get("models") | ||||||
|         for model in models: |         for model in models: | ||||||
|             old_model_files.append(import_module(model).__file__) |             old_model_files.append(import_module(model).__file__) | ||||||
|  |         pattern = rf"(\n)?('|\")({app})(.\w+)('|\")" | ||||||
|         cls.cp_models(app, old_model_files, os.path.join(location, app, cls.get_old_model_file())) |         str_io = StringIO() | ||||||
|  |         for i, model_file in enumerate(old_model_files): | ||||||
|  |             with open(model_file, "r", encoding="utf-8") as f: | ||||||
|  |                 content = f.read() | ||||||
|  |             ret = re.sub(pattern, rf"\2{cls.diff_app}\4\5", content) | ||||||
|  |             str_io.write(f"{ret}\n") | ||||||
|  |         return str_io.getvalue() | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def diff_models( |     def diff_models( | ||||||
| @@ -266,10 +269,34 @@ class Migrate: | |||||||
|             if cls._exclude_field(new_field, upgrade): |             if cls._exclude_field(new_field, upgrade): | ||||||
|                 continue |                 continue | ||||||
|             if new_key not in old_keys: |             if new_key not in old_keys: | ||||||
|  |                 new_field_dict = new_field.describe(serializable=True) | ||||||
|  |                 new_field_dict.pop("name", None) | ||||||
|  |                 new_field_dict.pop("db_column", None) | ||||||
|  |                 for diff_key in old_keys - new_keys: | ||||||
|  |                     old_field = old_fields_map.get(diff_key) | ||||||
|  |                     old_field_dict = old_field.describe(serializable=True) | ||||||
|  |                     old_field_dict.pop("name", None) | ||||||
|  |                     old_field_dict.pop("db_column", None) | ||||||
|  |                     if old_field_dict == new_field_dict: | ||||||
|  |                         if upgrade: | ||||||
|  |                             is_rename = click.prompt( | ||||||
|  |                                 f"Rename {diff_key} to {new_key}?", | ||||||
|  |                                 default=True, | ||||||
|  |                                 type=bool, | ||||||
|  |                                 show_choices=True, | ||||||
|  |                             ) | ||||||
|  |                             cls._rename_new.append(new_key) | ||||||
|  |                             cls._rename_old.append(diff_key) | ||||||
|  |                         else: | ||||||
|  |                             is_rename = diff_key in cls._rename_new | ||||||
|  |                         if is_rename: | ||||||
|                             cls._add_operator( |                             cls._add_operator( | ||||||
|                     cls._add_field(new_model, new_field), |                                 cls._rename_field(new_model, old_field, new_field), upgrade, | ||||||
|                     upgrade, |                             ) | ||||||
|                     isinstance(new_field, (ForeignKeyFieldInstance, ManyToManyFieldInstance)), |                             break | ||||||
|  |                 else: | ||||||
|  |                     cls._add_operator( | ||||||
|  |                         cls._add_field(new_model, new_field), upgrade, cls._is_fk_m2m(new_field), | ||||||
|                     ) |                     ) | ||||||
|             else: |             else: | ||||||
|                 old_field = old_fields_map.get(new_key) |                 old_field = old_fields_map.get(new_key) | ||||||
| @@ -293,6 +320,11 @@ class Migrate: | |||||||
|                             cls._add_operator( |                             cls._add_operator( | ||||||
|                                 cls._set_comment(new_model, new_field), upgrade=upgrade |                                 cls._set_comment(new_model, new_field), upgrade=upgrade | ||||||
|                             ) |                             ) | ||||||
|  |                         if new_field.field_type != old_field.field_type: | ||||||
|  |                             cls._add_operator( | ||||||
|  |                                 cls._modify_field(new_model, new_field), upgrade=upgrade | ||||||
|  |                             ) | ||||||
|  |                     else: | ||||||
|                         cls._add_operator(cls._modify_field(new_model, new_field), upgrade=upgrade) |                         cls._add_operator(cls._modify_field(new_model, new_field), upgrade=upgrade) | ||||||
|                 if (old_field.index and not new_field.index) or ( |                 if (old_field.index and not new_field.index) or ( | ||||||
|                     old_field.unique and not new_field.unique |                     old_field.unique and not new_field.unique | ||||||
| @@ -312,10 +344,22 @@ class Migrate: | |||||||
|                         upgrade, |                         upgrade, | ||||||
|                         cls._is_fk_m2m(new_field), |                         cls._is_fk_m2m(new_field), | ||||||
|                     ) |                     ) | ||||||
|  |                 if isinstance(new_field, ForeignKeyFieldInstance): | ||||||
|  |                     if old_field.db_constraint and not new_field.db_constraint: | ||||||
|  |                         cls._add_operator( | ||||||
|  |                             cls._drop_fk(new_model, new_field), upgrade, True, | ||||||
|  |                         ) | ||||||
|  |                     if new_field.db_constraint and not old_field.db_constraint: | ||||||
|  |                         cls._add_operator( | ||||||
|  |                             cls._add_fk(new_model, new_field), upgrade, True, | ||||||
|  |                         ) | ||||||
|  |  | ||||||
|         for old_key in old_keys: |         for old_key in old_keys: | ||||||
|             field = old_fields_map.get(old_key) |             field = old_fields_map.get(old_key) | ||||||
|             if old_key not in new_keys and not cls._exclude_field(field, upgrade): |             if old_key not in new_keys and not cls._exclude_field(field, upgrade): | ||||||
|  |                 if (upgrade and old_key not in cls._rename_old) or ( | ||||||
|  |                     not upgrade and old_key not in cls._rename_new | ||||||
|  |                 ): | ||||||
|                     cls._add_operator( |                     cls._add_operator( | ||||||
|                         cls._remove_field(old_model, field), upgrade, cls._is_fk_m2m(field), |                         cls._remove_field(old_model, field), upgrade, cls._is_fk_m2m(field), | ||||||
|                     ) |                     ) | ||||||
| @@ -402,6 +446,10 @@ class Migrate: | |||||||
|     def _modify_field(cls, model: Type[Model], field: Field): |     def _modify_field(cls, model: Type[Model], field: Field): | ||||||
|         return cls.ddl.modify_column(model, field) |         return cls.ddl.modify_column(model, field) | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def _drop_fk(cls, model: Type[Model], field: ForeignKeyFieldInstance): | ||||||
|  |         return cls.ddl.drop_fk(model, field) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _remove_field(cls, model: Type[Model], field: Field): |     def _remove_field(cls, model: Type[Model], field: Field): | ||||||
|         if isinstance(field, ForeignKeyFieldInstance): |         if isinstance(field, ForeignKeyFieldInstance): | ||||||
| @@ -410,6 +458,10 @@ class Migrate: | |||||||
|             return cls.ddl.drop_m2m(field) |             return cls.ddl.drop_m2m(field) | ||||||
|         return cls.ddl.drop_column(model, field.model_field_name) |         return cls.ddl.drop_column(model, field.model_field_name) | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def _rename_field(cls, model: Type[Model], old_field: Field, new_field: Field): | ||||||
|  |         return cls.ddl.rename_column(model, old_field.model_field_name, new_field.model_field_name) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _add_fk(cls, model: Type[Model], field: ForeignKeyFieldInstance): |     def _add_fk(cls, model: Type[Model], field: ForeignKeyFieldInstance): | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ MAX_VERSION_LENGTH = 255 | |||||||
| class Aerich(Model): | class Aerich(Model): | ||||||
|     version = fields.CharField(max_length=MAX_VERSION_LENGTH) |     version = fields.CharField(max_length=MAX_VERSION_LENGTH) | ||||||
|     app = fields.CharField(max_length=20) |     app = fields.CharField(max_length=20) | ||||||
|  |     content = fields.TextField() | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         ordering = ["-id"] |         ordering = ["-id"] | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import importlib | import importlib | ||||||
|  |  | ||||||
| from asyncclick import BadOptionUsage, Context | from click import BadOptionUsage, Context | ||||||
| from tortoise import BaseDBAsyncClient, Tortoise | from tortoise import BaseDBAsyncClient, Tortoise | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								conftest.py
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								conftest.py
									
									
									
									
									
								
							| @@ -31,24 +31,29 @@ def reset_migrate(): | |||||||
|     Migrate._downgrade_m2m = [] |     Migrate._downgrade_m2m = [] | ||||||
|  |  | ||||||
|  |  | ||||||
| @pytest.fixture(scope="session") | @pytest.yield_fixture(scope="session") | ||||||
| def loop(): | def event_loop(): | ||||||
|     loop = asyncio.get_event_loop() |     policy = asyncio.get_event_loop_policy() | ||||||
|     return loop |     res = policy.new_event_loop() | ||||||
|  |     asyncio.set_event_loop(res) | ||||||
|  |     res._close = res.close | ||||||
|  |     res.close = lambda: None | ||||||
|  |  | ||||||
|  |     yield res | ||||||
|  |  | ||||||
|  |     res._close() | ||||||
|  |  | ||||||
|  |  | ||||||
| @pytest.fixture(scope="session", autouse=True) | @pytest.fixture(scope="session", autouse=True) | ||||||
| def initialize_tests(loop, request): | async def initialize_tests(event_loop, request): | ||||||
|     tortoise_orm["connections"]["diff_models"] = "sqlite://:memory:" |     tortoise_orm["connections"]["diff_models"] = "sqlite://:memory:" | ||||||
|     tortoise_orm["apps"]["diff_models"] = { |     tortoise_orm["apps"]["diff_models"] = { | ||||||
|         "models": ["tests.diff_models"], |         "models": ["tests.diff_models"], | ||||||
|         "default_connection": "diff_models", |         "default_connection": "diff_models", | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     loop.run_until_complete(Tortoise.init(config=tortoise_orm, _create_db=True)) |     await Tortoise.init(config=tortoise_orm, _create_db=True) | ||||||
|     loop.run_until_complete( |     await generate_schema_for_client(Tortoise.get_connection("default"), safe=True) | ||||||
|         generate_schema_for_client(Tortoise.get_connection("default"), safe=True) |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     client = Tortoise.get_connection("default") |     client = Tortoise.get_connection("default") | ||||||
|     if client.schema_generator is MySQLSchemaGenerator: |     if client.schema_generator is MySQLSchemaGenerator: | ||||||
| @@ -58,4 +63,4 @@ def initialize_tests(loop, request): | |||||||
|     elif client.schema_generator is AsyncpgSchemaGenerator: |     elif client.schema_generator is AsyncpgSchemaGenerator: | ||||||
|         Migrate.ddl = PostgresDDL(client) |         Migrate.ddl = PostgresDDL(client) | ||||||
|  |  | ||||||
|     request.addfinalizer(lambda: loop.run_until_complete(Tortoise._drop_databases())) |     request.addfinalizer(lambda: event_loop.run_until_complete(Tortoise._drop_databases())) | ||||||
|   | |||||||
							
								
								
									
										473
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										473
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							| @@ -23,25 +23,6 @@ version = "0.15.0" | |||||||
| [package.dependencies] | [package.dependencies] | ||||||
| typing_extensions = "*" | typing_extensions = "*" | ||||||
|  |  | ||||||
| [[package]] |  | ||||||
| category = "main" |  | ||||||
| description = "High level compatibility layer for multiple asynchronous event loop implementations" |  | ||||||
| name = "anyio" |  | ||||||
| optional = false |  | ||||||
| python-versions = ">=3.5.3" |  | ||||||
| version = "1.4.0" |  | ||||||
|  |  | ||||||
| [package.dependencies] |  | ||||||
| async-generator = "*" |  | ||||||
| idna = ">=2.8" |  | ||||||
| sniffio = ">=1.1" |  | ||||||
|  |  | ||||||
| [package.extras] |  | ||||||
| curio = ["curio (>=0.9)"] |  | ||||||
| doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] |  | ||||||
| test = ["coverage (>=4.5)", "hypothesis (>=4.0)", "pytest (>=3.7.2)", "uvloop"] |  | ||||||
| trio = ["trio (>=0.12)"] |  | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| description = "apipkg: namespace control and lazy-import mechanism" | description = "apipkg: namespace control and lazy-import mechanism" | ||||||
| @@ -58,39 +39,16 @@ optional = false | |||||||
| python-versions = "*" | python-versions = "*" | ||||||
| version = "1.4.4" | version = "1.4.4" | ||||||
|  |  | ||||||
| [[package]] |  | ||||||
| category = "main" |  | ||||||
| description = "Async generators and context managers for Python 3.5+" |  | ||||||
| name = "async-generator" |  | ||||||
| optional = false |  | ||||||
| python-versions = ">=3.5" |  | ||||||
| version = "1.10" |  | ||||||
|  |  | ||||||
| [[package]] |  | ||||||
| category = "main" |  | ||||||
| description = "A simple anyio-compatible fork of Click, for powerful command line utilities." |  | ||||||
| name = "asyncclick" |  | ||||||
| optional = false |  | ||||||
| python-versions = ">=3.6" |  | ||||||
| version = "7.0.9" |  | ||||||
|  |  | ||||||
| [package.dependencies] |  | ||||||
| anyio = "*" |  | ||||||
|  |  | ||||||
| [package.extras] |  | ||||||
| dev = ["coverage", "pytest-runner", "pytest-trio", "pytest (>=3)", "sphinx", "tox"] |  | ||||||
| docs = ["sphinx"] |  | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "main" | category = "main" | ||||||
| description = "An asyncio PostgreSQL driver" | description = "An asyncio PostgreSQL driver" | ||||||
| name = "asyncpg" | name = "asyncpg" | ||||||
| optional = true | optional = true | ||||||
| python-versions = ">=3.5.0" | python-versions = ">=3.5.0" | ||||||
| version = "0.20.1" | version = "0.21.0" | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| dev = ["Cython (0.29.14)", "pytest (>=3.6.0)", "Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "pycodestyle (>=2.5.0,<2.6.0)", "flake8 (>=3.7.9,<3.8.0)", "uvloop (>=0.14.0,<0.15.0)"] | dev = ["Cython (0.29.20)", "pytest (>=3.6.0)", "Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "pycodestyle (>=2.5.0,<2.6.0)", "flake8 (>=3.7.9,<3.8.0)", "uvloop (>=0.14.0,<0.15.0)"] | ||||||
| docs = ["Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)"] | docs = ["Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)"] | ||||||
| test = ["pycodestyle (>=2.5.0,<2.6.0)", "flake8 (>=3.7.9,<3.8.0)", "uvloop (>=0.14.0,<0.15.0)"] | test = ["pycodestyle (>=2.5.0,<2.6.0)", "flake8 (>=3.7.9,<3.8.0)", "uvloop (>=0.14.0,<0.15.0)"] | ||||||
|  |  | ||||||
| @@ -109,13 +67,13 @@ description = "Classes Without Boilerplate" | |||||||
| name = "attrs" | name = "attrs" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | ||||||
| version = "19.3.0" | version = "20.2.0" | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] | dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] | ||||||
| dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] | docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] | ||||||
| docs = ["sphinx", "zope.interface"] | tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] | ||||||
| tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] | tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| @@ -158,13 +116,13 @@ description = "Foreign Function Interface for Python calling C code." | |||||||
| name = "cffi" | name = "cffi" | ||||||
| optional = true | optional = true | ||||||
| python-versions = "*" | python-versions = "*" | ||||||
| version = "1.14.0" | version = "1.14.3" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| pycparser = "*" | pycparser = "*" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "main" | ||||||
| description = "Composable command line interface toolkit" | description = "Composable command line interface toolkit" | ||||||
| name = "click" | name = "click" | ||||||
| optional = false | optional = false | ||||||
| @@ -186,17 +144,17 @@ description = "cryptography is a package which provides cryptographic recipes an | |||||||
| name = "cryptography" | name = "cryptography" | ||||||
| optional = true | optional = true | ||||||
| python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" | ||||||
| version = "2.9.2" | version = "3.1.1" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| cffi = ">=1.8,<1.11.3 || >1.11.3" | cffi = ">=1.8,<1.11.3 || >1.11.3" | ||||||
| six = ">=1.4.1" | six = ">=1.4.1" | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0)", "sphinx-rtd-theme"] | docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0,<3.1.0 || >3.1.0,<3.1.1 || >3.1.1)", "sphinx-rtd-theme"] | ||||||
| docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] | docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] | ||||||
| idna = ["idna (>=2.1)"] | pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] | ||||||
| pep8test = ["flake8", "flake8-import-order", "pep8-naming"] | ssh = ["bcrypt (>=3.1.5)"] | ||||||
| test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"] | test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -219,7 +177,7 @@ description = "the modular source code checker: pep8 pyflakes and co" | |||||||
| name = "flake8" | name = "flake8" | ||||||
| optional = false | optional = false | ||||||
| python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" | ||||||
| version = "3.8.3" | version = "3.8.4" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| mccabe = ">=0.6.0,<0.7.0" | mccabe = ">=0.6.0,<0.7.0" | ||||||
| @@ -247,19 +205,11 @@ description = "Python Git Library" | |||||||
| name = "gitpython" | name = "gitpython" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.4" | python-versions = ">=3.4" | ||||||
| version = "3.1.7" | version = "3.1.9" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| gitdb = ">=4.0.1,<5" | gitdb = ">=4.0.1,<5" | ||||||
|  |  | ||||||
| [[package]] |  | ||||||
| category = "main" |  | ||||||
| description = "Internationalized Domain Names in Applications (IDNA)" |  | ||||||
| name = "idna" |  | ||||||
| optional = false |  | ||||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" |  | ||||||
| version = "2.10" |  | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| description = "Read metadata from Python packages" | description = "Read metadata from Python packages" | ||||||
| @@ -267,7 +217,7 @@ marker = "python_version < \"3.8\"" | |||||||
| name = "importlib-metadata" | name = "importlib-metadata" | ||||||
| optional = false | optional = false | ||||||
| python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" | ||||||
| version = "1.7.0" | version = "2.0.0" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| zipp = ">=0.5" | zipp = ">=0.5" | ||||||
| @@ -276,13 +226,21 @@ zipp = ">=0.5" | |||||||
| docs = ["sphinx", "rst.linker"] | docs = ["sphinx", "rst.linker"] | ||||||
| testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] | testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | category = "dev" | ||||||
|  | description = "iniconfig: brain-dead simple config-ini parsing" | ||||||
|  | name = "iniconfig" | ||||||
|  | optional = false | ||||||
|  | python-versions = "*" | ||||||
|  | version = "1.0.1" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "main" | category = "main" | ||||||
| description = "Simple module to parse ISO 8601 dates" | description = "Simple module to parse ISO 8601 dates" | ||||||
| name = "iso8601" | name = "iso8601" | ||||||
| optional = false | optional = false | ||||||
| python-versions = "*" | python-versions = "*" | ||||||
| version = "0.1.12" | version = "0.1.13" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| @@ -290,10 +248,11 @@ description = "A Python utility / library to sort Python imports." | |||||||
| name = "isort" | name = "isort" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.6,<4.0" | python-versions = ">=3.6,<4.0" | ||||||
| version = "5.1.3" | version = "5.6.1" | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| pipfile_deprecated_finder = ["pipreqs", "requirementslib", "tomlkit (>=0.5.3)"] | colors = ["colorama (>=0.4.3,<0.5.0)"] | ||||||
|  | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] | ||||||
| requirements_deprecated_finder = ["pipreqs", "pip-api"] | requirements_deprecated_finder = ["pipreqs", "pip-api"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -304,14 +263,6 @@ optional = false | |||||||
| python-versions = "*" | python-versions = "*" | ||||||
| version = "0.6.1" | version = "0.6.1" | ||||||
|  |  | ||||||
| [[package]] |  | ||||||
| category = "dev" |  | ||||||
| description = "More routines for operating on iterables, beyond itertools" |  | ||||||
| name = "more-itertools" |  | ||||||
| optional = false |  | ||||||
| python-versions = ">=3.5" |  | ||||||
| version = "8.4.0" |  | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| description = "Core utilities for Python packages" | description = "Core utilities for Python packages" | ||||||
| @@ -337,8 +288,8 @@ category = "dev" | |||||||
| description = "Python Build Reasonableness" | description = "Python Build Reasonableness" | ||||||
| name = "pbr" | name = "pbr" | ||||||
| optional = false | optional = false | ||||||
| python-versions = "*" | python-versions = ">=2.6" | ||||||
| version = "5.4.5" | version = "5.5.0" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| @@ -426,7 +377,7 @@ description = "A SQL query builder API for Python" | |||||||
| name = "pypika" | name = "pypika" | ||||||
| optional = false | optional = false | ||||||
| python-versions = "*" | python-versions = "*" | ||||||
| version = "0.37.16" | version = "0.42.1" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| @@ -434,24 +385,24 @@ description = "pytest: simple powerful testing with Python" | |||||||
| name = "pytest" | name = "pytest" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.5" | python-versions = ">=3.5" | ||||||
| version = "5.4.3" | version = "6.1.1" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| atomicwrites = ">=1.0" | atomicwrites = ">=1.0" | ||||||
| attrs = ">=17.4.0" | attrs = ">=17.4.0" | ||||||
| colorama = "*" | colorama = "*" | ||||||
| more-itertools = ">=4.0.0" | iniconfig = "*" | ||||||
| packaging = "*" | packaging = "*" | ||||||
| pluggy = ">=0.12,<1.0" | pluggy = ">=0.12,<1.0" | ||||||
| py = ">=1.5.0" | py = ">=1.8.2" | ||||||
| wcwidth = "*" | toml = "*" | ||||||
|  |  | ||||||
| [package.dependencies.importlib-metadata] | [package.dependencies.importlib-metadata] | ||||||
| python = "<3.8" | python = "<3.8" | ||||||
| version = ">=0.12" | version = ">=0.12" | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| checkqa-mypy = ["mypy (v0.761)"] | checkqa_mypy = ["mypy (0.780)"] | ||||||
| testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -474,26 +425,41 @@ description = "run tests in isolated forked subprocesses" | |||||||
| name = "pytest-forked" | name = "pytest-forked" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | ||||||
| version = "1.2.0" | version = "1.3.0" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| pytest = ">=3.1.0" | py = "*" | ||||||
|  | pytest = ">=3.10" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | category = "dev" | ||||||
|  | description = "Thin-wrapper around the mock package for easier use with pytest" | ||||||
|  | name = "pytest-mock" | ||||||
|  | optional = false | ||||||
|  | python-versions = ">=3.5" | ||||||
|  | version = "3.3.1" | ||||||
|  |  | ||||||
|  | [package.dependencies] | ||||||
|  | pytest = ">=5.0" | ||||||
|  |  | ||||||
|  | [package.extras] | ||||||
|  | dev = ["pre-commit", "tox", "pytest-asyncio"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| description = "pytest xdist plugin for distributed testing and loop-on-failing modes" | description = "pytest xdist plugin for distributed testing and loop-on-failing modes" | ||||||
| name = "pytest-xdist" | name = "pytest-xdist" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | python-versions = ">=3.5" | ||||||
| version = "1.33.0" | version = "2.1.0" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| execnet = ">=1.1" | execnet = ">=1.1" | ||||||
| pytest = ">=4.4.0" | pytest = ">=6.0.0" | ||||||
| pytest-forked = "*" | pytest-forked = "*" | ||||||
| six = "*" |  | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
|  | psutil = ["psutil (>=3.0)"] | ||||||
| testing = ["filelock"] | testing = ["filelock"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -510,7 +476,7 @@ description = "Alternative regular expression module, to replace re." | |||||||
| name = "regex" | name = "regex" | ||||||
| optional = false | optional = false | ||||||
| python-versions = "*" | python-versions = "*" | ||||||
| version = "2020.7.14" | version = "2020.9.27" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "main" | category = "main" | ||||||
| @@ -528,21 +494,13 @@ optional = false | |||||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | ||||||
| version = "3.0.4" | version = "3.0.4" | ||||||
|  |  | ||||||
| [[package]] |  | ||||||
| category = "main" |  | ||||||
| description = "Sniff out which async library your code is running under" |  | ||||||
| name = "sniffio" |  | ||||||
| optional = false |  | ||||||
| python-versions = ">=3.5" |  | ||||||
| version = "1.1.0" |  | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| description = "Manage dynamic plugins for Python applications" | description = "Manage dynamic plugins for Python applications" | ||||||
| name = "stevedore" | name = "stevedore" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.6" | python-versions = ">=3.6" | ||||||
| version = "3.2.0" | version = "3.2.2" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| pbr = ">=2.0.0,<2.1.0 || >2.1.0" | pbr = ">=2.0.0,<2.1.0 || >2.1.0" | ||||||
| @@ -565,12 +523,12 @@ description = "Easy async ORM for python, built with relations in mind" | |||||||
| name = "tortoise-orm" | name = "tortoise-orm" | ||||||
| optional = false | optional = false | ||||||
| python-versions = "*" | python-versions = "*" | ||||||
| version = "0.16.13" | version = "0.16.16" | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| aiosqlite = ">=0.11.0" | aiosqlite = ">=0.11.0" | ||||||
| iso8601 = ">=0.1.12" | iso8601 = ">=0.1.12" | ||||||
| pypika = ">=0.36.5" | pypika = ">=0.39.0" | ||||||
| typing-extensions = ">=3.7" | typing-extensions = ">=3.7" | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| @@ -590,15 +548,7 @@ description = "Backported and Experimental Type Hints for Python 3.5+" | |||||||
| name = "typing-extensions" | name = "typing-extensions" | ||||||
| optional = false | optional = false | ||||||
| python-versions = "*" | python-versions = "*" | ||||||
| version = "3.7.4.2" | version = "3.7.4.3" | ||||||
|  |  | ||||||
| [[package]] |  | ||||||
| category = "dev" |  | ||||||
| description = "Measures the displayed width of unicode strings in a terminal" |  | ||||||
| name = "wcwidth" |  | ||||||
| optional = false |  | ||||||
| python-versions = "*" |  | ||||||
| version = "0.2.5" |  | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| category = "dev" | category = "dev" | ||||||
| @@ -607,17 +557,17 @@ marker = "python_version < \"3.8\"" | |||||||
| name = "zipp" | name = "zipp" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.6" | python-versions = ">=3.6" | ||||||
| version = "3.1.0" | version = "3.3.0" | ||||||
|  |  | ||||||
| [package.extras] | [package.extras] | ||||||
| docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] | docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] | ||||||
| testing = ["jaraco.itertools", "func-timeout"] | testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] | ||||||
|  |  | ||||||
| [extras] | [extras] | ||||||
| dbdrivers = ["aiomysql", "asyncpg"] | dbdrivers = ["aiomysql", "asyncpg"] | ||||||
|  |  | ||||||
| [metadata] | [metadata] | ||||||
| content-hash = "2e02de89517b7461909007ef09e1697e78a4202ffe086444e6027a0a91db58b7" | content-hash = "43fe9c0036f4d55d38f82c263887d8a7d9a35a597e02036b70a631955ff73149" | ||||||
| python-versions = "^3.7" | python-versions = "^3.7" | ||||||
|  |  | ||||||
| [metadata.files] | [metadata.files] | ||||||
| @@ -629,10 +579,6 @@ aiosqlite = [ | |||||||
|     {file = "aiosqlite-0.15.0-py3-none-any.whl", hash = "sha256:19b984b6702aed9f1c85c023f37296954547fc4030dae8e9d027b2a930bed78b"}, |     {file = "aiosqlite-0.15.0-py3-none-any.whl", hash = "sha256:19b984b6702aed9f1c85c023f37296954547fc4030dae8e9d027b2a930bed78b"}, | ||||||
|     {file = "aiosqlite-0.15.0.tar.gz", hash = "sha256:a2884793f4dc8f2798d90e1dfecb2b56a6d479cf039f7ec52356a7fd5f3bdc57"}, |     {file = "aiosqlite-0.15.0.tar.gz", hash = "sha256:a2884793f4dc8f2798d90e1dfecb2b56a6d479cf039f7ec52356a7fd5f3bdc57"}, | ||||||
| ] | ] | ||||||
| anyio = [ |  | ||||||
|     {file = "anyio-1.4.0-py3-none-any.whl", hash = "sha256:9ee67e8131853f42957e214d4531cee6f2b66dda164a298d9686a768b7161a4f"}, |  | ||||||
|     {file = "anyio-1.4.0.tar.gz", hash = "sha256:95f60964fc4583f3f226f8dc275dfb02aefe7b39b85a999c6d14f4ec5323c1d8"}, |  | ||||||
| ] |  | ||||||
| apipkg = [ | apipkg = [ | ||||||
|     {file = "apipkg-1.5-py2.py3-none-any.whl", hash = "sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"}, |     {file = "apipkg-1.5-py2.py3-none-any.whl", hash = "sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"}, | ||||||
|     {file = "apipkg-1.5.tar.gz", hash = "sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6"}, |     {file = "apipkg-1.5.tar.gz", hash = "sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6"}, | ||||||
| @@ -641,43 +587,40 @@ appdirs = [ | |||||||
|     {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, |     {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, | ||||||
|     {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, |     {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, | ||||||
| ] | ] | ||||||
| async-generator = [ |  | ||||||
|     {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, |  | ||||||
|     {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"}, |  | ||||||
| ] |  | ||||||
| asyncclick = [ |  | ||||||
|     {file = "asyncclick-7.0.9.tar.gz", hash = "sha256:62cebf3eca36d973802e2dd521ca1db11c5bf4544e9795e093d1a53cb688a8c2"}, |  | ||||||
| ] |  | ||||||
| asyncpg = [ | asyncpg = [ | ||||||
|     {file = "asyncpg-0.20.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:f7184689177eeb5a11fa1b2baf3f6f2e26bfd7a85acf4de1a3adbd0867d7c0e2"}, |     {file = "asyncpg-0.21.0-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:09badce47a4645cfe523cc8a182bd047d5d62af0caaea77935e6a3c9e77dc364"}, | ||||||
|     {file = "asyncpg-0.20.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:f0c9719ac00615f097fe91082b785bce36dbf02a5ec4115ede0ebfd2cd9500cb"}, |     {file = "asyncpg-0.21.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b7807bfedd24dd15cfb2c17c60977ce01410615ecc285268b5144a944ec97ff"}, | ||||||
|     {file = "asyncpg-0.20.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1388caa456070dab102be874205e3ae8fd1de2577d5de9fa22e65ba5c0f8b110"}, |     {file = "asyncpg-0.21.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dfd491e9865e64a3e91f1587b1d88d71dde1cfb850429253a73d4d44b98c3a0f"}, | ||||||
|     {file = "asyncpg-0.20.1-cp35-cp35m-win32.whl", hash = "sha256:ec6e7046c98730cb2ba4df41387e10cb8963a3ac2918f69ae416f8aab9ca7b1b"}, |     {file = "asyncpg-0.21.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:8587e206d78e739ca83a40c9982e03b28f8904c95a54dc782da99e86cf768f73"}, | ||||||
|     {file = "asyncpg-0.20.1-cp35-cp35m-win_amd64.whl", hash = "sha256:25edb0b947eb632b6b53e5a4b36cba5677297bb34cbaba270019714d0a5fed76"}, |     {file = "asyncpg-0.21.0-cp35-cp35m-win32.whl", hash = "sha256:b1b10916c006e5c2c0dcd5dadeb38cbf61ecd20d66c50164e82f31c22c7e329d"}, | ||||||
|     {file = "asyncpg-0.20.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:95cd2df61ee00b789bdcd04a080e6d9188693b841db2bf9a87ebaed9e53147e0"}, |     {file = "asyncpg-0.21.0-cp35-cp35m-win_amd64.whl", hash = "sha256:22d161618b59e4b56fb2a5cc956aa9eeb336d07cae924a5b90c9aa1c2d137f15"}, | ||||||
|     {file = "asyncpg-0.20.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:058baec9d6b75612412baa872a1aa47317d0ff88c318a49f9c4a2389043d5a8d"}, |     {file = "asyncpg-0.21.0-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:f2d1aa890ffd1ad062a38b7ff7488764b3da4b0a24e0c83d7bbb1d1a6609df15"}, | ||||||
|     {file = "asyncpg-0.20.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c773c7dbe2f4d3ebc9e3030e94303e45d6742e6c2fc25da0c46a56ea3d83caeb"}, |     {file = "asyncpg-0.21.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e7bfb9269aeb11d78d50accf1be46823683ced99209b7199e307cdf7da849522"}, | ||||||
|     {file = "asyncpg-0.20.1-cp36-cp36m-win32.whl", hash = "sha256:5664d1bd8abe64fc60a0e701eb85fa1d8c9a4a8018a5a59164d27238f2caf395"}, |     {file = "asyncpg-0.21.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:68f7981f65317a5d5f497ec76919b488dbe0e838f8b924e7517a680bdca0f308"}, | ||||||
|     {file = "asyncpg-0.20.1-cp36-cp36m-win_amd64.whl", hash = "sha256:57666dfae38f4dbf84ffbf0c5c0f78733fef0e8e083230275dcb9ccad1d5ee09"}, |     {file = "asyncpg-0.21.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:a4c1feb285ec3807ecd5b54ab718a3d065bb55c93ebaf800670eadde31484be8"}, | ||||||
|     {file = "asyncpg-0.20.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:0c336903c3b08e970f8af2f606332f1738dba156bca83ed0467dc2f5c70da796"}, |     {file = "asyncpg-0.21.0-cp36-cp36m-win32.whl", hash = "sha256:dddf4d4c5e781310a36529c3c87c1746837c2d2c7ec0f2ec4e4f06450d83c50a"}, | ||||||
|     {file = "asyncpg-0.20.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ad5ba062e09673b1a4b8d0facaf5a6d9719bf7b337440d10b07fe994d90a9552"}, |     {file = "asyncpg-0.21.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7ee29c4707eb8fb3d3a0348ac4495e06f4afaca3ee38c3bebedc9c8b239125ff"}, | ||||||
|     {file = "asyncpg-0.20.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba90d3578bc6dddcbce461875672fd9bdb34f0b8215b68612dd3b65a956ff51c"}, |     {file = "asyncpg-0.21.0-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:4421407b07b4e22291a226d9de0bf6f3ea8158aa1c12d83bfedbf5c22e13cd55"}, | ||||||
|     {file = "asyncpg-0.20.1-cp37-cp37m-win32.whl", hash = "sha256:da238592235717419a6a7b5edc8564da410ebfd056ca4ecc41e70b1b5df86fba"}, |     {file = "asyncpg-0.21.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:aa2e0cb14c01a2f58caeeca7196681b30aa22dd22c82845560b401df5e98e171"}, | ||||||
|     {file = "asyncpg-0.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:74510234c294c6a6767089ba9c938f09a491426c24405634eb357bd91dffd734"}, |     {file = "asyncpg-0.21.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:28584783dd0d21b2a0db3bfe54fb12f21425a4cc015e4419083ea99e6de0de9b"}, | ||||||
|     {file = "asyncpg-0.20.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:391aea89871df8c1560750af6c7170f2772c2d133b34772acf3637e3cf4db93e"}, |     {file = "asyncpg-0.21.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:915cebc8a7693c8a5e89804fa106678dbedcc50d0270ebab0b75f16e668bd59b"}, | ||||||
|     {file = "asyncpg-0.20.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a981500bf6947926e53c48f4d60ae080af1b4ad7fa78e363465a5b5ad4f2b65e"}, |     {file = "asyncpg-0.21.0-cp37-cp37m-win32.whl", hash = "sha256:308b8ba32c42ea1ed84c034320678ec307296bb4faf3fbbeb9f9e20b46db99a5"}, | ||||||
|     {file = "asyncpg-0.20.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a9e6fd6f0f9e8bd77e9a4e1ef9a4f83a80674d9136a754ae3603e915da96b627"}, |     {file = "asyncpg-0.21.0-cp37-cp37m-win_amd64.whl", hash = "sha256:888593b6688faa7ec1c97ff7f2ca3b5a5b8abb15478fe2a13c5012b607a28737"}, | ||||||
|     {file = "asyncpg-0.20.1-cp38-cp38-win32.whl", hash = "sha256:e39aac2b3a2f839ce65aa255ce416de899c58b7d38d601d24ca35558e13b48e3"}, |     {file = "asyncpg-0.21.0-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:ecd5232cf64f58caac3b85103f1223fdf20e9eb43bfa053c56ef9e5dd76ab099"}, | ||||||
|     {file = "asyncpg-0.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:2af6a5a705accd36e13292ea43d08c20b15e52d684beb522cb3a7d3c9c8f3f48"}, |     {file = "asyncpg-0.21.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3ade59cef35bffae6dbc6f5f3ef56e1d53c67f0a7adc3cc4c714f07568d2d717"}, | ||||||
|     {file = "asyncpg-0.20.1.tar.gz", hash = "sha256:394bf19bdddbba07a38cd6fb526ebf66e120444d6b3097332b78efd5b26495b0"}, |     {file = "asyncpg-0.21.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ea26604932719b3612541e606508d9d604211f56a65806ccf8c92c64104f4f8a"}, | ||||||
|  |     {file = "asyncpg-0.21.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e51d1a012b779e0ebf0195f80d004f65d3c60cc06f0fa1cef9d3e536262abbd"}, | ||||||
|  |     {file = "asyncpg-0.21.0-cp38-cp38-win32.whl", hash = "sha256:615c7e3adb46e1f2e3aff45e4ee9401b4f24f9f7153e5530a0753369be72a5c6"}, | ||||||
|  |     {file = "asyncpg-0.21.0-cp38-cp38-win_amd64.whl", hash = "sha256:823eca36108bd64a8600efe7bbf1230aa00f2defa3be42852f3b61ab40cf1226"}, | ||||||
|  |     {file = "asyncpg-0.21.0.tar.gz", hash = "sha256:53cb2a0eb326f61e34ef4da2db01d87ce9c0ebe396f65a295829df334e31863f"}, | ||||||
| ] | ] | ||||||
| atomicwrites = [ | atomicwrites = [ | ||||||
|     {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, |     {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, | ||||||
|     {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, |     {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, | ||||||
| ] | ] | ||||||
| attrs = [ | attrs = [ | ||||||
|     {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, |     {file = "attrs-20.2.0-py2.py3-none-any.whl", hash = "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"}, | ||||||
|     {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, |     {file = "attrs-20.2.0.tar.gz", hash = "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594"}, | ||||||
| ] | ] | ||||||
| bandit = [ | bandit = [ | ||||||
|     {file = "bandit-1.6.2-py2.py3-none-any.whl", hash = "sha256:336620e220cf2d3115877685e264477ff9d9abaeb0afe3dc7264f55fa17a3952"}, |     {file = "bandit-1.6.2-py2.py3-none-any.whl", hash = "sha256:336620e220cf2d3115877685e264477ff9d9abaeb0afe3dc7264f55fa17a3952"}, | ||||||
| @@ -688,34 +631,42 @@ black = [ | |||||||
|     {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, |     {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, | ||||||
| ] | ] | ||||||
| cffi = [ | cffi = [ | ||||||
|     {file = "cffi-1.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384"}, |     {file = "cffi-1.14.3-2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc"}, | ||||||
|     {file = "cffi-1.14.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30"}, |     {file = "cffi-1.14.3-2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768"}, | ||||||
|     {file = "cffi-1.14.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"}, |     {file = "cffi-1.14.3-2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d"}, | ||||||
|     {file = "cffi-1.14.0-cp27-cp27m-win32.whl", hash = "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78"}, |     {file = "cffi-1.14.3-2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1"}, | ||||||
|     {file = "cffi-1.14.0-cp27-cp27m-win_amd64.whl", hash = "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793"}, |     {file = "cffi-1.14.3-2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca"}, | ||||||
|     {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e"}, |     {file = "cffi-1.14.3-2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a"}, | ||||||
|     {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a"}, |     {file = "cffi-1.14.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c"}, | ||||||
|     {file = "cffi-1.14.0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff"}, |     {file = "cffi-1.14.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730"}, | ||||||
|     {file = "cffi-1.14.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f"}, |     {file = "cffi-1.14.3-cp27-cp27m-win32.whl", hash = "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d"}, | ||||||
|     {file = "cffi-1.14.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa"}, |     {file = "cffi-1.14.3-cp27-cp27m-win_amd64.whl", hash = "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05"}, | ||||||
|     {file = "cffi-1.14.0-cp35-cp35m-win32.whl", hash = "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5"}, |     {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b"}, | ||||||
|     {file = "cffi-1.14.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4"}, |     {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171"}, | ||||||
|     {file = "cffi-1.14.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d"}, |     {file = "cffi-1.14.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f"}, | ||||||
|     {file = "cffi-1.14.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc"}, |     {file = "cffi-1.14.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4"}, | ||||||
|     {file = "cffi-1.14.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac"}, |     {file = "cffi-1.14.3-cp35-cp35m-win32.whl", hash = "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d"}, | ||||||
|     {file = "cffi-1.14.0-cp36-cp36m-win32.whl", hash = "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f"}, |     {file = "cffi-1.14.3-cp35-cp35m-win_amd64.whl", hash = "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d"}, | ||||||
|     {file = "cffi-1.14.0-cp36-cp36m-win_amd64.whl", hash = "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b"}, |     {file = "cffi-1.14.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3"}, | ||||||
|     {file = "cffi-1.14.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3"}, |     {file = "cffi-1.14.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808"}, | ||||||
|     {file = "cffi-1.14.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66"}, |     {file = "cffi-1.14.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537"}, | ||||||
|     {file = "cffi-1.14.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0"}, |     {file = "cffi-1.14.3-cp36-cp36m-win32.whl", hash = "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0"}, | ||||||
|     {file = "cffi-1.14.0-cp37-cp37m-win32.whl", hash = "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f"}, |     {file = "cffi-1.14.3-cp36-cp36m-win_amd64.whl", hash = "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e"}, | ||||||
|     {file = "cffi-1.14.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26"}, |     {file = "cffi-1.14.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1"}, | ||||||
|     {file = "cffi-1.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd"}, |     {file = "cffi-1.14.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579"}, | ||||||
|     {file = "cffi-1.14.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55"}, |     {file = "cffi-1.14.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394"}, | ||||||
|     {file = "cffi-1.14.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2"}, |     {file = "cffi-1.14.3-cp37-cp37m-win32.whl", hash = "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc"}, | ||||||
|     {file = "cffi-1.14.0-cp38-cp38-win32.whl", hash = "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8"}, |     {file = "cffi-1.14.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869"}, | ||||||
|     {file = "cffi-1.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b"}, |     {file = "cffi-1.14.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e"}, | ||||||
|     {file = "cffi-1.14.0.tar.gz", hash = "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6"}, |     {file = "cffi-1.14.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828"}, | ||||||
|  |     {file = "cffi-1.14.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9"}, | ||||||
|  |     {file = "cffi-1.14.3-cp38-cp38-win32.whl", hash = "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522"}, | ||||||
|  |     {file = "cffi-1.14.3-cp38-cp38-win_amd64.whl", hash = "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15"}, | ||||||
|  |     {file = "cffi-1.14.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d"}, | ||||||
|  |     {file = "cffi-1.14.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c"}, | ||||||
|  |     {file = "cffi-1.14.3-cp39-cp39-win32.whl", hash = "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b"}, | ||||||
|  |     {file = "cffi-1.14.3-cp39-cp39-win_amd64.whl", hash = "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3"}, | ||||||
|  |     {file = "cffi-1.14.3.tar.gz", hash = "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"}, | ||||||
| ] | ] | ||||||
| click = [ | click = [ | ||||||
|     {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, |     {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, | ||||||
| @@ -726,67 +677,66 @@ colorama = [ | |||||||
|     {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, |     {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, | ||||||
| ] | ] | ||||||
| cryptography = [ | cryptography = [ | ||||||
|     {file = "cryptography-2.9.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e"}, |     {file = "cryptography-3.1.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719"}, | ||||||
|     {file = "cryptography-2.9.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b"}, |     {file = "cryptography-3.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe"}, | ||||||
|     {file = "cryptography-2.9.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365"}, |     {file = "cryptography-3.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3"}, | ||||||
|     {file = "cryptography-2.9.2-cp27-cp27m-win32.whl", hash = "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0"}, |     {file = "cryptography-3.1.1-cp27-cp27m-win32.whl", hash = "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba"}, | ||||||
|     {file = "cryptography-2.9.2-cp27-cp27m-win_amd64.whl", hash = "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55"}, |     {file = "cryptography-3.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118"}, | ||||||
|     {file = "cryptography-2.9.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270"}, |     {file = "cryptography-3.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db"}, | ||||||
|     {file = "cryptography-2.9.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf"}, |     {file = "cryptography-3.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396"}, | ||||||
|     {file = "cryptography-2.9.2-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d"}, |     {file = "cryptography-3.1.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc"}, | ||||||
|     {file = "cryptography-2.9.2-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785"}, |     {file = "cryptography-3.1.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7"}, | ||||||
|     {file = "cryptography-2.9.2-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b"}, |     {file = "cryptography-3.1.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6"}, | ||||||
|     {file = "cryptography-2.9.2-cp35-cp35m-win32.whl", hash = "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae"}, |     {file = "cryptography-3.1.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536"}, | ||||||
|     {file = "cryptography-2.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b"}, |     {file = "cryptography-3.1.1-cp35-cp35m-win32.whl", hash = "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f"}, | ||||||
|     {file = "cryptography-2.9.2-cp36-cp36m-win32.whl", hash = "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6"}, |     {file = "cryptography-3.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154"}, | ||||||
|     {file = "cryptography-2.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3"}, |     {file = "cryptography-3.1.1-cp36-abi3-win32.whl", hash = "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70"}, | ||||||
|     {file = "cryptography-2.9.2-cp37-cp37m-win32.whl", hash = "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b"}, |     {file = "cryptography-3.1.1-cp36-abi3-win_amd64.whl", hash = "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8"}, | ||||||
|     {file = "cryptography-2.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e"}, |     {file = "cryptography-3.1.1-cp36-cp36m-win32.whl", hash = "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499"}, | ||||||
|     {file = "cryptography-2.9.2-cp38-cp38-win32.whl", hash = "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0"}, |     {file = "cryptography-3.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49"}, | ||||||
|     {file = "cryptography-2.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5"}, |     {file = "cryptography-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921"}, | ||||||
|     {file = "cryptography-2.9.2.tar.gz", hash = "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229"}, |     {file = "cryptography-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2"}, | ||||||
|  |     {file = "cryptography-3.1.1-cp38-cp38-win32.whl", hash = "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490"}, | ||||||
|  |     {file = "cryptography-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba"}, | ||||||
|  |     {file = "cryptography-3.1.1.tar.gz", hash = "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d"}, | ||||||
| ] | ] | ||||||
| execnet = [ | execnet = [ | ||||||
|     {file = "execnet-1.7.1-py2.py3-none-any.whl", hash = "sha256:d4efd397930c46415f62f8a31388d6be4f27a91d7550eb79bc64a756e0056547"}, |     {file = "execnet-1.7.1-py2.py3-none-any.whl", hash = "sha256:d4efd397930c46415f62f8a31388d6be4f27a91d7550eb79bc64a756e0056547"}, | ||||||
|     {file = "execnet-1.7.1.tar.gz", hash = "sha256:cacb9df31c9680ec5f95553976c4da484d407e85e41c83cb812aa014f0eddc50"}, |     {file = "execnet-1.7.1.tar.gz", hash = "sha256:cacb9df31c9680ec5f95553976c4da484d407e85e41c83cb812aa014f0eddc50"}, | ||||||
| ] | ] | ||||||
| flake8 = [ | flake8 = [ | ||||||
|     {file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"}, |     {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, | ||||||
|     {file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"}, |     {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, | ||||||
| ] | ] | ||||||
| gitdb = [ | gitdb = [ | ||||||
|     {file = "gitdb-4.0.5-py3-none-any.whl", hash = "sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac"}, |     {file = "gitdb-4.0.5-py3-none-any.whl", hash = "sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac"}, | ||||||
|     {file = "gitdb-4.0.5.tar.gz", hash = "sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9"}, |     {file = "gitdb-4.0.5.tar.gz", hash = "sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9"}, | ||||||
| ] | ] | ||||||
| gitpython = [ | gitpython = [ | ||||||
|     {file = "GitPython-3.1.7-py3-none-any.whl", hash = "sha256:fa3b92da728a457dd75d62bb5f3eb2816d99a7fe6c67398e260637a40e3fafb5"}, |     {file = "GitPython-3.1.9-py3-none-any.whl", hash = "sha256:138016d519bf4dd55b22c682c904ed2fd0235c3612b2f8f65ce218ff358deed8"}, | ||||||
|     {file = "GitPython-3.1.7.tar.gz", hash = "sha256:2db287d71a284e22e5c2846042d0602465c7434d910406990d5b74df4afb0858"}, |     {file = "GitPython-3.1.9.tar.gz", hash = "sha256:a03f728b49ce9597a6655793207c6ab0da55519368ff5961e4a74ae475b9fa8e"}, | ||||||
| ] |  | ||||||
| idna = [ |  | ||||||
|     {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, |  | ||||||
|     {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, |  | ||||||
| ] | ] | ||||||
| importlib-metadata = [ | importlib-metadata = [ | ||||||
|     {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"}, |     {file = "importlib_metadata-2.0.0-py2.py3-none-any.whl", hash = "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3"}, | ||||||
|     {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, |     {file = "importlib_metadata-2.0.0.tar.gz", hash = "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da"}, | ||||||
|  | ] | ||||||
|  | iniconfig = [ | ||||||
|  |     {file = "iniconfig-1.0.1-py3-none-any.whl", hash = "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437"}, | ||||||
|  |     {file = "iniconfig-1.0.1.tar.gz", hash = "sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69"}, | ||||||
| ] | ] | ||||||
| iso8601 = [ | iso8601 = [ | ||||||
|     {file = "iso8601-0.1.12-py2.py3-none-any.whl", hash = "sha256:210e0134677cc0d02f6028087fee1df1e1d76d372ee1db0bf30bf66c5c1c89a3"}, |     {file = "iso8601-0.1.13-py2.py3-none-any.whl", hash = "sha256:694be0743e9f1535ea873bfc7bd6fb62380c62b75822761859428073a17fd39c"}, | ||||||
|     {file = "iso8601-0.1.12-py3-none-any.whl", hash = "sha256:bbbae5fb4a7abfe71d4688fd64bff70b91bbd74ef6a99d964bab18f7fdf286dd"}, |     {file = "iso8601-0.1.13-py3-none-any.whl", hash = "sha256:6f02f01dd13320a7f280e58516dc8d1950dfaf77527cc365a398cd9de4d3c692"}, | ||||||
|     {file = "iso8601-0.1.12.tar.gz", hash = "sha256:49c4b20e1f38aa5cf109ddcd39647ac419f928512c869dc01d5c7098eddede82"}, |     {file = "iso8601-0.1.13.tar.gz", hash = "sha256:f7dec22af52025d4526be94cc1303c7d8f5379b746a3f54a8c8446384392eeb1"}, | ||||||
| ] | ] | ||||||
| isort = [ | isort = [ | ||||||
|     {file = "isort-5.1.3-py3-none-any.whl", hash = "sha256:001ab55382453892ddb6956f65c6331f610041eed7ddc9c6bdf7975cb504f66f"}, |     {file = "isort-5.6.1-py3-none-any.whl", hash = "sha256:dd3211f513f4a92ec1ec1876fc1dc3c686649c349d49523f5b5adbb0814e5960"}, | ||||||
|     {file = "isort-5.1.3.tar.gz", hash = "sha256:9be41d107294ddf25ef6af83979cb3dfa1b5752311b6d50611c8f8d321d4a4eb"}, |     {file = "isort-5.6.1.tar.gz", hash = "sha256:2f510f34ae18a8d0958c53eec51ef84fd099f07c4c639676525acbcd7b5bd3ff"}, | ||||||
| ] | ] | ||||||
| mccabe = [ | mccabe = [ | ||||||
|     {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, |     {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, | ||||||
|     {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, |     {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, | ||||||
| ] | ] | ||||||
| more-itertools = [ |  | ||||||
|     {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, |  | ||||||
|     {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, |  | ||||||
| ] |  | ||||||
| packaging = [ | packaging = [ | ||||||
|     {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, |     {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, | ||||||
|     {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, |     {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, | ||||||
| @@ -796,8 +746,8 @@ pathspec = [ | |||||||
|     {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"}, |     {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"}, | ||||||
| ] | ] | ||||||
| pbr = [ | pbr = [ | ||||||
|     {file = "pbr-5.4.5-py2.py3-none-any.whl", hash = "sha256:579170e23f8e0c2f24b0de612f71f648eccb79fb1322c814ae6b3c07b5ba23e8"}, |     {file = "pbr-5.5.0-py2.py3-none-any.whl", hash = "sha256:5adc0f9fc64319d8df5ca1e4e06eea674c26b80e6f00c530b18ce6a6592ead15"}, | ||||||
|     {file = "pbr-5.4.5.tar.gz", hash = "sha256:07f558fece33b05caf857474a366dfcc00562bca13dd8b47b2b3e22d9f9bf55c"}, |     {file = "pbr-5.5.0.tar.gz", hash = "sha256:14bfd98f51c78a3dd22a1ef45cf194ad79eee4a19e8e1a0d5c7f8e81ffe182ea"}, | ||||||
| ] | ] | ||||||
| pluggy = [ | pluggy = [ | ||||||
|     {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, |     {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, | ||||||
| @@ -847,23 +797,27 @@ pyparsing = [ | |||||||
|     {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, |     {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, | ||||||
| ] | ] | ||||||
| pypika = [ | pypika = [ | ||||||
|     {file = "PyPika-0.37.16.tar.gz", hash = "sha256:42b86fc00d767fe8d4d3d82acb3b55f76b32c211cc5dff9337fc0fbfe3d121cb"}, |     {file = "PyPika-0.42.1.tar.gz", hash = "sha256:f9466259179d5cb12d6cf3d453f4370a511039a6dd88fcb5089f22ba8824b408"}, | ||||||
| ] | ] | ||||||
| pytest = [ | pytest = [ | ||||||
|     {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, |     {file = "pytest-6.1.1-py3-none-any.whl", hash = "sha256:7a8190790c17d79a11f847fba0b004ee9a8122582ebff4729a082c109e81a4c9"}, | ||||||
|     {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, |     {file = "pytest-6.1.1.tar.gz", hash = "sha256:8f593023c1a0f916110285b6efd7f99db07d59546e3d8c36fc60e2ab05d3be92"}, | ||||||
| ] | ] | ||||||
| pytest-asyncio = [ | pytest-asyncio = [ | ||||||
|     {file = "pytest-asyncio-0.14.0.tar.gz", hash = "sha256:9882c0c6b24429449f5f969a5158b528f39bde47dc32e85b9f0403965017e700"}, |     {file = "pytest-asyncio-0.14.0.tar.gz", hash = "sha256:9882c0c6b24429449f5f969a5158b528f39bde47dc32e85b9f0403965017e700"}, | ||||||
|     {file = "pytest_asyncio-0.14.0-py3-none-any.whl", hash = "sha256:2eae1e34f6c68fc0a9dc12d4bea190483843ff4708d24277c41568d6b6044f1d"}, |     {file = "pytest_asyncio-0.14.0-py3-none-any.whl", hash = "sha256:2eae1e34f6c68fc0a9dc12d4bea190483843ff4708d24277c41568d6b6044f1d"}, | ||||||
| ] | ] | ||||||
| pytest-forked = [ | pytest-forked = [ | ||||||
|     {file = "pytest-forked-1.2.0.tar.gz", hash = "sha256:65f96334863d9cbe53d21f73e8febc4dd61b8d1fdcac7b487d9af07a5d02a938"}, |     {file = "pytest-forked-1.3.0.tar.gz", hash = "sha256:6aa9ac7e00ad1a539c41bec6d21011332de671e938c7637378ec9710204e37ca"}, | ||||||
|     {file = "pytest_forked-1.2.0-py2.py3-none-any.whl", hash = "sha256:42a438336731465c5bd76ab38e1645647ac55914a08b507efbabe8783a08aa6c"}, |     {file = "pytest_forked-1.3.0-py2.py3-none-any.whl", hash = "sha256:dc4147784048e70ef5d437951728825a131b81714b398d5d52f17c7c144d8815"}, | ||||||
|  | ] | ||||||
|  | pytest-mock = [ | ||||||
|  |     {file = "pytest-mock-3.3.1.tar.gz", hash = "sha256:a4d6d37329e4a893e77d9ffa89e838dd2b45d5dc099984cf03c703ac8411bb82"}, | ||||||
|  |     {file = "pytest_mock-3.3.1-py3-none-any.whl", hash = "sha256:024e405ad382646318c4281948aadf6fe1135632bea9cc67366ea0c4098ef5f2"}, | ||||||
| ] | ] | ||||||
| pytest-xdist = [ | pytest-xdist = [ | ||||||
|     {file = "pytest-xdist-1.33.0.tar.gz", hash = "sha256:833b902b16473162cc0572d5bd7b63f8b843df63a4ada9c8af1270d31db17607"}, |     {file = "pytest-xdist-2.1.0.tar.gz", hash = "sha256:82d938f1a24186520e2d9d3a64ef7d9ac7ecdf1a0659e095d18e596b8cbd0672"}, | ||||||
|     {file = "pytest_xdist-1.33.0-py2.py3-none-any.whl", hash = "sha256:9149b4010240643a5e206fd0ece6f0a1cd5955dae014cf04610d359e22e22453"}, |     {file = "pytest_xdist-2.1.0-py3-none-any.whl", hash = "sha256:7c629016b3bb006b88ac68e2b31551e7becf173c76b977768848e2bbed594d90"}, | ||||||
| ] | ] | ||||||
| pyyaml = [ | pyyaml = [ | ||||||
|     {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, |     {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, | ||||||
| @@ -879,26 +833,33 @@ pyyaml = [ | |||||||
|     {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, |     {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, | ||||||
| ] | ] | ||||||
| regex = [ | regex = [ | ||||||
|     {file = "regex-2020.7.14-cp27-cp27m-win32.whl", hash = "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"}, |     {file = "regex-2020.9.27-cp27-cp27m-win32.whl", hash = "sha256:d23a18037313714fb3bb5a94434d3151ee4300bae631894b1ac08111abeaa4a3"}, | ||||||
|     {file = "regex-2020.7.14-cp27-cp27m-win_amd64.whl", hash = "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644"}, |     {file = "regex-2020.9.27-cp27-cp27m-win_amd64.whl", hash = "sha256:84e9407db1b2eb368b7ecc283121b5e592c9aaedbe8c78b1a2f1102eb2e21d19"}, | ||||||
|     {file = "regex-2020.7.14-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc"}, |     {file = "regex-2020.9.27-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f18875ac23d9aa2f060838e8b79093e8bb2313dbaaa9f54c6d8e52a5df097be"}, | ||||||
|     {file = "regex-2020.7.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067"}, |     {file = "regex-2020.9.27-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ae91972f8ac958039920ef6e8769277c084971a142ce2b660691793ae44aae6b"}, | ||||||
|     {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd"}, |     {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9a02d0ae31d35e1ec12a4ea4d4cca990800f66a917d0fb997b20fbc13f5321fc"}, | ||||||
|     {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88"}, |     {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ebbe29186a3d9b0c591e71b7393f1ae08c83cb2d8e517d2a822b8f7ec99dfd8b"}, | ||||||
|     {file = "regex-2020.7.14-cp36-cp36m-win32.whl", hash = "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4"}, |     {file = "regex-2020.9.27-cp36-cp36m-win32.whl", hash = "sha256:4707f3695b34335afdfb09be3802c87fa0bc27030471dbc082f815f23688bc63"}, | ||||||
|     {file = "regex-2020.7.14-cp36-cp36m-win_amd64.whl", hash = "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f"}, |     {file = "regex-2020.9.27-cp36-cp36m-win_amd64.whl", hash = "sha256:9bc13e0d20b97ffb07821aa3e113f9998e84994fe4d159ffa3d3a9d1b805043b"}, | ||||||
|     {file = "regex-2020.7.14-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162"}, |     {file = "regex-2020.9.27-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f1b3afc574a3db3b25c89161059d857bd4909a1269b0b3cb3c904677c8c4a3f7"}, | ||||||
|     {file = "regex-2020.7.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf"}, |     {file = "regex-2020.9.27-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5533a959a1748a5c042a6da71fe9267a908e21eded7a4f373efd23a2cbdb0ecc"}, | ||||||
|     {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7"}, |     {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:1fe0a41437bbd06063aa184c34804efa886bcc128222e9916310c92cd54c3b4c"}, | ||||||
|     {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89"}, |     {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c570f6fa14b9c4c8a4924aaad354652366577b4f98213cf76305067144f7b100"}, | ||||||
|     {file = "regex-2020.7.14-cp37-cp37m-win32.whl", hash = "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6"}, |     {file = "regex-2020.9.27-cp37-cp37m-win32.whl", hash = "sha256:eda4771e0ace7f67f58bc5b560e27fb20f32a148cbc993b0c3835970935c2707"}, | ||||||
|     {file = "regex-2020.7.14-cp37-cp37m-win_amd64.whl", hash = "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204"}, |     {file = "regex-2020.9.27-cp37-cp37m-win_amd64.whl", hash = "sha256:60b0e9e6dc45683e569ec37c55ac20c582973841927a85f2d8a7d20ee80216ab"}, | ||||||
|     {file = "regex-2020.7.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99"}, |     {file = "regex-2020.9.27-cp38-cp38-manylinux1_i686.whl", hash = "sha256:088afc8c63e7bd187a3c70a94b9e50ab3f17e1d3f52a32750b5b77dbe99ef5ef"}, | ||||||
|     {file = "regex-2020.7.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e"}, |     {file = "regex-2020.9.27-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eaf548d117b6737df379fdd53bdde4f08870e66d7ea653e230477f071f861121"}, | ||||||
|     {file = "regex-2020.7.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e"}, |     {file = "regex-2020.9.27-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:41bb65f54bba392643557e617316d0d899ed5b4946dccee1cb6696152b29844b"}, | ||||||
|     {file = "regex-2020.7.14-cp38-cp38-win32.whl", hash = "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341"}, |     {file = "regex-2020.9.27-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637"}, | ||||||
|     {file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"}, |     {file = "regex-2020.9.27-cp38-cp38-win32.whl", hash = "sha256:f2388013e68e750eaa16ccbea62d4130180c26abb1d8e5d584b9baf69672b30f"}, | ||||||
|     {file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"}, |     {file = "regex-2020.9.27-cp38-cp38-win_amd64.whl", hash = "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c"}, | ||||||
|  |     {file = "regex-2020.9.27-cp39-cp39-manylinux1_i686.whl", hash = "sha256:84cada8effefe9a9f53f9b0d2ba9b7b6f5edf8d2155f9fdbe34616e06ececf81"}, | ||||||
|  |     {file = "regex-2020.9.27-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:816064fc915796ea1f26966163f6845de5af78923dfcecf6551e095f00983650"}, | ||||||
|  |     {file = "regex-2020.9.27-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:5d892a4f1c999834eaa3c32bc9e8b976c5825116cde553928c4c8e7e48ebda67"}, | ||||||
|  |     {file = "regex-2020.9.27-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c9443124c67b1515e4fe0bb0aa18df640965e1030f468a2a5dc2589b26d130ad"}, | ||||||
|  |     {file = "regex-2020.9.27-cp39-cp39-win32.whl", hash = "sha256:49f23ebd5ac073765ecbcf046edc10d63dcab2f4ae2bce160982cb30df0c0302"}, | ||||||
|  |     {file = "regex-2020.9.27-cp39-cp39-win_amd64.whl", hash = "sha256:3d20024a70b97b4f9546696cbf2fd30bae5f42229fbddf8661261b1eaff0deb7"}, | ||||||
|  |     {file = "regex-2020.9.27.tar.gz", hash = "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d"}, | ||||||
| ] | ] | ||||||
| six = [ | six = [ | ||||||
|     {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, |     {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, | ||||||
| @@ -908,20 +869,16 @@ smmap = [ | |||||||
|     {file = "smmap-3.0.4-py2.py3-none-any.whl", hash = "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4"}, |     {file = "smmap-3.0.4-py2.py3-none-any.whl", hash = "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4"}, | ||||||
|     {file = "smmap-3.0.4.tar.gz", hash = "sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24"}, |     {file = "smmap-3.0.4.tar.gz", hash = "sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24"}, | ||||||
| ] | ] | ||||||
| sniffio = [ |  | ||||||
|     {file = "sniffio-1.1.0-py3-none-any.whl", hash = "sha256:20ed6d5b46f8ae136d00b9dcb807615d83ed82ceea6b2058cecb696765246da5"}, |  | ||||||
|     {file = "sniffio-1.1.0.tar.gz", hash = "sha256:8e3810100f69fe0edd463d02ad407112542a11ffdc29f67db2bf3771afb87a21"}, |  | ||||||
| ] |  | ||||||
| stevedore = [ | stevedore = [ | ||||||
|     {file = "stevedore-3.2.0-py3-none-any.whl", hash = "sha256:c8f4f0ebbc394e52ddf49de8bcc3cf8ad2b4425ebac494106bbc5e3661ac7633"}, |     {file = "stevedore-3.2.2-py3-none-any.whl", hash = "sha256:5e1ab03eaae06ef6ce23859402de785f08d97780ed774948ef16c4652c41bc62"}, | ||||||
|     {file = "stevedore-3.2.0.tar.gz", hash = "sha256:38791aa5bed922b0a844513c5f9ed37774b68edc609e5ab8ab8d8fe0ce4315e5"}, |     {file = "stevedore-3.2.2.tar.gz", hash = "sha256:f845868b3a3a77a2489d226568abe7328b5c2d4f6a011cc759dfa99144a521f0"}, | ||||||
| ] | ] | ||||||
| toml = [ | toml = [ | ||||||
|     {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, |     {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, | ||||||
|     {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, |     {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, | ||||||
| ] | ] | ||||||
| tortoise-orm = [ | tortoise-orm = [ | ||||||
|     {file = "tortoise-orm-0.16.13.tar.gz", hash = "sha256:5f6fa4430a570172cb49517a97d45338dbfb1a690ed707030467efd154e67855"}, |     {file = "tortoise-orm-0.16.16.tar.gz", hash = "sha256:dc0c7909324a6c224076936adb3b1b154a8d2b13ff51e4232f0bdc3d65841185"}, | ||||||
| ] | ] | ||||||
| typed-ast = [ | typed-ast = [ | ||||||
|     {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, |     {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, | ||||||
| @@ -947,15 +904,11 @@ typed-ast = [ | |||||||
|     {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, |     {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, | ||||||
| ] | ] | ||||||
| typing-extensions = [ | typing-extensions = [ | ||||||
|     {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"}, |     {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, | ||||||
|     {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"}, |     {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, | ||||||
|     {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"}, |     {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, | ||||||
| ] |  | ||||||
| wcwidth = [ |  | ||||||
|     {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, |  | ||||||
|     {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, |  | ||||||
| ] | ] | ||||||
| zipp = [ | zipp = [ | ||||||
|     {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, |     {file = "zipp-3.3.0-py3-none-any.whl", hash = "sha256:eed8ec0b8d1416b2ca33516a37a08892442f3954dee131e92cfd92d8fe3e7066"}, | ||||||
|     {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, |     {file = "zipp-3.3.0.tar.gz", hash = "sha256:64ad89efee774d1897a58607895d80789c59778ea02185dd846ac38394a8642b"}, | ||||||
| ] | ] | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| [tool.poetry] | [tool.poetry] | ||||||
| name = "aerich" | name = "aerich" | ||||||
| version = "0.2.2" | version = "0.3.1" | ||||||
| description = "A database migrations tool for Tortoise ORM." | description = "A database migrations tool for Tortoise ORM." | ||||||
| authors = ["long2ice <long2ice@gmail.com>"] | authors = ["long2ice <long2ice@gmail.com>"] | ||||||
| license = "Apache-2.0" | license = "Apache-2.0" | ||||||
| @@ -12,12 +12,12 @@ keywords = ["migrate", "Tortoise-ORM", "mysql"] | |||||||
| packages = [ | packages = [ | ||||||
|     { include = "aerich" } |     { include = "aerich" } | ||||||
| ] | ] | ||||||
| include = ["CHANGELOG.rst", "LICENSE", "README.md"] | include = ["CHANGELOG.md", "LICENSE", "README.md"] | ||||||
|  |  | ||||||
| [tool.poetry.dependencies] | [tool.poetry.dependencies] | ||||||
| python = "^3.7" | python = "^3.7" | ||||||
| tortoise-orm = "*" | tortoise-orm = "*" | ||||||
| asyncclick = "*" | click = "*" | ||||||
| pydantic = "*" | pydantic = "*" | ||||||
| aiomysql = {version = "*", optional = true} | aiomysql = {version = "*", optional = true} | ||||||
| asyncpg = {version = "*", optional = true} | asyncpg = {version = "*", optional = true} | ||||||
| @@ -30,6 +30,7 @@ pytest = "*" | |||||||
| pytest-xdist = "*" | pytest-xdist = "*" | ||||||
| pytest-asyncio = "*" | pytest-asyncio = "*" | ||||||
| bandit = "*" | bandit = "*" | ||||||
|  | pytest-mock = "*" | ||||||
|  |  | ||||||
| [tool.poetry.extras] | [tool.poetry.extras] | ||||||
| dbdrivers = ["aiomysql", "asyncpg"] | dbdrivers = ["aiomysql", "asyncpg"] | ||||||
|   | |||||||
| @@ -22,15 +22,21 @@ class Status(IntEnum): | |||||||
|  |  | ||||||
|  |  | ||||||
| class User(Model): | class User(Model): | ||||||
|     username = fields.CharField(max_length=20,) |     username = fields.CharField(max_length=20) | ||||||
|     password = fields.CharField(max_length=200) |     password = fields.CharField(max_length=200) | ||||||
|     last_login = fields.DatetimeField(description="Last Login", default=datetime.datetime.now) |     last_login_at = fields.DatetimeField(description="Last Login", default=datetime.datetime.now) | ||||||
|     is_active = fields.BooleanField(default=True, description="Is Active") |     is_active = fields.BooleanField(default=True, description="Is Active") | ||||||
|     is_superuser = fields.BooleanField(default=False, description="Is SuperUser") |     is_superuser = fields.BooleanField(default=False, description="Is SuperUser") | ||||||
|     avatar = fields.CharField(max_length=200, default="") |     avatar = fields.CharField(max_length=200, default="") | ||||||
|     intro = fields.TextField(default="") |     intro = fields.TextField(default="") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Email(Model): | ||||||
|  |     email = fields.CharField(max_length=200) | ||||||
|  |     is_primary = fields.BooleanField(default=False) | ||||||
|  |     user = fields.ForeignKeyField("diff_models.User", db_constraint=True) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Category(Model): | class Category(Model): | ||||||
|     slug = fields.CharField(max_length=200) |     slug = fields.CharField(max_length=200) | ||||||
|     user = fields.ForeignKeyField("diff_models.User", description="User") |     user = fields.ForeignKeyField("diff_models.User", description="User") | ||||||
|   | |||||||
| @@ -31,6 +31,12 @@ class User(Model): | |||||||
|     intro = fields.TextField(default="") |     intro = fields.TextField(default="") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Email(Model): | ||||||
|  |     email = fields.CharField(max_length=200) | ||||||
|  |     is_primary = fields.BooleanField(default=False) | ||||||
|  |     user = fields.ForeignKeyField("models.User", db_constraint=False) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Category(Model): | class Category(Model): | ||||||
|     slug = fields.CharField(max_length=200) |     slug = fields.CharField(max_length=200) | ||||||
|     name = fields.CharField(max_length=200) |     name = fields.CharField(max_length=200) | ||||||
|   | |||||||
| @@ -1,6 +1,9 @@ | |||||||
|  | import pytest | ||||||
|  |  | ||||||
| from aerich.ddl.mysql import MysqlDDL | from aerich.ddl.mysql import MysqlDDL | ||||||
| from aerich.ddl.postgres import PostgresDDL | from aerich.ddl.postgres import PostgresDDL | ||||||
| from aerich.ddl.sqlite import SqliteDDL | from aerich.ddl.sqlite import SqliteDDL | ||||||
|  | from aerich.exceptions import NotSupportError | ||||||
| from aerich.migrate import Migrate | from aerich.migrate import Migrate | ||||||
| from tests.models import Category, User | from tests.models import Category, User | ||||||
|  |  | ||||||
| @@ -63,27 +66,26 @@ def test_add_column(): | |||||||
|  |  | ||||||
|  |  | ||||||
| def test_modify_column(): | def test_modify_column(): | ||||||
|     ret = Migrate.ddl.modify_column(Category, Category._meta.fields_map.get("name")) |     if isinstance(Migrate.ddl, SqliteDDL): | ||||||
|     if isinstance(Migrate.ddl, MysqlDDL): |         with pytest.raises(NotSupportError): | ||||||
|         assert ret == "ALTER TABLE `category` MODIFY COLUMN `name` VARCHAR(200) NOT NULL" |             ret0 = Migrate.ddl.modify_column(Category, Category._meta.fields_map.get("name")) | ||||||
|     elif isinstance(Migrate.ddl, PostgresDDL): |             ret1 = Migrate.ddl.modify_column(User, User._meta.fields_map.get("is_active")) | ||||||
|         assert ret == 'ALTER TABLE "category" ALTER COLUMN "name" TYPE VARCHAR(200)' |  | ||||||
|     else: |     else: | ||||||
|         assert ret == 'ALTER TABLE "category" MODIFY COLUMN "name" VARCHAR(200) NOT NULL' |         ret0 = Migrate.ddl.modify_column(Category, Category._meta.fields_map.get("name")) | ||||||
|  |         ret1 = Migrate.ddl.modify_column(User, User._meta.fields_map.get("is_active")) | ||||||
|  |     if isinstance(Migrate.ddl, MysqlDDL): | ||||||
|  |         assert ret0 == "ALTER TABLE `category` MODIFY COLUMN `name` VARCHAR(200) NOT NULL" | ||||||
|  |     elif isinstance(Migrate.ddl, PostgresDDL): | ||||||
|  |         assert ret0 == 'ALTER TABLE "category" ALTER COLUMN "name" TYPE VARCHAR(200)' | ||||||
|  |  | ||||||
|     ret = Migrate.ddl.modify_column(User, User._meta.fields_map.get("is_active")) |  | ||||||
|     if isinstance(Migrate.ddl, MysqlDDL): |     if isinstance(Migrate.ddl, MysqlDDL): | ||||||
|         assert ( |         assert ( | ||||||
|             ret |             ret1 | ||||||
|             == "ALTER TABLE `user` MODIFY COLUMN `is_active` BOOL NOT NULL  COMMENT 'Is Active' DEFAULT 1" |             == "ALTER TABLE `user` MODIFY COLUMN `is_active` BOOL NOT NULL  COMMENT 'Is Active' DEFAULT 1" | ||||||
|         ) |         ) | ||||||
|     elif isinstance(Migrate.ddl, PostgresDDL): |     elif isinstance(Migrate.ddl, PostgresDDL): | ||||||
|         assert ret == 'ALTER TABLE "user" ALTER COLUMN "is_active" TYPE BOOL' |         assert ret1 == 'ALTER TABLE "user" ALTER COLUMN "is_active" TYPE BOOL' | ||||||
|     else: |  | ||||||
|         assert ( |  | ||||||
|             ret |  | ||||||
|             == 'ALTER TABLE "user" MODIFY COLUMN "is_active" INT NOT NULL  DEFAULT 1 /* Is Active */' |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_alter_column_default(): | def test_alter_column_default(): | ||||||
| @@ -131,10 +133,14 @@ def test_set_comment(): | |||||||
|  |  | ||||||
|  |  | ||||||
| def test_drop_column(): | def test_drop_column(): | ||||||
|  |     if isinstance(Migrate.ddl, SqliteDDL): | ||||||
|  |         with pytest.raises(NotSupportError): | ||||||
|  |             ret = Migrate.ddl.drop_column(Category, "name") | ||||||
|  |     else: | ||||||
|         ret = Migrate.ddl.drop_column(Category, "name") |         ret = Migrate.ddl.drop_column(Category, "name") | ||||||
|     if isinstance(Migrate.ddl, MysqlDDL): |     if isinstance(Migrate.ddl, MysqlDDL): | ||||||
|         assert ret == "ALTER TABLE `category` DROP COLUMN `name`" |         assert ret == "ALTER TABLE `category` DROP COLUMN `name`" | ||||||
|     else: |     elif isinstance(Migrate.ddl, PostgresDDL): | ||||||
|         assert ret == 'ALTER TABLE "category" DROP COLUMN "name"' |         assert ret == 'ALTER TABLE "category" DROP COLUMN "name"' | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -146,6 +152,12 @@ def test_add_index(): | |||||||
|         assert ( |         assert ( | ||||||
|             index_u == "ALTER TABLE `category` ADD UNIQUE INDEX `uid_category_name_8b0cb9` (`name`)" |             index_u == "ALTER TABLE `category` ADD UNIQUE INDEX `uid_category_name_8b0cb9` (`name`)" | ||||||
|         ) |         ) | ||||||
|  |     elif isinstance(Migrate.ddl, PostgresDDL): | ||||||
|  |         assert index == 'CREATE INDEX "idx_category_name_8b0cb9" ON "category" ("name")' | ||||||
|  |         assert ( | ||||||
|  |             index_u | ||||||
|  |             == 'ALTER TABLE "category" ADD CONSTRAINT "uid_category_name_8b0cb9" UNIQUE ("name")' | ||||||
|  |         ) | ||||||
|     else: |     else: | ||||||
|         assert index == 'ALTER TABLE "category" ADD  INDEX "idx_category_name_8b0cb9" ("name")' |         assert index == 'ALTER TABLE "category" ADD  INDEX "idx_category_name_8b0cb9" ("name")' | ||||||
|         assert ( |         assert ( | ||||||
| @@ -155,10 +167,16 @@ def test_add_index(): | |||||||
|  |  | ||||||
| def test_drop_index(): | def test_drop_index(): | ||||||
|     ret = Migrate.ddl.drop_index(Category, ["name"]) |     ret = Migrate.ddl.drop_index(Category, ["name"]) | ||||||
|  |     ret_u = Migrate.ddl.drop_index(Category, ["name"], True) | ||||||
|     if isinstance(Migrate.ddl, MysqlDDL): |     if isinstance(Migrate.ddl, MysqlDDL): | ||||||
|         assert ret == "ALTER TABLE `category` DROP INDEX `idx_category_name_8b0cb9`" |         assert ret == "ALTER TABLE `category` DROP INDEX `idx_category_name_8b0cb9`" | ||||||
|  |         assert ret_u == "ALTER TABLE `category` DROP INDEX `uid_category_name_8b0cb9`" | ||||||
|  |     elif isinstance(Migrate.ddl, PostgresDDL): | ||||||
|  |         assert ret == 'DROP INDEX "idx_category_name_8b0cb9"' | ||||||
|  |         assert ret_u == 'ALTER TABLE "category" DROP CONSTRAINT "uid_category_name_8b0cb9"' | ||||||
|     else: |     else: | ||||||
|         assert ret == 'ALTER TABLE "category" DROP INDEX "idx_category_name_8b0cb9"' |         assert ret == 'ALTER TABLE "category" DROP INDEX "idx_category_name_8b0cb9"' | ||||||
|  |         assert ret_u == 'ALTER TABLE "category" DROP INDEX "uid_category_name_8b0cb9"' | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_add_fk(): | def test_add_fk(): | ||||||
|   | |||||||
| @@ -1,30 +1,79 @@ | |||||||
|  | import pytest | ||||||
|  | from pytest_mock import MockerFixture | ||||||
| from tortoise import Tortoise | from tortoise import Tortoise | ||||||
|  |  | ||||||
| from aerich.ddl.mysql import MysqlDDL | from aerich.ddl.mysql import MysqlDDL | ||||||
|  | from aerich.ddl.postgres import PostgresDDL | ||||||
|  | from aerich.ddl.sqlite import SqliteDDL | ||||||
|  | from aerich.exceptions import NotSupportError | ||||||
| from aerich.migrate import Migrate | from aerich.migrate import Migrate | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_migrate(): | def test_migrate(mocker: MockerFixture): | ||||||
|  |     mocker.patch("click.prompt", return_value=True) | ||||||
|     apps = Tortoise.apps |     apps = Tortoise.apps | ||||||
|     models = apps.get("models") |     models = apps.get("models") | ||||||
|     diff_models = apps.get("diff_models") |     diff_models = apps.get("diff_models") | ||||||
|     Migrate.diff_models(diff_models, models) |     Migrate.diff_models(diff_models, models) | ||||||
|  |     if isinstance(Migrate.ddl, SqliteDDL): | ||||||
|  |         with pytest.raises(NotSupportError): | ||||||
|             Migrate.diff_models(models, diff_models, False) |             Migrate.diff_models(models, diff_models, False) | ||||||
|  |     else: | ||||||
|  |         Migrate.diff_models(models, diff_models, False) | ||||||
|  |     Migrate._merge_operators() | ||||||
|     if isinstance(Migrate.ddl, MysqlDDL): |     if isinstance(Migrate.ddl, MysqlDDL): | ||||||
|         assert Migrate.upgrade_operators == [ |         assert Migrate.upgrade_operators == [ | ||||||
|  |             "ALTER TABLE `email` DROP FOREIGN KEY `fk_email_user_5b58673d`", | ||||||
|             "ALTER TABLE `category` ADD `name` VARCHAR(200) NOT NULL", |             "ALTER TABLE `category` ADD `name` VARCHAR(200) NOT NULL", | ||||||
|             "ALTER TABLE `user` ADD UNIQUE INDEX `uid_user_usernam_9987ab` (`username`)", |             "ALTER TABLE `user` ADD UNIQUE INDEX `uid_user_usernam_9987ab` (`username`)", | ||||||
|  |             "ALTER TABLE `user` RENAME COLUMN `last_login_at` TO `last_login`", | ||||||
|         ] |         ] | ||||||
|         assert Migrate.downgrade_operators == [ |         assert Migrate.downgrade_operators == [ | ||||||
|             "ALTER TABLE `category` DROP COLUMN `name`", |             "ALTER TABLE `category` DROP COLUMN `name`", | ||||||
|             "ALTER TABLE `user` DROP INDEX `uid_user_usernam_9987ab`", |             "ALTER TABLE `user` DROP INDEX `uid_user_usernam_9987ab`", | ||||||
|  |             "ALTER TABLE `user` RENAME COLUMN `last_login` TO `last_login_at`", | ||||||
|  |             "ALTER TABLE `email` ADD CONSTRAINT `fk_email_user_5b58673d` FOREIGN KEY " | ||||||
|  |             "(`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE", | ||||||
|         ] |         ] | ||||||
|     else: |     elif isinstance(Migrate.ddl, PostgresDDL): | ||||||
|         assert Migrate.upgrade_operators == [ |         assert Migrate.upgrade_operators == [ | ||||||
|  |             'ALTER TABLE "email" DROP CONSTRAINT "fk_email_user_5b58673d"', | ||||||
|             'ALTER TABLE "category" ADD "name" VARCHAR(200) NOT NULL', |             'ALTER TABLE "category" ADD "name" VARCHAR(200) NOT NULL', | ||||||
|             'ALTER TABLE "user" ADD UNIQUE INDEX "uid_user_usernam_9987ab" ("username")', |             'ALTER TABLE "user" ADD CONSTRAINT "uid_user_usernam_9987ab" UNIQUE ("username")', | ||||||
|  |             'ALTER TABLE "user" RENAME COLUMN "last_login_at" TO "last_login"', | ||||||
|         ] |         ] | ||||||
|         assert Migrate.downgrade_operators == [ |         assert Migrate.downgrade_operators == [ | ||||||
|             'ALTER TABLE "category" DROP COLUMN "name"', |             'ALTER TABLE "category" DROP COLUMN "name"', | ||||||
|             'ALTER TABLE "user" DROP INDEX "uid_user_usernam_9987ab"', |             'ALTER TABLE "user" DROP CONSTRAINT "uid_user_usernam_9987ab"', | ||||||
|  |             'ALTER TABLE "user" RENAME COLUMN "last_login" TO "last_login_at"', | ||||||
|  |             'ALTER TABLE "email" ADD CONSTRAINT "fk_email_user_5b58673d" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE CASCADE', | ||||||
|  |         ] | ||||||
|  |     elif isinstance(Migrate.ddl, SqliteDDL): | ||||||
|  |         assert Migrate.upgrade_operators == [ | ||||||
|  |             'ALTER TABLE "email" DROP FOREIGN KEY "fk_email_user_5b58673d"', | ||||||
|  |             'ALTER TABLE "category" ADD "name" VARCHAR(200) NOT NULL', | ||||||
|  |             'ALTER TABLE "user" ADD UNIQUE INDEX "uid_user_usernam_9987ab" ("username")', | ||||||
|  |             'ALTER TABLE "user" RENAME COLUMN "last_login_at" TO "last_login"', | ||||||
|  |         ] | ||||||
|  |         assert Migrate.downgrade_operators == [] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_sort_all_version_files(mocker): | ||||||
|  |     mocker.patch( | ||||||
|  |         "os.listdir", | ||||||
|  |         return_value=[ | ||||||
|  |             "1_datetime_update.json", | ||||||
|  |             "11_datetime_update.json", | ||||||
|  |             "10_datetime_update.json", | ||||||
|  |             "2_datetime_update.json", | ||||||
|  |         ], | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     Migrate.migrate_location = "." | ||||||
|  |  | ||||||
|  |     assert Migrate.get_all_version_files() == [ | ||||||
|  |         "1_datetime_update.json", | ||||||
|  |         "2_datetime_update.json", | ||||||
|  |         "10_datetime_update.json", | ||||||
|  |         "11_datetime_update.json", | ||||||
|     ] |     ] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user