├── .github ├── dependabot.yml └── workflows │ ├── automerge.yml │ ├── docs.yml │ └── test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ROADMAP.md ├── assets └── readme.gif ├── docs ├── docs │ ├── index.md │ └── release-notes.md ├── docs_assets │ ├── fastapi-help.png │ └── startproject-help.png └── mkdocs.yml ├── manage_fastapi ├── __init__.py ├── __main__.py ├── config.py ├── constants.py ├── context.py ├── generator.py ├── helpers.py ├── main.py └── templates │ ├── __init__.py │ ├── app │ ├── __init__.py │ ├── cookiecutter.json │ ├── hooks │ │ └── post_gen_project.py │ └── {{ cookiecutter.folder_name }} │ │ ├── __init__.py │ │ ├── api │ │ ├── __init__.py │ │ └── v1.py │ │ ├── crud.py │ │ ├── models.py │ │ └── schemas.py │ └── project │ ├── __init__.py │ ├── cookiecutter.json │ ├── hooks │ └── post_gen_project.py │ └── {{ cookiecutter.folder_name }} │ ├── .pre-commit-config.yaml │ ├── Dockerfile │ ├── LICENSE │ ├── README.md │ ├── app │ ├── __init__.py │ ├── core │ │ ├── __init__.py │ │ └── config.py │ ├── database.py │ └── main.py │ ├── docker-compose.yaml │ ├── pyproject.toml │ ├── requirements.txt │ ├── setup.cfg │ ├── tests │ └── __init__.py │ ├── {{ cookiecutter.env }} │ └── {{ cookiecutter.gitignore }} ├── poetry.lock ├── pyproject.toml ├── scripts └── check_typing.sh ├── setup.cfg └── tests ├── __init__.py ├── test_startapp.py └── test_startproject.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/" 5 | schedule: 6 | interval: "monthly" 7 | 8 | - package-ecosystem: "pip" 9 | directory: "/" 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | name: Automerge 2 | on: [push, pull_request] 3 | 4 | 5 | jobs: 6 | automerge: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: fastify/github-action-merge-dependabot@v2.1.1 10 | with: 11 | github-token: ${{ secrets.GITHUB_TOKEN }} 12 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-python@v2 14 | with: 15 | python-version: 3.x 16 | - run: pip install mkdocs-material 17 | - run: mkdocs gh-deploy --config-file=docs/mkdocs.yml --force 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | pull_request: 7 | branches: ["master"] 8 | 9 | jobs: 10 | test: 11 | runs-on: "${{ matrix.os }}" 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, windows-latest, macos-latest] 15 | python-version: [3.6, 3.7, 3.8, 3.9] 16 | fail-fast: true 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set up Python 21 | uses: actions/setup-python@v2 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Install Poetry 25 | run: pip install poetry 26 | - name: Install Dependencies 27 | run: poetry install 28 | - name: Test 29 | run: poetry run pytest tests/ --cov=manage_fastapi --cov-report=term-missing:skip-covered --cov-report=xml 30 | - name: Upload coverage 31 | uses: codecov/codecov-action@v1 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Editors 2 | .vscode/ 3 | .idea/ 4 | 5 | # Vagrant 6 | .vagrant/ 7 | 8 | # Mac/OSX 9 | .DS_Store 10 | 11 | # Windows 12 | Thumbs.db 13 | 14 | # Source for the following rules: https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore 15 | # Byte-compiled / optimized / DLL files 16 | __pycache__/ 17 | *.py[cod] 18 | *$py.class 19 | 20 | site 21 | 22 | # Distribution / packaging 23 | .Python 24 | build/ 25 | develop-eggs/ 26 | dist/ 27 | downloads/ 28 | eggs/ 29 | .eggs/ 30 | lib/ 31 | lib64/ 32 | parts/ 33 | sdist/ 34 | var/ 35 | wheels/ 36 | *.egg-info/ 37 | .installed.cfg 38 | *.egg 39 | MANIFEST 40 | 41 | # PyInstaller 42 | # Usually these files are written by a python script from a template 43 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 44 | *.manifest 45 | *.spec 46 | 47 | # Installer logs 48 | pip-log.txt 49 | pip-delete-this-directory.txt 50 | 51 | # Unit test / coverage reports 52 | htmlcov/ 53 | .tox/ 54 | .nox/ 55 | .coverage 56 | .coverage.* 57 | .cache 58 | nosetests.xml 59 | coverage.xml 60 | *.cover 61 | .hypothesis/ 62 | .pytest_cache/ 63 | 64 | # PyBuilder 65 | target/ 66 | 67 | # pyenv 68 | .python-version 69 | 70 | # celery beat schedule file 71 | celerybeat-schedule 72 | 73 | # SageMath parsed files 74 | *.sage.py 75 | 76 | # Environments 77 | .env 78 | .venv 79 | env/ 80 | venv/ 81 | ENV/ 82 | env.bak/ 83 | venv.bak/ 84 | 85 | # Rope project settings 86 | .ropeproject 87 | 88 | # mypy 89 | .mypy_cache/ 90 | .dmypy.json 91 | dmypy.json 92 | 93 | # Temporary Files 94 | tmp 95 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/myint/autoflake 3 | rev: v1.4 4 | hooks: 5 | - id: autoflake 6 | exclude: .*/__init__.py|.*/templates/.* 7 | args: 8 | - --in-place 9 | - --remove-all-unused-imports 10 | - --expand-star-imports 11 | - --remove-duplicate-keys 12 | - --remove-unused-variables 13 | - repo: local 14 | hooks: 15 | - id: flake8 16 | name: flake8 17 | entry: flake8 18 | exclude: .*/templates/.* 19 | language: system 20 | types: [python] 21 | - repo: https://github.com/pre-commit/mirrors-isort 22 | rev: v5.4.2 23 | hooks: 24 | - id: isort 25 | exclude: .*/templates/.* 26 | args: ["--profile", "black"] 27 | - repo: local 28 | hooks: 29 | - id: mypy 30 | name: mypy 31 | entry: mypy 32 | language: system 33 | exclude: .*/templates/.* 34 | types: [python] 35 | - repo: https://github.com/pre-commit/pre-commit-hooks 36 | rev: v3.3.0 37 | hooks: 38 | - id: trailing-whitespace 39 | - id: end-of-file-fixer 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Manage FastAPI 2 | 3 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: 4 | 5 | - Reporting a bug 6 | - Discussing the current state of the code 7 | - Submitting a fix 8 | - Proposing new features 9 | - Becoming a maintainer 10 | 11 | ## We Develop with Github 12 | 13 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 14 | 15 | ## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests 16 | 17 | Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: 18 | 19 | 1. Fork the repo and create your branch from `master`. 20 | 2. If you've added code that should be tested, add tests. 21 | 3. If you've changed APIs, update the documentation. 22 | 4. Ensure the test suite passes. 23 | 5. Make sure your code lints. 24 | 6. Issue that pull request! 25 | 26 | ## Report bugs using Github's [issues](https://github.com/ycd/manage-fastapi/issues) 27 | 28 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](); it's that easy! 29 | 30 | ## Write bug reports with detail, background, and sample code 31 | 32 | [This is a good example](http://stackoverflow.com/q/12488905/180626) of a bug report. 33 | 34 | **Great Bug Reports** tend to have: 35 | 36 | - A quick summary and/or background. 37 | - Steps to reproduce. 38 | - Be specific! 39 | - Give sample code if you can. 40 | - Express what you expected would happen. 41 | - Express what actually happens. 42 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 43 | 44 | People *love* thorough bug reports. I'm not even kidding. 45 | 46 | ## License 47 | 48 | By contributing, you agree that your contributions will be licensed under its MIT License. 49 | 50 | ## References 51 | 52 | This document was adapted from the open-source contribution document created by [Brian A. Danielak](https://gist.github.com/briandk) based on the [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md) contributing guidelines. 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yağızcan Değirmenci 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

manage-fastapi

