Python backend for a Slack's kudos plugin.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: tests with migrations working

+659 -11
+100
alembic.ini
··· 1 + # A generic, single database configuration. 2 + 3 + [alembic] 4 + # path to migration scripts 5 + script_location = kefi/migrations 6 + 7 + # template used to generate migration files 8 + # file_template = %%(rev)s_%%(slug)s 9 + 10 + # sys.path path, will be prepended to sys.path if present. 11 + # defaults to the current working directory. 12 + prepend_sys_path = . 13 + 14 + # timezone to use when rendering the date within the migration file 15 + # as well as the filename. 16 + # If specified, requires the python-dateutil library that can be 17 + # installed by adding `alembic[tz]` to the pip requirements 18 + # string value is passed to dateutil.tz.gettz() 19 + # leave blank for localtime 20 + # timezone = 21 + 22 + # max length of characters to apply to the 23 + # "slug" field 24 + # truncate_slug_length = 40 25 + 26 + # set to 'true' to run the environment during 27 + # the 'revision' command, regardless of autogenerate 28 + # revision_environment = false 29 + 30 + # set to 'true' to allow .pyc and .pyo files without 31 + # a source .py file to be detected as revisions in the 32 + # versions/ directory 33 + # sourceless = false 34 + 35 + # version location specification; This defaults 36 + # to alembic/versions. When using multiple version 37 + # directories, initial revisions must be specified with --version-path. 38 + # The path separator used here should be the separator specified by "version_path_separator" 39 + # version_locations = %(here)s/bar:%(here)s/bat:alembic/versions 40 + 41 + # version path separator; As mentioned above, this is the character used to split 42 + # version_locations. Valid values are: 43 + # 44 + # version_path_separator = : 45 + # version_path_separator = ; 46 + # version_path_separator = space 47 + version_path_separator = os # default: use os.pathsep 48 + 49 + # the output encoding used when revision files 50 + # are written from script.py.mako 51 + # output_encoding = utf-8 52 + 53 + sqlalchemy.url = driver://user:pass@localhost/dbname 54 + 55 + 56 + [post_write_hooks] 57 + # post_write_hooks defines scripts or Python functions that are run 58 + # on newly generated revision scripts. See the documentation for further 59 + # detail and examples 60 + 61 + # format using "black" - use the console_scripts runner, against the "black" entrypoint 62 + # hooks = black 63 + # black.type = console_scripts 64 + # black.entrypoint = black 65 + # black.options = -l 79 REVISION_SCRIPT_FILENAME 66 + 67 + # Logging configuration 68 + [loggers] 69 + keys = root,sqlalchemy,alembic 70 + 71 + [handlers] 72 + keys = console 73 + 74 + [formatters] 75 + keys = generic 76 + 77 + [logger_root] 78 + level = WARN 79 + handlers = console 80 + qualname = 81 + 82 + [logger_sqlalchemy] 83 + level = WARN 84 + handlers = 85 + qualname = sqlalchemy.engine 86 + 87 + [logger_alembic] 88 + level = INFO 89 + handlers = 90 + qualname = alembic 91 + 92 + [handler_console] 93 + class = StreamHandler 94 + args = (sys.stderr,) 95 + level = NOTSET 96 + formatter = generic 97 + 98 + [formatter_generic] 99 + format = %(levelname)-5.5s [%(name)s] %(message)s 100 + datefmt = %H:%M:%S
+24
docker-compose.yml
··· 1 + # Docker-compose file to launch in the local machine all the services that 2 + # the project needs to run. 3 + version: '3' 4 + 5 + volumes: 6 + postgres_data: {} 7 + postgres_data_backups: {} 8 + 9 + services: 10 + 11 + postgres: 12 + image: registry.dekaside.com/library/postgres:12 13 + volumes: 14 + - postgres_data:/var/lib/postgresql/data 15 + - postgres_data_backups:/backups 16 + env_file: 17 + - ./.env 18 + ports: 19 + - "5432:5432" 20 + 21 + redis: 22 + image: redis:latest 23 + ports: 24 + - "6379:6379"
+11 -3
kefi/config/__init__.py
··· 2 2 from functools import lru_cache 3 3 from importlib import import_module 4 4 5 - from pydantic import BaseSettings 5 + from pydantic import BaseSettings, PostgresDsn 6 6 7 7 8 8 class Settings(BaseSettings): 9 9 APP_NAME: str = "Kefi" 10 10 SLACK_BOT_TOKEN: str 11 + DATABASE_URL: PostgresDsn 12 + IS_TEST: bool = False 13 + DATABASE_TEST_SUFFIX: str = "_test" 11 14 12 15 class Config: 13 16 env_file = ".env" ··· 18 21 """Uses the KEFI_SETTINGS_MODULE environment variable to dinamically load 19 22 different Settings class. 20 23 """ 21 - settings = import_module(os.getenv("KEFI_SETTINGS_MODULE", "kefi.config.local")) 22 - return settings.Settings() 24 + settings_module = import_module( 25 + os.getenv("KEFI_SETTINGS_MODULE", "kefi.config.test") 26 + ) 27 + return settings_module.Settings() 28 + 29 + 30 + settings = get_settings()
+1 -1
kefi/config/test.py
··· 2 2 3 3 4 4 class Settings(BaseSettings): 5 - ... 5 + IS_TEST: bool = True
+9
kefi/dependencies.py
··· 1 1 from fastapi import Form 2 + from sqlmodel import Session 3 + 4 + from kefi.models import engine 2 5 3 6 4 7 class SlashCommandParams: ··· 29 32 self.response_url = response_url 30 33 self.trigger_id = trigger_id 31 34 self.api_app_id = api_app_id 35 + 36 + 37 + def get_session(): 38 + """Session dependency.""" 39 + with Session(engine) as session: 40 + yield session
+1
kefi/migrations/README
··· 1 + Generic single-database configuration.
+82
kefi/migrations/env.py
··· 1 + from logging.config import fileConfig 2 + 3 + from alembic import context 4 + from sqlalchemy import engine_from_config, pool 5 + 6 + # this is the Alembic Config object, which provides 7 + # access to the values within the .ini file in use. 8 + config = context.config 9 + 10 + # Interpret the config file for Python logging. 11 + # This line sets up loggers basically. 12 + fileConfig(config.config_file_name) 13 + 14 + 15 + # add your model's MetaData object here 16 + from sqlmodel import SQLModel 17 + 18 + from kefi.config import settings 19 + from kefi.models.users import User 20 + 21 + if settings.IS_TEST: 22 + config.set_main_option( 23 + "sqlalchemy.url", f"{settings.DATABASE_URL}{settings.DATABASE_TEST_SUFFIX}" 24 + ) 25 + else: 26 + config.set_main_option("sqlalchemy.url", f"{settings.DATABASE_URL}") 27 + target_metadata = SQLModel.metadata 28 + 29 + # other values from the config, defined by the needs of env.py, 30 + # can be acquired: 31 + # my_important_option = config.get_main_option("my_important_option") 32 + # ... etc. 33 + 34 + 35 + def run_migrations_offline(): 36 + """Run migrations in 'offline' mode. 37 + 38 + This configures the context with just a URL 39 + and not an Engine, though an Engine is acceptable 40 + here as well. By skipping the Engine creation 41 + we don't even need a DBAPI to be available. 42 + 43 + Calls to context.execute() here emit the given string to the 44 + script output. 45 + 46 + """ 47 + url = config.get_main_option("sqlalchemy.url") 48 + context.configure( 49 + url=url, 50 + target_metadata=target_metadata, 51 + literal_binds=True, 52 + dialect_opts={"paramstyle": "named"}, 53 + ) 54 + 55 + with context.begin_transaction(): 56 + context.run_migrations() 57 + 58 + 59 + def run_migrations_online(): 60 + """Run migrations in 'online' mode. 61 + 62 + In this scenario we need to create an Engine 63 + and associate a connection with the context. 64 + 65 + """ 66 + connectable = engine_from_config( 67 + config.get_section(config.config_ini_section), 68 + prefix="sqlalchemy.", 69 + poolclass=pool.NullPool, 70 + ) 71 + 72 + with connectable.connect() as connection: 73 + context.configure(connection=connection, target_metadata=target_metadata) 74 + 75 + with context.begin_transaction(): 76 + context.run_migrations() 77 + 78 + 79 + if context.is_offline_mode(): 80 + run_migrations_offline() 81 + else: 82 + run_migrations_online()
+25
kefi/migrations/script.py.mako
··· 1 + """${message} 2 + 3 + Revision ID: ${up_revision} 4 + Revises: ${down_revision | comma,n} 5 + Create Date: ${create_date} 6 + 7 + """ 8 + from alembic import op 9 + import sqlalchemy as sa 10 + import sqlmodel 11 + ${imports if imports else ""} 12 + 13 + # revision identifiers, used by Alembic. 14 + revision = ${repr(up_revision)} 15 + down_revision = ${repr(down_revision)} 16 + branch_labels = ${repr(branch_labels)} 17 + depends_on = ${repr(depends_on)} 18 + 19 + 20 + def upgrade(): 21 + ${upgrades if upgrades else "pass"} 22 + 23 + 24 + def downgrade(): 25 + ${downgrades if downgrades else "pass"}
+37
kefi/migrations/versions/bdd01590a965_init_user_model.py
··· 1 + """Init User model 2 + 3 + Revision ID: bdd01590a965 4 + Revises: 5 + Create Date: 2021-11-05 16:56:27.724680 6 + 7 + """ 8 + from alembic import op 9 + import sqlalchemy as sa 10 + import sqlmodel 11 + 12 + 13 + # revision identifiers, used by Alembic. 14 + revision = 'bdd01590a965' 15 + down_revision = None 16 + branch_labels = None 17 + depends_on = None 18 + 19 + 20 + def upgrade(): 21 + # ### commands auto generated by Alembic - please adjust! ### 22 + op.create_table('user', 23 + sa.Column('slack_user_id', sa.String(length=100), nullable=True), 24 + sa.Column('id', sa.Integer(), nullable=True), 25 + sa.PrimaryKeyConstraint('id') 26 + ) 27 + op.create_index(op.f('ix_user_id'), 'user', ['id'], unique=False) 28 + op.create_index(op.f('ix_user_slack_user_id'), 'user', ['slack_user_id'], unique=True) 29 + # ### end Alembic commands ### 30 + 31 + 32 + def downgrade(): 33 + # ### commands auto generated by Alembic - please adjust! ### 34 + op.drop_index(op.f('ix_user_slack_user_id'), table_name='user') 35 + op.drop_index(op.f('ix_user_id'), table_name='user') 36 + op.drop_table('user') 37 + # ### end Alembic commands ###
+5
kefi/models/__init__.py
··· 1 + from sqlmodel import create_engine 2 + 3 + from kefi.config import settings 4 + 5 + engine = create_engine(settings.DATABASE_URL)
+11
kefi/models/users.py
··· 1 + from typing import Optional 2 + 3 + from sqlalchemy import Column, String 4 + from sqlmodel import Field, SQLModel 5 + 6 + 7 + class User(SQLModel, table=True): 8 + """A slack user.""" 9 + 10 + id: Optional[int] = Field(default=None, primary_key=True) 11 + slack_user_id: str = Field(sa_column=Column(String(100), unique=True, index=True))
+9
kefi/models/wallets.py
··· 1 + from decimal import Decimal 2 + from typing import Optional 3 + 4 + from sqlmodel import Field, SQLModel 5 + 6 + 7 + class Transaction(SQLModel, table=True): 8 + id: Optional[int] = Field(default=None, primary_key=True) 9 + amount: Decimal
+22 -3
kefi/routers/commands.py
··· 1 1 import random 2 2 3 3 from fastapi import APIRouter, Depends 4 + from sqlmodel import Session, select 4 5 5 - from kefi.dependencies import SlashCommandParams 6 + from kefi.dependencies import SlashCommandParams, get_session 7 + from kefi.models import engine 8 + from kefi.models.users import User 6 9 from kefi.templates import template_command_not_found, template_command_wallet 7 10 8 11 router = APIRouter() ··· 10 13 11 14 class Commands: 12 15 WALLET: str = "wallet" 16 + INIT: str = "init" 13 17 14 18 15 19 @router.post("/command/", tags=["command"]) 16 - async def handle_command(command: SlashCommandParams = Depends(SlashCommandParams)): 17 - if Commands.WALLET in command.text: 20 + def handle_command( 21 + session: Session = Depends(get_session), 22 + command: SlashCommandParams = Depends(SlashCommandParams), 23 + ): 24 + """Handle different commands.""" 25 + 26 + # Init commad to create the user 27 + if Commands.INIT in command.text.lower(): 28 + statement = select(User).where(User.slack_user_id == command.user_id) 29 + results = session.exec(statement) 30 + user = results.first() 31 + if not user: 32 + user = User(slack_user_id=command.user_id) 33 + session.add(user) 34 + session.commit() 35 + 36 + if Commands.WALLET in command.text.lower(): 18 37 amount = random.randint(1, 100) 19 38 return template_command_wallet(command.user_name, amount) 20 39 return template_command_not_found()
+51
kefi/tests/conftest.py
··· 1 + import pytest 2 + from alembic import command 3 + from alembic.config import Config 4 + from fastapi.testclient import TestClient 5 + from sqlalchemy_utils import create_database, database_exists, drop_database 6 + from sqlmodel import Session, create_engine 7 + 8 + from kefi.config import settings 9 + from kefi.dependencies import get_session 10 + from kefi.main import app 11 + 12 + 13 + @pytest.fixture(scope="session") 14 + def test_database_session(): 15 + """Fixtrue to get a session with the test database.""" 16 + # Adds '_test' to default database name 17 + url = f"{settings.DATABASE_URL}_test" 18 + # Creates the session 19 + engine = create_engine(url) 20 + with Session(engine) as session: 21 + yield session 22 + 23 + 24 + def run_schema_migrations(sqlalchemy_url: str) -> None: 25 + config = Config("alembic.ini") 26 + config.set_main_option("sqlalchemy.url", sqlalchemy_url) 27 + command.upgrade(config, "head") 28 + 29 + 30 + @pytest.fixture(name="session") 31 + def session_fixture(test_database_session: Session): 32 + """Creates the tests database if not exists and runs the migration.""" 33 + url = str(test_database_session.bind.url) 34 + if database_exists(url): 35 + drop_database(url) 36 + create_database(url) 37 + run_schema_migrations(url) 38 + yield test_database_session 39 + 40 + 41 + @pytest.fixture(name="client") 42 + def client_fixture(session: Session): 43 + """Overrides the get_session dependency to use the one defined in the fixture.""" 44 + 45 + def get_session_override(): 46 + return session 47 + 48 + app.dependency_overrides[get_session] = get_session_override 49 + client = TestClient(app) 50 + yield client 51 + app.dependency_overrides.clear()
+26 -3
kefi/tests/test_commands.py
··· 1 + from faker import Faker 1 2 from fastapi.testclient import TestClient 2 3 3 - from kefi.main import app 4 + fake = Faker() 5 + 4 6 5 - client = TestClient(app) 7 + def test_init_commad(client: TestClient): 8 + """Test command /kefi wallet""" 9 + 10 + response = client.post( 11 + "/command/", 12 + data={ 13 + "token": "token", 14 + "team_id": "team_id", 15 + "team_domain": "team_domain", 16 + "channel_id": "channel_id", 17 + "channel_name": "channel_name", 18 + "user_id": f"user_{fake.pyint()}", 19 + "user_name": fake.name().split(" ")[0], 20 + "command": "/kefi", 21 + "text": "init", 22 + "response_url": "response_url", 23 + "trigger_id": "trigger_id", 24 + "api_app_id": "api_app_id", 25 + }, 26 + ) 27 + assert response.status_code == 28 + 6 29 7 30 8 - def test_wallet_commad(): 31 + def test_wallet_commad(client: TestClient): 9 32 """Test command /kefi wallet""" 10 33 response = client.post( 11 34 "/command/",
+215 -1
poetry.lock
··· 11 11 hiredis = "*" 12 12 13 13 [[package]] 14 + name = "alembic" 15 + version = "1.7.4" 16 + description = "A database migration tool for SQLAlchemy." 17 + category = "main" 18 + optional = false 19 + python-versions = ">=3.6" 20 + 21 + [package.dependencies] 22 + Mako = "*" 23 + SQLAlchemy = ">=1.3.0" 24 + 25 + [package.extras] 26 + tz = ["python-dateutil"] 27 + 28 + [[package]] 14 29 name = "anyio" 15 30 version = "3.3.4" 16 31 description = "High level compatibility layer for multiple asynchronous event loop implementations" ··· 188 203 category = "main" 189 204 optional = false 190 205 python-versions = ">=3.5" 206 + 207 + [[package]] 208 + name = "faker" 209 + version = "9.8.0" 210 + description = "Faker is a Python package that generates fake data for you." 211 + category = "dev" 212 + optional = false 213 + python-versions = ">=3.6" 214 + 215 + [package.dependencies] 216 + python-dateutil = ">=2.4" 217 + text-unidecode = "1.3" 191 218 192 219 [[package]] 193 220 name = "fastapi" ··· 334 361 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 335 362 336 363 [[package]] 364 + name = "mako" 365 + version = "1.1.5" 366 + description = "A super-fast templating language that borrows the best ideas from the existing templating languages." 367 + category = "main" 368 + optional = false 369 + python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 370 + 371 + [package.dependencies] 372 + MarkupSafe = ">=0.9.2" 373 + 374 + [package.extras] 375 + babel = ["babel"] 376 + lingua = ["lingua"] 377 + 378 + [[package]] 379 + name = "markupsafe" 380 + version = "2.0.1" 381 + description = "Safely add untrusted strings to HTML/XML markup." 382 + category = "main" 383 + optional = false 384 + python-versions = ">=3.6" 385 + 386 + [[package]] 337 387 name = "matplotlib-inline" 338 388 version = "0.1.3" 339 389 description = "Inline Matplotlib backend for Jupyter" ··· 463 513 wcwidth = "*" 464 514 465 515 [[package]] 516 + name = "psycopg2-binary" 517 + version = "2.9.1" 518 + description = "psycopg2 - Python-PostgreSQL Database Adapter" 519 + category = "main" 520 + optional = false 521 + python-versions = ">=3.6" 522 + 523 + [[package]] 466 524 name = "ptyprocess" 467 525 version = "0.7.0" 468 526 description = "Run a subprocess in a pseudo terminal" ··· 559 617 pytest = ">=2.6.0" 560 618 561 619 [[package]] 620 + name = "python-dateutil" 621 + version = "2.8.2" 622 + description = "Extensions to the standard Python datetime module" 623 + category = "dev" 624 + optional = false 625 + python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 626 + 627 + [package.dependencies] 628 + six = ">=1.5" 629 + 630 + [[package]] 562 631 name = "python-dotenv" 563 632 version = "0.19.1" 564 633 description = "Read key-value pairs from a .env file and set them as environment variables" ··· 667 736 sqlcipher = ["sqlcipher3-binary"] 668 737 669 738 [[package]] 739 + name = "sqlalchemy-utils" 740 + version = "0.37.9" 741 + description = "Various utility functions for SQLAlchemy." 742 + category = "dev" 743 + optional = false 744 + python-versions = "~=3.4" 745 + 746 + [package.dependencies] 747 + six = "*" 748 + SQLAlchemy = ">=1.0" 749 + 750 + [package.extras] 751 + arrow = ["arrow (>=0.3.4)"] 752 + babel = ["Babel (>=1.3)"] 753 + color = ["colour (>=0.0.4)"] 754 + encrypted = ["cryptography (>=0.6)"] 755 + intervals = ["intervals (>=0.7.1)"] 756 + password = ["passlib (>=1.6,<2.0)"] 757 + pendulum = ["pendulum (>=2.0.5)"] 758 + phone = ["phonenumbers (>=5.9.2)"] 759 + test = ["pytest (>=2.7.1)", "Pygments (>=1.2)", "Jinja2 (>=2.3)", "docutils (>=0.10)", "flexmock (>=0.9.7)", "mock (==2.0.0)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pg8000 (>=1.12.4)", "pytz (>=2014.2)", "python-dateutil (>=2.6)", "pymysql", "flake8 (>=2.4.0)", "isort (>=4.2.2)", "pyodbc", "backports.zoneinfo"] 760 + test_all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "mock (==2.0.0)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)", "backports.zoneinfo"] 761 + timezone = ["python-dateutil"] 762 + url = ["furl (>=0.4.1)"] 763 + 764 + [[package]] 670 765 name = "sqlalchemy2-stubs" 671 766 version = "0.0.2a19" 672 767 description = "Typing Stubs for SQLAlchemy 1.4" ··· 703 798 704 799 [package.extras] 705 800 full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "graphene"] 801 + 802 + [[package]] 803 + name = "text-unidecode" 804 + version = "1.3" 805 + description = "The most basic Text::Unidecode port" 806 + category = "dev" 807 + optional = false 808 + python-versions = "*" 706 809 707 810 [[package]] 708 811 name = "toml" ··· 787 890 [metadata] 788 891 lock-version = "1.1" 789 892 python-versions = "^3.9" 790 - content-hash = "51457b5e3fdbed641eeaa839d9db35b5d8f196bc7ff673caf6d53f473d790879" 893 + content-hash = "af355514354b1d2bf8d342ccf57bf99d27dad492d84658c87b1bf85858c424d3" 791 894 792 895 [metadata.files] 793 896 aioredis = [ 794 897 {file = "aioredis-1.3.1-py3-none-any.whl", hash = "sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3"}, 795 898 {file = "aioredis-1.3.1.tar.gz", hash = "sha256:15f8af30b044c771aee6787e5ec24694c048184c7b9e54c3b60c750a4b93273a"}, 796 899 ] 900 + alembic = [ 901 + {file = "alembic-1.7.4-py3-none-any.whl", hash = "sha256:e3cab9e59778b3b6726bb2da9ced451c6622d558199fd3ef914f3b1e8f4ef704"}, 902 + {file = "alembic-1.7.4.tar.gz", hash = "sha256:9d33f3ff1488c4bfab1e1a6dfebbf085e8a8e1a3e047a43ad29ad1f67f012a1d"}, 903 + ] 797 904 anyio = [ 798 905 {file = "anyio-3.3.4-py3-none-any.whl", hash = "sha256:4fd09a25ab7fa01d34512b7249e366cd10358cdafc95022c7ff8c8f8a5026d66"}, 799 906 {file = "anyio-3.3.4.tar.gz", hash = "sha256:67da67b5b21f96b9d3d65daa6ea99f5d5282cb09f50eb4456f8fb51dffefc3ff"}, ··· 853 960 decorator = [ 854 961 {file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"}, 855 962 {file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"}, 963 + ] 964 + faker = [ 965 + {file = "Faker-9.8.0-py3-none-any.whl", hash = "sha256:810182ef3597e0dfc4999a29f7cf17b99c70b361aae0f16743de6b926619ae21"}, 966 + {file = "Faker-9.8.0.tar.gz", hash = "sha256:22e53b8082890cca9b595ec22f9b01676b9d96c5f2f1890bcb49e4d612aa40a2"}, 856 967 ] 857 968 fastapi = [ 858 969 {file = "fastapi-0.70.0-py3-none-any.whl", hash = "sha256:a36d5f2fad931aa3575c07a3472c784e81f3e664e3bb5c8b9c88d0ec1104f59c"}, ··· 1005 1116 {file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"}, 1006 1117 {file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"}, 1007 1118 ] 1119 + mako = [ 1120 + {file = "Mako-1.1.5-py2.py3-none-any.whl", hash = "sha256:6804ee66a7f6a6416910463b00d76a7b25194cd27f1918500c5bd7be2a088a23"}, 1121 + {file = "Mako-1.1.5.tar.gz", hash = "sha256:169fa52af22a91900d852e937400e79f535496191c63712e3b9fda5a9bed6fc3"}, 1122 + ] 1123 + markupsafe = [ 1124 + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, 1125 + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, 1126 + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, 1127 + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, 1128 + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, 1129 + {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, 1130 + {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, 1131 + {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, 1132 + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, 1133 + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, 1134 + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, 1135 + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, 1136 + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, 1137 + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, 1138 + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, 1139 + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, 1140 + {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, 1141 + {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, 1142 + {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, 1143 + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, 1144 + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, 1145 + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, 1146 + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, 1147 + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, 1148 + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, 1149 + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, 1150 + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, 1151 + {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, 1152 + {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, 1153 + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, 1154 + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, 1155 + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, 1156 + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, 1157 + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, 1158 + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, 1159 + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, 1160 + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, 1161 + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, 1162 + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, 1163 + {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, 1164 + {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, 1165 + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, 1166 + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, 1167 + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, 1168 + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, 1169 + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, 1170 + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, 1171 + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, 1172 + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, 1173 + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, 1174 + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, 1175 + {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, 1176 + {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, 1177 + {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, 1178 + ] 1008 1179 matplotlib-inline = [ 1009 1180 {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, 1010 1181 {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"}, ··· 1074 1245 {file = "prompt_toolkit-3.0.22-py3-none-any.whl", hash = "sha256:48d85cdca8b6c4f16480c7ce03fd193666b62b0a21667ca56b4bb5ad679d1170"}, 1075 1246 {file = "prompt_toolkit-3.0.22.tar.gz", hash = "sha256:449f333dd120bd01f5d296a8ce1452114ba3a71fae7288d2f0ae2c918764fa72"}, 1076 1247 ] 1248 + psycopg2-binary = [ 1249 + {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"}, 1250 + {file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"}, 1251 + {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698"}, 1252 + {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616"}, 1253 + {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7"}, 1254 + {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e"}, 1255 + {file = "psycopg2_binary-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d"}, 1256 + {file = "psycopg2_binary-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce"}, 1257 + {file = "psycopg2_binary-2.9.1-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a"}, 1258 + {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a"}, 1259 + {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0"}, 1260 + {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed"}, 1261 + {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f"}, 1262 + {file = "psycopg2_binary-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e"}, 1263 + {file = "psycopg2_binary-2.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2"}, 1264 + {file = "psycopg2_binary-2.9.1-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140"}, 1265 + {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917"}, 1266 + {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90"}, 1267 + {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72"}, 1268 + {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34"}, 1269 + {file = "psycopg2_binary-2.9.1-cp38-cp38-win32.whl", hash = "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32"}, 1270 + {file = "psycopg2_binary-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf"}, 1271 + {file = "psycopg2_binary-2.9.1-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a"}, 1272 + {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4"}, 1273 + {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31"}, 1274 + {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd"}, 1275 + {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a"}, 1276 + {file = "psycopg2_binary-2.9.1-cp39-cp39-win32.whl", hash = "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975"}, 1277 + {file = "psycopg2_binary-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68"}, 1278 + ] 1077 1279 ptyprocess = [ 1078 1280 {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, 1079 1281 {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ··· 1124 1326 ] 1125 1327 pytest-env = [ 1126 1328 {file = "pytest-env-0.6.2.tar.gz", hash = "sha256:7e94956aef7f2764f3c147d216ce066bf6c42948bb9e293169b1b1c880a580c2"}, 1329 + ] 1330 + python-dateutil = [ 1331 + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 1332 + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 1127 1333 ] 1128 1334 python-dotenv = [ 1129 1335 {file = "python-dotenv-0.19.1.tar.gz", hash = "sha256:14f8185cc8d494662683e6914addcb7e95374771e707601dfc70166946b4c4b8"}, ··· 1237 1443 {file = "SQLAlchemy-1.4.26-cp39-cp39-win_amd64.whl", hash = "sha256:5c6774b34782116ad9bdec61c2dbce9faaca4b166a0bc8e7b03c2b870b121d94"}, 1238 1444 {file = "SQLAlchemy-1.4.26.tar.gz", hash = "sha256:6bc7f9d7d90ef55e8c6db1308a8619cd8f40e24a34f759119b95e7284dca351a"}, 1239 1445 ] 1446 + sqlalchemy-utils = [ 1447 + {file = "SQLAlchemy-Utils-0.37.9.tar.gz", hash = "sha256:4667edbdcb1ece011076b69772ef524bfbb17cc97e03f11ee6b85d98e7741d61"}, 1448 + {file = "SQLAlchemy_Utils-0.37.9-py3-none-any.whl", hash = "sha256:bb6f4da8ac044cb0dd4d0278b1fb434141a5ee9d1881c757a076830ddbb04160"}, 1449 + ] 1240 1450 sqlalchemy2-stubs = [ 1241 1451 {file = "sqlalchemy2-stubs-0.0.2a19.tar.gz", hash = "sha256:2117c48ce5acfe33bf9c9bfce2a981632d931949e68fa313aa5c2a3bc980ca7a"}, 1242 1452 {file = "sqlalchemy2_stubs-0.0.2a19-py3-none-any.whl", hash = "sha256:aac7dca77a2c49e5f0934976421d5e25ae4dc5e27db48c01e055f81caa1e3ead"}, ··· 1248 1458 starlette = [ 1249 1459 {file = "starlette-0.16.0-py3-none-any.whl", hash = "sha256:38eb24bf705a2c317e15868e384c1b8a12ca396e5a3c3a003db7e667c43f939f"}, 1250 1460 {file = "starlette-0.16.0.tar.gz", hash = "sha256:e1904b5d0007aee24bdd3c43994be9b3b729f4f58e740200de1d623f8c3a8870"}, 1461 + ] 1462 + text-unidecode = [ 1463 + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, 1464 + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, 1251 1465 ] 1252 1466 toml = [ 1253 1467 {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
+30
pyproject.toml
··· 17 17 ipython = "^7.29.0" 18 18 requests = "^2.26.0" 19 19 arq = "^0.22" 20 + alembic = "^1.7.4" 21 + psycopg2-binary = "^2.9.1" 20 22 21 23 [tool.poetry.dev-dependencies] 22 24 black = "^21.10b0" ··· 25 27 pytest = "^6.2.5" 26 28 pylint = "^2.11.1" 27 29 pytest-env = "^0.6.2" 30 + SQLAlchemy-Utils = "^0.37.9" 31 + Faker = "^9.8.0" 32 + 33 + [tool.isort] 34 + multi_line_output = 3 35 + include_trailing_comma = true 36 + force_grid_wrap = 0 37 + use_parentheses = true 38 + ensure_newline_before_comments = true 39 + line_length = 88 40 + 41 + [tool.black] 42 + line-length = 88 43 + include = '\.pyi?$' 44 + exclude = ''' 45 + /( 46 + \.git 47 + | \.hg 48 + | \.mypy_cache 49 + | \.tox 50 + | \.venv 51 + | _build 52 + | buck-out 53 + | build 54 + | dist 55 + | node_modules 56 + )/ 57 + ''' 28 58 29 59 [tool.mypy] 30 60 python_version = "3.9"