├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── LICENSE ├── README.md ├── copier.yml ├── docker_integration.yml ├── extensions └── context.py ├── pre_commit_hooks.yml ├── root ├── .gitignore ├── .pre-commit-config.yaml.jinja ├── .pylintrc.jinja ├── Makefile.jinja ├── README.md.jinja ├── ci │ └── Dockerfile.jinja ├── pyproject.toml.jinja ├── pytype.cfg.jinja ├── src │ └── {{ package_name }} │ │ ├── __init__.py.jinja │ │ ├── configurator │ │ ├── __init__.py │ │ └── settings │ │ │ ├── __init__.py │ │ │ └── base.py │ │ ├── domain │ │ └── __init__.py │ │ ├── entrypoints │ │ ├── __init__.py │ │ ├── cli │ │ │ └── __init__.py │ │ ├── main.py.jinja │ │ └── web │ │ │ └── __init__.py │ │ ├── service │ │ └── __init__.py │ │ └── utils │ │ ├── __init__.py │ │ └── common.py └── tests │ ├── __init__.py │ └── conftest.py ├── tasks.yml └── virtualenv.yml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior below: 15 | 1. Install foo 16 | 2. Run foo 17 | 3. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Desktop (please complete the following information):** 26 | - OS: [e.g. Linux Mint, iOS, Windows] 27 | 28 | - Version [e.g. 1.2.0] 29 | 30 | - Python Interpreter 31 | 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | For example, what input values were supplied to the *generate-python* 36 | cli (ie paste contents of your *config file*) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * **Please check if the PR fulfills these requirements** 2 | - [ ] The commit message follows our guidelines 3 | - [ ] Tests for the changes have been added (for bug fixes/features) 4 | - [ ] Docs have been added / updated (for bug fixes / features) 5 | 6 | 7 | * **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) 8 | 9 | 10 | * **What is the current behavior?** (You can also link to an open issue here) 11 | 12 | 13 | * **What is the new behavior (if this is a feature change)?** 14 | 15 | 16 | * **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?) 17 | 18 | 19 | * **Other information**: 20 | -------------------------------------------------------------------------------- /.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 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | .idea/ 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Shahriyar Rzayev 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 | # Python Project Template 2 | 3 | Python project structure based on best practices, focused on security, automation and coding style. 4 | 5 | ## Prerequisites 6 | 7 | Python 3.8+ 8 | 9 | First install [copier](https://pypi.org/project/copier/) and [copier_templates_extensions](https://pypi.org/project/copier-templates-extensions/) 10 | 11 | ```console 12 | pip3 install copier 13 | ``` 14 | 15 | ```console 16 | pip3 install copier-templates-extensions 17 | ``` 18 | Then of course you need to have [GNU make](https://www.gnu.org/software/make/) installed on your machine. 19 | 20 | ## How to create the project? 21 | 22 | * Go to the root folder where you are going to create the Python project. For eg, for me it is called `~/REPOS/others/` 23 | `cd ~/REPOS/others/` 24 | 25 | * Run copier as (if you prefer clone over SSH): 26 | 27 | ```console 28 | copier copy --trust git@github.com:ShahriyarR/py-project-template.git py-remove-me 29 | ``` 30 | 31 | or 32 | 33 | ```console 34 | python3 -m copier copy --trust git@github.com:ShahriyarR/py-project-template.git py-remove-me 35 | ``` 36 | 37 | or 38 | 39 | * Run copier as (if you prefer clone over HTTPS): 40 | 41 | ```console 42 | copier copy --trust https://github.com/ShahriyarR/py-project-template.git py-remove-me 43 | ``` 44 | 45 | or 46 | 47 | ```console 48 | python3 -m copier copy --trust https://github.com/ShahriyarR/py-project-template.git py-remove-me 49 | ``` 50 | 51 | 52 | * Answer the questions. 53 | 54 | > **_NOTE:_** Please, note that the uppercase options are defaults for Yes/No type prompts. 55 | 56 | ```console 57 | 🎤 Project name 58 | py-remove-me 59 | 🎤 Package name 60 | pyremoveme 61 | 🎤 Authors full name 62 | rzayev sehriyar 63 | 🎤 Authors email 64 | rzayev.sehriyar@gmail.com 65 | 🎤 Project description 66 | awesome project 67 | 🎤 Project version 68 | 0.0.1 69 | 🎤 Do you want to install virtualenv? 70 | Y/n) 71 | 🎤 Python version. 72 | 3.10 73 | 🎤 Enable pre-commit hooks? 74 | Y/n) 75 | 🎤 Enable Docker? 76 | Y/n) 77 | 78 | 79 | Copying from template version 0.0.0.post11.dev0+15688cd 80 | create . 81 | create Makefile 82 | create .pylintrc 83 | create tests 84 | create tests/__init__.py 85 | create tests/conftest.py 86 | create ci 87 | create ci/Dockerfile 88 | create pyproject.toml 89 | create pytype.cfg 90 | create src 91 | create src/pyremoveme 92 | create src/pyremoveme/__init__.py 93 | create src/pyremoveme/service 94 | create src/pyremoveme/service/__init__.py 95 | create src/pyremoveme/domain 96 | create src/pyremoveme/domain/__init__.py 97 | create src/pyremoveme/configurator 98 | create src/pyremoveme/configurator/__init__.py 99 | create src/pyremoveme/configurator/settings 100 | create src/pyremoveme/configurator/settings/base.py 101 | create src/pyremoveme/configurator/settings/__init__.py 102 | create src/pyremoveme/entrypoints 103 | create src/pyremoveme/entrypoints/__init__.py 104 | create src/pyremoveme/entrypoints/main.py 105 | create src/pyremoveme/entrypoints/web 106 | create src/pyremoveme/entrypoints/web/__init__.py 107 | create src/pyremoveme/entrypoints/cli 108 | create src/pyremoveme/entrypoints/cli/__init__.py 109 | create src/pyremoveme/utils 110 | create src/pyremoveme/utils/__init__.py 111 | create src/pyremoveme/utils/common.py 112 | create .gitignore 113 | create README.md 114 | create .pre-commit-config.yaml 115 | ``` 116 | 117 | * You will have ready-to-go project structure with all optional dependencies. 118 | 119 | ## How to use? 120 | 121 | You need to master the Makefile and make command to fully create advance developer experience. 122 | 123 | Let's see what we have? 124 | 125 | * First go to the new created project directory: 126 | 127 | ```console 128 | cd ~/REPOS/others/py-remove-me 129 | ``` 130 | 131 | * Install project in production mode: 132 | 133 | ```console 134 | make install 135 | ``` 136 | 137 | * Install project in development mode, especially useful 138 | 139 | ```console 140 | make install-dev 141 | ``` 142 | 143 | * Format, sort the imports and also check the style 144 | 145 | ```console 146 | make format 147 | ``` 148 | 149 | * Run linter for final check 150 | 151 | ```console 152 | make lint 153 | ``` 154 | 155 | * Run tests all non-slow and non-integrated tests 156 | 157 | ```console 158 | make test 159 | ``` 160 | 161 | * Run slow tests 162 | 163 | ```console 164 | make test-slow 165 | ``` 166 | 167 | * Run integration tests 168 | 169 | ```console 170 | make test-integration 171 | ``` 172 | 173 | * Run test coverage 174 | 175 | ```console 176 | make test-cov 177 | ``` 178 | 179 | * Run type check 180 | 181 | ```console 182 | make type-check 183 | ``` 184 | 185 | * Run security check 186 | 187 | ```console 188 | make secure 189 | ``` 190 | 191 | 192 | # TODO: 193 | 194 | * Add GitHub workflows/CI runs. 195 | * Update Makefile for adding separate test runs. test-slow, test-integration etc. 196 | * Respectively add pytest marks. -------------------------------------------------------------------------------- /copier.yml: -------------------------------------------------------------------------------- 1 | --- 2 | !include tasks.yml 3 | --- 4 | 5 | _subdirectory: 'root' 6 | _jinja_extensions: 7 | - copier_templates_extensions.TemplateExtensionLoader 8 | - extensions/context.py:ContextUpdater 9 | 10 | project_name: 11 | type: str 12 | help: Project name 13 | validator: >- 14 | {% if not project_name | regex_search('py-[\w+-]*\w') %}Wrong project name. 15 | Please follow template 'py-project-name' {% endif %} 16 | 17 | package_name: 18 | type: str 19 | help: Package name 20 | default: '{{ project_name.replace("-", "") }}' 21 | validator: >- 22 | {% if not package_name | regex_search('py[\w+]*\w') %} 23 | Wrong package name. Please follow template 'pypackagename'{% endif %} 24 | 25 | author_full_name: 26 | type: str 27 | help: Authors full name 28 | validator: >- 29 | {% if not author_full_name %}Please fill authors full name{% endif %} 30 | 31 | author_email: 32 | type: str 33 | help: Authors email 34 | default: '{{ author_full_name.replace(" ", ".") + "@gmail.com" }}' 35 | 36 | description: 37 | type: str 38 | help: Project description 39 | default: '' 40 | 41 | version: 42 | type: str 43 | help: Project version 44 | default: '0.0.1' 45 | 46 | virtualenv: 47 | type: bool 48 | help: Do you want to install virtualenv? 49 | default: true 50 | 51 | --- 52 | !include virtualenv.yml 53 | --- 54 | 55 | pre_commit_hooks: 56 | type: bool 57 | help: Enable pre-commit hooks? 58 | default: true 59 | 60 | 61 | --- 62 | !include docker_integration.yml 63 | --- 64 | 65 | docker_integration: 66 | type: bool 67 | help: Enable Docker? 68 | default: true 69 | 70 | -------------------------------------------------------------------------------- /docker_integration.yml: -------------------------------------------------------------------------------- 1 | _docker: &docker 2 | when: '{{ docker_integration }}' -------------------------------------------------------------------------------- /extensions/context.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from copier_templates_extensions import ContextHook 4 | 5 | 6 | class ContextUpdater(ContextHook): 7 | 8 | update = False 9 | 10 | def __init__(self, environment): 11 | super().__init__(environment) 12 | environment.globals['sys'] = sys 13 | environment.globals['python_path'] = sys.executable 14 | environment.globals['python_version'] = f"{sys.version_info.major}.{sys.version_info.minor}" 15 | 16 | def hook(self, context): 17 | pass 18 | -------------------------------------------------------------------------------- /pre_commit_hooks.yml: -------------------------------------------------------------------------------- 1 | _hooks: &hooks 2 | when: '{{ pre_commit_hooks }}' 3 | -------------------------------------------------------------------------------- /root/.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 | clamav_file_scanner.egg-info/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | .DS_Store 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # SSH stuff 74 | start_ssh.sh 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | # For a library or package, you might want to ignore these files since the code is 92 | # intended to run in multiple environments; otherwise, check them in: 93 | # .python-version 94 | 95 | # pipenv 96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 99 | # install all needed dependencies. 100 | #Pipfile.lock 101 | 102 | # poetry 103 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 104 | # This is especially recommended for binary packages to ensure reproducibility, and is more 105 | # commonly ignored for libraries. 106 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 107 | #poetry.lock 108 | 109 | # pdm 110 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 111 | #pdm.lock 112 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 113 | # in version control. 114 | # https://pdm.fming.dev/#use-with-ide 115 | .pdm.toml 116 | 117 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 118 | __pypackages__/ 119 | 120 | # Celery stuff 121 | celerybeat-schedule 122 | celerybeat.pid 123 | 124 | # SageMath parsed files 125 | *.sage.py 126 | 127 | # Environments 128 | .env 129 | .venv 130 | env/ 131 | venv/ 132 | ENV/ 133 | env.bak/ 134 | venv.bak/ 135 | 136 | # Spyder project settings 137 | .spyderproject 138 | .spyproject 139 | 140 | # Rope project settings 141 | .ropeproject 142 | 143 | # mkdocs documentation 144 | /site 145 | 146 | # mypy 147 | .mypy_cache/ 148 | .dmypy.json 149 | dmypy.json 150 | 151 | # Pyre type checker 152 | .pyre/ 153 | 154 | # pytype static type analyzer 155 | .pytype/ 156 | 157 | # Cython debug symbols 158 | cython_debug/ 159 | 160 | # PyCharm 161 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 162 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 163 | # and can be added to the global gitignore or merged into this file. For a more nuclear 164 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 165 | .idea/ 166 | 167 | # Local dev files 168 | .tranco/ 169 | *.json 170 | *.csv 171 | Screenshots/ 172 | old_benchmarks/ 173 | logs/ -------------------------------------------------------------------------------- /root/.pre-commit-config.yaml.jinja: -------------------------------------------------------------------------------- 1 | default_language_version: 2 | python: python{{ python_version }} 3 | 4 | default_stages: [commit, push] 5 | 6 | repos: 7 | - repo: https://github.com/MarcoGorelli/absolufy-imports 8 | rev: v0.3.1 9 | hooks: 10 | - id: absolufy-imports 11 | - repo: local 12 | hooks: 13 | - id: lint 14 | name: lint 15 | entry: make lint 16 | language: system 17 | types: [ python ] 18 | pass_filenames: false 19 | - id: secure 20 | name: secure 21 | entry: make secure 22 | language: system 23 | types: [python] 24 | pass_filenames: false 25 | - id: pytype 26 | name: pytype 27 | entry: make type-check 28 | language: system 29 | types: [python] 30 | pass_filenames: false 31 | -------------------------------------------------------------------------------- /root/.pylintrc.jinja: -------------------------------------------------------------------------------- 1 | [MAIN] 2 | 3 | # Analyse import fallback blocks. This can be used to support both Python 2 and 4 | # 3 compatible code, which means that the block might have code that exists 5 | # only in one or another interpreter, leading to false positives when analysed. 6 | analyse-fallback-blocks=no 7 | 8 | # Load and enable all available extensions. Use --list-extensions to see a list 9 | # all available extensions. 10 | #enable-all-extensions= 11 | 12 | # In error mode, messages with a category besides ERROR or FATAL are 13 | # suppressed, and no reports are done by default. Error mode is compatible with 14 | # disabling specific errors. 15 | #errors-only= 16 | 17 | # Always return a 0 (non-error) status code, even if lint errors are found. 18 | # This is primarily useful in continuous integration scripts. 19 | #exit-zero= 20 | 21 | # A comma-separated list of package or module names from where C extensions may 22 | # be loaded. Extensions are loading into the active Python interpreter and may 23 | # run arbitrary code. 24 | extension-pkg-allow-list= 25 | 26 | # A comma-separated list of package or module names from where C extensions may 27 | # be loaded. Extensions are loading into the active Python interpreter and may 28 | # run arbitrary code. (This is an alternative name to extension-pkg-allow-list 29 | # for backward compatibility.) 30 | extension-pkg-whitelist= 31 | 32 | # Return non-zero exit code if any of these messages/categories are detected, 33 | # even if score is above --fail-under value. Syntax same as enable. Messages 34 | # specified are enabled, while categories only check already-enabled messages. 35 | fail-on= 36 | 37 | # Specify a score threshold under which the program will exit with error. 38 | fail-under=10 39 | 40 | # Interpret the stdin as a python script, whose filename needs to be passed as 41 | # the module_or_package argument. 42 | #from-stdin= 43 | 44 | # Files or directories to be skipped. They should be base names, not paths. 45 | ignore=CVS 46 | 47 | # Add files or directories matching the regular expressions patterns to the 48 | # ignore-list. The regex matches against paths and can be in Posix or Windows 49 | # format. Because '\' represents the directory delimiter on Windows systems, it 50 | # can't be used as an escape character. 51 | ignore-paths= 52 | 53 | # Files or directories matching the regular expression patterns are skipped. 54 | # The regex matches against base names, not paths. The default value ignores 55 | # Emacs file locks 56 | ignore-patterns=^\.# 57 | 58 | # List of module names for which member attributes should not be checked 59 | # (useful for modules/projects where namespaces are manipulated during runtime 60 | # and thus existing member attributes cannot be deduced by static analysis). It 61 | # supports qualified module names, as well as Unix pattern matching. 62 | ignored-modules= 63 | 64 | # Python code to execute, usually for sys.path manipulation such as 65 | # pygtk.require(). 66 | #init-hook= 67 | 68 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 69 | # number of processors available to use, and will cap the count on Windows to 70 | # avoid hangs. 71 | jobs=1 72 | 73 | # Control the amount of potential inferred values when inferring a single 74 | # object. This can help the performance when dealing with large functions or 75 | # complex, nested conditions. 76 | limit-inference-results=100 77 | 78 | # List of plugins (as comma separated values of python module names) to load, 79 | # usually to register additional checkers. 80 | load-plugins= 81 | 82 | # Pickle collected data for later comparisons. 83 | persistent=yes 84 | 85 | # Minimum Python version to use for version dependent checks. Will default to 86 | # the version used to run pylint. 87 | py-version={{ python_version }} 88 | 89 | # Discover python modules and packages in the file system subtree. 90 | recursive=no 91 | 92 | # When enabled, pylint would attempt to guess common misconfiguration and emit 93 | # user-friendly hints instead of false-positive error messages. 94 | suggestion-mode=yes 95 | 96 | # Allow loading of arbitrary C extensions. Extensions are imported into the 97 | # active Python interpreter and may run arbitrary code. 98 | unsafe-load-any-extension=no 99 | 100 | # In verbose mode, extra non-checker-related info will be displayed. 101 | #verbose= 102 | 103 | 104 | [BASIC] 105 | 106 | # Naming style matching correct argument names. 107 | argument-naming-style=snake_case 108 | 109 | # Regular expression matching correct argument names. Overrides argument- 110 | # naming-style. If left empty, argument names will be checked with the set 111 | # naming style. 112 | #argument-rgx= 113 | 114 | # Naming style matching correct attribute names. 115 | attr-naming-style=snake_case 116 | 117 | # Regular expression matching correct attribute names. Overrides attr-naming- 118 | # style. If left empty, attribute names will be checked with the set naming 119 | # style. 120 | #attr-rgx= 121 | 122 | # Bad variable names which should always be refused, separated by a comma. 123 | bad-names=foo, 124 | bar, 125 | baz, 126 | toto, 127 | tutu, 128 | tata 129 | 130 | # Bad variable names regexes, separated by a comma. If names match any regex, 131 | # they will always be refused 132 | bad-names-rgxs= 133 | 134 | # Naming style matching correct class attribute names. 135 | class-attribute-naming-style=any 136 | 137 | # Regular expression matching correct class attribute names. Overrides class- 138 | # attribute-naming-style. If left empty, class attribute names will be checked 139 | # with the set naming style. 140 | #class-attribute-rgx= 141 | 142 | # Naming style matching correct class constant names. 143 | class-const-naming-style=UPPER_CASE 144 | 145 | # Regular expression matching correct class constant names. Overrides class- 146 | # const-naming-style. If left empty, class constant names will be checked with 147 | # the set naming style. 148 | #class-const-rgx= 149 | 150 | # Naming style matching correct class names. 151 | class-naming-style=PascalCase 152 | 153 | # Regular expression matching correct class names. Overrides class-naming- 154 | # style. If left empty, class names will be checked with the set naming style. 155 | #class-rgx= 156 | 157 | # Naming style matching correct constant names. 158 | const-naming-style=UPPER_CASE 159 | 160 | # Regular expression matching correct constant names. Overrides const-naming- 161 | # style. If left empty, constant names will be checked with the set naming 162 | # style. 163 | #const-rgx= 164 | 165 | # Minimum line length for functions/classes that require docstrings, shorter 166 | # ones are exempt. 167 | docstring-min-length=-1 168 | 169 | # Naming style matching correct function names. 170 | function-naming-style=snake_case 171 | 172 | # Regular expression matching correct function names. Overrides function- 173 | # naming-style. If left empty, function names will be checked with the set 174 | # naming style. 175 | #function-rgx= 176 | 177 | # Good variable names which should always be accepted, separated by a comma. 178 | good-names=i, 179 | j, 180 | k, 181 | ex, 182 | Run, 183 | _ 184 | 185 | # Good variable names regexes, separated by a comma. If names match any regex, 186 | # they will always be accepted 187 | good-names-rgxs= 188 | 189 | # Include a hint for the correct naming format with invalid-name. 190 | include-naming-hint=no 191 | 192 | # Naming style matching correct inline iteration names. 193 | inlinevar-naming-style=any 194 | 195 | # Regular expression matching correct inline iteration names. Overrides 196 | # inlinevar-naming-style. If left empty, inline iteration names will be checked 197 | # with the set naming style. 198 | #inlinevar-rgx= 199 | 200 | # Naming style matching correct method names. 201 | method-naming-style=snake_case 202 | 203 | # Regular expression matching correct method names. Overrides method-naming- 204 | # style. If left empty, method names will be checked with the set naming style. 205 | #method-rgx= 206 | 207 | # Naming style matching correct module names. 208 | module-naming-style=snake_case 209 | 210 | # Regular expression matching correct module names. Overrides module-naming- 211 | # style. If left empty, module names will be checked with the set naming style. 212 | #module-rgx= 213 | 214 | # Colon-delimited sets of names that determine each other's naming style when 215 | # the name regexes allow several styles. 216 | name-group= 217 | 218 | # Regular expression which should only match function or class names that do 219 | # not require a docstring. 220 | no-docstring-rgx=^_ 221 | 222 | # List of decorators that produce properties, such as abc.abstractproperty. Add 223 | # to this list to register other decorators that produce valid properties. 224 | # These decorators are taken in consideration only for invalid-name. 225 | property-classes=abc.abstractproperty 226 | 227 | # Regular expression matching correct type variable names. If left empty, type 228 | # variable names will be checked with the set naming style. 229 | #typevar-rgx= 230 | 231 | # Naming style matching correct variable names. 232 | variable-naming-style=snake_case 233 | 234 | # Regular expression matching correct variable names. Overrides variable- 235 | # naming-style. If left empty, variable names will be checked with the set 236 | # naming style. 237 | #variable-rgx= 238 | 239 | 240 | [CLASSES] 241 | 242 | # Warn about protected attribute access inside special methods 243 | check-protected-access-in-special-methods=no 244 | 245 | # List of method names used to declare (i.e. assign) instance attributes. 246 | defining-attr-methods=__init__, 247 | __new__, 248 | setUp, 249 | __post_init__ 250 | 251 | # List of member names, which should be excluded from the protected access 252 | # warning. 253 | exclude-protected=_asdict, 254 | _fields, 255 | _replace, 256 | _source, 257 | _make 258 | 259 | # List of valid names for the first argument in a class method. 260 | valid-classmethod-first-arg=cls 261 | 262 | # List of valid names for the first argument in a metaclass class method. 263 | valid-metaclass-classmethod-first-arg=cls 264 | 265 | 266 | [DESIGN] 267 | 268 | # List of regular expressions of class ancestor names to ignore when counting 269 | # public methods (see R0903) 270 | exclude-too-few-public-methods= 271 | 272 | # List of qualified class names to ignore when counting class parents (see 273 | # R0901) 274 | ignored-parents= 275 | 276 | # Maximum number of arguments for function / method. 277 | max-args=5 278 | 279 | # Maximum number of attributes for a class (see R0902). 280 | max-attributes=7 281 | 282 | # Maximum number of boolean expressions in an if statement (see R0916). 283 | max-bool-expr=5 284 | 285 | # Maximum number of branch for function / method body. 286 | max-branches=12 287 | 288 | # Maximum number of locals for function / method body. 289 | max-locals=15 290 | 291 | # Maximum number of parents for a class (see R0901). 292 | max-parents=7 293 | 294 | # Maximum number of public methods for a class (see R0904). 295 | max-public-methods=20 296 | 297 | # Maximum number of return / yield for function / method body. 298 | max-returns=6 299 | 300 | # Maximum number of statements in function / method body. 301 | max-statements=50 302 | 303 | # Minimum number of public methods for a class (see R0903). 304 | min-public-methods=2 305 | 306 | 307 | [EXCEPTIONS] 308 | 309 | # Exceptions that will emit a warning when caught. 310 | overgeneral-exceptions=BaseException, 311 | Exception 312 | 313 | 314 | [FORMAT] 315 | 316 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 317 | expected-line-ending-format= 318 | 319 | # Regexp for a line that is allowed to be longer than the limit. 320 | ignore-long-lines=^\s*(# )??$ 321 | 322 | # Number of spaces of indent required inside a hanging or continued line. 323 | indent-after-paren=4 324 | 325 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 326 | # tab). 327 | indent-string=' ' 328 | 329 | # Maximum number of characters on a single line. 330 | max-line-length=140 331 | 332 | # Maximum number of lines in a module. 333 | max-module-lines=1000 334 | 335 | # Allow the body of a class to be on the same line as the declaration if body 336 | # contains single statement. 337 | single-line-class-stmt=no 338 | 339 | # Allow the body of an if to be on the same line as the test if there is no 340 | # else. 341 | single-line-if-stmt=no 342 | 343 | 344 | [IMPORTS] 345 | 346 | # List of modules that can be imported at any level, not just the top level 347 | # one. 348 | allow-any-import-level= 349 | 350 | # Allow wildcard imports from modules that define __all__. 351 | allow-wildcard-with-all=no 352 | 353 | # Deprecated modules which should not be used, separated by a comma. 354 | deprecated-modules= 355 | 356 | # Output a graph (.gv or any supported image format) of external dependencies 357 | # to the given file (report RP0402 must not be disabled). 358 | ext-import-graph= 359 | 360 | # Output a graph (.gv or any supported image format) of all (i.e. internal and 361 | # external) dependencies to the given file (report RP0402 must not be 362 | # disabled). 363 | import-graph= 364 | 365 | # Output a graph (.gv or any supported image format) of internal dependencies 366 | # to the given file (report RP0402 must not be disabled). 367 | int-import-graph= 368 | 369 | # Force import order to recognize a module as part of the standard 370 | # compatibility libraries. 371 | known-standard-library= 372 | 373 | # Force import order to recognize a module as part of a third party library. 374 | known-third-party=enchant 375 | 376 | # Couples of modules and preferred modules, separated by a comma. 377 | preferred-modules= 378 | 379 | 380 | [LOGGING] 381 | 382 | # The type of string formatting that logging methods do. `old` means using % 383 | # formatting, `new` is for `{}` formatting. 384 | logging-format-style=old 385 | 386 | # Logging modules to check that the string format arguments are in logging 387 | # function parameter format. 388 | logging-modules=logging 389 | 390 | 391 | [MESSAGES CONTROL] 392 | 393 | # Only show warnings with the listed confidence levels. Leave empty to show 394 | # all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, 395 | # UNDEFINED. 396 | confidence=HIGH, 397 | CONTROL_FLOW, 398 | INFERENCE, 399 | INFERENCE_FAILURE, 400 | UNDEFINED 401 | 402 | # Disable the message, report, category or checker with the given id(s). You 403 | # can either give multiple identifiers separated by comma (,) or put this 404 | # option multiple times (only on the command line, not in the configuration 405 | # file where it should appear only once). You can also use "--disable=all" to 406 | # disable everything first and then re-enable specific checks. For example, if 407 | # you want to run only the similarities checker, you can use "--disable=all 408 | # --enable=similarities". If you want to run only the classes checker, but have 409 | # no Warning level messages displayed, use "--disable=all --enable=classes 410 | # --disable=W". 411 | disable=raw-checker-failed, 412 | bad-inline-option, 413 | locally-disabled, 414 | file-ignored, 415 | suppressed-message, 416 | useless-suppression, 417 | deprecated-pragma, 418 | use-symbolic-message-instead 419 | 420 | # Enable the message, report, category or checker with the given id(s). You can 421 | # either give multiple identifier separated by comma (,) or put this option 422 | # multiple time (only on the command line, not in the configuration file where 423 | # it should appear only once). See also the "--disable" option for examples. 424 | enable=c-extension-no-member 425 | 426 | 427 | [METHOD_ARGS] 428 | 429 | # List of qualified names (i.e., library.method) which require a timeout 430 | # parameter e.g. 'requests.api.get,requests.api.post' 431 | timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request 432 | 433 | 434 | [MISCELLANEOUS] 435 | 436 | # List of note tags to take in consideration, separated by a comma. 437 | notes=FIXME, 438 | XXX, 439 | TODO 440 | 441 | # Regular expression of note tags to take in consideration. 442 | notes-rgx= 443 | 444 | 445 | [REFACTORING] 446 | 447 | # Maximum number of nested blocks for function / method body 448 | max-nested-blocks=5 449 | 450 | # Complete name of functions that never returns. When checking for 451 | # inconsistent-return-statements if a never returning function is called then 452 | # it will be considered as an explicit return statement and no message will be 453 | # printed. 454 | never-returning-functions=sys.exit,argparse.parse_error 455 | 456 | 457 | [REPORTS] 458 | 459 | # Python expression which should return a score less than or equal to 10. You 460 | # have access to the variables 'fatal', 'error', 'warning', 'refactor', 461 | # 'convention', and 'info' which contain the number of messages in each 462 | # category, as well as 'statement' which is the total number of statements 463 | # analyzed. This score is used by the global evaluation report (RP0004). 464 | evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) 465 | 466 | # Template used to display messages. This is a python new-style format string 467 | # used to format the message information. See doc for all details. 468 | msg-template= 469 | 470 | # Set the output format. Available formats are text, parseable, colorized, json 471 | # and msvs (visual studio). You can also give a reporter class, e.g. 472 | # mypackage.mymodule.MyReporterClass. 473 | #output-format= 474 | 475 | # Tells whether to display a full report or only the messages. 476 | reports=no 477 | 478 | # Activate the evaluation score. 479 | score=yes 480 | 481 | 482 | [SIMILARITIES] 483 | 484 | # Comments are removed from the similarity computation 485 | ignore-comments=yes 486 | 487 | # Docstrings are removed from the similarity computation 488 | ignore-docstrings=yes 489 | 490 | # Imports are removed from the similarity computation 491 | ignore-imports=yes 492 | 493 | # Signatures are removed from the similarity computation 494 | ignore-signatures=yes 495 | 496 | # Minimum lines number of a similarity. 497 | min-similarity-lines=4 498 | 499 | 500 | [SPELLING] 501 | 502 | # Limits count of emitted suggestions for spelling mistakes. 503 | max-spelling-suggestions=4 504 | 505 | # Spelling dictionary name. Available dictionaries: none. To make it work, 506 | # install the 'python-enchant' package. 507 | spelling-dict= 508 | 509 | # List of comma separated words that should be considered directives if they 510 | # appear at the beginning of a comment and should not be checked. 511 | spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: 512 | 513 | # List of comma separated words that should not be checked. 514 | spelling-ignore-words= 515 | 516 | # A path to a file that contains the private dictionary; one word per line. 517 | spelling-private-dict-file= 518 | 519 | # Tells whether to store unknown words to the private dictionary (see the 520 | # --spelling-private-dict-file option) instead of raising a message. 521 | spelling-store-unknown-words=no 522 | 523 | 524 | [STRING] 525 | 526 | # This flag controls whether inconsistent-quotes generates a warning when the 527 | # character used as a quote delimiter is used inconsistently within a module. 528 | check-quote-consistency=no 529 | 530 | # This flag controls whether the implicit-str-concat should generate a warning 531 | # on implicit string concatenation in sequences defined over several lines. 532 | check-str-concat-over-line-jumps=no 533 | 534 | 535 | [TYPECHECK] 536 | 537 | # List of decorators that produce context managers, such as 538 | # contextlib.contextmanager. Add to this list to register other decorators that 539 | # produce valid context managers. 540 | contextmanager-decorators=contextlib.contextmanager 541 | 542 | # List of members which are set dynamically and missed by pylint inference 543 | # system, and so shouldn't trigger E1101 when accessed. Python regular 544 | # expressions are accepted. 545 | generated-members= 546 | 547 | # Tells whether to warn about missing members when the owner of the attribute 548 | # is inferred to be None. 549 | ignore-none=yes 550 | 551 | # This flag controls whether pylint should warn about no-member and similar 552 | # checks whenever an opaque object is returned when inferring. The inference 553 | # can return multiple potential results while evaluating a Python object, but 554 | # some branches might not be evaluated, which results in partial inference. In 555 | # that case, it might be useful to still emit no-member and other checks for 556 | # the rest of the inferred objects. 557 | ignore-on-opaque-inference=yes 558 | 559 | # List of symbolic message names to ignore for Mixin members. 560 | ignored-checks-for-mixins=no-member, 561 | not-async-context-manager, 562 | not-context-manager, 563 | attribute-defined-outside-init 564 | 565 | # List of class names for which member attributes should not be checked (useful 566 | # for classes with dynamically set attributes). This supports the use of 567 | # qualified names. 568 | ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace 569 | 570 | # Show a hint with possible names when a member name was not found. The aspect 571 | # of finding the hint is based on edit distance. 572 | missing-member-hint=yes 573 | 574 | # The minimum edit distance a name should have in order to be considered a 575 | # similar match for a missing member name. 576 | missing-member-hint-distance=1 577 | 578 | # The total number of similar names that should be taken in consideration when 579 | # showing a hint for a missing member. 580 | missing-member-max-choices=1 581 | 582 | # Regex pattern to define which classes are considered mixins. 583 | mixin-class-rgx=.*[Mm]ixin 584 | 585 | # List of decorators that change the signature of a decorated function. 586 | signature-mutators= 587 | 588 | 589 | [VARIABLES] 590 | 591 | # List of additional names supposed to be defined in builtins. Remember that 592 | # you should avoid defining new builtins when possible. 593 | additional-builtins= 594 | 595 | # Tells whether unused global variables should be treated as a violation. 596 | allow-global-unused-variables=yes 597 | 598 | # List of names allowed to shadow builtins 599 | allowed-redefined-builtins= 600 | 601 | # List of strings which can identify a callback function by name. A callback 602 | # name must start or end with one of those strings. 603 | callbacks=cb_, 604 | _cb 605 | 606 | # A regular expression matching the name of dummy variables (i.e. expected to 607 | # not be used). 608 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 609 | 610 | # Argument names that match this expression will be ignored. 611 | ignored-argument-names=_.*|^ignored_|^unused_ 612 | 613 | # Tells whether we should check for unused import in __init__ files. 614 | init-import=no 615 | 616 | # List of qualified module names which can have objects that can redefine 617 | # builtins. 618 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 619 | -------------------------------------------------------------------------------- /root/Makefile.jinja: -------------------------------------------------------------------------------- 1 | ifdef OS 2 | PYTHON ?= .venv/Scripts/python.exe 3 | TYPE_CHECK_COMMAND ?= echo Pytype package doesn't support Windows OS 4 | else 5 | PYTHON ?= .venv/bin/python 6 | TYPE_CHECK_COMMAND ?= ${PYTHON} -m pytype --config=pytype.cfg src 7 | endif 8 | 9 | SETTINGS_FILENAME = pyproject.toml 10 | 11 | PHONY = help install install-dev build format lint type-check secure test test-slow test-integration install-flit enable-pre-commit-hooks run 12 | 13 | help: 14 | @echo "--------------- HELP ---------------" 15 | @egrep "^# target:" [Mm]akefile | sed -e 's/target://' 16 | @echo "------------------------------------" 17 | 18 | # target: To install the project -> make install 19 | install: 20 | ${PYTHON} -m flit install --env --deps=production 21 | 22 | # target: To install the project using symlinks (for development) -> make install-dev 23 | install-dev: 24 | ${PYTHON} -m flit install -s --env --deps=develop --symlink 25 | 26 | # target: To install flit -> make install-flit 27 | install-flit: 28 | ${PYTHON} -m pip install flit==3.8.0 29 | 30 | # target: To enable pre-commit hooks -> make enable-pre-commit-hooks 31 | enable-pre-commit-hooks: 32 | ${PYTHON} -m pre_commit install 33 | 34 | # target: To build the wheel package -> make build 35 | build: 36 | ${PYTHON} -m flit build --format wheel 37 | ${PYTHON} -m pip install dist/*.whl 38 | ${PYTHON} -c 'import {{ package_name }}; print({{ package_name }}.__version__)' 39 | 40 | # target: To format code -> make format 41 | format: 42 | ${PYTHON} -m isort src tests --force-single-line-imports --settings-file ${SETTINGS_FILENAME} 43 | ${PYTHON} -m autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place src --exclude=__init__.py 44 | ${PYTHON} -m black src tests --config ${SETTINGS_FILENAME} 45 | ${PYTHON} -m isort src tests --settings-file ${SETTINGS_FILENAME} 46 | 47 | # target: To check linter -> make lint 48 | lint: 49 | ${PYTHON} -m flake8 --toml-config ${SETTINGS_FILENAME} --max-complexity 5 --max-cognitive-complexity=5 src 50 | ${PYTHON} -m black src tests --check --diff --config ${SETTINGS_FILENAME} 51 | ${PYTHON} -m isort src tests --check --diff --settings-file ${SETTINGS_FILENAME} 52 | 53 | # target: To run type checker -> make type-check 54 | type-check: 55 | @$(TYPE_CHECK_COMMAND) 56 | 57 | # target: To run all security related commands -> make secure 58 | secure: 59 | ${PYTHON} -m bandit -r src --config ${SETTINGS_FILENAME} 60 | ${PYTHON} -m safety check 61 | pip-audit . 62 | 63 | # target: To test the project -> make test 64 | test: 65 | ${PYTHON} -m pytest -svvv -m "not slow and not integration" tests 66 | 67 | # target: To test running only slow tests -> make test-slow 68 | test-slow: 69 | ${PYTHON} -m pytest -svvv -m "slow" tests 70 | 71 | # target: To test running only integration tests -> make test-integration 72 | test-integration: 73 | ${PYTHON} -m pytest -svvv -m "integration" tests 74 | 75 | # target: To run the project -> make run 76 | run: 77 | ${PYTHON} -m src.{{ package_name }}.main 78 | -------------------------------------------------------------------------------- /root/README.md.jinja: -------------------------------------------------------------------------------- 1 | {{description}} 2 | 3 | 4 | ## Setup 5 | ### Project dependencies: 6 | * Python {{ python_version }} 7 | * [GNU make](https://www.gnu.org/software/make/) 8 | * [Virtualenv](https://virtualenv.pypa.io/en/latest/) 9 | 10 | Create virtualenv in the project root directory named ".venv" 11 | 12 | ### Commands 13 | 14 | `make install-flit` 15 | 16 | `make install-dev` 17 | 18 | `make run` or `make test` 19 | 20 | 21 | ## Flow 22 | 23 | 24 | ## Current limitations & future improvements 25 | 26 | #### Authors: [{{ author_full_name | title }}](mailto:{{ author_email }}) 27 | 28 | 29 | -------------------------------------------------------------------------------- /root/ci/Dockerfile.jinja: -------------------------------------------------------------------------------- 1 | FROM python:{{ python_version }}-buster 2 | 3 | WORKDIR /app 4 | RUN apt-get update -y && apt-get install make -y 5 | 6 | COPY . . 7 | RUN pip install --upgrade pip 8 | RUN pip install flit==3.8.0 9 | RUN export PIP_INDEX_URL=$PIP_INDEX_URL 10 | RUN FLIT_ROOT_INSTALL=1 flit install 11 | 12 | ENTRYPOINT TEST_RUN="TRUE" pytest -s tests 13 | -------------------------------------------------------------------------------- /root/pyproject.toml.jinja: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core==3.8.0"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "{{ package_name }}" 7 | authors = [ 8 | { name = "{{ author_full_name }}", email = "{{ author_email }}" }, 9 | ] 10 | dynamic = ["version"] 11 | description = "{{ description }}" 12 | # Add here the production dependencies 13 | dependencies = [] 14 | 15 | [project.optional-dependencies] 16 | dev = [ 17 | "black", 18 | "isort", 19 | "autoflake", 20 | "pytype; platform_system != 'Windows'", 21 | "flake8", 22 | "Flake8-pyproject", 23 | "bandit", 24 | "flake8-bugbear", 25 | "flake8-cognitive-complexity", 26 | "pre-commit", 27 | "safety", 28 | "pip-audit", 29 | ] 30 | test = [ 31 | "pytest", 32 | "pytest-cov", 33 | "pytest-xdist", 34 | ] 35 | 36 | [tool.isort] 37 | profile = "black" 38 | line_length = 140 39 | py_version = {{ python_version.replace('.', "") }} 40 | order_by_type = false 41 | skip = [".gitignore", ".dockerignore"] 42 | extend_skip = [".md", ".json"] 43 | skip_glob = ["docs/*"] 44 | 45 | [tool.flake8] 46 | max-line-length = 140 47 | select = ["C", "E", "F", "W", "B", "B9"] 48 | ignore = ["E203", "E501", "W503", "C812", "E731", "F811"] 49 | exclude = ["__init__.py"] 50 | 51 | [tool.black] 52 | line-length = 140 53 | target-version = ['py{{ python_version.replace(".", "") }}'] 54 | include = '\.pyi?$' 55 | 56 | [tool.bandit] 57 | skips = ["B311", "B404"] 58 | 59 | [tool.pytest.ini_options] 60 | pythonpath = [ 61 | "src" 62 | ] 63 | markers = [ 64 | "slow: marks tests as slow (deselect with '-m \"not slow\"')", 65 | "integration: marks tests as integration relatively slow (deselect with '-m \"not integration\"')", 66 | "serial", 67 | ] 68 | addopts = [ 69 | "--strict-markers", 70 | "--strict-config", 71 | "-ra", 72 | ] 73 | 74 | testpaths = "tests" -------------------------------------------------------------------------------- /root/pytype.cfg.jinja: -------------------------------------------------------------------------------- 1 | # NOTE: All relative paths are relative to the location of this file. 2 | 3 | [pytype] 4 | 5 | # Space-separated list of files or directories to exclude. 6 | exclude = 7 | **/*_test.py 8 | **/test_*.py 9 | 10 | # Space-separated list of files or directories to process. 11 | inputs = 12 | . 13 | 14 | # Keep going past errors to analyze as many files as possible. 15 | keep_going = False 16 | 17 | # Run N jobs in parallel. When 'auto' is used, this will be equivalent to the 18 | # number of CPUs on the host system. 19 | jobs = 4 20 | 21 | # All pytype output goes here. 22 | output = .pytype 23 | 24 | # Platform (e.g., "linux", "win32") that the target code runs on. 25 | platform = linux 26 | 27 | # Paths to source code directories, separated by ':'. 28 | pythonpath = 29 | ./src 30 | 31 | # Python version (major.minor) of the target code. 32 | python_version = {{ python_version }} 33 | 34 | # Always use function return type annotations. This flag is temporary and will 35 | # be removed once this behavior is enabled by default. 36 | always_use_return_annotations = True 37 | 38 | # Enable parameter count checks for overriding methods. This flag is temporary 39 | # and will be removed once this behavior is enabled by default. 40 | overriding_parameter_count_checks = True 41 | 42 | # Enable return type checks for overriding methods. This flag is temporary and 43 | # will be removed once this behavior is enabled by default. 44 | overriding_return_type_checks = True 45 | 46 | # Use the enum overlay for more precise enum checking. This flag is temporary 47 | # and will be removed once this behavior is enabled by default. 48 | use_enum_overlay = True 49 | 50 | # Opt-in: Do not allow Any as a return type. 51 | no_return_any = False 52 | 53 | # Experimental: Support pyglib's @cached.property. 54 | enable_cached_property = True 55 | 56 | # Experimental: Infer precise return types even for invalid function calls. 57 | precise_return = True 58 | 59 | # Experimental: Solve unknown types to label with structural types. 60 | protocols = False 61 | 62 | # Experimental: Only load submodules that are explicitly imported. 63 | strict_import = True 64 | 65 | # Experimental: Enable exhaustive checking of function parameter types. 66 | strict_parameter_checks = True 67 | 68 | # Experimental: Emit errors for comparisons between incompatible primitive 69 | # types. 70 | strict_primitive_comparisons = True 71 | 72 | # Comma or space separated list of error names to ignore. 73 | disable = 74 | pyi-error 75 | 76 | # Don't report errors. 77 | report_errors = True 78 | -------------------------------------------------------------------------------- /root/src/{{ package_name }}/__init__.py.jinja: -------------------------------------------------------------------------------- 1 | __version__ = "{{ version }}" -------------------------------------------------------------------------------- /root/src/{{ package_name }}/configurator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShahriyarR/py-project-template/5d54c0e093e23c7451f849baffb48db17b0933ec/root/src/{{ package_name }}/configurator/__init__.py -------------------------------------------------------------------------------- /root/src/{{ package_name }}/configurator/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShahriyarR/py-project-template/5d54c0e093e23c7451f849baffb48db17b0933ec/root/src/{{ package_name }}/configurator/settings/__init__.py -------------------------------------------------------------------------------- /root/src/{{ package_name }}/configurator/settings/base.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | logging.basicConfig( 5 | level=logging.INFO, 6 | filename=None, 7 | format="[%(asctime)s] [%(levelname)s] %(message)s" 8 | ) 9 | -------------------------------------------------------------------------------- /root/src/{{ package_name }}/domain/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShahriyarR/py-project-template/5d54c0e093e23c7451f849baffb48db17b0933ec/root/src/{{ package_name }}/domain/__init__.py -------------------------------------------------------------------------------- /root/src/{{ package_name }}/entrypoints/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShahriyarR/py-project-template/5d54c0e093e23c7451f849baffb48db17b0933ec/root/src/{{ package_name }}/entrypoints/__init__.py -------------------------------------------------------------------------------- /root/src/{{ package_name }}/entrypoints/cli/__init__.py: -------------------------------------------------------------------------------- 1 | """Here you should define your CLI entrypoint""" 2 | -------------------------------------------------------------------------------- /root/src/{{ package_name }}/entrypoints/main.py.jinja: -------------------------------------------------------------------------------- 1 | from {{ package_name }} import __version__ 2 | print("Hey! I'm entrypoint of {{ project_name }} version: %s" % __version__) -------------------------------------------------------------------------------- /root/src/{{ package_name }}/entrypoints/web/__init__.py: -------------------------------------------------------------------------------- 1 | """Here you should define your Web entrypoint""" 2 | -------------------------------------------------------------------------------- /root/src/{{ package_name }}/service/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShahriyarR/py-project-template/5d54c0e093e23c7451f849baffb48db17b0933ec/root/src/{{ package_name }}/service/__init__.py -------------------------------------------------------------------------------- /root/src/{{ package_name }}/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShahriyarR/py-project-template/5d54c0e093e23c7451f849baffb48db17b0933ec/root/src/{{ package_name }}/utils/__init__.py -------------------------------------------------------------------------------- /root/src/{{ package_name }}/utils/common.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShahriyarR/py-project-template/5d54c0e093e23c7451f849baffb48db17b0933ec/root/src/{{ package_name }}/utils/common.py -------------------------------------------------------------------------------- /root/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShahriyarR/py-project-template/5d54c0e093e23c7451f849baffb48db17b0933ec/root/tests/__init__.py -------------------------------------------------------------------------------- /root/tests/conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShahriyarR/py-project-template/5d54c0e093e23c7451f849baffb48db17b0933ec/root/tests/conftest.py -------------------------------------------------------------------------------- /tasks.yml: -------------------------------------------------------------------------------- 1 | _tasks: 2 | ##Virtualenv 3 | - '{% if virtualenv %} "{{ python_path }}" -m pip install virtualenv {% endif %}' 4 | - '{% if virtualenv %} "{{ python_path }}" -m virtualenv --python={{ python_version }} .venv {% endif %}' 5 | - '{% if virtualenv %} make install-flit {% endif %}' 6 | - '{% if virtualenv %} make install-dev {% endif %}' 7 | ##Pre-commit hooks 8 | - '{% if pre_commit_hooks %} git init {% endif %}' 9 | - '{% if pre_commit_hooks %} make enable-pre-commit-hooks {% endif %}' 10 | -------------------------------------------------------------------------------- /virtualenv.yml: -------------------------------------------------------------------------------- 1 | _venv: &venv 2 | when: '{{ virtualenv }}' 3 | 4 | python_version: 5 | <<: *venv 6 | type: str 7 | help: 'Python version.' 8 | default: '{{ python_version }}' 9 | 10 | python_path: 11 | <<: *venv 12 | help: 'Please provide correct python executable path! 13 | Current python version: {{ [sys.version_info.major, sys.version_info.minor] | join(".") }} Chosen version: {{ python_version }}' 14 | default: '{{ python_path }}' 15 | when: '{{ [sys.version_info.major, sys.version_info.minor] | join(".") != python_version }}' 16 | --------------------------------------------------------------------------------