3 | 4 | [manage-fastapi](https://github.com/ycd/manage-fastapi) Project generator and manager for FastAPI 5 | 6 | 7 | ![manage_fastapi](assets/readme.gif) 8 | 9 |

10 | 11 | Latest Commit 12 | 13 | 14 | 15 |
16 | 17 | Package version 18 | 19 | 20 | 21 |

22 |
23 | 24 | 25 | --- 26 | 27 | **Source Code**: View it on [Github](https://github.com/ycd/manage-fastapi/) 28 | 29 | --- 30 | 31 | 32 | ## Features 🚀 33 | 34 | * #### Creates customizable **project boilerplate.** 35 | * #### Creates customizable **app boilerplate.** 36 | * #### Handles the project structuring for you. 37 | * #### Optional Dockerfile generation. 38 | * #### Optional docker-compose generation for your project needs. 39 | * #### Optional pre-commit hook generation. 40 | 41 | 42 | ## Installation 📌 43 | 44 | * Prerequisites 45 | * Python 3.6 + 46 | 47 | Manage FastAPI can be installed by running 48 | 49 | ```python 50 | pip install manage-fastapi 51 | ``` 52 | 53 | 54 | ## Getting started 🎈 55 | 56 | Easiest way to start is using the defaults: 57 | 58 | ```bash 59 | fastapi startproject [name] 60 | ``` 61 | 62 | But there is an **interactive** mode! 63 | 64 | ```bash 65 | fastapi startproject [name] --interactive 66 | ``` 67 | 68 | 69 | 70 | ## Command line options 🧰 71 | 72 | Manage FastAPI provides three different commands. 73 | 74 | You can list them with 75 | 76 | ```bash 77 | fastapi --help 78 | ``` 79 | 80 | 81 | 82 | The idea is to have a highly customizable CLI, but at the same time a simple interface for new users. You can see the available options for `startproject` running `fastapi startproject --help`: 83 | 84 | 85 | 86 | The other commands are already available but the current implementation is too shallow. More details about `startapp` and `run` commands will be provided once they have more functionalities, at the moment you can run `startapp` by just: 87 | 88 | ```bash 89 | fastapi startapp {name} 90 | ``` 91 | 92 | On the other hand, the `run` command expects you to have a `startproject` structure: 93 | 94 | ```bash 95 | fastapi run 96 | ``` 97 | 98 | ## License 99 | 100 | This project is licensed under the terms of the MIT license. 101 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Manage FastAPI Roadmap 2 | 3 | Hi there! :wave: 4 | 5 | The package plans are here. If you want to contribute with new ideas, or develop the ones that are listed, read our contributing guidelines! 🤓 6 | 7 | ## Checklist 8 | 9 | ### Must 10 | 11 | * [X] License support on `startproject`. 12 | * [X] Docker/Docker-compose support on `startproject`. 13 | * [X] Add basic linter tools on `startproject` (flake8, mypy and isort). 14 | * [X] Add `.pre-commit-config.yaml` on `startproject`. 15 | * [X] Integrate databases on `startproject`. 16 | - [ ] SQLALchemy 17 | - [X] PostgreSQL 18 | - [ ] MySQL 19 | - [ ] SQLite 20 | - [ ] Async SQLAlchemy 21 | - [ ] PostgreSQL 22 | - [ ] MySQL 23 | - [ ] SQLite 24 | - [ ] Gino (only supports PostgreSQL) 25 | - [ ] Tortoise 26 | - [ ] PostgreSQL 27 | - [ ] MySQL 28 | - [ ] SQLite 29 | - [ ] MongoDB 30 | * [ ] Different Authentication support on `startproject`. 31 | * [X] Support `startapp` command. 32 | - [X] Simple app creation. 33 | - [ ] Append the APIRouter to the FastAPI app. 34 | - [ ] Add `--app-file` and `--app-variable` options on `startapp`. 35 | * [ ] Add tests. 36 | * [X] Fix documentation accordingly. 37 | 38 | ### Nice to have 39 | 40 | * [ ] VSCode debugger support on `startproject` (available via docker). 41 | * [ ] Support different CI on `startproject`. 42 | * [ ] Add support for `hypercorn` on `run`. 43 | * [ ] Create `migrations`/`migrate` command. 44 | * [ ] Add `logger` to `startproject` structure. 45 | * [ ] Base CRUD class to `startproject`. 46 | 47 | ### Additional 48 | 49 | * [ ] Script to copy `index.md` to `README.md` and verify if they are the same. 50 | 51 | ## Questions 52 | 53 | * Should we support .git by default? 54 | * Should CORSMiddleware be optional? 55 | -------------------------------------------------------------------------------- /assets/readme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/assets/readme.gif -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 |

2 | Manage FastAPI projects easily 3 |

4 |

5 | 6 | Latest Commit 7 | 8 | 9 | 10 |
11 | 12 | Package version 13 | 14 | 15 | 16 |

17 | 18 | 19 | 20 | --- 21 | 22 | **Documentation**: View it on [website](https://ycd.github.io/manage-fastapi/) 23 | 24 | **Source Code**: View it on [Github](https://github.com/ycd/manage-fastapi/) 25 | 26 | 27 | --- 28 | 29 | ## Features 🚀 30 | 31 | * #### Creates customizable **project boilerplate.** 32 | * #### Creates customizable **app boilerplate.** 33 | * #### Handles the project structuring for you. 34 | 35 | ## Installation 36 | 37 | Manage FastAPI can be installed by running `pip install manage-fastapi`. It requires Python 3.6+ to run. 38 | 39 | ## Usage 40 | 41 | To get started right away with sensible defaults: 42 | 43 | ```bash 44 | fastapi startproject {name} 45 | ``` 46 | 47 | You can run _Manage FastAPI_ as a package if running it as a script doesn’t work: 48 | 49 | ```bash 50 | python -m fastapi startproject {name} 51 | ``` 52 | 53 | ## Command line options 54 | 55 | Manage FastAPI has three commands for now. You can list them by running `fastapi --help`: 56 | 57 | 58 | 59 | The idea is to have a highly customizable CLI, but at the same time a simple interface for new users. You can see the available options for `startproject` running `fastapi startproject --help`: 60 | 61 | 62 | 63 | The other commands are already available but the current implementation is too shallow. More details about `startapp` and `run` commands will be provided once they have more functionalities, at the moment you can run `startapp` by just: 64 | 65 | ```bash 66 | fastapi startapp {name} 67 | ``` 68 | 69 | On the other hand, the `run` command expects you to have a `startproject` structure: 70 | 71 | ```bash 72 | fastapi run 73 | ``` 74 | 75 | ## License 76 | 77 | This project is licensed under the terms of the MIT license. 78 | -------------------------------------------------------------------------------- /docs/docs/release-notes.md: -------------------------------------------------------------------------------- 1 | ## Release Notes 📣 2 | 3 | ### Latest Changes 4 | 5 | ### 1.0.1 6 | 7 | * Added MySQL support. 8 | 9 | ### 1.0.0 10 | 11 | * Add `run` command, previously known as `run-server`. 12 | * Create a roadmap. 13 | * Massive design update on the package logic. File template (cookiecutter) base instead of variable. 14 | 15 | ### 0.1.60 16 | 17 | * Delete run-server command 18 | * Delete show-models command 19 | * Create new template for settings without database 20 | * Small fix for project utils 21 | 22 | ### 0.1.52 23 | 24 | * Temporary fix for Path issue when running with uvicorn 25 | 26 | ### 0.1.51 27 | 28 | * Little update on API template 29 | 30 | ### 0.1.5 31 | 32 | * Added showmodels 33 | * Added runserver 34 | * Fix little bugs 35 | * Update docs 36 | 37 | ### 0.1.41 38 | 39 | * Quick fix for a little bug 40 | 41 | ### 0.1.4 42 | 43 | * Changed project architecture 44 | * Increased travis tests 45 | 46 | ### 0.1.3 47 | 48 | * Make database optional 49 | * Now Manage FastAPI has support for MongoDB, PostgreSQL, SQLite, MySQL, Tortoise ORM 50 | 51 | ### 0.1.2 52 | 53 | * Add tests 54 | * Fix and relocate success message 55 | * Add travis 56 | 57 | ### 0.1.1 58 | 59 | * Added documentation 60 | * Fixed typos 61 | * Additional response for successfuly creation. 62 | 63 | ### 0.1.0 64 | 65 | * Prototype of project with two functionalities. 66 | -------------------------------------------------------------------------------- /docs/docs_assets/fastapi-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/docs/docs_assets/fastapi-help.png -------------------------------------------------------------------------------- /docs/docs_assets/startproject-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/docs/docs_assets/startproject-help.png -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Manage FastAPI 2 | theme: 3 | name: material 4 | palette: 5 | primary: 'green' 6 | accent: 'orange' 7 | font: 8 | text: 'Ubuntu' 9 | repo_name: ycd/Manage-FastAPI 10 | repo_url: https://github.com/ycd/manage-fastapi 11 | 12 | nav: 13 | - Manage FastAPI: index.md 14 | - Manage Projects: 15 | - Start new project: managing_projects/startproject.md 16 | - Manage Apps: 17 | - Start new app: managing_apps/startapp.md 18 | - Release Notes: 'release-notes.md' 19 | 20 | 21 | markdown_extensions: 22 | - toc: 23 | permalink: true 24 | - markdown.extensions.codehilite: 25 | guess_lang: false 26 | - admonition 27 | - codehilite 28 | - extra 29 | - tables 30 | - smarty 31 | - pymdownx.tabbed 32 | -------------------------------------------------------------------------------- /manage_fastapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/__init__.py -------------------------------------------------------------------------------- /manage_fastapi/__main__.py: -------------------------------------------------------------------------------- 1 | from .main import app 2 | 3 | app(prog_name="fastapi") 4 | -------------------------------------------------------------------------------- /manage_fastapi/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates") 4 | 5 | try: 6 | import fastapi 7 | 8 | FASTAPI_VERSION = fastapi.__version__ 9 | except ModuleNotFoundError: 10 | FASTAPI_VERSION = "0.70.0" 11 | -------------------------------------------------------------------------------- /manage_fastapi/constants.py: -------------------------------------------------------------------------------- 1 | from enum import Enum, EnumMeta 2 | 3 | 4 | class BaseMetadataEnum(EnumMeta): 5 | def __contains__(self, other): 6 | try: 7 | self(other) 8 | except ValueError: 9 | return False 10 | else: 11 | return True 12 | 13 | 14 | class BaseEnum(str, Enum, metaclass=BaseMetadataEnum): 15 | """Base enum class.""" 16 | 17 | 18 | class PackageManager(BaseEnum): 19 | PIP = "pip" 20 | POETRY = "poetry" 21 | 22 | 23 | class PythonVersion(BaseEnum): 24 | THREE_DOT_SIX = "3.6" 25 | THREE_DOT_SEV = "3.7" 26 | THREE_DOT_EIG = "3.8" 27 | THREE_DOT_NIN = "3.9" 28 | 29 | 30 | class License(BaseEnum): 31 | MIT = "MIT" 32 | BSD = "BSD" 33 | GNU = "GNU" 34 | APACHE = "Apache" 35 | 36 | 37 | class Database(BaseEnum): 38 | POSTGRES = "Postgres" 39 | MYSQL = "MySQL" 40 | -------------------------------------------------------------------------------- /manage_fastapi/context.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from datetime import datetime 3 | from typing import Optional 4 | 5 | from pydantic import BaseModel, EmailStr, root_validator 6 | 7 | from manage_fastapi.config import FASTAPI_VERSION 8 | from manage_fastapi.constants import Database, License, PackageManager, PythonVersion 9 | 10 | 11 | class AppContext(BaseModel): 12 | name: str 13 | folder_name: str 14 | snake_name: str 15 | 16 | @root_validator(pre=True) 17 | def validate_app(cls, values: dict): 18 | values["folder_name"] = values["name"].lower().replace(" ", "-").strip() 19 | values["snake_name"] = values["folder_name"].replace("-", "_") 20 | return values 21 | 22 | 23 | class ProjectContext(BaseModel): 24 | name: str 25 | folder_name: str 26 | packaging: PackageManager 27 | 28 | username: Optional[str] = None 29 | email: Optional[EmailStr] = None 30 | 31 | python: PythonVersion 32 | fastapi: str = FASTAPI_VERSION 33 | 34 | license: Optional[License] 35 | year: int 36 | 37 | pre_commit: bool 38 | docker: bool 39 | 40 | database: Optional[Database] 41 | 42 | @root_validator(pre=True) 43 | def validate_project(cls, values: dict): 44 | try: 45 | values["username"] = subprocess.check_output( 46 | ["git", "config", "--get", "user.name"] 47 | ) 48 | values["email"] = subprocess.check_output( 49 | ["git", "config", "--get", "user.email"] 50 | ) 51 | except subprocess.CalledProcessError: 52 | ... 53 | values["folder_name"] = values["name"].lower().replace(" ", "-").strip() 54 | values["year"] = datetime.today().year 55 | return values 56 | 57 | class Config: 58 | use_enum_values = True 59 | -------------------------------------------------------------------------------- /manage_fastapi/generator.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import TypeVar 3 | 4 | import typer 5 | from cookiecutter.exceptions import OutputDirExistsException 6 | from cookiecutter.main import cookiecutter 7 | from pydantic.main import BaseModel 8 | 9 | from manage_fastapi.config import TEMPLATES_DIR 10 | from manage_fastapi.context import AppContext, ProjectContext 11 | 12 | ContextType = TypeVar("ContextType", bound=BaseModel) 13 | 14 | 15 | def fill_template(template_name: str, context: ContextType): 16 | try: 17 | cookiecutter( 18 | os.path.join(TEMPLATES_DIR, template_name), 19 | extra_context=context.dict(), 20 | no_input=True, 21 | ) 22 | except OutputDirExistsException: 23 | typer.echo(f"Folder '{context.folder_name}' already exists. 😞") 24 | else: 25 | typer.echo(f"FastAPI {template_name} created successfully! 🎉") 26 | 27 | 28 | def generate_app(context: AppContext): 29 | fill_template("app", context) 30 | 31 | 32 | def generate_project(context: ProjectContext): 33 | fill_template("project", context) 34 | -------------------------------------------------------------------------------- /manage_fastapi/helpers.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import TypeVar 3 | 4 | import questionary 5 | 6 | EnumType = TypeVar("EnumType") 7 | 8 | 9 | def camel_to_snake(text: str) -> str: 10 | return re.sub(r"(? questionary.Question: 14 | prompt = camel_to_snake(choices.__name__).replace("_", " ") # type: ignore 15 | return questionary.select(f"Select the {prompt}: ", choices=list(choices)) 16 | 17 | 18 | def binary_question(option: str) -> questionary.Question: 19 | return questionary.confirm(f"Do you want {option}?", default=False) 20 | -------------------------------------------------------------------------------- /manage_fastapi/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | from typing import Optional 4 | 5 | import pkg_resources 6 | import typer 7 | from questionary.form import form 8 | 9 | from manage_fastapi.constants import Database, License, PackageManager, PythonVersion 10 | from manage_fastapi.context import AppContext, ProjectContext 11 | from manage_fastapi.generator import generate_app, generate_project 12 | from manage_fastapi.helpers import binary_question, question 13 | 14 | app = typer.Typer( 15 | add_completion=False, 16 | help="Managing FastAPI projects made easy!", 17 | name="Manage FastAPI", 18 | ) 19 | 20 | 21 | @app.command(help="Creates a FastAPI project.") 22 | def startproject( 23 | name: str, 24 | interactive: bool = typer.Option(False, help="Run in interactive mode."), 25 | database: Optional[Database] = typer.Option(None, case_sensitive=False), 26 | docker: bool = typer.Option(False), 27 | license_: Optional[License] = typer.Option(None, "--license", case_sensitive=False), 28 | packaging: PackageManager = typer.Option(PackageManager.PIP), 29 | pre_commit: bool = typer.Option(False, "--pre-commit"), 30 | python: PythonVersion = typer.Option(PythonVersion.THREE_DOT_EIG), 31 | ): 32 | if interactive: 33 | result = form( 34 | packaging=question(PackageManager), 35 | python=question(PythonVersion), 36 | license=question(License), 37 | pre_commit=binary_question("pre commit"), 38 | docker=binary_question("docker"), 39 | database=question(Database), 40 | ).ask() 41 | context = ProjectContext(name=name, **result) 42 | else: 43 | context = ProjectContext( 44 | name=name, 45 | packaging=packaging, 46 | python=python, 47 | license=license_, 48 | pre_commit=pre_commit, 49 | docker=docker, 50 | database=database, 51 | ) 52 | generate_project(context) 53 | 54 | 55 | @app.command(help="Creates a FastAPI component.") 56 | def startapp(name: str): 57 | context = AppContext(name=name) 58 | generate_app(context) 59 | 60 | 61 | @app.command(help="Run a FastAPI application.") 62 | def run(prod: bool = typer.Option(False)): 63 | args = [] 64 | if not prod: 65 | args.append("--reload") 66 | app_file = os.getenv("FASTAPI_APP", "app.main") 67 | subprocess.call(["uvicorn", f"{app_file}:app", *args]) 68 | 69 | 70 | def version_callback(value: bool): 71 | if value: 72 | version = pkg_resources.get_distribution("manage-fastapi").version 73 | typer.echo(f"manage-fastapi, version {version}") 74 | raise typer.Exit() 75 | 76 | 77 | @app.callback() 78 | def main( 79 | version: bool = typer.Option( 80 | None, 81 | "--version", 82 | callback=version_callback, 83 | is_eager=True, 84 | help="Show the Manage FastAPI version information.", 85 | ) 86 | ): 87 | ... 88 | -------------------------------------------------------------------------------- /manage_fastapi/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/__init__.py -------------------------------------------------------------------------------- /manage_fastapi/templates/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/app/__init__.py -------------------------------------------------------------------------------- /manage_fastapi/templates/app/cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "folder_name": "{{ cookiecutter.folder_name }}", 3 | "name": "{{ cookiecutter.name }}", 4 | "snake_name": "{{ cookiecutter.snake_name }}" 5 | } 6 | -------------------------------------------------------------------------------- /manage_fastapi/templates/app/hooks/post_gen_project.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/app/hooks/post_gen_project.py -------------------------------------------------------------------------------- /manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/__init__.py -------------------------------------------------------------------------------- /manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/api/__init__.py -------------------------------------------------------------------------------- /manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/api/v1.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | 3 | router = APIRouter() 4 | 5 | 6 | @router.get("/") 7 | def get_{{ cookiecutter.snake_name }}(): 8 | return "{{ cookiecutter.name }} app created!" 9 | -------------------------------------------------------------------------------- /manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/crud.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/crud.py -------------------------------------------------------------------------------- /manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/models.py -------------------------------------------------------------------------------- /manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/schemas.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/app/{{ cookiecutter.folder_name }}/schemas.py -------------------------------------------------------------------------------- /manage_fastapi/templates/project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/project/__init__.py -------------------------------------------------------------------------------- /manage_fastapi/templates/project/cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": "{{ cookiecutter.database }}", 3 | "docker": "{{ cookiecutter.docker }}", 4 | "email": "{{ cookiecutter.email }}", 5 | "env": ".env", 6 | "fastapi": "{{ cookiecutter.fastapi }}", 7 | "folder_name": "{{ cookiecutter.folder_name }}", 8 | "gitignore": ".gitignore", 9 | "license": "{{ cookiecutter.license }}", 10 | "name": "{{ cookiecutter.name }}", 11 | "packaging": "{{ cookiecutter.packaging }}", 12 | "pre_commit": "{{ cookiecutter.pre_commit }}", 13 | "python": "{{ cookiecutter.python }}", 14 | "username": "{{ cookiecutter.username }}", 15 | "year": "{{ cookiecutter.year }}" 16 | } 17 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/hooks/post_gen_project.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from manage_fastapi.constants import PackageManager 4 | 5 | 6 | def remove_paths(paths: list): 7 | base_dir = os.getcwd() 8 | 9 | for path in paths: 10 | path = os.path.join(base_dir, path) 11 | if path and os.path.exists(path): 12 | if os.path.isdir(path): 13 | os.rmdir(path) 14 | else: 15 | os.unlink(path) 16 | 17 | 18 | def set_packaging(): 19 | packaging = "{{ cookiecutter.packaging }}" 20 | if packaging == PackageManager.PIP: 21 | remove_paths(["poetry.lock", "pyproject.toml"]) 22 | elif packaging == PackageManager.POETRY: 23 | remove_paths(["requirements.txt"]) 24 | 25 | 26 | def set_pre_commit(): 27 | pre_commit: bool = eval("{{ cookiecutter.pre_commit }}") 28 | if pre_commit is False: 29 | remove_paths([".pre-commit-config.yaml", "setup.cfg"]) 30 | 31 | 32 | def set_docker(): 33 | docker: bool = eval("{{ cookiecutter.docker }}") 34 | if docker is False: 35 | remove_paths(["Dockerfile", "docker-compose.yaml"]) 36 | 37 | 38 | def set_database(): 39 | database = "{{ cookiecutter.database }}" 40 | if database == "None": 41 | remove_paths(["app/database.py"]) 42 | 43 | 44 | def set_license(): 45 | license_ = "{{ cookiecutter.license }}" 46 | if license_ == "None": 47 | remove_paths(["LICENSE"]) 48 | 49 | 50 | # TODO(Marcelo): Alter config.py location according to the project complexity. 51 | # def set_config_location(): 52 | # database = "{{ cookiecutter.database }}" 53 | # if database == "None": 54 | # remove_paths(["app/core/config.py"]) 55 | # else: 56 | # remove_paths(["app/config.py"]) 57 | 58 | 59 | def main(): 60 | set_database() 61 | set_docker() 62 | set_license() 63 | set_packaging() 64 | set_pre_commit() 65 | 66 | 67 | if __name__ == "__main__": 68 | main() 69 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/myint/autoflake 3 | rev: v1.4 4 | hooks: 5 | - id: autoflake 6 | exclude: .*/__init__.py 7 | args: 8 | - --in-place 9 | - --remove-all-unused-imports 10 | - --expand-star-imports 11 | - --remove-duplicate-keys 12 | - --remove-unused-variables 13 | - repo: local 14 | hooks: 15 | - id: flake8 16 | name: flake8 17 | entry: flake8 18 | language: system 19 | types: [python] 20 | - repo: https://github.com/pre-commit/mirrors-isort 21 | rev: v5.4.2 22 | hooks: 23 | - id: isort 24 | args: ["--profile", "black"] 25 | - repo: local 26 | hooks: 27 | - id: mypy 28 | name: mypy 29 | entry: mypy 30 | language: system 31 | types: [python] 32 | - repo: https://github.com/pre-commit/pre-commit-hooks 33 | rev: v3.3.0 34 | hooks: 35 | - id: trailing-whitespace 36 | - id: end-of-file-fixer 37 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tiangolo/uvicorn-gunicorn-fastapi:python{{ cookiecutter.python }} 2 | 3 | ENV PYTHONPATH "${PYTHONPATH}:/" 4 | ENV PORT=8000 5 | {% if cookiecutter.packaging == "poetry" %} 6 | # Install Poetry 7 | RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python && \ 8 | cd /usr/local/bin && \ 9 | ln -s /opt/poetry/bin/poetry && \ 10 | poetry config virtualenvs.create false 11 | 12 | # Copy using poetry.lock* in case it doesn't exist yet 13 | COPY ./pyproject.toml ./poetry.lock* /app/ 14 | 15 | RUN poetry install --no-root --no-dev 16 | {% else %} 17 | RUN pip install --upgrade pip 18 | 19 | COPY ./requirements.txt /app/ 20 | 21 | RUN pip install -r requirements.txt 22 | {% endif %} 23 | COPY ./app /app 24 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/LICENSE: -------------------------------------------------------------------------------- 1 | {%- if cookiecutter.license == "MIT" -%} 2 | MIT License 3 | 4 | Copyright (c) {{ cookiecutter.year }} {{ cookiecutter.username }} 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | {%- elif cookiecutter.license == "BSD-3" -%} 24 | BSD 3-Clause License 25 | 26 | Copyright (c) {{ cookiecutter.year}}, {{ cookiecutter.username }} 27 | All rights reserved. 28 | 29 | Redistribution and use in source and binary forms, with or without 30 | modification, are permitted provided that the following conditions are met: 31 | 32 | 1. Redistributions of source code must retain the above copyright notice, this 33 | list of conditions and the following disclaimer. 34 | 35 | 2. Redistributions in binary form must reproduce the above copyright notice, 36 | this list of conditions and the following disclaimer in the documentation 37 | and/or other materials provided with the distribution. 38 | 39 | 3. Neither the name of the copyright holder nor the names of its 40 | contributors may be used to endorse or promote products derived from 41 | this software without specific prior written permission. 42 | 43 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 44 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 46 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 47 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 49 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 50 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 51 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 52 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | {%- elif cookiecutter.license == "GNU GPL v3.0" -%} 54 | GNU GENERAL PUBLIC LICENSE 55 | Version 3, 29 June 2007 56 | 57 | Copyright (C) 2007 Free Software Foundation, Inc. 58 | Everyone is permitted to copy and distribute verbatim copies 59 | of this license document, but changing it is not allowed. 60 | 61 | Preamble 62 | 63 | The GNU General Public License is a free, copyleft license for 64 | software and other kinds of works. 65 | 66 | The licenses for most software and other practical works are designed 67 | to take away your freedom to share and change the works. By contrast, 68 | the GNU General Public License is intended to guarantee your freedom to 69 | share and change all versions of a program--to make sure it remains free 70 | software for all its users. We, the Free Software Foundation, use the 71 | GNU General Public License for most of our software; it applies also to 72 | any other work released this way by its authors. You can apply it to 73 | your programs, too. 74 | 75 | When we speak of free software, we are referring to freedom, not 76 | price. Our General Public Licenses are designed to make sure that you 77 | have the freedom to distribute copies of free software (and charge for 78 | them if you wish), that you receive source code or can get it if you 79 | want it, that you can change the software or use pieces of it in new 80 | free programs, and that you know you can do these things. 81 | 82 | To protect your rights, we need to prevent others from denying you 83 | these rights or asking you to surrender the rights. Therefore, you have 84 | certain responsibilities if you distribute copies of the software, or if 85 | you modify it: responsibilities to respect the freedom of others. 86 | 87 | For example, if you distribute copies of such a program, whether 88 | gratis or for a fee, you must pass on to the recipients the same 89 | freedoms that you received. You must make sure that they, too, receive 90 | or can get the source code. And you must show them these terms so they 91 | know their rights. 92 | 93 | Developers that use the GNU GPL protect your rights with two steps: 94 | (1) assert copyright on the software, and (2) offer you this License 95 | giving you legal permission to copy, distribute and/or modify it. 96 | 97 | For the developers' and authors' protection, the GPL clearly explains 98 | that there is no warranty for this free software. For both users' and 99 | authors' sake, the GPL requires that modified versions be marked as 100 | changed, so that their problems will not be attributed erroneously to 101 | authors of previous versions. 102 | 103 | Some devices are designed to deny users access to install or run 104 | modified versions of the software inside them, although the manufacturer 105 | can do so. This is fundamentally incompatible with the aim of 106 | protecting users' freedom to change the software. The systematic 107 | pattern of such abuse occurs in the area of products for individuals to 108 | use, which is precisely where it is most unacceptable. Therefore, we 109 | have designed this version of the GPL to prohibit the practice for those 110 | products. If such problems arise substantially in other domains, we 111 | stand ready to extend this provision to those domains in future versions 112 | of the GPL, as needed to protect the freedom of users. 113 | 114 | Finally, every program is threatened constantly by software patents. 115 | States should not allow patents to restrict development and use of 116 | software on general-purpose computers, but in those that do, we wish to 117 | avoid the special danger that patents applied to a free program could 118 | make it effectively proprietary. To prevent this, the GPL assures that 119 | patents cannot be used to render the program non-free. 120 | 121 | The precise terms and conditions for copying, distribution and 122 | modification follow. 123 | 124 | TERMS AND CONDITIONS 125 | 126 | 0. Definitions. 127 | 128 | "This License" refers to version 3 of the GNU General Public License. 129 | 130 | "Copyright" also means copyright-like laws that apply to other kinds of 131 | works, such as semiconductor masks. 132 | 133 | "The Program" refers to any copyrightable work licensed under this 134 | License. Each licensee is addressed as "you". "Licensees" and 135 | "recipients" may be individuals or organizations. 136 | 137 | To "modify" a work means to copy from or adapt all or part of the work 138 | in a fashion requiring copyright permission, other than the making of an 139 | exact copy. The resulting work is called a "modified version" of the 140 | earlier work or a work "based on" the earlier work. 141 | 142 | A "covered work" means either the unmodified Program or a work based 143 | on the Program. 144 | 145 | To "propagate" a work means to do anything with it that, without 146 | permission, would make you directly or secondarily liable for 147 | infringement under applicable copyright law, except executing it on a 148 | computer or modifying a private copy. Propagation includes copying, 149 | distribution (with or without modification), making available to the 150 | public, and in some countries other activities as well. 151 | 152 | To "convey" a work means any kind of propagation that enables other 153 | parties to make or receive copies. Mere interaction with a user through 154 | a computer network, with no transfer of a copy, is not conveying. 155 | 156 | An interactive user interface displays "Appropriate Legal Notices" 157 | to the extent that it includes a convenient and prominently visible 158 | feature that (1) displays an appropriate copyright notice, and (2) 159 | tells the user that there is no warranty for the work (except to the 160 | extent that warranties are provided), that licensees may convey the 161 | work under this License, and how to view a copy of this License. If 162 | the interface presents a list of user commands or options, such as a 163 | menu, a prominent item in the list meets this criterion. 164 | 165 | 1. Source Code. 166 | 167 | The "source code" for a work means the preferred form of the work 168 | for making modifications to it. "Object code" means any non-source 169 | form of a work. 170 | 171 | A "Standard Interface" means an interface that either is an official 172 | standard defined by a recognized standards body, or, in the case of 173 | interfaces specified for a particular programming language, one that 174 | is widely used among developers working in that language. 175 | 176 | The "System Libraries" of an executable work include anything, other 177 | than the work as a whole, that (a) is included in the normal form of 178 | packaging a Major Component, but which is not part of that Major 179 | Component, and (b) serves only to enable use of the work with that 180 | Major Component, or to implement a Standard Interface for which an 181 | implementation is available to the public in source code form. A 182 | "Major Component", in this context, means a major essential component 183 | (kernel, window system, and so on) of the specific operating system 184 | (if any) on which the executable work runs, or a compiler used to 185 | produce the work, or an object code interpreter used to run it. 186 | 187 | The "Corresponding Source" for a work in object code form means all 188 | the source code needed to generate, install, and (for an executable 189 | work) run the object code and to modify the work, including scripts to 190 | control those activities. However, it does not include the work's 191 | System Libraries, or general-purpose tools or generally available free 192 | programs which are used unmodified in performing those activities but 193 | which are not part of the work. For example, Corresponding Source 194 | includes interface definition files associated with source files for 195 | the work, and the source code for shared libraries and dynamically 196 | linked subprograms that the work is specifically designed to require, 197 | such as by intimate data communication or control flow between those 198 | subprograms and other parts of the work. 199 | 200 | The Corresponding Source need not include anything that users 201 | can regenerate automatically from other parts of the Corresponding 202 | Source. 203 | 204 | The Corresponding Source for a work in source code form is that 205 | same work. 206 | 207 | 2. Basic Permissions. 208 | 209 | All rights granted under this License are granted for the term of 210 | copyright on the Program, and are irrevocable provided the stated 211 | conditions are met. This License explicitly affirms your unlimited 212 | permission to run the unmodified Program. The output from running a 213 | covered work is covered by this License only if the output, given its 214 | content, constitutes a covered work. This License acknowledges your 215 | rights of fair use or other equivalent, as provided by copyright law. 216 | 217 | You may make, run and propagate covered works that you do not 218 | convey, without conditions so long as your license otherwise remains 219 | in force. You may convey covered works to others for the sole purpose 220 | of having them make modifications exclusively for you, or provide you 221 | with facilities for running those works, provided that you comply with 222 | the terms of this License in conveying all material for which you do 223 | not control copyright. Those thus making or running the covered works 224 | for you must do so exclusively on your behalf, under your direction 225 | and control, on terms that prohibit them from making any copies of 226 | your copyrighted material outside their relationship with you. 227 | 228 | Conveying under any other circumstances is permitted solely under 229 | the conditions stated below. Sublicensing is not allowed; section 10 230 | makes it unnecessary. 231 | 232 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 233 | 234 | No covered work shall be deemed part of an effective technological 235 | measure under any applicable law fulfilling obligations under article 236 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 237 | similar laws prohibiting or restricting circumvention of such 238 | measures. 239 | 240 | When you convey a covered work, you waive any legal power to forbid 241 | circumvention of technological measures to the extent such circumvention 242 | is effected by exercising rights under this License with respect to 243 | the covered work, and you disclaim any intention to limit operation or 244 | modification of the work as a means of enforcing, against the work's 245 | users, your or third parties' legal rights to forbid circumvention of 246 | technological measures. 247 | 248 | 4. Conveying Verbatim Copies. 249 | 250 | You may convey verbatim copies of the Program's source code as you 251 | receive it, in any medium, provided that you conspicuously and 252 | appropriately publish on each copy an appropriate copyright notice; 253 | keep intact all notices stating that this License and any 254 | non-permissive terms added in accord with section 7 apply to the code; 255 | keep intact all notices of the absence of any warranty; and give all 256 | recipients a copy of this License along with the Program. 257 | 258 | You may charge any price or no price for each copy that you convey, 259 | and you may offer support or warranty protection for a fee. 260 | 261 | 5. Conveying Modified Source Versions. 262 | 263 | You may convey a work based on the Program, or the modifications to 264 | produce it from the Program, in the form of source code under the 265 | terms of section 4, provided that you also meet all of these conditions: 266 | 267 | a) The work must carry prominent notices stating that you modified 268 | it, and giving a relevant date. 269 | 270 | b) The work must carry prominent notices stating that it is 271 | released under this License and any conditions added under section 272 | 7. This requirement modifies the requirement in section 4 to 273 | "keep intact all notices". 274 | 275 | c) You must license the entire work, as a whole, under this 276 | License to anyone who comes into possession of a copy. This 277 | License will therefore apply, along with any applicable section 7 278 | additional terms, to the whole of the work, and all its parts, 279 | regardless of how they are packaged. This License gives no 280 | permission to license the work in any other way, but it does not 281 | invalidate such permission if you have separately received it. 282 | 283 | d) If the work has interactive user interfaces, each must display 284 | Appropriate Legal Notices; however, if the Program has interactive 285 | interfaces that do not display Appropriate Legal Notices, your 286 | work need not make them do so. 287 | 288 | A compilation of a covered work with other separate and independent 289 | works, which are not by their nature extensions of the covered work, 290 | and which are not combined with it such as to form a larger program, 291 | in or on a volume of a storage or distribution medium, is called an 292 | "aggregate" if the compilation and its resulting copyright are not 293 | used to limit the access or legal rights of the compilation's users 294 | beyond what the individual works permit. Inclusion of a covered work 295 | in an aggregate does not cause this License to apply to the other 296 | parts of the aggregate. 297 | 298 | 6. Conveying Non-Source Forms. 299 | 300 | You may convey a covered work in object code form under the terms 301 | of sections 4 and 5, provided that you also convey the 302 | machine-readable Corresponding Source under the terms of this License, 303 | in one of these ways: 304 | 305 | a) Convey the object code in, or embodied in, a physical product 306 | (including a physical distribution medium), accompanied by the 307 | Corresponding Source fixed on a durable physical medium 308 | customarily used for software interchange. 309 | 310 | b) Convey the object code in, or embodied in, a physical product 311 | (including a physical distribution medium), accompanied by a 312 | written offer, valid for at least three years and valid for as 313 | long as you offer spare parts or customer support for that product 314 | model, to give anyone who possesses the object code either (1) a 315 | copy of the Corresponding Source for all the software in the 316 | product that is covered by this License, on a durable physical 317 | medium customarily used for software interchange, for a price no 318 | more than your reasonable cost of physically performing this 319 | conveying of source, or (2) access to copy the 320 | Corresponding Source from a network server at no charge. 321 | 322 | c) Convey individual copies of the object code with a copy of the 323 | written offer to provide the Corresponding Source. This 324 | alternative is allowed only occasionally and noncommercially, and 325 | only if you received the object code with such an offer, in accord 326 | with subsection 6b. 327 | 328 | d) Convey the object code by offering access from a designated 329 | place (gratis or for a charge), and offer equivalent access to the 330 | Corresponding Source in the same way through the same place at no 331 | further charge. You need not require recipients to copy the 332 | Corresponding Source along with the object code. If the place to 333 | copy the object code is a network server, the Corresponding Source 334 | may be on a different server (operated by you or a third party) 335 | that supports equivalent copying facilities, provided you maintain 336 | clear directions next to the object code saying where to find the 337 | Corresponding Source. Regardless of what server hosts the 338 | Corresponding Source, you remain obligated to ensure that it is 339 | available for as long as needed to satisfy these requirements. 340 | 341 | e) Convey the object code using peer-to-peer transmission, provided 342 | you inform other peers where the object code and Corresponding 343 | Source of the work are being offered to the general public at no 344 | charge under subsection 6d. 345 | 346 | A separable portion of the object code, whose source code is excluded 347 | from the Corresponding Source as a System Library, need not be 348 | included in conveying the object code work. 349 | 350 | A "User Product" is either (1) a "consumer product", which means any 351 | tangible personal property which is normally used for personal, family, 352 | or household purposes, or (2) anything designed or sold for incorporation 353 | into a dwelling. In determining whether a product is a consumer product, 354 | doubtful cases shall be resolved in favor of coverage. For a particular 355 | product received by a particular user, "normally used" refers to a 356 | typical or common use of that class of product, regardless of the status 357 | of the particular user or of the way in which the particular user 358 | actually uses, or expects or is expected to use, the product. A product 359 | is a consumer product regardless of whether the product has substantial 360 | commercial, industrial or non-consumer uses, unless such uses represent 361 | the only significant mode of use of the product. 362 | 363 | "Installation Information" for a User Product means any methods, 364 | procedures, authorization keys, or other information required to install 365 | and execute modified versions of a covered work in that User Product from 366 | a modified version of its Corresponding Source. The information must 367 | suffice to ensure that the continued functioning of the modified object 368 | code is in no case prevented or interfered with solely because 369 | modification has been made. 370 | 371 | If you convey an object code work under this section in, or with, or 372 | specifically for use in, a User Product, and the conveying occurs as 373 | part of a transaction in which the right of possession and use of the 374 | User Product is transferred to the recipient in perpetuity or for a 375 | fixed term (regardless of how the transaction is characterized), the 376 | Corresponding Source conveyed under this section must be accompanied 377 | by the Installation Information. But this requirement does not apply 378 | if neither you nor any third party retains the ability to install 379 | modified object code on the User Product (for example, the work has 380 | been installed in ROM). 381 | 382 | The requirement to provide Installation Information does not include a 383 | requirement to continue to provide support service, warranty, or updates 384 | for a work that has been modified or installed by the recipient, or for 385 | the User Product in which it has been modified or installed. Access to a 386 | network may be denied when the modification itself materially and 387 | adversely affects the operation of the network or violates the rules and 388 | protocols for communication across the network. 389 | 390 | Corresponding Source conveyed, and Installation Information provided, 391 | in accord with this section must be in a format that is publicly 392 | documented (and with an implementation available to the public in 393 | source code form), and must require no special password or key for 394 | unpacking, reading or copying. 395 | 396 | 7. Additional Terms. 397 | 398 | "Additional permissions" are terms that supplement the terms of this 399 | License by making exceptions from one or more of its conditions. 400 | Additional permissions that are applicable to the entire Program shall 401 | be treated as though they were included in this License, to the extent 402 | that they are valid under applicable law. If additional permissions 403 | apply only to part of the Program, that part may be used separately 404 | under those permissions, but the entire Program remains governed by 405 | this License without regard to the additional permissions. 406 | 407 | When you convey a copy of a covered work, you may at your option 408 | remove any additional permissions from that copy, or from any part of 409 | it. (Additional permissions may be written to require their own 410 | removal in certain cases when you modify the work.) You may place 411 | additional permissions on material, added by you to a covered work, 412 | for which you have or can give appropriate copyright permission. 413 | 414 | Notwithstanding any other provision of this License, for material you 415 | add to a covered work, you may (if authorized by the copyright holders of 416 | that material) supplement the terms of this License with terms: 417 | 418 | a) Disclaiming warranty or limiting liability differently from the 419 | terms of sections 15 and 16 of this License; or 420 | 421 | b) Requiring preservation of specified reasonable legal notices or 422 | author attributions in that material or in the Appropriate Legal 423 | Notices displayed by works containing it; or 424 | 425 | c) Prohibiting misrepresentation of the origin of that material, or 426 | requiring that modified versions of such material be marked in 427 | reasonable ways as different from the original version; or 428 | 429 | d) Limiting the use for publicity purposes of names of licensors or 430 | authors of the material; or 431 | 432 | e) Declining to grant rights under trademark law for use of some 433 | trade names, trademarks, or service marks; or 434 | 435 | f) Requiring indemnification of licensors and authors of that 436 | material by anyone who conveys the material (or modified versions of 437 | it) with contractual assumptions of liability to the recipient, for 438 | any liability that these contractual assumptions directly impose on 439 | those licensors and authors. 440 | 441 | All other non-permissive additional terms are considered "further 442 | restrictions" within the meaning of section 10. If the Program as you 443 | received it, or any part of it, contains a notice stating that it is 444 | governed by this License along with a term that is a further 445 | restriction, you may remove that term. If a license document contains 446 | a further restriction but permits relicensing or conveying under this 447 | License, you may add to a covered work material governed by the terms 448 | of that license document, provided that the further restriction does 449 | not survive such relicensing or conveying. 450 | 451 | If you add terms to a covered work in accord with this section, you 452 | must place, in the relevant source files, a statement of the 453 | additional terms that apply to those files, or a notice indicating 454 | where to find the applicable terms. 455 | 456 | Additional terms, permissive or non-permissive, may be stated in the 457 | form of a separately written license, or stated as exceptions; 458 | the above requirements apply either way. 459 | 460 | 8. Termination. 461 | 462 | You may not propagate or modify a covered work except as expressly 463 | provided under this License. Any attempt otherwise to propagate or 464 | modify it is void, and will automatically terminate your rights under 465 | this License (including any patent licenses granted under the third 466 | paragraph of section 11). 467 | 468 | However, if you cease all violation of this License, then your 469 | license from a particular copyright holder is reinstated (a) 470 | provisionally, unless and until the copyright holder explicitly and 471 | finally terminates your license, and (b) permanently, if the copyright 472 | holder fails to notify you of the violation by some reasonable means 473 | prior to 60 days after the cessation. 474 | 475 | Moreover, your license from a particular copyright holder is 476 | reinstated permanently if the copyright holder notifies you of the 477 | violation by some reasonable means, this is the first time you have 478 | received notice of violation of this License (for any work) from that 479 | copyright holder, and you cure the violation prior to 30 days after 480 | your receipt of the notice. 481 | 482 | Termination of your rights under this section does not terminate the 483 | licenses of parties who have received copies or rights from you under 484 | this License. If your rights have been terminated and not permanently 485 | reinstated, you do not qualify to receive new licenses for the same 486 | material under section 10. 487 | 488 | 9. Acceptance Not Required for Having Copies. 489 | 490 | You are not required to accept this License in order to receive or 491 | run a copy of the Program. Ancillary propagation of a covered work 492 | occurring solely as a consequence of using peer-to-peer transmission 493 | to receive a copy likewise does not require acceptance. However, 494 | nothing other than this License grants you permission to propagate or 495 | modify any covered work. These actions infringe copyright if you do 496 | not accept this License. Therefore, by modifying or propagating a 497 | covered work, you indicate your acceptance of this License to do so. 498 | 499 | 10. Automatic Licensing of Downstream Recipients. 500 | 501 | Each time you convey a covered work, the recipient automatically 502 | receives a license from the original licensors, to run, modify and 503 | propagate that work, subject to this License. You are not responsible 504 | for enforcing compliance by third parties with this License. 505 | 506 | An "entity transaction" is a transaction transferring control of an 507 | organization, or substantially all assets of one, or subdividing an 508 | organization, or merging organizations. If propagation of a covered 509 | work results from an entity transaction, each party to that 510 | transaction who receives a copy of the work also receives whatever 511 | licenses to the work the party's predecessor in interest had or could 512 | give under the previous paragraph, plus a right to possession of the 513 | Corresponding Source of the work from the predecessor in interest, if 514 | the predecessor has it or can get it with reasonable efforts. 515 | 516 | You may not impose any further restrictions on the exercise of the 517 | rights granted or affirmed under this License. For example, you may 518 | not impose a license fee, royalty, or other charge for exercise of 519 | rights granted under this License, and you may not initiate litigation 520 | (including a cross-claim or counterclaim in a lawsuit) alleging that 521 | any patent claim is infringed by making, using, selling, offering for 522 | sale, or importing the Program or any portion of it. 523 | 524 | 11. Patents. 525 | 526 | A "contributor" is a copyright holder who authorizes use under this 527 | License of the Program or a work on which the Program is based. The 528 | work thus licensed is called the contributor's "contributor version". 529 | 530 | A contributor's "essential patent claims" are all patent claims 531 | owned or controlled by the contributor, whether already acquired or 532 | hereafter acquired, that would be infringed by some manner, permitted 533 | by this License, of making, using, or selling its contributor version, 534 | but do not include claims that would be infringed only as a 535 | consequence of further modification of the contributor version. For 536 | purposes of this definition, "control" includes the right to grant 537 | patent sublicenses in a manner consistent with the requirements of 538 | this License. 539 | 540 | Each contributor grants you a non-exclusive, worldwide, royalty-free 541 | patent license under the contributor's essential patent claims, to 542 | make, use, sell, offer for sale, import and otherwise run, modify and 543 | propagate the contents of its contributor version. 544 | 545 | In the following three paragraphs, a "patent license" is any express 546 | agreement or commitment, however denominated, not to enforce a patent 547 | (such as an express permission to practice a patent or covenant not to 548 | sue for patent infringement). To "grant" such a patent license to a 549 | party means to make such an agreement or commitment not to enforce a 550 | patent against the party. 551 | 552 | If you convey a covered work, knowingly relying on a patent license, 553 | and the Corresponding Source of the work is not available for anyone 554 | to copy, free of charge and under the terms of this License, through a 555 | publicly available network server or other readily accessible means, 556 | then you must either (1) cause the Corresponding Source to be so 557 | available, or (2) arrange to deprive yourself of the benefit of the 558 | patent license for this particular work, or (3) arrange, in a manner 559 | consistent with the requirements of this License, to extend the patent 560 | license to downstream recipients. "Knowingly relying" means you have 561 | actual knowledge that, but for the patent license, your conveying the 562 | covered work in a country, or your recipient's use of the covered work 563 | in a country, would infringe one or more identifiable patents in that 564 | country that you have reason to believe are valid. 565 | 566 | If, pursuant to or in connection with a single transaction or 567 | arrangement, you convey, or propagate by procuring conveyance of, a 568 | covered work, and grant a patent license to some of the parties 569 | receiving the covered work authorizing them to use, propagate, modify 570 | or convey a specific copy of the covered work, then the patent license 571 | you grant is automatically extended to all recipients of the covered 572 | work and works based on it. 573 | 574 | A patent license is "discriminatory" if it does not include within 575 | the scope of its coverage, prohibits the exercise of, or is 576 | conditioned on the non-exercise of one or more of the rights that are 577 | specifically granted under this License. You may not convey a covered 578 | work if you are a party to an arrangement with a third party that is 579 | in the business of distributing software, under which you make payment 580 | to the third party based on the extent of your activity of conveying 581 | the work, and under which the third party grants, to any of the 582 | parties who would receive the covered work from you, a discriminatory 583 | patent license (a) in connection with copies of the covered work 584 | conveyed by you (or copies made from those copies), or (b) primarily 585 | for and in connection with specific products or compilations that 586 | contain the covered work, unless you entered into that arrangement, 587 | or that patent license was granted, prior to 28 March 2007. 588 | 589 | Nothing in this License shall be construed as excluding or limiting 590 | any implied license or other defenses to infringement that may 591 | otherwise be available to you under applicable patent law. 592 | 593 | 12. No Surrender of Others' Freedom. 594 | 595 | If conditions are imposed on you (whether by court order, agreement or 596 | otherwise) that contradict the conditions of this License, they do not 597 | excuse you from the conditions of this License. If you cannot convey a 598 | covered work so as to satisfy simultaneously your obligations under this 599 | License and any other pertinent obligations, then as a consequence you may 600 | not convey it at all. For example, if you agree to terms that obligate you 601 | to collect a royalty for further conveying from those to whom you convey 602 | the Program, the only way you could satisfy both those terms and this 603 | License would be to refrain entirely from conveying the Program. 604 | 605 | 13. Use with the GNU Affero General Public License. 606 | 607 | Notwithstanding any other provision of this License, you have 608 | permission to link or combine any covered work with a work licensed 609 | under version 3 of the GNU Affero General Public License into a single 610 | combined work, and to convey the resulting work. The terms of this 611 | License will continue to apply to the part which is the covered work, 612 | but the special requirements of the GNU Affero General Public License, 613 | section 13, concerning interaction through a network will apply to the 614 | combination as such. 615 | 616 | 14. Revised Versions of this License. 617 | 618 | The Free Software Foundation may publish revised and/or new versions of 619 | the GNU General Public License from time to time. Such new versions will 620 | be similar in spirit to the present version, but may differ in detail to 621 | address new problems or concerns. 622 | 623 | Each version is given a distinguishing version number. If the 624 | Program specifies that a certain numbered version of the GNU General 625 | Public License "or any later version" applies to it, you have the 626 | option of following the terms and conditions either of that numbered 627 | version or of any later version published by the Free Software 628 | Foundation. If the Program does not specify a version number of the 629 | GNU General Public License, you may choose any version ever published 630 | by the Free Software Foundation. 631 | 632 | If the Program specifies that a proxy can decide which future 633 | versions of the GNU General Public License can be used, that proxy's 634 | public statement of acceptance of a version permanently authorizes you 635 | to choose that version for the Program. 636 | 637 | Later license versions may give you additional or different 638 | permissions. However, no additional obligations are imposed on any 639 | author or copyright holder as a result of your choosing to follow a 640 | later version. 641 | 642 | 15. Disclaimer of Warranty. 643 | 644 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 645 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 646 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 647 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 648 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 649 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 650 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 651 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 652 | 653 | 16. Limitation of Liability. 654 | 655 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 656 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 657 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 658 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 659 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 660 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 661 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 662 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 663 | SUCH DAMAGES. 664 | 665 | 17. Interpretation of Sections 15 and 16. 666 | 667 | If the disclaimer of warranty and limitation of liability provided 668 | above cannot be given local legal effect according to their terms, 669 | reviewing courts shall apply local law that most closely approximates 670 | an absolute waiver of all civil liability in connection with the 671 | Program, unless a warranty or assumption of liability accompanies a 672 | copy of the Program in return for a fee. 673 | 674 | END OF TERMS AND CONDITIONS 675 | 676 | How to Apply These Terms to Your New Programs 677 | 678 | If you develop a new program, and you want it to be of the greatest 679 | possible use to the public, the best way to achieve this is to make it 680 | free software which everyone can redistribute and change under these terms. 681 | 682 | To do so, attach the following notices to the program. It is safest 683 | to attach them to the start of each source file to most effectively 684 | state the exclusion of warranty; and each file should have at least 685 | the "copyright" line and a pointer to where the full notice is found. 686 | 687 | 688 | Copyright (C) 689 | 690 | This program is free software: you can redistribute it and/or modify 691 | it under the terms of the GNU General Public License as published by 692 | the Free Software Foundation, either version 3 of the License, or 693 | (at your option) any later version. 694 | 695 | This program is distributed in the hope that it will be useful, 696 | but WITHOUT ANY WARRANTY; without even the implied warranty of 697 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 698 | GNU General Public License for more details. 699 | 700 | You should have received a copy of the GNU General Public License 701 | along with this program. If not, see . 702 | 703 | Also add information on how to contact you by electronic and paper mail. 704 | 705 | If the program does terminal interaction, make it output a short 706 | notice like this when it starts in an interactive mode: 707 | 708 | Copyright (C) 709 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 710 | This is free software, and you are welcome to redistribute it 711 | under certain conditions; type `show c' for details. 712 | 713 | The hypothetical commands `show w' and `show c' should show the appropriate 714 | parts of the General Public License. Of course, your program's commands 715 | might be different; for a GUI interface, you would use an "about box". 716 | 717 | You should also get your employer (if you work as a programmer) or school, 718 | if any, to sign a "copyright disclaimer" for the program, if necessary. 719 | For more information on this, and how to apply and follow the GNU GPL, see 720 | . 721 | 722 | The GNU General Public License does not permit incorporating your program 723 | into proprietary programs. If your program is a subroutine library, you 724 | may consider it more useful to permit linking proprietary applications with 725 | the library. If this is what you want to do, use the GNU Lesser General 726 | Public License instead of this License. But first, please read 727 | . 728 | {%- elif cookiecutter.license == "Apache Software License 2.0" -%} 729 | Apache License 730 | Version 2.0, January 2004 731 | http://www.apache.org/licenses/ 732 | 733 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 734 | 735 | 1. Definitions. 736 | 737 | "License" shall mean the terms and conditions for use, reproduction, 738 | and distribution as defined by Sections 1 through 9 of this document. 739 | 740 | "Licensor" shall mean the copyright owner or entity authorized by 741 | the copyright owner that is granting the License. 742 | 743 | "Legal Entity" shall mean the union of the acting entity and all 744 | other entities that control, are controlled by, or are under common 745 | control with that entity. For the purposes of this definition, 746 | "control" means (i) the power, direct or indirect, to cause the 747 | direction or management of such entity, whether by contract or 748 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 749 | outstanding shares, or (iii) beneficial ownership of such entity. 750 | 751 | "You" (or "Your") shall mean an individual or Legal Entity 752 | exercising permissions granted by this License. 753 | 754 | "Source" form shall mean the preferred form for making modifications, 755 | including but not limited to software source code, documentation 756 | source, and configuration files. 757 | 758 | "Object" form shall mean any form resulting from mechanical 759 | transformation or translation of a Source form, including but 760 | not limited to compiled object code, generated documentation, 761 | and conversions to other media types. 762 | 763 | "Work" shall mean the work of authorship, whether in Source or 764 | Object form, made available under the License, as indicated by a 765 | copyright notice that is included in or attached to the work 766 | (an example is provided in the Appendix below). 767 | 768 | "Derivative Works" shall mean any work, whether in Source or Object 769 | form, that is based on (or derived from) the Work and for which the 770 | editorial revisions, annotations, elaborations, or other modifications 771 | represent, as a whole, an original work of authorship. For the purposes 772 | of this License, Derivative Works shall not include works that remain 773 | separable from, or merely link (or bind by name) to the interfaces of, 774 | the Work and Derivative Works thereof. 775 | 776 | "Contribution" shall mean any work of authorship, including 777 | the original version of the Work and any modifications or additions 778 | to that Work or Derivative Works thereof, that is intentionally 779 | submitted to Licensor for inclusion in the Work by the copyright owner 780 | or by an individual or Legal Entity authorized to submit on behalf of 781 | the copyright owner. For the purposes of this definition, "submitted" 782 | means any form of electronic, verbal, or written communication sent 783 | to the Licensor or its representatives, including but not limited to 784 | communication on electronic mailing lists, source code control systems, 785 | and issue tracking systems that are managed by, or on behalf of, the 786 | Licensor for the purpose of discussing and improving the Work, but 787 | excluding communication that is conspicuously marked or otherwise 788 | designated in writing by the copyright owner as "Not a Contribution." 789 | 790 | "Contributor" shall mean Licensor and any individual or Legal Entity 791 | on behalf of whom a Contribution has been received by Licensor and 792 | subsequently incorporated within the Work. 793 | 794 | 2. Grant of Copyright License. Subject to the terms and conditions of 795 | this License, each Contributor hereby grants to You a perpetual, 796 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 797 | copyright license to reproduce, prepare Derivative Works of, 798 | publicly display, publicly perform, sublicense, and distribute the 799 | Work and such Derivative Works in Source or Object form. 800 | 801 | 3. Grant of Patent License. Subject to the terms and conditions of 802 | this License, each Contributor hereby grants to You a perpetual, 803 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 804 | (except as stated in this section) patent license to make, have made, 805 | use, offer to sell, sell, import, and otherwise transfer the Work, 806 | where such license applies only to those patent claims licensable 807 | by such Contributor that are necessarily infringed by their 808 | Contribution(s) alone or by combination of their Contribution(s) 809 | with the Work to which such Contribution(s) was submitted. If You 810 | institute patent litigation against any entity (including a 811 | cross-claim or counterclaim in a lawsuit) alleging that the Work 812 | or a Contribution incorporated within the Work constitutes direct 813 | or contributory patent infringement, then any patent licenses 814 | granted to You under this License for that Work shall terminate 815 | as of the date such litigation is filed. 816 | 817 | 4. Redistribution. You may reproduce and distribute copies of the 818 | Work or Derivative Works thereof in any medium, with or without 819 | modifications, and in Source or Object form, provided that You 820 | meet the following conditions: 821 | 822 | (a) You must give any other recipients of the Work or 823 | Derivative Works a copy of this License; and 824 | 825 | (b) You must cause any modified files to carry prominent notices 826 | stating that You changed the files; and 827 | 828 | (c) You must retain, in the Source form of any Derivative Works 829 | that You distribute, all copyright, patent, trademark, and 830 | attribution notices from the Source form of the Work, 831 | excluding those notices that do not pertain to any part of 832 | the Derivative Works; and 833 | 834 | (d) If the Work includes a "NOTICE" text file as part of its 835 | distribution, then any Derivative Works that You distribute must 836 | include a readable copy of the attribution notices contained 837 | within such NOTICE file, excluding those notices that do not 838 | pertain to any part of the Derivative Works, in at least one 839 | of the following places: within a NOTICE text file distributed 840 | as part of the Derivative Works; within the Source form or 841 | documentation, if provided along with the Derivative Works; or, 842 | within a display generated by the Derivative Works, if and 843 | wherever such third-party notices normally appear. The contents 844 | of the NOTICE file are for informational purposes only and 845 | do not modify the License. You may add Your own attribution 846 | notices within Derivative Works that You distribute, alongside 847 | or as an addendum to the NOTICE text from the Work, provided 848 | that such additional attribution notices cannot be construed 849 | as modifying the License. 850 | 851 | You may add Your own copyright statement to Your modifications and 852 | may provide additional or different license terms and conditions 853 | for use, reproduction, or distribution of Your modifications, or 854 | for any such Derivative Works as a whole, provided Your use, 855 | reproduction, and distribution of the Work otherwise complies with 856 | the conditions stated in this License. 857 | 858 | 5. Submission of Contributions. Unless You explicitly state otherwise, 859 | any Contribution intentionally submitted for inclusion in the Work 860 | by You to the Licensor shall be under the terms and conditions of 861 | this License, without any additional terms or conditions. 862 | Notwithstanding the above, nothing herein shall supersede or modify 863 | the terms of any separate license agreement you may have executed 864 | with Licensor regarding such Contributions. 865 | 866 | 6. Trademarks. This License does not grant permission to use the trade 867 | names, trademarks, service marks, or product names of the Licensor, 868 | except as required for reasonable and customary use in describing the 869 | origin of the Work and reproducing the content of the NOTICE file. 870 | 871 | 7. Disclaimer of Warranty. Unless required by applicable law or 872 | agreed to in writing, Licensor provides the Work (and each 873 | Contributor provides its Contributions) on an "AS IS" BASIS, 874 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 875 | implied, including, without limitation, any warranties or conditions 876 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 877 | PARTICULAR PURPOSE. You are solely responsible for determining the 878 | appropriateness of using or redistributing the Work and assume any 879 | risks associated with Your exercise of permissions under this License. 880 | 881 | 8. Limitation of Liability. In no event and under no legal theory, 882 | whether in tort (including negligence), contract, or otherwise, 883 | unless required by applicable law (such as deliberate and grossly 884 | negligent acts) or agreed to in writing, shall any Contributor be 885 | liable to You for damages, including any direct, indirect, special, 886 | incidental, or consequential damages of any character arising as a 887 | result of this License or out of the use or inability to use the 888 | Work (including but not limited to damages for loss of goodwill, 889 | work stoppage, computer failure or malfunction, or any and all 890 | other commercial damages or losses), even if such Contributor 891 | has been advised of the possibility of such damages. 892 | 893 | 9. Accepting Warranty or Additional Liability. While redistributing 894 | the Work or Derivative Works thereof, You may choose to offer, 895 | and charge a fee for, acceptance of support, warranty, indemnity, 896 | or other liability obligations and/or rights consistent with this 897 | License. However, in accepting such obligations, You may act only 898 | on Your own behalf and on Your sole responsibility, not on behalf 899 | of any other Contributor, and only if You agree to indemnify, 900 | defend, and hold each Contributor harmless for any liability 901 | incurred by, or claims asserted against, such Contributor by reason 902 | of your accepting any such warranty or additional liability. 903 | 904 | END OF TERMS AND CONDITIONS 905 | 906 | APPENDIX: How to apply the Apache License to your work. 907 | 908 | To apply the Apache License to your work, attach the following 909 | boilerplate notice, with the fields enclosed by brackets "[]" 910 | replaced with your own identifying information. (Don't include 911 | the brackets!) The text should be enclosed in the appropriate 912 | comment syntax for the file format. We also recommend that a 913 | file or class name and description of purpose be included on the 914 | same "printed page" as the copyright notice for easier 915 | identification within third-party archives. 916 | 917 | Copyright [yyyy] [name of copyright owner] 918 | 919 | Licensed under the Apache License, Version 2.0 (the "License"); 920 | you may not use this file except in compliance with the License. 921 | You may obtain a copy of the License at 922 | 923 | http://www.apache.org/licenses/LICENSE-2.0 924 | 925 | Unless required by applicable law or agreed to in writing, software 926 | distributed under the License is distributed on an "AS IS" BASIS, 927 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 928 | See the License for the specific language governing permissions and 929 | limitations under the License. 930 | {% endif %} 931 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/README.md: -------------------------------------------------------------------------------- 1 | # {{ cookiecutter.name }} 2 | 3 | This project was generated via [manage-fastapi](https://ycd.github.io/manage-fastapi/)! :tada: 4 | 5 | ## License 6 | 7 | This project is licensed under the terms of the {{ cookiecutter.license }} license. 8 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/__init__.py -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/core/__init__.py -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/core/config.py: -------------------------------------------------------------------------------- 1 | {% if cookiecutter.database == "Postgres" %} 2 | from typing import Any, Dict, List, Optional, Union 3 | 4 | from pydantic import AnyHttpUrl, BaseSettings, PostgresDsn, validator 5 | {% elif cookiecutter.database == "MySQL" %} 6 | from typing import Any, Dict, List, Optional, Union 7 | 8 | from pydantic import AnyHttpUrl, BaseSettings, validator 9 | {% else %} 10 | from typing import List, Union 11 | 12 | from pydantic import AnyHttpUrl, BaseSettings, validator 13 | 14 | {% endif %} 15 | 16 | class Settings(BaseSettings): 17 | PROJECT_NAME: str 18 | BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = [] 19 | 20 | @validator("BACKEND_CORS_ORIGINS", pre=True) 21 | def assemble_cors_origins(cls, v: Union[str, List[str]]) -> Union[List[str], str]: 22 | if isinstance(v, str) and not v.startswith("["): 23 | return [i.strip() for i in v.split(",")] 24 | elif isinstance(v, (list, str)): 25 | return v 26 | raise ValueError(v) 27 | 28 | {% if cookiecutter.database == "Postgres" -%} 29 | POSTGRES_SERVER: str 30 | POSTGRES_USER: str 31 | POSTGRES_PASSWORD: str 32 | POSTGRES_DB: str 33 | DATABASE_URI: Optional[PostgresDsn] = None 34 | 35 | @validator("DATABASE_URI", pre=True) 36 | def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any: 37 | if isinstance(v, str): 38 | return v 39 | return PostgresDsn.build( 40 | scheme="postgresql", 41 | user=values.get("POSTGRES_USER"), 42 | password=values.get("POSTGRES_PASSWORD"), 43 | host=values.get("POSTGRES_SERVER"), 44 | path=f"/{values.get('POSTGRES_DB') or ''}", 45 | ) 46 | {% elif cookiecutter.database == "MySQL" -%} 47 | MYSQL_USER: str 48 | MYSQL_PASSWORD: str 49 | MYSQL_HOST: str 50 | MYSQL_PORT: str 51 | MYSQL_DATABASE: str 52 | DATABASE_URI: Optional[str] = None 53 | 54 | @validator("DATABASE_URI", pre=True) 55 | def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any: 56 | if isinstance(v, str): 57 | return v 58 | return f"mysql://{values.get('MYSQL_USER')}:{values.get('MYSQL_PASSWORD')}@{values.get('MYSQL_HOST')}:" \ 59 | f"{values.get('MYSQL_PORT')}/{values.get('MYSQL_DATABASE')}" 60 | {%- endif %} 61 | 62 | class Config: 63 | case_sensitive = True 64 | env_file = ".env" 65 | 66 | 67 | settings = Settings() 68 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/database.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.ext.declarative import as_declarative, declared_attr 3 | from sqlalchemy.orm import sessionmaker 4 | 5 | from app.core.config import settings 6 | 7 | engine = create_engine(settings.DATABASE_URI, pool_pre_ping=True) 8 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 9 | 10 | 11 | @as_declarative() 12 | class Base: 13 | 14 | @declared_attr 15 | def __tablename__(cls) -> str: 16 | return cls.__name__.lower() 17 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/main.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from fastapi.middleware.cors import CORSMiddleware 3 | 4 | from app.core.config import settings 5 | 6 | 7 | def get_application(): 8 | _app = FastAPI(title=settings.PROJECT_NAME) 9 | 10 | _app.add_middleware( 11 | CORSMiddleware, 12 | allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS], 13 | allow_credentials=True, 14 | allow_methods=["*"], 15 | allow_headers=["*"], 16 | ) 17 | 18 | return _app 19 | 20 | 21 | app = get_application() 22 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | app: 5 | build: . 6 | env_file: 7 | - .env 8 | ports: 9 | - "8000:8000" 10 | 11 | {% if cookiecutter.database == "Postgres" %} 12 | database: 13 | image: postgres:12 14 | env_file: 15 | - .env 16 | ports: 17 | - "5432:5432" 18 | {% elif cookiecutter.database == "MySQL" %} 19 | database: 20 | image: mysql:5.7 21 | env_file: 22 | - .env 23 | ports: 24 | - "3306:3306" 25 | {% endif %} 26 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "{{ cookiecutter.name }}" 3 | version = "0.1.0" 4 | description = "" 5 | {% if cookiecutter.username != None %}authors = ["{{ cookiecutter.username }} <{{ cookiecutter.email }}>"]{% endif %} 6 | 7 | [tool.poetry.dependencies] 8 | python = "^{{ cookiecutter.python }}" 9 | fastapi = "^{{ cookiecutter.fastapi }}" 10 | 11 | [tool.poetry.dev-dependencies] 12 | pytest = "^5.2" 13 | pytest-cov = "^2.10.1" 14 | {%- if cookiecutter.pre_commit == "True" %} 15 | autoflake = "^1.4" 16 | flake8 = "^3.8.4" 17 | mypy = "^0.790" 18 | isort = "^5.0" 19 | pre-commit = "^2.8.2" 20 | black = "^20.8b1" 21 | {%- endif %} 22 | 23 | [build-system] 24 | requires = ["poetry-core>=1.0.0"] 25 | build-backend = "poetry.core.masonry.api" 26 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi=={{ cookiecutter.fastapi }} 2 | uvicorn==0.12.2 3 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/setup.cfg: -------------------------------------------------------------------------------- 1 | [isort] 2 | profile = black 3 | known_first_party = app 4 | 5 | [flake8] 6 | max-complexity = 7 7 | statistics = True 8 | max-line-length = 88 9 | ignore = W503,E203 10 | per-file-ignores = 11 | __init__.py: F401 12 | 13 | [mypy] 14 | plugins = pydantic.mypy 15 | ignore_missing_imports = True 16 | follow_imports = skip 17 | strict_optional = True 18 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/tests/__init__.py -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/{{ cookiecutter.env }}: -------------------------------------------------------------------------------- 1 | PROJECT_NAME={{ cookiecutter.name }} 2 | BACKEND_CORS_ORIGINS=["http://localhost:8000", "https://localhost:8000", "http://localhost", "https://localhost"] 3 | 4 | {% if cookiecutter.database == "Postgres" %} 5 | POSTGRES_USER=postgres 6 | POSTGRES_PASSWORD=postgres 7 | POSTGRES_SERVER=database 8 | POSTGRES_DB=app 9 | {% elif cookiecutter.database == "MySQL" %} 10 | MYSQL_ROOT_PASSWORD=development 11 | MYSQL_USER=user 12 | MYSQL_PASSWORD=password 13 | MYSQL_HOST=database 14 | MYSQL_PORT=3306 15 | MYSQL_DATABASE=app 16 | {% endif %} 17 | -------------------------------------------------------------------------------- /manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/{{ cookiecutter.gitignore }}: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | 140 | # Text Editor 141 | .vscode 142 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "appdirs" 3 | version = "1.4.4" 4 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 5 | category = "dev" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "arrow" 11 | version = "1.1.1" 12 | description = "Better dates & times for Python" 13 | category = "main" 14 | optional = false 15 | python-versions = ">=3.6" 16 | 17 | [package.dependencies] 18 | python-dateutil = ">=2.7.0" 19 | typing-extensions = {version = "*", markers = "python_version < \"3.8\""} 20 | 21 | [[package]] 22 | name = "atomicwrites" 23 | version = "1.4.0" 24 | description = "Atomic file writes." 25 | category = "dev" 26 | optional = false 27 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 28 | 29 | [[package]] 30 | name = "attrs" 31 | version = "21.2.0" 32 | description = "Classes Without Boilerplate" 33 | category = "dev" 34 | optional = false 35 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 36 | 37 | [package.extras] 38 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] 39 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 40 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] 41 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] 42 | 43 | [[package]] 44 | name = "autoflake" 45 | version = "1.4" 46 | description = "Removes unused imports and unused variables" 47 | category = "dev" 48 | optional = false 49 | python-versions = "*" 50 | 51 | [package.dependencies] 52 | pyflakes = ">=1.1.0" 53 | 54 | [[package]] 55 | name = "backports.entry-points-selectable" 56 | version = "1.1.0" 57 | description = "Compatibility shim providing selectable entry points for older implementations" 58 | category = "dev" 59 | optional = false 60 | python-versions = ">=2.7" 61 | 62 | [package.dependencies] 63 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 64 | 65 | [package.extras] 66 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 67 | testing = ["pytest (>=4.6)", "pytest-flake8", "pytest-cov", "pytest-black (>=0.3.7)", "pytest-mypy", "pytest-checkdocs (>=2.4)", "pytest-enabler (>=1.0.1)"] 68 | 69 | [[package]] 70 | name = "binaryornot" 71 | version = "0.4.4" 72 | description = "Ultra-lightweight pure Python package to check if a file is binary or text." 73 | category = "main" 74 | optional = false 75 | python-versions = "*" 76 | 77 | [package.dependencies] 78 | chardet = ">=3.0.2" 79 | 80 | [[package]] 81 | name = "black" 82 | version = "20.8b1" 83 | description = "The uncompromising code formatter." 84 | category = "dev" 85 | optional = false 86 | python-versions = ">=3.6" 87 | 88 | [package.dependencies] 89 | appdirs = "*" 90 | click = ">=7.1.2" 91 | dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} 92 | mypy-extensions = ">=0.4.3" 93 | pathspec = ">=0.6,<1" 94 | regex = ">=2020.1.8" 95 | toml = ">=0.10.1" 96 | typed-ast = ">=1.4.0" 97 | typing-extensions = ">=3.7.4" 98 | 99 | [package.extras] 100 | colorama = ["colorama (>=0.4.3)"] 101 | d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] 102 | 103 | [[package]] 104 | name = "certifi" 105 | version = "2021.5.30" 106 | description = "Python package for providing Mozilla's CA Bundle." 107 | category = "main" 108 | optional = false 109 | python-versions = "*" 110 | 111 | [[package]] 112 | name = "cfgv" 113 | version = "3.3.1" 114 | description = "Validate configuration and produce human readable error messages." 115 | category = "dev" 116 | optional = false 117 | python-versions = ">=3.6.1" 118 | 119 | [[package]] 120 | name = "chardet" 121 | version = "4.0.0" 122 | description = "Universal encoding detector for Python 2 and 3" 123 | category = "main" 124 | optional = false 125 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 126 | 127 | [[package]] 128 | name = "charset-normalizer" 129 | version = "2.0.4" 130 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 131 | category = "main" 132 | optional = false 133 | python-versions = ">=3.5.0" 134 | 135 | [package.extras] 136 | unicode_backport = ["unicodedata2"] 137 | 138 | [[package]] 139 | name = "click" 140 | version = "8.0.1" 141 | description = "Composable command line interface toolkit" 142 | category = "main" 143 | optional = false 144 | python-versions = ">=3.6" 145 | 146 | [package.dependencies] 147 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 148 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 149 | 150 | [[package]] 151 | name = "colorama" 152 | version = "0.4.4" 153 | description = "Cross-platform colored terminal text." 154 | category = "main" 155 | optional = false 156 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 157 | 158 | [[package]] 159 | name = "cookiecutter" 160 | version = "1.7.3" 161 | description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." 162 | category = "main" 163 | optional = false 164 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 165 | 166 | [package.dependencies] 167 | binaryornot = ">=0.4.4" 168 | click = ">=7.0" 169 | Jinja2 = ">=2.7,<4.0.0" 170 | jinja2-time = ">=0.2.0" 171 | poyo = ">=0.5.0" 172 | python-slugify = ">=4.0.0" 173 | requests = ">=2.23.0" 174 | six = ">=1.10" 175 | 176 | [[package]] 177 | name = "coverage" 178 | version = "6.2" 179 | description = "Code coverage measurement for Python" 180 | category = "dev" 181 | optional = false 182 | python-versions = ">=3.6" 183 | 184 | [package.dependencies] 185 | tomli = {version = "*", optional = true, markers = "extra == \"toml\""} 186 | 187 | [package.extras] 188 | toml = ["tomli"] 189 | 190 | [[package]] 191 | name = "dataclasses" 192 | version = "0.8" 193 | description = "A backport of the dataclasses module for Python 3.6" 194 | category = "main" 195 | optional = false 196 | python-versions = ">=3.6, <3.7" 197 | 198 | [[package]] 199 | name = "distlib" 200 | version = "0.3.2" 201 | description = "Distribution utilities" 202 | category = "dev" 203 | optional = false 204 | python-versions = "*" 205 | 206 | [[package]] 207 | name = "dnspython" 208 | version = "2.1.0" 209 | description = "DNS toolkit" 210 | category = "main" 211 | optional = false 212 | python-versions = ">=3.6" 213 | 214 | [package.extras] 215 | dnssec = ["cryptography (>=2.6)"] 216 | doh = ["requests", "requests-toolbelt"] 217 | idna = ["idna (>=2.1)"] 218 | curio = ["curio (>=1.2)", "sniffio (>=1.1)"] 219 | trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"] 220 | 221 | [[package]] 222 | name = "email-validator" 223 | version = "1.1.3" 224 | description = "A robust email syntax and deliverability validation library for Python 2.x/3.x." 225 | category = "main" 226 | optional = false 227 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 228 | 229 | [package.dependencies] 230 | dnspython = ">=1.15.0" 231 | idna = ">=2.0.0" 232 | 233 | [[package]] 234 | name = "filelock" 235 | version = "3.0.12" 236 | description = "A platform independent file lock." 237 | category = "dev" 238 | optional = false 239 | python-versions = "*" 240 | 241 | [[package]] 242 | name = "flake8" 243 | version = "4.0.1" 244 | description = "the modular source code checker: pep8 pyflakes and co" 245 | category = "dev" 246 | optional = false 247 | python-versions = ">=3.6" 248 | 249 | [package.dependencies] 250 | importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} 251 | mccabe = ">=0.6.0,<0.7.0" 252 | pycodestyle = ">=2.8.0,<2.9.0" 253 | pyflakes = ">=2.4.0,<2.5.0" 254 | 255 | [[package]] 256 | name = "identify" 257 | version = "2.2.13" 258 | description = "File identification library for Python" 259 | category = "dev" 260 | optional = false 261 | python-versions = ">=3.6.1" 262 | 263 | [package.extras] 264 | license = ["editdistance-s"] 265 | 266 | [[package]] 267 | name = "idna" 268 | version = "3.2" 269 | description = "Internationalized Domain Names in Applications (IDNA)" 270 | category = "main" 271 | optional = false 272 | python-versions = ">=3.5" 273 | 274 | [[package]] 275 | name = "importlib-metadata" 276 | version = "4.2.0" 277 | description = "Read metadata from Python packages" 278 | category = "main" 279 | optional = false 280 | python-versions = ">=3.6" 281 | 282 | [package.dependencies] 283 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 284 | zipp = ">=0.5" 285 | 286 | [package.extras] 287 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 288 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] 289 | 290 | [[package]] 291 | name = "importlib-resources" 292 | version = "5.2.2" 293 | description = "Read resources from Python packages" 294 | category = "dev" 295 | optional = false 296 | python-versions = ">=3.6" 297 | 298 | [package.dependencies] 299 | zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} 300 | 301 | [package.extras] 302 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 303 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] 304 | 305 | [[package]] 306 | name = "iniconfig" 307 | version = "1.1.1" 308 | description = "iniconfig: brain-dead simple config-ini parsing" 309 | category = "dev" 310 | optional = false 311 | python-versions = "*" 312 | 313 | [[package]] 314 | name = "isort" 315 | version = "5.10.1" 316 | description = "A Python utility / library to sort Python imports." 317 | category = "dev" 318 | optional = false 319 | python-versions = ">=3.6.1,<4.0" 320 | 321 | [package.extras] 322 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] 323 | requirements_deprecated_finder = ["pipreqs", "pip-api"] 324 | colors = ["colorama (>=0.4.3,<0.5.0)"] 325 | plugins = ["setuptools"] 326 | 327 | [[package]] 328 | name = "jinja2" 329 | version = "3.0.1" 330 | description = "A very fast and expressive template engine." 331 | category = "main" 332 | optional = false 333 | python-versions = ">=3.6" 334 | 335 | [package.dependencies] 336 | MarkupSafe = ">=2.0" 337 | 338 | [package.extras] 339 | i18n = ["Babel (>=2.7)"] 340 | 341 | [[package]] 342 | name = "jinja2-time" 343 | version = "0.2.0" 344 | description = "Jinja2 Extension for Dates and Times" 345 | category = "main" 346 | optional = false 347 | python-versions = "*" 348 | 349 | [package.dependencies] 350 | arrow = "*" 351 | jinja2 = "*" 352 | 353 | [[package]] 354 | name = "markupsafe" 355 | version = "2.0.1" 356 | description = "Safely add untrusted strings to HTML/XML markup." 357 | category = "main" 358 | optional = false 359 | python-versions = ">=3.6" 360 | 361 | [[package]] 362 | name = "mccabe" 363 | version = "0.6.1" 364 | description = "McCabe checker, plugin for flake8" 365 | category = "dev" 366 | optional = false 367 | python-versions = "*" 368 | 369 | [[package]] 370 | name = "mypy" 371 | version = "0.790" 372 | description = "Optional static typing for Python" 373 | category = "dev" 374 | optional = false 375 | python-versions = ">=3.5" 376 | 377 | [package.dependencies] 378 | mypy-extensions = ">=0.4.3,<0.5.0" 379 | typed-ast = ">=1.4.0,<1.5.0" 380 | typing-extensions = ">=3.7.4" 381 | 382 | [package.extras] 383 | dmypy = ["psutil (>=4.0)"] 384 | 385 | [[package]] 386 | name = "mypy-extensions" 387 | version = "0.4.3" 388 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 389 | category = "dev" 390 | optional = false 391 | python-versions = "*" 392 | 393 | [[package]] 394 | name = "nodeenv" 395 | version = "1.6.0" 396 | description = "Node.js virtual environment builder" 397 | category = "dev" 398 | optional = false 399 | python-versions = "*" 400 | 401 | [[package]] 402 | name = "packaging" 403 | version = "21.0" 404 | description = "Core utilities for Python packages" 405 | category = "dev" 406 | optional = false 407 | python-versions = ">=3.6" 408 | 409 | [package.dependencies] 410 | pyparsing = ">=2.0.2" 411 | 412 | [[package]] 413 | name = "pathspec" 414 | version = "0.9.0" 415 | description = "Utility library for gitignore style pattern matching of file paths." 416 | category = "dev" 417 | optional = false 418 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 419 | 420 | [[package]] 421 | name = "platformdirs" 422 | version = "2.3.0" 423 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 424 | category = "dev" 425 | optional = false 426 | python-versions = ">=3.6" 427 | 428 | [package.extras] 429 | docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] 430 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 431 | 432 | [[package]] 433 | name = "pluggy" 434 | version = "0.13.1" 435 | description = "plugin and hook calling mechanisms for python" 436 | category = "dev" 437 | optional = false 438 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 439 | 440 | [package.dependencies] 441 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 442 | 443 | [package.extras] 444 | dev = ["pre-commit", "tox"] 445 | 446 | [[package]] 447 | name = "poyo" 448 | version = "0.5.0" 449 | description = "A lightweight YAML Parser for Python. 🐓" 450 | category = "main" 451 | optional = false 452 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 453 | 454 | [[package]] 455 | name = "pre-commit" 456 | version = "2.16.0" 457 | description = "A framework for managing and maintaining multi-language pre-commit hooks." 458 | category = "dev" 459 | optional = false 460 | python-versions = ">=3.6.1" 461 | 462 | [package.dependencies] 463 | cfgv = ">=2.0.0" 464 | identify = ">=1.0.0" 465 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 466 | importlib-resources = {version = "<5.3", markers = "python_version < \"3.7\""} 467 | nodeenv = ">=0.11.1" 468 | pyyaml = ">=5.1" 469 | toml = "*" 470 | virtualenv = ">=20.0.8" 471 | 472 | [[package]] 473 | name = "prompt-toolkit" 474 | version = "3.0.19" 475 | description = "Library for building powerful interactive command lines in Python" 476 | category = "main" 477 | optional = false 478 | python-versions = ">=3.6.1" 479 | 480 | [package.dependencies] 481 | wcwidth = "*" 482 | 483 | [[package]] 484 | name = "py" 485 | version = "1.10.0" 486 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 487 | category = "dev" 488 | optional = false 489 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 490 | 491 | [[package]] 492 | name = "pycodestyle" 493 | version = "2.8.0" 494 | description = "Python style guide checker" 495 | category = "dev" 496 | optional = false 497 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 498 | 499 | [[package]] 500 | name = "pydantic" 501 | version = "1.8.2" 502 | description = "Data validation and settings management using python 3.6 type hinting" 503 | category = "main" 504 | optional = false 505 | python-versions = ">=3.6.1" 506 | 507 | [package.dependencies] 508 | dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} 509 | email-validator = {version = ">=1.0.3", optional = true, markers = "extra == \"email\""} 510 | typing-extensions = ">=3.7.4.3" 511 | 512 | [package.extras] 513 | dotenv = ["python-dotenv (>=0.10.4)"] 514 | email = ["email-validator (>=1.0.3)"] 515 | 516 | [[package]] 517 | name = "pyflakes" 518 | version = "2.4.0" 519 | description = "passive checker of Python programs" 520 | category = "dev" 521 | optional = false 522 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 523 | 524 | [[package]] 525 | name = "pyparsing" 526 | version = "2.4.7" 527 | description = "Python parsing module" 528 | category = "dev" 529 | optional = false 530 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 531 | 532 | [[package]] 533 | name = "pytest" 534 | version = "6.2.5" 535 | description = "pytest: simple powerful testing with Python" 536 | category = "dev" 537 | optional = false 538 | python-versions = ">=3.6" 539 | 540 | [package.dependencies] 541 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 542 | attrs = ">=19.2.0" 543 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 544 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 545 | iniconfig = "*" 546 | packaging = "*" 547 | pluggy = ">=0.12,<2.0" 548 | py = ">=1.8.2" 549 | toml = "*" 550 | 551 | [package.extras] 552 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 553 | 554 | [[package]] 555 | name = "pytest-cov" 556 | version = "3.0.0" 557 | description = "Pytest plugin for measuring coverage." 558 | category = "dev" 559 | optional = false 560 | python-versions = ">=3.6" 561 | 562 | [package.dependencies] 563 | coverage = {version = ">=5.2.1", extras = ["toml"]} 564 | pytest = ">=4.6" 565 | 566 | [package.extras] 567 | testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] 568 | 569 | [[package]] 570 | name = "python-dateutil" 571 | version = "2.8.2" 572 | description = "Extensions to the standard Python datetime module" 573 | category = "main" 574 | optional = false 575 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 576 | 577 | [package.dependencies] 578 | six = ">=1.5" 579 | 580 | [[package]] 581 | name = "python-slugify" 582 | version = "5.0.2" 583 | description = "A Python Slugify application that handles Unicode" 584 | category = "main" 585 | optional = false 586 | python-versions = ">=3.6" 587 | 588 | [package.dependencies] 589 | text-unidecode = ">=1.3" 590 | 591 | [package.extras] 592 | unidecode = ["Unidecode (>=1.1.1)"] 593 | 594 | [[package]] 595 | name = "pyyaml" 596 | version = "5.4.1" 597 | description = "YAML parser and emitter for Python" 598 | category = "dev" 599 | optional = false 600 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 601 | 602 | [[package]] 603 | name = "questionary" 604 | version = "1.10.0" 605 | description = "Python library to build pretty command line user prompts ⭐️" 606 | category = "main" 607 | optional = false 608 | python-versions = ">=3.6,<4.0" 609 | 610 | [package.dependencies] 611 | prompt_toolkit = ">=2.0,<4.0" 612 | 613 | [package.extras] 614 | docs = ["Sphinx (>=3.3,<4.0)", "sphinx-rtd-theme (>=0.5.0,<0.6.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphinx-copybutton (>=0.3.1,<0.4.0)", "sphinx-autodoc-typehints (>=1.11.1,<2.0.0)"] 615 | 616 | [[package]] 617 | name = "regex" 618 | version = "2021.8.28" 619 | description = "Alternative regular expression module, to replace re." 620 | category = "dev" 621 | optional = false 622 | python-versions = "*" 623 | 624 | [[package]] 625 | name = "requests" 626 | version = "2.26.0" 627 | description = "Python HTTP for Humans." 628 | category = "main" 629 | optional = false 630 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 631 | 632 | [package.dependencies] 633 | certifi = ">=2017.4.17" 634 | charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} 635 | idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} 636 | urllib3 = ">=1.21.1,<1.27" 637 | 638 | [package.extras] 639 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 640 | use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] 641 | 642 | [[package]] 643 | name = "six" 644 | version = "1.16.0" 645 | description = "Python 2 and 3 compatibility utilities" 646 | category = "main" 647 | optional = false 648 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 649 | 650 | [[package]] 651 | name = "text-unidecode" 652 | version = "1.3" 653 | description = "The most basic Text::Unidecode port" 654 | category = "main" 655 | optional = false 656 | python-versions = "*" 657 | 658 | [[package]] 659 | name = "toml" 660 | version = "0.10.2" 661 | description = "Python Library for Tom's Obvious, Minimal Language" 662 | category = "dev" 663 | optional = false 664 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 665 | 666 | [[package]] 667 | name = "tomli" 668 | version = "1.2.2" 669 | description = "A lil' TOML parser" 670 | category = "dev" 671 | optional = false 672 | python-versions = ">=3.6" 673 | 674 | [[package]] 675 | name = "typed-ast" 676 | version = "1.4.3" 677 | description = "a fork of Python 2 and 3 ast modules with type comment support" 678 | category = "dev" 679 | optional = false 680 | python-versions = "*" 681 | 682 | [[package]] 683 | name = "typer" 684 | version = "0.4.0" 685 | description = "Typer, build great CLIs. Easy to code. Based on Python type hints." 686 | category = "main" 687 | optional = false 688 | python-versions = ">=3.6" 689 | 690 | [package.dependencies] 691 | click = ">=7.1.1,<9.0.0" 692 | 693 | [package.extras] 694 | all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] 695 | dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] 696 | doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)"] 697 | test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=19.10b0,<20.0b0)", "isort (>=5.0.6,<6.0.0)"] 698 | 699 | [[package]] 700 | name = "typing-extensions" 701 | version = "3.10.0.2" 702 | description = "Backported and Experimental Type Hints for Python 3.5+" 703 | category = "main" 704 | optional = false 705 | python-versions = "*" 706 | 707 | [[package]] 708 | name = "urllib3" 709 | version = "1.26.6" 710 | description = "HTTP library with thread-safe connection pooling, file post, and more." 711 | category = "main" 712 | optional = false 713 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 714 | 715 | [package.extras] 716 | brotli = ["brotlipy (>=0.6.0)"] 717 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 718 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 719 | 720 | [[package]] 721 | name = "virtualenv" 722 | version = "20.7.2" 723 | description = "Virtual Python Environment builder" 724 | category = "dev" 725 | optional = false 726 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 727 | 728 | [package.dependencies] 729 | "backports.entry-points-selectable" = ">=1.0.4" 730 | distlib = ">=0.3.1,<1" 731 | filelock = ">=3.0.0,<4" 732 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 733 | importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} 734 | platformdirs = ">=2,<3" 735 | six = ">=1.9.0,<2" 736 | 737 | [package.extras] 738 | docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] 739 | testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] 740 | 741 | [[package]] 742 | name = "wcwidth" 743 | version = "0.2.5" 744 | description = "Measures the displayed width of unicode strings in a terminal" 745 | category = "main" 746 | optional = false 747 | python-versions = "*" 748 | 749 | [[package]] 750 | name = "zipp" 751 | version = "3.5.0" 752 | description = "Backport of pathlib-compatible object wrapper for zip files" 753 | category = "main" 754 | optional = false 755 | python-versions = ">=3.6" 756 | 757 | [package.extras] 758 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 759 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] 760 | 761 | [metadata] 762 | lock-version = "1.1" 763 | python-versions = "^3.6.1" 764 | content-hash = "38a90a2c0ecbb4dc455cba579120a318b53157176abb1e3d94cf5d5c7b535249" 765 | 766 | [metadata.files] 767 | appdirs = [ 768 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, 769 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, 770 | ] 771 | arrow = [ 772 | {file = "arrow-1.1.1-py3-none-any.whl", hash = "sha256:77a60a4db5766d900a2085ce9074c5c7b8e2c99afeaa98ad627637ff6f292510"}, 773 | {file = "arrow-1.1.1.tar.gz", hash = "sha256:dee7602f6c60e3ec510095b5e301441bc56288cb8f51def14dcb3079f623823a"}, 774 | ] 775 | atomicwrites = [ 776 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 777 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 778 | ] 779 | attrs = [ 780 | {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, 781 | {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, 782 | ] 783 | autoflake = [ 784 | {file = "autoflake-1.4.tar.gz", hash = "sha256:61a353012cff6ab94ca062823d1fb2f692c4acda51c76ff83a8d77915fba51ea"}, 785 | ] 786 | "backports.entry-points-selectable" = [ 787 | {file = "backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl", hash = "sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc"}, 788 | {file = "backports.entry_points_selectable-1.1.0.tar.gz", hash = "sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a"}, 789 | ] 790 | binaryornot = [ 791 | {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, 792 | {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, 793 | ] 794 | black = [ 795 | {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, 796 | ] 797 | certifi = [ 798 | {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, 799 | {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, 800 | ] 801 | cfgv = [ 802 | {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, 803 | {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, 804 | ] 805 | chardet = [ 806 | {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, 807 | {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, 808 | ] 809 | charset-normalizer = [ 810 | {file = "charset-normalizer-2.0.4.tar.gz", hash = "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3"}, 811 | {file = "charset_normalizer-2.0.4-py3-none-any.whl", hash = "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b"}, 812 | ] 813 | click = [ 814 | {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, 815 | {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, 816 | ] 817 | colorama = [ 818 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 819 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 820 | ] 821 | cookiecutter = [ 822 | {file = "cookiecutter-1.7.3-py2.py3-none-any.whl", hash = "sha256:f8671531fa96ab14339d0c59b4f662a4f12a2ecacd94a0f70a3500843da588e2"}, 823 | {file = "cookiecutter-1.7.3.tar.gz", hash = "sha256:6b9a4d72882e243be077a7397d0f1f76fe66cf3df91f3115dbb5330e214fa457"}, 824 | ] 825 | coverage = [ 826 | {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, 827 | {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, 828 | {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, 829 | {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, 830 | {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, 831 | {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, 832 | {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, 833 | {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, 834 | {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, 835 | {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, 836 | {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, 837 | {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, 838 | {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, 839 | {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, 840 | {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, 841 | {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, 842 | {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, 843 | {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, 844 | {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, 845 | {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, 846 | {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, 847 | {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, 848 | {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, 849 | {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, 850 | {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, 851 | {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, 852 | {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, 853 | {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, 854 | {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, 855 | {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, 856 | {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, 857 | {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, 858 | {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, 859 | {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, 860 | {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, 861 | {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, 862 | {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, 863 | {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, 864 | {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, 865 | {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, 866 | {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, 867 | {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, 868 | {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, 869 | {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, 870 | {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, 871 | {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, 872 | {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, 873 | ] 874 | dataclasses = [ 875 | {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, 876 | {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, 877 | ] 878 | distlib = [ 879 | {file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"}, 880 | {file = "distlib-0.3.2.zip", hash = "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736"}, 881 | ] 882 | dnspython = [ 883 | {file = "dnspython-2.1.0-py3-none-any.whl", hash = "sha256:95d12f6ef0317118d2a1a6fc49aac65ffec7eb8087474158f42f26a639135216"}, 884 | {file = "dnspython-2.1.0.zip", hash = "sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"}, 885 | ] 886 | email-validator = [ 887 | {file = "email_validator-1.1.3-py2.py3-none-any.whl", hash = "sha256:5675c8ceb7106a37e40e2698a57c056756bf3f272cfa8682a4f87ebd95d8440b"}, 888 | {file = "email_validator-1.1.3.tar.gz", hash = "sha256:aa237a65f6f4da067119b7df3f13e89c25c051327b2b5b66dc075f33d62480d7"}, 889 | ] 890 | filelock = [ 891 | {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, 892 | {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, 893 | ] 894 | flake8 = [ 895 | {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, 896 | {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, 897 | ] 898 | identify = [ 899 | {file = "identify-2.2.13-py2.py3-none-any.whl", hash = "sha256:7199679b5be13a6b40e6e19ea473e789b11b4e3b60986499b1f589ffb03c217c"}, 900 | {file = "identify-2.2.13.tar.gz", hash = "sha256:7bc6e829392bd017236531963d2d937d66fc27cadc643ac0aba2ce9f26157c79"}, 901 | ] 902 | idna = [ 903 | {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, 904 | {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, 905 | ] 906 | importlib-metadata = [ 907 | {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, 908 | {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, 909 | ] 910 | importlib-resources = [ 911 | {file = "importlib_resources-5.2.2-py3-none-any.whl", hash = "sha256:2480d8e07d1890056cb53c96e3de44fead9c62f2ba949b0f2e4c4345f4afa977"}, 912 | {file = "importlib_resources-5.2.2.tar.gz", hash = "sha256:a65882a4d0fe5fbf702273456ba2ce74fe44892c25e42e057aca526b702a6d4b"}, 913 | ] 914 | iniconfig = [ 915 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 916 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 917 | ] 918 | isort = [ 919 | {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, 920 | {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, 921 | ] 922 | jinja2 = [ 923 | {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, 924 | {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"}, 925 | ] 926 | jinja2-time = [ 927 | {file = "jinja2-time-0.2.0.tar.gz", hash = "sha256:d14eaa4d315e7688daa4969f616f226614350c48730bfa1692d2caebd8c90d40"}, 928 | {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa"}, 929 | ] 930 | markupsafe = [ 931 | {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, 932 | {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, 933 | {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, 934 | {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, 935 | {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"}, 936 | {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, 937 | {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, 938 | {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, 939 | {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, 940 | {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, 941 | {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, 942 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, 943 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, 944 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, 945 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, 946 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, 947 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, 948 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, 949 | {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"}, 950 | {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, 951 | {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, 952 | {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, 953 | {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, 954 | {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, 955 | {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, 956 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, 957 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, 958 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, 959 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, 960 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, 961 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, 962 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, 963 | {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"}, 964 | {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, 965 | {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, 966 | {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, 967 | {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, 968 | {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, 969 | {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, 970 | {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, 971 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, 972 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, 973 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, 974 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, 975 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, 976 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, 977 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, 978 | {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"}, 979 | {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, 980 | {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, 981 | {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, 982 | {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, 983 | {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, 984 | {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, 985 | {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, 986 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, 987 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, 988 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, 989 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, 990 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, 991 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, 992 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, 993 | {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"}, 994 | {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, 995 | {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, 996 | {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, 997 | {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, 998 | {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, 999 | {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, 1000 | ] 1001 | mccabe = [ 1002 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 1003 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 1004 | ] 1005 | mypy = [ 1006 | {file = "mypy-0.790-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669"}, 1007 | {file = "mypy-0.790-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802"}, 1008 | {file = "mypy-0.790-cp35-cp35m-win_amd64.whl", hash = "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de"}, 1009 | {file = "mypy-0.790-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1"}, 1010 | {file = "mypy-0.790-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc"}, 1011 | {file = "mypy-0.790-cp36-cp36m-win_amd64.whl", hash = "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7"}, 1012 | {file = "mypy-0.790-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"}, 1013 | {file = "mypy-0.790-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178"}, 1014 | {file = "mypy-0.790-cp37-cp37m-win_amd64.whl", hash = "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324"}, 1015 | {file = "mypy-0.790-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01"}, 1016 | {file = "mypy-0.790-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666"}, 1017 | {file = "mypy-0.790-cp38-cp38-win_amd64.whl", hash = "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea"}, 1018 | {file = "mypy-0.790-py3-none-any.whl", hash = "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122"}, 1019 | {file = "mypy-0.790.tar.gz", hash = "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975"}, 1020 | ] 1021 | mypy-extensions = [ 1022 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 1023 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 1024 | ] 1025 | nodeenv = [ 1026 | {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"}, 1027 | {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"}, 1028 | ] 1029 | packaging = [ 1030 | {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, 1031 | {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, 1032 | ] 1033 | pathspec = [ 1034 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 1035 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 1036 | ] 1037 | platformdirs = [ 1038 | {file = "platformdirs-2.3.0-py3-none-any.whl", hash = "sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648"}, 1039 | {file = "platformdirs-2.3.0.tar.gz", hash = "sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f"}, 1040 | ] 1041 | pluggy = [ 1042 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 1043 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 1044 | ] 1045 | poyo = [ 1046 | {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"}, 1047 | {file = "poyo-0.5.0.tar.gz", hash = "sha256:e26956aa780c45f011ca9886f044590e2d8fd8b61db7b1c1cf4e0869f48ed4dd"}, 1048 | ] 1049 | pre-commit = [ 1050 | {file = "pre_commit-2.16.0-py2.py3-none-any.whl", hash = "sha256:758d1dc9b62c2ed8881585c254976d66eae0889919ab9b859064fc2fe3c7743e"}, 1051 | {file = "pre_commit-2.16.0.tar.gz", hash = "sha256:fe9897cac830aa7164dbd02a4e7b90cae49630451ce88464bca73db486ba9f65"}, 1052 | ] 1053 | prompt-toolkit = [ 1054 | {file = "prompt_toolkit-3.0.19-py3-none-any.whl", hash = "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88"}, 1055 | {file = "prompt_toolkit-3.0.19.tar.gz", hash = "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f"}, 1056 | ] 1057 | py = [ 1058 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, 1059 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, 1060 | ] 1061 | pycodestyle = [ 1062 | {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, 1063 | {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, 1064 | ] 1065 | pydantic = [ 1066 | {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, 1067 | {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, 1068 | {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, 1069 | {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, 1070 | {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, 1071 | {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, 1072 | {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, 1073 | {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, 1074 | {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, 1075 | {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, 1076 | {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, 1077 | {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, 1078 | {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, 1079 | {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, 1080 | {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, 1081 | {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, 1082 | {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, 1083 | {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, 1084 | {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, 1085 | {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, 1086 | {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, 1087 | {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, 1088 | ] 1089 | pyflakes = [ 1090 | {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, 1091 | {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, 1092 | ] 1093 | pyparsing = [ 1094 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 1095 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 1096 | ] 1097 | pytest = [ 1098 | {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, 1099 | {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, 1100 | ] 1101 | pytest-cov = [ 1102 | {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, 1103 | {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, 1104 | ] 1105 | python-dateutil = [ 1106 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 1107 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 1108 | ] 1109 | python-slugify = [ 1110 | {file = "python-slugify-5.0.2.tar.gz", hash = "sha256:f13383a0b9fcbe649a1892b9c8eb4f8eab1d6d84b84bb7a624317afa98159cab"}, 1111 | {file = "python_slugify-5.0.2-py2.py3-none-any.whl", hash = "sha256:6d8c5df75cd4a7c3a2d21e257633de53f52ab0265cd2d1dc62a730e8194a7380"}, 1112 | ] 1113 | pyyaml = [ 1114 | {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, 1115 | {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, 1116 | {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, 1117 | {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, 1118 | {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, 1119 | {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, 1120 | {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, 1121 | {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, 1122 | {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, 1123 | {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, 1124 | {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, 1125 | {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, 1126 | {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, 1127 | {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, 1128 | {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, 1129 | {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, 1130 | {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, 1131 | {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, 1132 | {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, 1133 | {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, 1134 | {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, 1135 | {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, 1136 | {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, 1137 | {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, 1138 | {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, 1139 | {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, 1140 | {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, 1141 | {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, 1142 | {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, 1143 | ] 1144 | questionary = [ 1145 | {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, 1146 | {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, 1147 | ] 1148 | regex = [ 1149 | {file = "regex-2021.8.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d05ad5367c90814099000442b2125535e9d77581855b9bee8780f1b41f2b1a2"}, 1150 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3bf1bc02bc421047bfec3343729c4bbbea42605bcfd6d6bfe2c07ade8b12d2a"}, 1151 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f6a808044faae658f546dd5f525e921de9fa409de7a5570865467f03a626fc0"}, 1152 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a617593aeacc7a691cc4af4a4410031654f2909053bd8c8e7db837f179a630eb"}, 1153 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79aef6b5cd41feff359acaf98e040844613ff5298d0d19c455b3d9ae0bc8c35a"}, 1154 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0fc1f8f06977c2d4f5e3d3f0d4a08089be783973fc6b6e278bde01f0544ff308"}, 1155 | {file = "regex-2021.8.28-cp310-cp310-win32.whl", hash = "sha256:6eebf512aa90751d5ef6a7c2ac9d60113f32e86e5687326a50d7686e309f66ed"}, 1156 | {file = "regex-2021.8.28-cp310-cp310-win_amd64.whl", hash = "sha256:ac88856a8cbccfc14f1b2d0b829af354cc1743cb375e7f04251ae73b2af6adf8"}, 1157 | {file = "regex-2021.8.28-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c206587c83e795d417ed3adc8453a791f6d36b67c81416676cad053b4104152c"}, 1158 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8690ed94481f219a7a967c118abaf71ccc440f69acd583cab721b90eeedb77c"}, 1159 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328a1fad67445550b982caa2a2a850da5989fd6595e858f02d04636e7f8b0b13"}, 1160 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c7cb4c512d2d3b0870e00fbbac2f291d4b4bf2634d59a31176a87afe2777c6f0"}, 1161 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66256b6391c057305e5ae9209941ef63c33a476b73772ca967d4a2df70520ec1"}, 1162 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e44769068d33e0ea6ccdf4b84d80c5afffe5207aa4d1881a629cf0ef3ec398f"}, 1163 | {file = "regex-2021.8.28-cp36-cp36m-win32.whl", hash = "sha256:08d74bfaa4c7731b8dac0a992c63673a2782758f7cfad34cf9c1b9184f911354"}, 1164 | {file = "regex-2021.8.28-cp36-cp36m-win_amd64.whl", hash = "sha256:abb48494d88e8a82601af905143e0de838c776c1241d92021e9256d5515b3645"}, 1165 | {file = "regex-2021.8.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b4c220a1fe0d2c622493b0a1fd48f8f991998fb447d3cd368033a4b86cf1127a"}, 1166 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a332404baa6665b54e5d283b4262f41f2103c255897084ec8f5487ce7b9e8e"}, 1167 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c61dcc1cf9fd165127a2853e2c31eb4fb961a4f26b394ac9fe5669c7a6592892"}, 1168 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ee329d0387b5b41a5dddbb6243a21cb7896587a651bebb957e2d2bb8b63c0791"}, 1169 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f60667673ff9c249709160529ab39667d1ae9fd38634e006bec95611f632e759"}, 1170 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b844fb09bd9936ed158ff9df0ab601e2045b316b17aa8b931857365ea8586906"}, 1171 | {file = "regex-2021.8.28-cp37-cp37m-win32.whl", hash = "sha256:4cde065ab33bcaab774d84096fae266d9301d1a2f5519d7bd58fc55274afbf7a"}, 1172 | {file = "regex-2021.8.28-cp37-cp37m-win_amd64.whl", hash = "sha256:1413b5022ed6ac0d504ba425ef02549a57d0f4276de58e3ab7e82437892704fc"}, 1173 | {file = "regex-2021.8.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ed4b50355b066796dacdd1cf538f2ce57275d001838f9b132fab80b75e8c84dd"}, 1174 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28fc475f560d8f67cc8767b94db4c9440210f6958495aeae70fac8faec631797"}, 1175 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdc178caebd0f338d57ae445ef8e9b737ddf8fbc3ea187603f65aec5b041248f"}, 1176 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:999ad08220467b6ad4bd3dd34e65329dd5d0df9b31e47106105e407954965256"}, 1177 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:808ee5834e06f57978da3e003ad9d6292de69d2bf6263662a1a8ae30788e080b"}, 1178 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d5111d4c843d80202e62b4fdbb4920db1dcee4f9366d6b03294f45ed7b18b42e"}, 1179 | {file = "regex-2021.8.28-cp38-cp38-win32.whl", hash = "sha256:473858730ef6d6ff7f7d5f19452184cd0caa062a20047f6d6f3e135a4648865d"}, 1180 | {file = "regex-2021.8.28-cp38-cp38-win_amd64.whl", hash = "sha256:31a99a4796bf5aefc8351e98507b09e1b09115574f7c9dbb9cf2111f7220d2e2"}, 1181 | {file = "regex-2021.8.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:04f6b9749e335bb0d2f68c707f23bb1773c3fb6ecd10edf0f04df12a8920d468"}, 1182 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b006628fe43aa69259ec04ca258d88ed19b64791693df59c422b607b6ece8bb"}, 1183 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:121f4b3185feaade3f85f70294aef3f777199e9b5c0c0245c774ae884b110a2d"}, 1184 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a577a21de2ef8059b58f79ff76a4da81c45a75fe0bfb09bc8b7bb4293fa18983"}, 1185 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1743345e30917e8c574f273f51679c294effba6ad372db1967852f12c76759d8"}, 1186 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e1e8406b895aba6caa63d9fd1b6b1700d7e4825f78ccb1e5260551d168db38ed"}, 1187 | {file = "regex-2021.8.28-cp39-cp39-win32.whl", hash = "sha256:ed283ab3a01d8b53de3a05bfdf4473ae24e43caee7dcb5584e86f3f3e5ab4374"}, 1188 | {file = "regex-2021.8.28-cp39-cp39-win_amd64.whl", hash = "sha256:610b690b406653c84b7cb6091facb3033500ee81089867ee7d59e675f9ca2b73"}, 1189 | {file = "regex-2021.8.28.tar.gz", hash = "sha256:f585cbbeecb35f35609edccb95efd95a3e35824cd7752b586503f7e6087303f1"}, 1190 | ] 1191 | requests = [ 1192 | {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, 1193 | {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, 1194 | ] 1195 | six = [ 1196 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1197 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1198 | ] 1199 | text-unidecode = [ 1200 | {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, 1201 | {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, 1202 | ] 1203 | toml = [ 1204 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1205 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1206 | ] 1207 | tomli = [ 1208 | {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, 1209 | {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, 1210 | ] 1211 | typed-ast = [ 1212 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, 1213 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, 1214 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, 1215 | {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, 1216 | {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, 1217 | {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, 1218 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, 1219 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, 1220 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, 1221 | {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, 1222 | {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, 1223 | {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, 1224 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, 1225 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, 1226 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, 1227 | {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, 1228 | {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, 1229 | {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, 1230 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, 1231 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, 1232 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, 1233 | {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, 1234 | {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, 1235 | {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, 1236 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, 1237 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, 1238 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, 1239 | {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, 1240 | {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, 1241 | {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, 1242 | ] 1243 | typer = [ 1244 | {file = "typer-0.4.0-py3-none-any.whl", hash = "sha256:d81169725140423d072df464cad1ff25ee154ef381aaf5b8225352ea187ca338"}, 1245 | {file = "typer-0.4.0.tar.gz", hash = "sha256:63c3aeab0549750ffe40da79a1b524f60e08a2cbc3126c520ebf2eeaf507f5dd"}, 1246 | ] 1247 | typing-extensions = [ 1248 | {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, 1249 | {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, 1250 | {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, 1251 | ] 1252 | urllib3 = [ 1253 | {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, 1254 | {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, 1255 | ] 1256 | virtualenv = [ 1257 | {file = "virtualenv-20.7.2-py2.py3-none-any.whl", hash = "sha256:e4670891b3a03eb071748c569a87cceaefbf643c5bac46d996c5a45c34aa0f06"}, 1258 | {file = "virtualenv-20.7.2.tar.gz", hash = "sha256:9ef4e8ee4710826e98ff3075c9a4739e2cb1040de6a2a8d35db0055840dc96a0"}, 1259 | ] 1260 | wcwidth = [ 1261 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, 1262 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, 1263 | ] 1264 | zipp = [ 1265 | {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"}, 1266 | {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"}, 1267 | ] 1268 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | authors = [ 3 | "ycd ", 4 | "Marcelo Trylesinski ", 5 | ] 6 | description = "Managing FastAPI projects made easy." 7 | homepage = "https://github.com/ycd/manage-fastapi" 8 | license = "MIT" 9 | name = "manage-fastapi" 10 | readme = "README.md" 11 | repository = "https://github.com/ycd/manage-fastapi" 12 | version = "1.1.1" 13 | 14 | classifiers = [ 15 | "Topic :: Software Development :: Build Tools", 16 | "Topic :: Software Development :: Libraries :: Python Modules", 17 | "Intended Audience :: Developers", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | "Programming Language :: Python :: 3", 21 | "Programming Language :: Python", 22 | "Programming Language :: Python :: 3 :: Only", 23 | "Programming Language :: Python :: 3.6", 24 | "Programming Language :: Python :: 3.7", 25 | "Programming Language :: Python :: 3.8", 26 | "Programming Language :: Python :: 3.9", 27 | ] 28 | 29 | [tool.poetry.dependencies] 30 | cookiecutter = "^1.7.2" 31 | pydantic = {extras = ["email"], version = "^1.7.2"} 32 | python = "^3.6.1" 33 | questionary = "^1.10.0" 34 | typer = "^0.4.0" 35 | 36 | [tool.poetry.dev-dependencies] 37 | autoflake = "^1.4" 38 | black = "^20.8b1" 39 | flake8 = "^4.0.1" 40 | isort = "^5.10" 41 | mypy = "^0.790" 42 | pre-commit = "^2.16.0" 43 | pytest = "^6.2" 44 | pytest-cov = "^3.0.0" 45 | 46 | [tool.poetry.scripts] 47 | fastapi = 'manage_fastapi.main:app' 48 | 49 | [build-system] 50 | build-backend = "poetry.masonry.api" 51 | requires = ["poetry>=0.12"] 52 | -------------------------------------------------------------------------------- /scripts/check_typing.sh: -------------------------------------------------------------------------------- 1 | find manage_fastapi/. -type f -name "*.py" | xargs mypy 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [isort] 2 | profile = black 3 | known_first_party = manage_fastapi 4 | skip = */templates/* 5 | 6 | [flake8] 7 | max-complexity = 7 8 | statistics = True 9 | max-line-length = 88 10 | ignore = W503,E203 11 | per-file-ignores = 12 | __init__.py: F401 13 | 14 | [mypy] 15 | plugins = pydantic.mypy 16 | ignore_missing_imports = True 17 | follow_imports = skip 18 | strict_optional = True 19 | 20 | [coverage:run] 21 | omit = */templates/* 22 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BilalAlpaslan/manage-fastapi/5284e941d606e530d9369f85b99f371d25ff630e/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_startapp.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from typer.testing import CliRunner 4 | 5 | from manage_fastapi.main import app 6 | 7 | runner = CliRunner() 8 | 9 | CREATED_SUCCESSFULLY = "FastAPI app created successfully! 🎉\n" 10 | ALREADY_EXISTS = "Folder 'potato' already exists. 😞\n" 11 | 12 | 13 | def test_startproject_default(tmp_path: Path): 14 | with runner.isolated_filesystem(temp_dir=tmp_path): 15 | result = runner.invoke(app, ["startapp", "potato"]) 16 | assert result.output == CREATED_SUCCESSFULLY 17 | assert result.exit_code == 0 18 | -------------------------------------------------------------------------------- /tests/test_startproject.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from typer.testing import CliRunner 4 | 5 | from manage_fastapi.main import app 6 | 7 | runner = CliRunner() 8 | 9 | CREATED_SUCCESSFULLY = "FastAPI project created successfully! 🎉\n" 10 | ALREADY_EXISTS = "Folder 'potato' already exists. 😞\n" 11 | 12 | 13 | def test_startproject_default(tmp_path: Path): 14 | with runner.isolated_filesystem(temp_dir=tmp_path): 15 | result = runner.invoke(app, ["startproject", "potato"]) 16 | assert result.output == CREATED_SUCCESSFULLY 17 | assert result.exit_code == 0 18 | 19 | 20 | def test_startproject_already_exists(tmp_path: Path): 21 | with runner.isolated_filesystem(temp_dir=tmp_path): 22 | result = runner.invoke(app, ["startproject", "potato"]) 23 | assert result.output == CREATED_SUCCESSFULLY 24 | assert result.exit_code == 0 25 | 26 | result = runner.invoke(app, ["startproject", "potato"]) 27 | assert result.output == ALREADY_EXISTS 28 | assert result.exit_code == 0 29 | --------------------------------------------------------------------------------