├── .coveragerc ├── .cruft.json ├── .github ├── FUNDING.yml └── workflows │ ├── lint.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── TROUBLESHOOTING.md ├── art ├── example.gif ├── logo.png ├── logo.xcf ├── logo_small.png └── repo_url.png ├── docs ├── contributing │ ├── 1.-contributing-guide.md │ ├── 2.-coding-standard.md │ ├── 3.-code-of-conduct.md │ └── 4.-acknowledgements.md └── quick_start │ ├── 1.-installation.md │ ├── 2.-cli.md │ ├── 3.-api.md │ └── 4.-configuration.md ├── poetry.lock ├── portray ├── __init__.py ├── __main__.py ├── _version.py ├── api.py ├── cli.py ├── config.py ├── exceptions.py ├── logo.py ├── mkdocs_templates │ └── partials │ │ └── footer.html └── render.py ├── pyproject.toml ├── scripts ├── clean.sh ├── done.sh ├── lint.sh └── test.sh ├── setup.cfg └── tests ├── __init__.py ├── conftest.py ├── test_api.py ├── test_config.py └── test_render.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | exclude_lines = 3 | pragma: no cover 4 | omit = 5 | portray/__main__.py 6 | *tests* 7 | -------------------------------------------------------------------------------- /.cruft.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": "https://github.com/timothycrosley/cookiecutter-python/", 3 | "commit": "71391fd9999067ef4b38aa05e7116087fac431f8", 4 | "context": { 5 | "cookiecutter": { 6 | "full_name": "Timothy Crosley", 7 | "email": "timothy.crosley@gmail.com", 8 | "github_username": "timothycrosley", 9 | "project_name": "portray", 10 | "description": "Your Project with Great Documentation", 11 | "version": "1.6.0", 12 | "_template": "https://github.com/timothycrosley/cookiecutter-python/" 13 | } 14 | }, 15 | "directory": null, 16 | "checkout": null 17 | } 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: "timothycrosley" 2 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: [3.9] 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: pip cache 16 | uses: actions/cache@v1 17 | with: 18 | path: ~/.cache/pip 19 | key: lint-pip-${{ hashFiles('**/pyproject.toml') }} 20 | restore-keys: | 21 | lint-pip- 22 | 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v1 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | python -m pip install --upgrade poetry 32 | poetry install 33 | 34 | - name: Lint 35 | run: ./scripts/lint.sh 36 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | python-version: [3.8, 3.9] 12 | os: [ubuntu-latest, ubuntu-18.04, macos-latest, windows-latest] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Ubuntu cache 17 | uses: actions/cache@v1 18 | if: startsWith(matrix.os, 'ubuntu') 19 | with: 20 | path: ~/.cache/pip 21 | key: 22 | ${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} 23 | restore-keys: | 24 | ${{ matrix.os }}-${{ matrix.python-version }}- 25 | 26 | - name: macOS cache 27 | uses: actions/cache@v1 28 | if: startsWith(matrix.os, 'macOS') 29 | with: 30 | path: ~/Library/Caches/pip 31 | key: 32 | ${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} 33 | restore-keys: | 34 | ${{ matrix.os }}-${{ matrix.python-version }}- 35 | 36 | - name: Windows cache 37 | uses: actions/cache@v1 38 | if: startsWith(matrix.os, 'windows') 39 | with: 40 | path: c:\users\runneradmin\appdata\local\pip\cache 41 | key: 42 | ${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} 43 | restore-keys: | 44 | ${{ matrix.os }}-${{ matrix.python-version }}- 45 | 46 | - name: Set up Python ${{ matrix.python-version }} 47 | uses: actions/setup-python@v1 48 | with: 49 | python-version: ${{ matrix.python-version }} 50 | 51 | - name: Install dependencies 52 | run: | 53 | python -m pip install --upgrade pip 54 | python -m pip install --upgrade poetry 55 | poetry install 56 | poetry run pip install --upgrade tornado --ignore-installed || true 57 | - name: Test 58 | shell: bash 59 | run: | 60 | poetry run pytest tests/ -s --cov=portray/ --cov-report=term-missing ${@-} 61 | poetry run coverage xml 62 | - name: Report Coverage 63 | if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.8' 64 | uses: codecov/codecov-action@v1.0.6 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | .DS_Store 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | build 10 | eggs 11 | .eggs 12 | parts 13 | var 14 | sdist 15 | develop-eggs 16 | .installed.cfg 17 | lib 18 | lib64 19 | MANIFEST 20 | 21 | # Installer logs 22 | pip-log.txt 23 | npm-debug.log 24 | pip-selfcheck.json 25 | 26 | # Unit test / coverage reports 27 | .coverage 28 | .tox 29 | nosetests.xml 30 | htmlcov 31 | .cache 32 | .pytest_cache 33 | .mypy_cache 34 | .hypothesis 35 | 36 | # Translations 37 | *.mo 38 | 39 | # Mr Developer 40 | .mr.developer.cfg 41 | .project 42 | .pydevproject 43 | 44 | # SQLite 45 | test_exp_framework 46 | 47 | # npm 48 | node_modules/ 49 | 50 | # dolphin 51 | .directory 52 | libpeerconnection.log 53 | 54 | # setuptools 55 | dist 56 | 57 | # IDE Files 58 | atlassian-ide-plugin.xml 59 | .idea/ 60 | *.swp 61 | *.kate-swp 62 | .ropeproject/ 63 | 64 | # Python3 Venv Files 65 | .venv/ 66 | bin/ 67 | include/ 68 | lib/ 69 | lib64 70 | pyvenv.cfg 71 | share/ 72 | venv/ 73 | .python-version 74 | 75 | # Cython 76 | *.c 77 | 78 | # Emacs backup 79 | *~ 80 | 81 | # VSCode 82 | /.vscode 83 | 84 | # Automatically generated files 85 | docs/preconvert 86 | site/ 87 | out 88 | 89 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Install the latest 2 | =================== 3 | 4 | To install the latest version of portray simply run: 5 | 6 | `pip3 install portray` 7 | 8 | OR 9 | 10 | `poetry add portray` 11 | 12 | OR 13 | 14 | `pipenv install portray` 15 | 16 | see the [Installation QuickStart](https://timothycrosley.github.io/portray/docs/quick_start/1.-installation/) for more instructions. 17 | 18 | Changelog 19 | ========= 20 | ## 1.8.0 - Dec 19 2022 21 | - dropped Python 3.7 support 22 | - upgraded pdocs and other sub dependencies 23 | - several small fixes 24 | 25 | ## 1.7.0 - June 13 2021 26 | - mkdocs version bump and compatibility fix for 1.2.x 27 | 28 | ## 1.6.0 - April 1 2021 29 | - Updated minimum version of mkdocs material to 7.0.0 to address security concerns on earlier versions. 30 | 31 | ## 1.5.2 - Feb 16 2021 32 | - Fix incompatibility with latest version of mkdocs and portray on github pages command. 33 | 34 | ## 1.5.1 - Jan 29 2020 35 | - Bump minimum version of pdocs as it has a bug rendering some doc strings. 36 | 37 | ## 1.5.0 - Jan 16 2020 38 | - Fixed issue #60: Portray in_browser ignores port. 39 | - Fixed issue #63: Custom "output_dir" ignored in pyproject.toml config. 40 | - Added support for live reload. 41 | - Added support for Google and Numpy doc string formats. 42 | 43 | ## 1.4.0 - June 12 2020 44 | - Upgraded mkdocs-material & pymdown-extensions 45 | 46 | ## 1.3.2 - June 07 2020 47 | - Bump pdocs dependency to include some package inclusion improvements. 48 | - Speed up doc copying by only copying markdown files. 49 | 50 | ## 1.3.1 - September 22 2019 51 | - Fixed [Issue 43](https://github.com/timothycrosley/portray/issues/43) - Automatically remap `-` to `_` when attempting to auto-determine module name. 52 | - Added `extra_markdown_extensions` config option, to enable adding markdown extensions without having to redefine default set included by portray. 53 | 54 | ## 1.3.0 - September 15 2019 55 | - Potentially backward compatibility breaking performance optimization: portray now only renders root project files + the specified documentation directory + any specified extra_dirs. 56 | If a previously utilized file used to be auto included, but is no longer, you can force its inclusion by adding its directory to `extra_dirs.` 57 | For many projects, this change results in a significantly smaller output size and significantly faster documentation generation. 58 | - Implemented [Issue 31](https://github.com/timothycrosley/portray/issues/31) - Improved repository auto-discovery and formatting. 59 | - Fixed [Issue 33](https://github.com/timothycrosley/portray/issues/33) - Improving usability and documentation of nav customization. 60 | - Added many additional test cases - Reaching 100% test coverage. 61 | - Added indicators that let users know what step is occurring during documentation generation. 62 | 63 | ## 1.2.4 - September 5 2019 64 | - Fixed [Issue 23](https://github.com/timothycrosley/portray/issues/23) - A confirmed regression from moving to `pdocs` that caused root modules to not be auto included. 65 | 66 | ## 1.2.3 - September 4 2019 67 | - Fixed a bug specifying the output dir for `as_html` command from the command line. 68 | - Updated to use Python3.6+ style variable annotations. 69 | 70 | ## 1.2.2 - September 4 2019 71 | - Fixed a bug specifying modules from command line when no configuration file is present. 72 | 73 | ## 1.2.1 - September 3 2019 74 | - General improvements to reference code documentation rendering 75 | 76 | ## 1.2.0 - September 3 2019 77 | Potentially breaking dependency change release from pdoc3 to [pdocs](https://timothycrosley.github.io/pdocs/). 78 | 79 | ### Migration guide: 80 | 81 | - `pyproject.toml` config section changes from `[tool.portray.pdoc3]` to `[tool.portray.pdocs]` 82 | - Type annotations are no longer toggleable but are rather always on. 83 | 84 | ## 1.1.0 - August 29 2019 85 | Minor Feature release w/ Bug Fixes 86 | 87 | - Added support for specifying modules directly from the CLI and API. 88 | - Added auto module detect for simple setup.py files. 89 | - Improved CLI subcommand documentation. 90 | - Implemented [Issue 12](https://github.com/timothycrosley/portray/issues/12) - Clarify what a "project" is in documentation. 91 | - Fixed [Issue 2](https://github.com/timothycrosley/portray/issues/2) - UnicodeEncodeError except when running portray. 92 | - Fixed [Issue 10](https://github.com/timothycrosley/portray/issues/10) - Class methods rendered incorrectly. 93 | - Fixed [Issue 17](https://github.com/timothycrosley/portray/issues/17) - Portray silently requires README.md file. 94 | 95 | ## 1.0.5 - August 26 PM 2019 96 | Bug fix release 97 | 98 | - Fixed [Issue 6](https://github.com/timothycrosley/portray/issues/6) - Failed to open web-browser on startup. 99 | - Fixed [Issue 5](https://github.com/timothycrosley/portray/issues/5) - Some links are missing a trailing slash. 100 | - Fixed [Issue 4](https://github.com/timothycrosley/portray/issues/4) - Class references generate large code block. 101 | 102 | Big thanks to Marcel Hellkamp ([@defnull](https://github.com/defnull)) for fixing these issues. 103 | 104 | ## 1.0.0 - August 26 AM 2019 105 | Initial API stable release of portray 106 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Timothy Crosley 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 | [![portray - Your Project with Great Documentation.](https://raw.github.com/timothycrosley/portray/main/art/logo.png)](https://timothycrosley.github.io/portray/) 2 | _________________ 3 | 4 | [![PyPI version](https://badge.fury.io/py/portray.svg)](http://badge.fury.io/py/portray) 5 | [![Test Status](https://github.com/timothycrosley/portray/workflows/Test/badge.svg?branch=main)](https://github.com/timothycrosley/portray/actions?query=workflow%3ATest) 6 | [![Lint Status](https://github.com/timothycrosley/portray/workflows/Lint/badge.svg?branch=main)](https://github.com/timothycrosley/portray/actions?query=workflow%3ALint) 7 | [![codecov](https://codecov.io/gh/timothycrosley/portray/branch/main/graph/badge.svg)](https://codecov.io/gh/timothycrosley/portray) 8 | [![Join the chat at https://gitter.im/timothycrosley/portray](https://badges.gitter.im/timothycrosley/portray.svg)](https://gitter.im/timothycrosley/portray?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 9 | [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.python.org/pypi/portray/) 10 | [![Downloads](https://pepy.tech/badge/portray)](https://pepy.tech/project/portray) 11 | _________________ 12 | 13 | [Read Latest Documentation](https://timothycrosley.github.io/portray/) - [Browse GitHub Code Repository](https://github.com/timothycrosley/portray/) 14 | _________________ 15 | 16 | > The only thing worse than documentation never written, is documentation written but never discovered. 17 | 18 | **portray** is a Python3 command line tool and library that helps you create great documentation websites for your Python projects with as little effort as possible. 19 | 20 | [![Example Usage Gif](https://raw.githubusercontent.com/timothycrosley/portray/main/art/example.gif)](https://raw.githubusercontent.com/timothycrosley/portray/main/art/example.gif) 21 | 22 | Key Features: 23 | 24 | * **Zero-Config**: No configuration is necessary to use `portray`. Just run `portray` in the root of your Python project and it will find your documentation. 25 | * **Statically Generated**: `portray` websites are easy to host on GitHub pages and other similar services as they are outputted as standard static HTML websites. 26 | * **Markdown Aware**: `portray` will automatically include your project's `.md` files and render them into HTML. It will also find and render Markdown within `__doc__` strings. 27 | * **Fully Configurable**: While `portray` doesn't have to be configured, you still can fully configure it within the standard `pyproject.toml` file. 28 | * **Easily Programmable**: `portray` exposes a clean and simple [Python API](https://timothycrosley.github.io/portray/reference/portray/api/). 29 | * **Searchable**: Out of the box, `portray` makes all of your documentation, even autogenerated code references, free-text searchable. 30 | * **Themeable**: `portray` is compatible with all existing [MkDocs Themes](https://github.com/mkdocs/mkdocs/wiki/MkDocs-Themes). 31 | 32 | Under the hood, portray combines the [Markdown](https://commonmark.org/help/) documentation rendering capabilities provided by [MkDocs](https://www.mkdocs.org/) 33 | with the automatic reference documentation generated by [pdocs](https://timothycrosley.github.io/pdocs/). 34 | 35 | ## Quick Start 36 | 37 | The following guides should get you up and running with a documentation website in no time. 38 | 39 | 1. [Installation](https://timothycrosley.github.io/portray/docs/quick_start/1.-installation/) - TL;DR: Run `pip3 install portray` within your project's virtual environment. 40 | 2. [Command Line Usage](https://timothycrosley.github.io/portray/docs/quick_start/2.-cli/) - TL;DR: Run `portray in_browser` to test and `portray on_github_pages` to deploy. 41 | 3. [API Usage](https://timothycrosley.github.io/portray/docs/quick_start/3.-api/) - TL;DR: Everything available via the CLI is also easily available programmatically from within Python. 42 | 4. [Configuration](https://timothycrosley.github.io/portray/docs/quick_start/4.-configuration/) - TL;DR: Put all configuration within a `[tool.portray]` section of your `pyproject.toml` file. 43 | 44 | ## Why Create Portray? 45 | 46 | I create a lot of Python projects and I've always wanted a quick way to build documentation sites for them that included both manually written guides and autogenerated reference documentation. 47 | Tools have improved over the years, but still nothing has given me this experience out of the box. Portray isn't a competitor for the 2 newer Python documentation projects (pdocs and MkDocs), rather it is melding of them 48 | together to give me the quick to create and comprehensive documentation website generation I've always wanted. 49 | 50 | Thanks and I hope you too find `portray` useful! 51 | 52 | ~Timothy Crosley 53 | -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting / FAQ Guide 2 | 3 | As common issues or questions are encountered solutions will be added to this guide. 4 | 5 | ## `NoProjectFound` 6 | 7 | portray raises this exception when it cant find a project in the current directory. 8 | This means that there is no `pyproject.toml` or `setup.py` file in the directory you ran portray 9 | AND that you haven't specified modules to include on the command line. 10 | 11 | ### Solution 1: Go to root of project 12 | If you do have a `pyproject.toml` or `setup.py` file in your project, chances are you simply accidentally ran 13 | portray from a different directory. Simply changing back to your projects root directory at the same level as 14 | these files should be enough to resolve your issue. 15 | 16 | ### Solution 2: Create a pyproject.toml file 17 | You can create a simplified `pyproject.toml` file that explicitly specifies what modules are included in your project: 18 | 19 | ```toml 20 | [tool.portray] 21 | modules = ["MY_MODULE"] 22 | ``` 23 | 24 | ### Solution 3: Specify the modules manually from the command line 25 | Every CLI command supports explicitly setting one or more modules using `-m`: 26 | 27 | 28 | 29 | ## `Unable to identify repo_name, repo_url, and edit_uri automatically.` 30 | 31 | This is a warning that is returned when you haven't manually specified these configuration options 32 | and portray is unable to determine them automatically based on your project's repository. 33 | There are many reasons that this can happen, the simplest of which are that your project isn't source 34 | controlled or you utilize a source control system other than git. 35 | 36 | ### Solution 1: Ignore the warning 37 | These settings are optional and are used only by certain mkdocs themes to provide links back to your repository. 38 | 39 | ![Features that repo_url settings are used for](art/repo_url.png) 40 | 41 | If these features are not relevant to you, you can safely ignore the warning. 42 | 43 | ### Solution 2: Manually specify settings in `pyproject.toml` 44 | If for any reason portray is unable to determine the repository information from your repo automatically you can 45 | manually specify the settings within your config file: 46 | 47 | ```toml 48 | [tool.portray.mkdocs] 49 | repo_url = "MY_REPO_URL" 50 | repo_name = "MY_REPO_NAME" 51 | edit_uri = "MY_EDIT_URL" 52 | ``` 53 | 54 | For more information about these settings, refer to the [mkdocs reference documentation](https://www.mkdocs.org/user-guide/configuration/#repo_url). 55 | 56 | ## Deploying to Netlify 57 | 58 | portray includes a built-in command to deploy to [Github Pages](https://pages.github.com/) but it's also compatible with every static website host, including the popular [Netlify](https://www.netlify.com). 59 | There's a great guide on how to set this up contributed by @sw-yx [here](https://scotch.io/@sw-yx/python-the-jamstack). 60 | -------------------------------------------------------------------------------- /art/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/portray/f8b6d664f0c4d6923958f805fce1a1f94c5eaa6f/art/example.gif -------------------------------------------------------------------------------- /art/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/portray/f8b6d664f0c4d6923958f805fce1a1f94c5eaa6f/art/logo.png -------------------------------------------------------------------------------- /art/logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/portray/f8b6d664f0c4d6923958f805fce1a1f94c5eaa6f/art/logo.xcf -------------------------------------------------------------------------------- /art/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/portray/f8b6d664f0c4d6923958f805fce1a1f94c5eaa6f/art/logo_small.png -------------------------------------------------------------------------------- /art/repo_url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/portray/f8b6d664f0c4d6923958f805fce1a1f94c5eaa6f/art/repo_url.png -------------------------------------------------------------------------------- /docs/contributing/1.-contributing-guide.md: -------------------------------------------------------------------------------- 1 | Contributing to portray 2 | ======== 3 | 4 | Looking for a useful open source project to contribute to? 5 | Want your contributions to be warmly welcomed and acknowledged? 6 | Welcome! You have found the right place. 7 | 8 | ## Getting portray set up for local development 9 | The first step when contributing to any project is getting it set up on your local machine. portray aims to make this as simple as possible. 10 | 11 | Account Requirements: 12 | 13 | - [A valid GitHub account](https://github.com/join) 14 | 15 | Base System Requirements: 16 | 17 | - Python3.6+ 18 | - poetry 19 | - bash or a bash compatible shell (should be auto-installed on Linux / Mac) 20 | 21 | Once you have verified that you system matches the base requirements you can start to get the project working by following these steps: 22 | 23 | 1. [Fork the project on GitHub](https://github.com/timothycrosley/portray/fork). 24 | 2. Clone your fork to your local file system: 25 | `git clone https://github.com/$GITHUB_ACCOUNT/portray.git` 26 | 3. `cd portray` 27 | 4. `poetry install` 28 | 29 | ## Making a contribution 30 | Congrats! You're now ready to make a contribution! Use the following as a guide to help you reach a successful pull-request: 31 | 32 | 1. Check the [issues page](https://github.com/timothycrosley/portray/issues) on GitHub to see if the task you want to complete is listed there. 33 | - If it's listed there, write a comment letting others know you are working on it. 34 | - If it's not listed in GitHub issues, go ahead and log a new issue. Then add a comment letting everyone know you have it under control. 35 | - If you're not sure if it's something that is good for the main portray project and want immediate feedback, you can discuss it [here](https://gitter.im/timothycrosley/portray). 36 | 2. Create an issue branch for your local work `git checkout -b issue/$ISSUE-NUMBER`. 37 | 3. Do your magic here. 38 | 4. Ensure your code matches the [HOPE-8 Coding Standard](https://github.com/hugapi/HOPE/blob/master/all/HOPE-8--Style-Guide-for-Hug-Code.md#hope-8----style-guide-for-hug-code) used by the project. 39 | 5. Submit a pull request to the main project repository via GitHub. 40 | 41 | Thanks for the contribution! It will quickly get reviewed, and, once accepted, will result in your name being added to the acknowledgments list :). 42 | 43 | ## Thank you! 44 | I can not tell you how thankful I am for the hard work done by portray contributors like *you*. 45 | 46 | Thank you! 47 | 48 | ~Timothy Crosley 49 | 50 | -------------------------------------------------------------------------------- /docs/contributing/2.-coding-standard.md: -------------------------------------------------------------------------------- 1 | # HOPE 8 -- Style Guide for Hug Code 2 | 3 | | | | 4 | | ------------| ------------------------------------------- | 5 | | HOPE: | 8 | 6 | | Title: | Style Guide for Hug Code | 7 | | Author(s): | Timothy Crosley | 8 | | Status: | Active | 9 | | Type: | Process | 10 | | Created: | 19-May-2019 | 11 | | Updated: | 17-August-2019 | 12 | 13 | ## Introduction 14 | 15 | This document gives coding conventions for the Hug code comprising the Hug core as well as all official interfaces, extensions, and plugins for the framework. 16 | Optionally, projects that use Hug are encouraged to follow this HOPE and link to it as a reference. 17 | 18 | ## PEP 8 Foundation 19 | 20 | All guidelines in this document are in addition to those defined in Python's [PEP 8](https://www.python.org/dev/peps/pep-0008/) and [PEP 257](https://www.python.org/dev/peps/pep-0257/) guidelines. 21 | 22 | ## Line Length 23 | 24 | Too short of lines discourage descriptive variable names where they otherwise make sense. 25 | Too long of lines reduce overall readability and make it hard to compare 2 files side by side. 26 | There is no perfect number: but for Hug, we've decided to cap the lines at 100 characters. 27 | 28 | ## Descriptive Variable names 29 | 30 | Naming things is hard. Hug has a few strict guidelines on the usage of variable names, which hopefully will reduce some of the guesswork: 31 | - No one character variable names. 32 | - Except for x, y, and z as coordinates. 33 | - It's not okay to override built-in functions. 34 | - Except for `id`. Guido himself thought that shouldn't have been moved to the system module. It's too commonly used, and alternatives feel very artificial. 35 | - Avoid Acronyms, Abbreviations, or any other short forms - unless they are almost universally understand. 36 | 37 | ## Adding new modules 38 | 39 | New modules added to the a project that follows the HOPE-8 standard should all live directly within the base `PROJECT_NAME/` directory without nesting. If the modules are meant only for internal use within the project, they should be prefixed with a leading underscore. For example, def _internal_function. Modules should contain a docstring at the top that gives a general explanation of the purpose and then restates the project's use of the MIT license. 40 | There should be a `tests/test_$MODULE_NAME.py` file created to correspond to every new module that contains test coverage for the module. Ideally, tests should be 1:1 (one test object per code object, one test method per code method) to the extent cleanly possible. 41 | 42 | ## Automated Code Cleaners 43 | 44 | All code submitted to Hug should be formatted using Black and isort. 45 | Black should be run with the line length set to 100, and isort with Black compatible settings in place. 46 | 47 | ## Automated Code Linting 48 | 49 | All code submitted to hug should run through the following tools: 50 | 51 | - Black and isort verification. 52 | - Flake8 53 | - flake8-bugbear 54 | - Bandit 55 | - pep8-naming 56 | - vulture 57 | - safety 58 | -------------------------------------------------------------------------------- /docs/contributing/3.-code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # HOPE 11 -- Code of Conduct 2 | 3 | | | | 4 | | ------------| ------------------------------------------- | 5 | | HOPE: | 11 | 6 | | Title: | Code of Conduct | 7 | | Author(s): | Timothy Crosley | 8 | | Status: | Active | 9 | | Type: | Process | 10 | | Created: | 17-August-2019 | 11 | | Updated: | 17-August-2019 | 12 | 13 | ## Abstract 14 | 15 | Defines the Code of Conduct for Hug and all related projects. 16 | 17 | ## Our Pledge 18 | 19 | In the interest of fostering an open and welcoming environment, we as 20 | contributors and maintainers pledge to making participation in our project and 21 | our community a harassment-free experience for everyone, regardless of age, body 22 | size, disability, ethnicity, sex characteristics, gender identity and expression, 23 | level of experience, education, socio-economic status, nationality, personal 24 | appearance, race, religion, or sexual identity and orientation. 25 | 26 | ## Our Standards 27 | 28 | Examples of behavior that contributes to creating a positive environment 29 | include: 30 | 31 | * Using welcoming and inclusive language 32 | * Being respectful of differing viewpoints and experiences 33 | * Gracefully accepting constructive criticism 34 | * Focusing on what is best for the community 35 | * Showing empathy towards other community members 36 | 37 | Examples of unacceptable behavior by participants include: 38 | 39 | * The use of sexualized language or imagery and unwelcome sexual attention or 40 | advances 41 | * Trolling, insulting/derogatory comments, and personal or political attacks 42 | * Public or private harassment 43 | * Publishing others' private information, such as a physical or electronic 44 | address, without explicit permission 45 | * Other conduct which could reasonably be considered inappropriate in a 46 | professional setting 47 | 48 | ## Our Responsibilities 49 | 50 | Project maintainers are responsible for clarifying the standards of acceptable 51 | behavior and are expected to take appropriate and fair corrective action in 52 | response to any instances of unacceptable behavior. 53 | 54 | Project maintainers have the right and responsibility to remove, edit, or 55 | reject comments, commits, code, wiki edits, issues, and other contributions 56 | that are not aligned to this Code of Conduct, or to ban temporarily or 57 | permanently any contributor for other behaviors that they deem inappropriate, 58 | threatening, offensive, or harmful. 59 | 60 | ## Scope 61 | 62 | This Code of Conduct applies both within project spaces and in public spaces 63 | when an individual is representing the project or its community. Examples of 64 | representing a project or community include using an official project e-mail 65 | address, posting via an official social media account, or acting as an appointed 66 | representative at an online or offline event. Representation of a project may be 67 | further defined and clarified by project maintainers. 68 | 69 | ## Enforcement 70 | 71 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 72 | reported by contacting [timothy.crosley@gmail.com](mailto:timothy.crosley@gmail.com). All 73 | complaints will be reviewed and investigated and will result in a response that 74 | is deemed necessary and appropriate to the circumstances. Confidentiality will be maintained 75 | with regard to the reporter of an incident. 76 | Further details of specific enforcement policies may be posted separately. 77 | 78 | Project maintainers who do not follow or enforce the Code of Conduct in good 79 | faith may face temporary or permanent repercussions as determined by other 80 | members of the project's leadership. 81 | 82 | ## Attribution 83 | 84 | This Code of Conduct is adapted from the [Contributor Covenant][https://www.contributor-covenant.org], version 1.4, 85 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 86 | 87 | For answers to common questions about this code of conduct, see 88 | https://www.contributor-covenant.org/faq 89 | -------------------------------------------------------------------------------- /docs/contributing/4.-acknowledgements.md: -------------------------------------------------------------------------------- 1 | Contributors 2 | =================== 3 | 4 | ## Core Developers 5 | - Timothy Edmund Crosley (@timothycrosley) 6 | 7 | ## Notable Bug Reporters 8 | - 9 | 10 | ## Code Contributors 11 | - Marcel Hellkamp (@defnull) 12 | - Zach Valenta (@zachvalenta) 13 | - Henrik Blidh (@hbldh) 14 | - Tim Savannah (@kata198) 15 | - Felix Soubelet (@fsoubelet) 16 | - @ucodery 17 | - @floriandeboissieu 18 | 19 | ## Documenters 20 | - Arman Ashrafian (@arman-ashrafian) 21 | - Aksel Lenes aksell (@aksell) 22 | - Семён Марьясин (@MarSoft) 23 | 24 | -------------------------------------------- 25 | 26 | A sincere thanks to everyone who helps make portray into a great Python3 project! 27 | 28 | ~Timothy Crosley 29 | -------------------------------------------------------------------------------- /docs/quick_start/1.-installation.md: -------------------------------------------------------------------------------- 1 | Install `portray` into your projects virtual environment: 2 | 3 | `pip3 install portray` 4 | 5 | OR 6 | 7 | `poetry add portray` 8 | 9 | OR 10 | 11 | `pipenv install portray` 12 | 13 | 14 | 15 | Always run portray within the root of your project directory or specify a project directory from the command line using `--directory`. 16 | 17 | !!! info 18 | It's important that `portray` be installed in your project's environment as it needs to introspect your package to generate reference documentation. 19 | You must also have your project installed in the environment for this to work. 20 | -------------------------------------------------------------------------------- /docs/quick_start/2.-cli.md: -------------------------------------------------------------------------------- 1 | # Command Line Usage 2 | 3 | Once installed, `portray` exposes a simple command line utility for generating documentation websites. 4 | 5 | To verify the tool is installed correctly, run `portray` from the command line and you should be given the available commands and the version of portray installed. 6 | To get help for any individual subcommand run `portray SUBCOMMAND --help`: 7 | 8 | 9 | 10 | ## Serving Documentation Locally 11 | 12 | Before you push documentation out publicly, it's a good idea to test it locally and ensure it looks as desired. 13 | 14 | Running `portray server` will generate a new static website for your project in a temporary directory and start a local server to allow you to browse it (at localhost:8000 by default). 15 | 16 | 17 | 18 | Running `portray in_browser` will do the same as `portray server` but also open up the generated website in your environments default web browser for you. 19 | 20 | Both commands take an optional `--port` and `--host` argument. 21 | 22 | !!! tip 23 | `portray` does its best to auto determine package modules in order to generate documentation. It does this by looking at `pyproject.toml` and your directory name. 24 | If it can't auto determine the Python modules used by your project, you can set them manually in your `pyproject.toml` file: 25 | 26 | [tool.portray] 27 | modules = ["portray"] 28 | 29 | Or, if you want to avoid configuration files entirely, you can set modules explicitly on the command line using -m (Example: `portray server -m portray`.) 30 | Finally, portray pulls .md files from the root of your project and one dedicated documentation directory (defaulting to `docs`) by default. 31 | You can change the directory where docs are located by setting the `tool.portray.docs_dir` setting in `pyproject.toml`. 32 | 33 | ## Outputting Documentation Locally 34 | 35 | You can also output `portray`'s generated documentation to a local directory. 36 | To do so run `portray as_html`: 37 | 38 | 39 | 40 | By default the generated documentation is outputted into a `site` subdirectory. 41 | If this directory exists for any reason, including previous documentation generation, 42 | the command will fail. Passing in `--overwrite` will delete any existing directory 43 | before output to ensure the command passes. You can change the output directory using `-o DIRECTORY`. 44 | 45 | ## Pushing Documentation to GitHub Pages 46 | 47 | If you are using GitHub Pages to share your generated documentation you can use `portray on_github_pages` to automate the process: 48 | 49 | 50 | 51 | ## Verifying Project Configuration 52 | 53 | You can verify the configuration auto determined by `portray` using `portray project_configuration` in the root of your project: 54 | 55 | 56 | 57 | If anything seems incorrect, or you would want to modify any values, you can override any config option by setting it in the `[tool.portray]` section of your `pyproject.toml` file. 58 | -------------------------------------------------------------------------------- /docs/quick_start/3.-api.md: -------------------------------------------------------------------------------- 1 | # Programmatic Python API Usage 2 | 3 | Every command available from the command line utility is also available directly as function calls within Python. 4 | To use the Python API, `import portray` and then call the desired function call: 5 | 6 | 7 | 8 | Every function is type hinted and takes and returns only builtin Python objects. 9 | 10 | For a full definition of the API see the [API reference documentation](https://timothycrosley.github.io/portray/reference/portray/api/). 11 | -------------------------------------------------------------------------------- /docs/quick_start/4.-configuration.md: -------------------------------------------------------------------------------- 1 | # Configuring portray 2 | 3 | Hopefully, `portray`'s automatic configuration will pick up everything you need to document your project. 4 | If not, `portray` enables configuration for every aspect of your documentation website using Python's standard `pyproject.toml` file. 5 | Configuration options should be placed within a `tool.portray` section of your config file: 6 | 7 | ```toml 8 | [tool.portray] 9 | output_dir = "documentation_website" 10 | ``` 11 | 12 | `portray` itself comes with a handful of configuration options: 13 | 14 | - **docs_dir**: The directory (beyond your project root directory) where your markdown documentation is located. Defaults to `"docs"`. 15 | - **extra_dirs**: A list of additional directories to make available during static documentation building. Defaults to `["art", "images", "media"]`. 16 | - **extra_markdown_extensions**: A list of additional markdown extensions to use when rendering documentation as HTML. 17 | - **output_dir**: The directory to output your generated documentation website when using `portray as_html`. Defaults to `"site"`. 18 | - **port**: The port to use when serving your website locally. Defaults to `8000`. 19 | - **host**: The host to use when serving your website locally. Defaults to `127.0.0.1`. 20 | - **labels**: Label remappings for documentation pages. 21 | - **modules**: A List of Python modules to generate reference documentation for. 22 | - **append_directory_to_python_path**: If set to `true` (the default) appends the projects root directory to the PYTHON_PATH before producing documentation. 23 | - **include_reference_documentation**: If set to `true` (the default) automatic reference documentation is produced by pdocs to live alongside your manually written documentation. 24 | 25 | 26 | Beyond portray's direct configuration options, you can modify any of MkDocs or pdocs configuration options in the same `pyproject.toml` file. 27 | Simply nest their configuration under a `.mkdocs` or `.pdocs`. 28 | 29 | For example, to change the MkDocs theme configuration used for portray the following is set in `pyproject.toml`: 30 | 31 | ```toml 32 | [tool.portray.mkdocs.theme] 33 | favicon = "art/logo_small.png" 34 | logo = "art/logo_small.png" 35 | name = "material" 36 | palette = {primary = "blue grey", accent = "pink"} 37 | ``` 38 | 39 | Another example, if you are stuck on a legacy `master` branch, set the following in `pyproject.toml`: 40 | 41 | ```toml 42 | [tool.portray.mkdocs] 43 | edit_uri = "edit/master" 44 | ``` 45 | 46 | TOML doesn't behave 1:1 with YAML, and as a result, in some cases portray forces a stricter form of configuration options. 47 | For instance, to set up a custom navigation structure with portray, you can either specify just a flat list of file names, or a list of mappings of `{LABEL: FILENAME or LIST_OF_MAPPING}` but not a mix of both. 48 | This still allows as much customization as is permitted by mkdocs just with more consistency enforced. The recommended form is always to use lists of mappings. 49 | Here is what that would look like for portray's documentation site if it chose to specify the navigation manually: 50 | 51 | ```toml 52 | 53 | [[tool.portray.mkdocs.nav]] 54 | Home = "README.md" 55 | 56 | [[tool.portray.mkdocs.nav]] 57 | Changelog = "CHANGELOG.md" 58 | 59 | [[tool.portray.mkdocs.nav]] 60 | Troubleshooting = "TROUBLESHOOTING.md" 61 | 62 | [[tool.portray.mkdocs.nav]] 63 | [[tool.portray.mkdocs.nav.Contributing]] 64 | "1. Contributing Guide" = "docs/contributing/1.-contributing-guide.md" 65 | 66 | [[tool.portray.mkdocs.nav.Contributing]] 67 | "2. Coding Standard" = "docs/contributing/2.-coding-standard.md" 68 | 69 | [[tool.portray.mkdocs.nav.Contributing]] 70 | "3. Code of Conduct" = "docs/contributing/3.-code-of-conduct.md" 71 | 72 | [[tool.portray.mkdocs.nav.Contributing]] 73 | "4. Acknowledgements" = "docs/contributing/4.-acknowledgements.md" 74 | 75 | [[tool.portray.mkdocs.nav]] 76 | [[tool.portray.mkdocs.nav."Quick Start"]] 77 | "1. Installation" = "docs/quick_start/1.-installation.md" 78 | 79 | [[tool.portray.mkdocs.nav."Quick Start"]] 80 | "2. CLI" = "docs/quick_start/2.-cli.md" 81 | 82 | [[tool.portray.mkdocs.nav."Quick Start"]] 83 | "3. API" = "docs/quick_start/3.-api.md" 84 | 85 | [[tool.portray.mkdocs.nav."Quick Start"]] 86 | "4. Configuration" = "docs/quick_start/4.-configuration.md" 87 | ``` 88 | 89 | Note that the above utilizes [TOMLs double bracket syntax to specify a List of Dicts](https://github.com/toml-lang/toml/blob/master/README.md#user-content-array-of-tables). 90 | 91 | For more information about available configuration options see [MkDocs configuration](https://www.mkdocs.org/user-guide/configuration/) and [pdocs](https://timothycrosley.github.io/pdocs/docs/quick_start/2.-cli/). 92 | 93 | !!! warning 94 | While portray allows configuring any aspect of MkDocs or pdocs, it only allows that configuration to be defined in a `pyproject.toml` file. Any other configuration files normally used for these projects will be ignored. 95 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "appnope" 3 | version = "0.1.3" 4 | description = "Disable App Nap on macOS >= 10.9" 5 | category = "dev" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "arrow" 11 | version = "1.2.3" 12 | description = "Better dates & times for Python" 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=3.6" 16 | 17 | [package.dependencies] 18 | python-dateutil = ">=2.7.0" 19 | 20 | [[package]] 21 | name = "asttokens" 22 | version = "2.2.1" 23 | description = "Annotate AST trees with source code positions" 24 | category = "dev" 25 | optional = false 26 | python-versions = "*" 27 | 28 | [package.dependencies] 29 | six = "*" 30 | 31 | [package.extras] 32 | test = ["astroid", "pytest"] 33 | 34 | [[package]] 35 | name = "attrs" 36 | version = "22.2.0" 37 | description = "Classes Without Boilerplate" 38 | category = "dev" 39 | optional = false 40 | python-versions = ">=3.6" 41 | 42 | [package.extras] 43 | cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] 44 | dev = ["attrs[docs,tests]"] 45 | docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] 46 | tests = ["attrs[tests-no-zope]", "zope.interface"] 47 | tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] 48 | 49 | [[package]] 50 | name = "backcall" 51 | version = "0.2.0" 52 | description = "Specifications for callback functions passed in to an API" 53 | category = "dev" 54 | optional = false 55 | python-versions = "*" 56 | 57 | [[package]] 58 | name = "bandit" 59 | version = "1.7.4" 60 | description = "Security oriented static analyser for python code." 61 | category = "dev" 62 | optional = false 63 | python-versions = ">=3.7" 64 | 65 | [package.dependencies] 66 | colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} 67 | GitPython = ">=1.0.1" 68 | PyYAML = ">=5.3.1" 69 | stevedore = ">=1.20.0" 70 | 71 | [package.extras] 72 | test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml"] 73 | toml = ["toml"] 74 | yaml = ["PyYAML"] 75 | 76 | [[package]] 77 | name = "binaryornot" 78 | version = "0.4.4" 79 | description = "Ultra-lightweight pure Python package to check if a file is binary or text." 80 | category = "dev" 81 | optional = false 82 | python-versions = "*" 83 | 84 | [package.dependencies] 85 | chardet = ">=3.0.2" 86 | 87 | [[package]] 88 | name = "black" 89 | version = "22.12.0" 90 | description = "The uncompromising code formatter." 91 | category = "dev" 92 | optional = false 93 | python-versions = ">=3.7" 94 | 95 | [package.dependencies] 96 | click = ">=8.0.0" 97 | mypy-extensions = ">=0.4.3" 98 | pathspec = ">=0.9.0" 99 | platformdirs = ">=2" 100 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 101 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 102 | 103 | [package.extras] 104 | colorama = ["colorama (>=0.4.3)"] 105 | d = ["aiohttp (>=3.7.4)"] 106 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 107 | uvloop = ["uvloop (>=0.15.2)"] 108 | 109 | [[package]] 110 | name = "certifi" 111 | version = "2022.12.7" 112 | description = "Python package for providing Mozilla's CA Bundle." 113 | category = "main" 114 | optional = false 115 | python-versions = ">=3.6" 116 | 117 | [[package]] 118 | name = "chardet" 119 | version = "5.1.0" 120 | description = "Universal encoding detector for Python 3" 121 | category = "dev" 122 | optional = false 123 | python-versions = ">=3.7" 124 | 125 | [[package]] 126 | name = "charset-normalizer" 127 | version = "2.0.12" 128 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 129 | category = "main" 130 | optional = false 131 | python-versions = ">=3.5.0" 132 | 133 | [package.extras] 134 | unicode-backport = ["unicodedata2"] 135 | 136 | [[package]] 137 | name = "click" 138 | version = "8.1.3" 139 | description = "Composable command line interface toolkit" 140 | category = "main" 141 | optional = false 142 | python-versions = ">=3.7" 143 | 144 | [package.dependencies] 145 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 146 | 147 | [[package]] 148 | name = "colorama" 149 | version = "0.4.6" 150 | description = "Cross-platform colored terminal text." 151 | category = "main" 152 | optional = false 153 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 154 | 155 | [[package]] 156 | name = "cookiecutter" 157 | version = "2.1.1" 158 | description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." 159 | category = "dev" 160 | optional = false 161 | python-versions = ">=3.7" 162 | 163 | [package.dependencies] 164 | binaryornot = ">=0.4.4" 165 | click = ">=7.0,<9.0.0" 166 | Jinja2 = ">=2.7,<4.0.0" 167 | jinja2-time = ">=0.2.0" 168 | python-slugify = ">=4.0.0" 169 | pyyaml = ">=5.3.1" 170 | requests = ">=2.23.0" 171 | 172 | [[package]] 173 | name = "coverage" 174 | version = "7.0.0" 175 | description = "Code coverage measurement for Python" 176 | category = "dev" 177 | optional = false 178 | python-versions = ">=3.7" 179 | 180 | [package.dependencies] 181 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 182 | 183 | [package.extras] 184 | toml = ["tomli"] 185 | 186 | [[package]] 187 | name = "cruft" 188 | version = "2.11.1" 189 | description = "Allows you to maintain all the necessary cruft for packaging and building projects separate from the code you intentionally write. Built on-top of CookieCutter." 190 | category = "dev" 191 | optional = false 192 | python-versions = ">=3.7,<4.0" 193 | 194 | [package.dependencies] 195 | click = ">=7.1.2,<9.0.0" 196 | cookiecutter = ">=1.7" 197 | gitpython = ">=3.0,<4.0" 198 | typer = ">=0.4.0,<0.7" 199 | 200 | [package.extras] 201 | examples = ["examples (>=1.0.2,<2.0.0)"] 202 | pyproject = ["toml (>=0.10,<0.11)"] 203 | 204 | [[package]] 205 | name = "decorator" 206 | version = "5.1.1" 207 | description = "Decorators for Humans" 208 | category = "dev" 209 | optional = false 210 | python-versions = ">=3.5" 211 | 212 | [[package]] 213 | name = "docstring-parser" 214 | version = "0.13" 215 | description = "\"Parse Python docstrings in reST, Google and Numpydoc format\"" 216 | category = "main" 217 | optional = false 218 | python-versions = ">=3.6" 219 | 220 | [package.extras] 221 | test = ["black", "pytest"] 222 | 223 | [[package]] 224 | name = "dparse" 225 | version = "0.6.2" 226 | description = "A parser for Python dependency files" 227 | category = "dev" 228 | optional = false 229 | python-versions = ">=3.5" 230 | 231 | [package.dependencies] 232 | packaging = "*" 233 | toml = "*" 234 | 235 | [package.extras] 236 | conda = ["pyyaml"] 237 | pipenv = ["pipenv"] 238 | 239 | [[package]] 240 | name = "exceptiongroup" 241 | version = "1.0.4" 242 | description = "Backport of PEP 654 (exception groups)" 243 | category = "dev" 244 | optional = false 245 | python-versions = ">=3.7" 246 | 247 | [package.extras] 248 | test = ["pytest (>=6)"] 249 | 250 | [[package]] 251 | name = "executing" 252 | version = "1.2.0" 253 | description = "Get the currently executing AST node of a frame, and other information" 254 | category = "dev" 255 | optional = false 256 | python-versions = "*" 257 | 258 | [package.extras] 259 | tests = ["asttokens", "littleutils", "pytest", "rich"] 260 | 261 | [[package]] 262 | name = "falcon" 263 | version = "2.0.0" 264 | description = "An unladen web framework for building APIs and app backends." 265 | category = "main" 266 | optional = false 267 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 268 | 269 | [[package]] 270 | name = "flake8" 271 | version = "5.0.4" 272 | description = "the modular source code checker: pep8 pyflakes and co" 273 | category = "dev" 274 | optional = false 275 | python-versions = ">=3.6.1" 276 | 277 | [package.dependencies] 278 | mccabe = ">=0.7.0,<0.8.0" 279 | pycodestyle = ">=2.9.0,<2.10.0" 280 | pyflakes = ">=2.5.0,<2.6.0" 281 | 282 | [[package]] 283 | name = "flake8-bugbear" 284 | version = "22.12.6" 285 | description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." 286 | category = "dev" 287 | optional = false 288 | python-versions = ">=3.7" 289 | 290 | [package.dependencies] 291 | attrs = ">=19.2.0" 292 | flake8 = ">=3.0.0" 293 | 294 | [package.extras] 295 | dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "tox"] 296 | 297 | [[package]] 298 | name = "ghp-import" 299 | version = "2.1.0" 300 | description = "Copy your docs directly to the gh-pages branch." 301 | category = "main" 302 | optional = false 303 | python-versions = "*" 304 | 305 | [package.dependencies] 306 | python-dateutil = ">=2.8.1" 307 | 308 | [package.extras] 309 | dev = ["flake8", "markdown", "twine", "wheel"] 310 | 311 | [[package]] 312 | name = "gitdb" 313 | version = "4.0.10" 314 | description = "Git Object Database" 315 | category = "main" 316 | optional = false 317 | python-versions = ">=3.7" 318 | 319 | [package.dependencies] 320 | smmap = ">=3.0.1,<6" 321 | 322 | [[package]] 323 | name = "gitpython" 324 | version = "3.1.29" 325 | description = "GitPython is a python library used to interact with Git repositories" 326 | category = "main" 327 | optional = false 328 | python-versions = ">=3.7" 329 | 330 | [package.dependencies] 331 | gitdb = ">=4.0.1,<5" 332 | 333 | [[package]] 334 | name = "hug" 335 | version = "2.6.1" 336 | description = "A Python framework that makes developing APIs as simple as possible, but no simpler." 337 | category = "main" 338 | optional = false 339 | python-versions = ">=3.5" 340 | 341 | [package.dependencies] 342 | falcon = "2.0.0" 343 | requests = "*" 344 | 345 | [[package]] 346 | name = "hypothesis" 347 | version = "6.61.0" 348 | description = "A library for property-based testing" 349 | category = "dev" 350 | optional = false 351 | python-versions = ">=3.7" 352 | 353 | [package.dependencies] 354 | attrs = ">=19.2.0" 355 | exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 356 | sortedcontainers = ">=2.1.0,<3.0.0" 357 | 358 | [package.extras] 359 | all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "django (>=3.2)", "dpcontracts (>=0.4)", "importlib-metadata (>=3.6)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=1.0)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2022.7)"] 360 | cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] 361 | codemods = ["libcst (>=0.3.16)"] 362 | dateutil = ["python-dateutil (>=1.4)"] 363 | django = ["django (>=3.2)"] 364 | dpcontracts = ["dpcontracts (>=0.4)"] 365 | ghostwriter = ["black (>=19.10b0)"] 366 | lark = ["lark (>=0.10.1)"] 367 | numpy = ["numpy (>=1.9.0)"] 368 | pandas = ["pandas (>=1.0)"] 369 | pytest = ["pytest (>=4.6)"] 370 | pytz = ["pytz (>=2014.1)"] 371 | redis = ["redis (>=3.0.0)"] 372 | zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2022.7)"] 373 | 374 | [[package]] 375 | name = "hypothesis-auto" 376 | version = "1.1.4" 377 | description = "Extends Hypothesis to add fully automatic testing of type annotated functions" 378 | category = "dev" 379 | optional = false 380 | python-versions = ">=3.6,<4.0" 381 | 382 | [package.dependencies] 383 | hypothesis = ">=4.36" 384 | pydantic = ">=0.32.2" 385 | 386 | [package.extras] 387 | pytest = ["pytest (>=4.0.0,<5.0.0)"] 388 | 389 | [[package]] 390 | name = "idna" 391 | version = "3.4" 392 | description = "Internationalized Domain Names in Applications (IDNA)" 393 | category = "main" 394 | optional = false 395 | python-versions = ">=3.5" 396 | 397 | [[package]] 398 | name = "importlib-metadata" 399 | version = "5.2.0" 400 | description = "Read metadata from Python packages" 401 | category = "main" 402 | optional = false 403 | python-versions = ">=3.7" 404 | 405 | [package.dependencies] 406 | zipp = ">=0.5" 407 | 408 | [package.extras] 409 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 410 | perf = ["ipython"] 411 | testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] 412 | 413 | [[package]] 414 | name = "iniconfig" 415 | version = "1.1.1" 416 | description = "iniconfig: brain-dead simple config-ini parsing" 417 | category = "dev" 418 | optional = false 419 | python-versions = "*" 420 | 421 | [[package]] 422 | name = "ipython" 423 | version = "8.7.0" 424 | description = "IPython: Productive Interactive Computing" 425 | category = "dev" 426 | optional = false 427 | python-versions = ">=3.8" 428 | 429 | [package.dependencies] 430 | appnope = {version = "*", markers = "sys_platform == \"darwin\""} 431 | backcall = "*" 432 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 433 | decorator = "*" 434 | jedi = ">=0.16" 435 | matplotlib-inline = "*" 436 | pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} 437 | pickleshare = "*" 438 | prompt-toolkit = ">=3.0.11,<3.1.0" 439 | pygments = ">=2.4.0" 440 | stack-data = "*" 441 | traitlets = ">=5" 442 | 443 | [package.extras] 444 | all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.20)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] 445 | black = ["black"] 446 | doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] 447 | kernel = ["ipykernel"] 448 | nbconvert = ["nbconvert"] 449 | nbformat = ["nbformat"] 450 | notebook = ["ipywidgets", "notebook"] 451 | parallel = ["ipyparallel"] 452 | qtconsole = ["qtconsole"] 453 | test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] 454 | test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.20)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] 455 | 456 | [[package]] 457 | name = "isort" 458 | version = "5.11.4" 459 | description = "A Python utility / library to sort Python imports." 460 | category = "dev" 461 | optional = false 462 | python-versions = ">=3.7.0" 463 | 464 | [package.extras] 465 | colors = ["colorama (>=0.4.3,<0.5.0)"] 466 | pipfile-deprecated-finder = ["pipreqs", "requirementslib"] 467 | plugins = ["setuptools"] 468 | requirements-deprecated-finder = ["pip-api", "pipreqs"] 469 | 470 | [[package]] 471 | name = "jedi" 472 | version = "0.18.2" 473 | description = "An autocompletion tool for Python that can be used for text editors." 474 | category = "dev" 475 | optional = false 476 | python-versions = ">=3.6" 477 | 478 | [package.dependencies] 479 | parso = ">=0.8.0,<0.9.0" 480 | 481 | [package.extras] 482 | docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] 483 | qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] 484 | testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] 485 | 486 | [[package]] 487 | name = "jinja2" 488 | version = "3.1.2" 489 | description = "A very fast and expressive template engine." 490 | category = "main" 491 | optional = false 492 | python-versions = ">=3.7" 493 | 494 | [package.dependencies] 495 | MarkupSafe = ">=2.0" 496 | 497 | [package.extras] 498 | i18n = ["Babel (>=2.7)"] 499 | 500 | [[package]] 501 | name = "jinja2-time" 502 | version = "0.2.0" 503 | description = "Jinja2 Extension for Dates and Times" 504 | category = "dev" 505 | optional = false 506 | python-versions = "*" 507 | 508 | [package.dependencies] 509 | arrow = "*" 510 | jinja2 = "*" 511 | 512 | [[package]] 513 | name = "livereload" 514 | version = "2.6.3" 515 | description = "Python LiveReload is an awesome tool for web developers" 516 | category = "main" 517 | optional = false 518 | python-versions = "*" 519 | 520 | [package.dependencies] 521 | six = "*" 522 | tornado = {version = "*", markers = "python_version > \"2.7\""} 523 | 524 | [[package]] 525 | name = "mako" 526 | version = "1.2.4" 527 | description = "A super-fast templating language that borrows the best ideas from the existing templating languages." 528 | category = "main" 529 | optional = false 530 | python-versions = ">=3.7" 531 | 532 | [package.dependencies] 533 | MarkupSafe = ">=0.9.2" 534 | 535 | [package.extras] 536 | babel = ["Babel"] 537 | lingua = ["lingua"] 538 | testing = ["pytest"] 539 | 540 | [[package]] 541 | name = "markdown" 542 | version = "3.3.7" 543 | description = "Python implementation of Markdown." 544 | category = "main" 545 | optional = false 546 | python-versions = ">=3.6" 547 | 548 | [package.dependencies] 549 | importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} 550 | 551 | [package.extras] 552 | testing = ["coverage", "pyyaml"] 553 | 554 | [[package]] 555 | name = "markupsafe" 556 | version = "2.1.1" 557 | description = "Safely add untrusted strings to HTML/XML markup." 558 | category = "main" 559 | optional = false 560 | python-versions = ">=3.7" 561 | 562 | [[package]] 563 | name = "matplotlib-inline" 564 | version = "0.1.6" 565 | description = "Inline Matplotlib backend for Jupyter" 566 | category = "dev" 567 | optional = false 568 | python-versions = ">=3.5" 569 | 570 | [package.dependencies] 571 | traitlets = "*" 572 | 573 | [[package]] 574 | name = "mccabe" 575 | version = "0.7.0" 576 | description = "McCabe checker, plugin for flake8" 577 | category = "dev" 578 | optional = false 579 | python-versions = ">=3.6" 580 | 581 | [[package]] 582 | name = "mergedeep" 583 | version = "1.3.4" 584 | description = "A deep merge function for 🐍." 585 | category = "main" 586 | optional = false 587 | python-versions = ">=3.6" 588 | 589 | [[package]] 590 | name = "mkdocs" 591 | version = "1.3.1" 592 | description = "Project documentation with Markdown." 593 | category = "main" 594 | optional = false 595 | python-versions = ">=3.6" 596 | 597 | [package.dependencies] 598 | click = ">=3.3" 599 | ghp-import = ">=1.0" 600 | importlib-metadata = ">=4.3" 601 | Jinja2 = ">=2.10.2" 602 | Markdown = ">=3.2.1,<3.4" 603 | mergedeep = ">=1.3.4" 604 | packaging = ">=20.5" 605 | PyYAML = ">=3.10" 606 | pyyaml-env-tag = ">=0.1" 607 | watchdog = ">=2.0" 608 | 609 | [package.extras] 610 | i18n = ["babel (>=2.9.0)"] 611 | 612 | [[package]] 613 | name = "mkdocs-material" 614 | version = "8.5.4" 615 | description = "Documentation that simply works" 616 | category = "main" 617 | optional = false 618 | python-versions = ">=3.7" 619 | 620 | [package.dependencies] 621 | jinja2 = ">=3.0.2" 622 | markdown = ">=3.2" 623 | mkdocs = ">=1.3.0" 624 | mkdocs-material-extensions = ">=1.0.3" 625 | pygments = ">=2.12" 626 | pymdown-extensions = ">=9.4" 627 | requests = ">=2.26" 628 | 629 | [[package]] 630 | name = "mkdocs-material-extensions" 631 | version = "1.1.1" 632 | description = "Extension pack for Python Markdown and MkDocs Material." 633 | category = "main" 634 | optional = false 635 | python-versions = ">=3.7" 636 | 637 | [[package]] 638 | name = "mypy" 639 | version = "0.991" 640 | description = "Optional static typing for Python" 641 | category = "dev" 642 | optional = false 643 | python-versions = ">=3.7" 644 | 645 | [package.dependencies] 646 | mypy-extensions = ">=0.4.3" 647 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 648 | typing-extensions = ">=3.10" 649 | 650 | [package.extras] 651 | dmypy = ["psutil (>=4.0)"] 652 | install-types = ["pip"] 653 | python2 = ["typed-ast (>=1.4.0,<2)"] 654 | reports = ["lxml"] 655 | 656 | [[package]] 657 | name = "mypy-extensions" 658 | version = "0.4.3" 659 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 660 | category = "dev" 661 | optional = false 662 | python-versions = "*" 663 | 664 | [[package]] 665 | name = "packaging" 666 | version = "22.0" 667 | description = "Core utilities for Python packages" 668 | category = "main" 669 | optional = false 670 | python-versions = ">=3.7" 671 | 672 | [[package]] 673 | name = "parso" 674 | version = "0.8.3" 675 | description = "A Python Parser" 676 | category = "dev" 677 | optional = false 678 | python-versions = ">=3.6" 679 | 680 | [package.extras] 681 | qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] 682 | testing = ["docopt", "pytest (<6.0.0)"] 683 | 684 | [[package]] 685 | name = "pathspec" 686 | version = "0.10.3" 687 | description = "Utility library for gitignore style pattern matching of file paths." 688 | category = "dev" 689 | optional = false 690 | python-versions = ">=3.7" 691 | 692 | [[package]] 693 | name = "pbr" 694 | version = "5.11.0" 695 | description = "Python Build Reasonableness" 696 | category = "dev" 697 | optional = false 698 | python-versions = ">=2.6" 699 | 700 | [[package]] 701 | name = "pdocs" 702 | version = "1.2.0" 703 | description = "A simple program and library to auto generate API documentation for Python modules." 704 | category = "main" 705 | optional = false 706 | python-versions = ">=3.7" 707 | 708 | [package.dependencies] 709 | docstring_parser = ">=0.7.2" 710 | hug = ">=2.6" 711 | Mako = ">1.2.2" 712 | Markdown = ">=3.0.0" 713 | 714 | [[package]] 715 | name = "pep8-naming" 716 | version = "0.13.3" 717 | description = "Check PEP-8 naming conventions, plugin for flake8" 718 | category = "dev" 719 | optional = false 720 | python-versions = ">=3.7" 721 | 722 | [package.dependencies] 723 | flake8 = ">=5.0.0" 724 | 725 | [[package]] 726 | name = "pexpect" 727 | version = "4.8.0" 728 | description = "Pexpect allows easy control of interactive console applications." 729 | category = "dev" 730 | optional = false 731 | python-versions = "*" 732 | 733 | [package.dependencies] 734 | ptyprocess = ">=0.5" 735 | 736 | [[package]] 737 | name = "pickleshare" 738 | version = "0.7.5" 739 | description = "Tiny 'shelve'-like database with concurrency support" 740 | category = "dev" 741 | optional = false 742 | python-versions = "*" 743 | 744 | [[package]] 745 | name = "platformdirs" 746 | version = "2.6.0" 747 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 748 | category = "dev" 749 | optional = false 750 | python-versions = ">=3.7" 751 | 752 | [package.extras] 753 | docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"] 754 | test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] 755 | 756 | [[package]] 757 | name = "pluggy" 758 | version = "1.0.0" 759 | description = "plugin and hook calling mechanisms for python" 760 | category = "dev" 761 | optional = false 762 | python-versions = ">=3.6" 763 | 764 | [package.extras] 765 | dev = ["pre-commit", "tox"] 766 | testing = ["pytest", "pytest-benchmark"] 767 | 768 | [[package]] 769 | name = "pprofile" 770 | version = "2.1.0" 771 | description = "Line-granularity, thread-aware deterministic and statistic pure-python profiler" 772 | category = "dev" 773 | optional = false 774 | python-versions = "*" 775 | 776 | [[package]] 777 | name = "prompt-toolkit" 778 | version = "3.0.36" 779 | description = "Library for building powerful interactive command lines in Python" 780 | category = "dev" 781 | optional = false 782 | python-versions = ">=3.6.2" 783 | 784 | [package.dependencies] 785 | wcwidth = "*" 786 | 787 | [[package]] 788 | name = "ptyprocess" 789 | version = "0.7.0" 790 | description = "Run a subprocess in a pseudo terminal" 791 | category = "dev" 792 | optional = false 793 | python-versions = "*" 794 | 795 | [[package]] 796 | name = "pure-eval" 797 | version = "0.2.2" 798 | description = "Safely evaluate AST nodes without side effects" 799 | category = "dev" 800 | optional = false 801 | python-versions = "*" 802 | 803 | [package.extras] 804 | tests = ["pytest"] 805 | 806 | [[package]] 807 | name = "pycodestyle" 808 | version = "2.9.1" 809 | description = "Python style guide checker" 810 | category = "dev" 811 | optional = false 812 | python-versions = ">=3.6" 813 | 814 | [[package]] 815 | name = "pydantic" 816 | version = "1.10.2" 817 | description = "Data validation and settings management using python type hints" 818 | category = "dev" 819 | optional = false 820 | python-versions = ">=3.7" 821 | 822 | [package.dependencies] 823 | typing-extensions = ">=4.1.0" 824 | 825 | [package.extras] 826 | dotenv = ["python-dotenv (>=0.10.4)"] 827 | email = ["email-validator (>=1.0.3)"] 828 | 829 | [[package]] 830 | name = "pyflakes" 831 | version = "2.5.0" 832 | description = "passive checker of Python programs" 833 | category = "dev" 834 | optional = false 835 | python-versions = ">=3.6" 836 | 837 | [[package]] 838 | name = "pygments" 839 | version = "2.13.0" 840 | description = "Pygments is a syntax highlighting package written in Python." 841 | category = "main" 842 | optional = false 843 | python-versions = ">=3.6" 844 | 845 | [package.extras] 846 | plugins = ["importlib-metadata"] 847 | 848 | [[package]] 849 | name = "pymdown-extensions" 850 | version = "9.9" 851 | description = "Extension pack for Python Markdown." 852 | category = "main" 853 | optional = false 854 | python-versions = ">=3.7" 855 | 856 | [package.dependencies] 857 | markdown = ">=3.2" 858 | 859 | [[package]] 860 | name = "pytest" 861 | version = "7.2.0" 862 | description = "pytest: simple powerful testing with Python" 863 | category = "dev" 864 | optional = false 865 | python-versions = ">=3.7" 866 | 867 | [package.dependencies] 868 | attrs = ">=19.2.0" 869 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 870 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 871 | iniconfig = "*" 872 | packaging = "*" 873 | pluggy = ">=0.12,<2.0" 874 | tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 875 | 876 | [package.extras] 877 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 878 | 879 | [[package]] 880 | name = "pytest-cov" 881 | version = "4.0.0" 882 | description = "Pytest plugin for measuring coverage." 883 | category = "dev" 884 | optional = false 885 | python-versions = ">=3.6" 886 | 887 | [package.dependencies] 888 | coverage = {version = ">=5.2.1", extras = ["toml"]} 889 | pytest = ">=4.6" 890 | 891 | [package.extras] 892 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] 893 | 894 | [[package]] 895 | name = "pytest-mock" 896 | version = "3.10.0" 897 | description = "Thin-wrapper around the mock package for easier use with pytest" 898 | category = "dev" 899 | optional = false 900 | python-versions = ">=3.7" 901 | 902 | [package.dependencies] 903 | pytest = ">=5.0" 904 | 905 | [package.extras] 906 | dev = ["pre-commit", "pytest-asyncio", "tox"] 907 | 908 | [[package]] 909 | name = "python-dateutil" 910 | version = "2.8.2" 911 | description = "Extensions to the standard Python datetime module" 912 | category = "main" 913 | optional = false 914 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 915 | 916 | [package.dependencies] 917 | six = ">=1.5" 918 | 919 | [[package]] 920 | name = "python-slugify" 921 | version = "7.0.0" 922 | description = "A Python slugify application that also handles Unicode" 923 | category = "dev" 924 | optional = false 925 | python-versions = ">=3.7" 926 | 927 | [package.dependencies] 928 | text-unidecode = ">=1.3" 929 | 930 | [package.extras] 931 | unidecode = ["Unidecode (>=1.1.1)"] 932 | 933 | [[package]] 934 | name = "pyyaml" 935 | version = "6.0" 936 | description = "YAML parser and emitter for Python" 937 | category = "main" 938 | optional = false 939 | python-versions = ">=3.6" 940 | 941 | [[package]] 942 | name = "pyyaml-env-tag" 943 | version = "0.1" 944 | description = "A custom YAML tag for referencing environment variables in YAML files. " 945 | category = "main" 946 | optional = false 947 | python-versions = ">=3.6" 948 | 949 | [package.dependencies] 950 | pyyaml = "*" 951 | 952 | [[package]] 953 | name = "requests" 954 | version = "2.27.1" 955 | description = "Python HTTP for Humans." 956 | category = "main" 957 | optional = false 958 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 959 | 960 | [package.dependencies] 961 | certifi = ">=2017.4.17" 962 | charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} 963 | idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} 964 | urllib3 = ">=1.21.1,<1.27" 965 | 966 | [package.extras] 967 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 968 | use-chardet-on-py3 = ["chardet (>=3.0.2,<5)"] 969 | 970 | [[package]] 971 | name = "ruamel-yaml" 972 | version = "0.17.21" 973 | description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" 974 | category = "dev" 975 | optional = false 976 | python-versions = ">=3" 977 | 978 | [package.dependencies] 979 | "ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} 980 | 981 | [package.extras] 982 | docs = ["ryd"] 983 | jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] 984 | 985 | [[package]] 986 | name = "ruamel-yaml-clib" 987 | version = "0.2.7" 988 | description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" 989 | category = "dev" 990 | optional = false 991 | python-versions = ">=3.5" 992 | 993 | [[package]] 994 | name = "ruff" 995 | version = "0.0.191" 996 | description = "An extremely fast Python linter, written in Rust." 997 | category = "dev" 998 | optional = false 999 | python-versions = ">=3.7" 1000 | 1001 | [[package]] 1002 | name = "safety" 1003 | version = "2.3.4" 1004 | description = "Checks installed dependencies for known vulnerabilities and licenses." 1005 | category = "dev" 1006 | optional = false 1007 | python-versions = "*" 1008 | 1009 | [package.dependencies] 1010 | Click = ">=8.0.2" 1011 | dparse = ">=0.6.2" 1012 | packaging = ">=21.0" 1013 | requests = "*" 1014 | "ruamel.yaml" = ">=0.17.21" 1015 | setuptools = ">=19.3" 1016 | 1017 | [package.extras] 1018 | github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] 1019 | gitlab = ["python-gitlab (>=1.3.0)"] 1020 | 1021 | [[package]] 1022 | name = "setuptools" 1023 | version = "65.6.3" 1024 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 1025 | category = "dev" 1026 | optional = false 1027 | python-versions = ">=3.7" 1028 | 1029 | [package.extras] 1030 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] 1031 | testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] 1032 | testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] 1033 | 1034 | [[package]] 1035 | name = "six" 1036 | version = "1.16.0" 1037 | description = "Python 2 and 3 compatibility utilities" 1038 | category = "main" 1039 | optional = false 1040 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 1041 | 1042 | [[package]] 1043 | name = "smmap" 1044 | version = "5.0.0" 1045 | description = "A pure Python implementation of a sliding window memory map manager" 1046 | category = "main" 1047 | optional = false 1048 | python-versions = ">=3.6" 1049 | 1050 | [[package]] 1051 | name = "sortedcontainers" 1052 | version = "2.4.0" 1053 | description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" 1054 | category = "dev" 1055 | optional = false 1056 | python-versions = "*" 1057 | 1058 | [[package]] 1059 | name = "stack-data" 1060 | version = "0.6.2" 1061 | description = "Extract data from python stack frames and tracebacks for informative displays" 1062 | category = "dev" 1063 | optional = false 1064 | python-versions = "*" 1065 | 1066 | [package.dependencies] 1067 | asttokens = ">=2.1.0" 1068 | executing = ">=1.2.0" 1069 | pure-eval = "*" 1070 | 1071 | [package.extras] 1072 | tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] 1073 | 1074 | [[package]] 1075 | name = "stevedore" 1076 | version = "4.1.1" 1077 | description = "Manage dynamic plugins for Python applications" 1078 | category = "dev" 1079 | optional = false 1080 | python-versions = ">=3.8" 1081 | 1082 | [package.dependencies] 1083 | pbr = ">=2.0.0,<2.1.0 || >2.1.0" 1084 | 1085 | [[package]] 1086 | name = "text-unidecode" 1087 | version = "1.3" 1088 | description = "The most basic Text::Unidecode port" 1089 | category = "dev" 1090 | optional = false 1091 | python-versions = "*" 1092 | 1093 | [[package]] 1094 | name = "toml" 1095 | version = "0.10.2" 1096 | description = "Python Library for Tom's Obvious, Minimal Language" 1097 | category = "main" 1098 | optional = false 1099 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 1100 | 1101 | [[package]] 1102 | name = "tomli" 1103 | version = "2.0.1" 1104 | description = "A lil' TOML parser" 1105 | category = "dev" 1106 | optional = false 1107 | python-versions = ">=3.7" 1108 | 1109 | [[package]] 1110 | name = "tornado" 1111 | version = "6.2" 1112 | description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." 1113 | category = "main" 1114 | optional = false 1115 | python-versions = ">= 3.7" 1116 | 1117 | [[package]] 1118 | name = "traitlets" 1119 | version = "5.8.0" 1120 | description = "Traitlets Python configuration system" 1121 | category = "dev" 1122 | optional = false 1123 | python-versions = ">=3.7" 1124 | 1125 | [package.extras] 1126 | docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] 1127 | test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] 1128 | 1129 | [[package]] 1130 | name = "typer" 1131 | version = "0.6.1" 1132 | description = "Typer, build great CLIs. Easy to code. Based on Python type hints." 1133 | category = "dev" 1134 | optional = false 1135 | python-versions = ">=3.6" 1136 | 1137 | [package.dependencies] 1138 | click = ">=7.1.1,<9.0.0" 1139 | 1140 | [package.extras] 1141 | all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] 1142 | dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] 1143 | doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)"] 1144 | test = ["black (>=22.3.0,<23.0.0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"] 1145 | 1146 | [[package]] 1147 | name = "types-toml" 1148 | version = "0.10.8.1" 1149 | description = "Typing stubs for toml" 1150 | category = "dev" 1151 | optional = false 1152 | python-versions = "*" 1153 | 1154 | [[package]] 1155 | name = "typing-extensions" 1156 | version = "4.4.0" 1157 | description = "Backported and Experimental Type Hints for Python 3.7+" 1158 | category = "dev" 1159 | optional = false 1160 | python-versions = ">=3.7" 1161 | 1162 | [[package]] 1163 | name = "urllib3" 1164 | version = "1.26.13" 1165 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1166 | category = "main" 1167 | optional = false 1168 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 1169 | 1170 | [package.extras] 1171 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] 1172 | secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] 1173 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 1174 | 1175 | [[package]] 1176 | name = "vulture" 1177 | version = "2.6" 1178 | description = "Find dead code" 1179 | category = "dev" 1180 | optional = false 1181 | python-versions = ">=3.6" 1182 | 1183 | [package.dependencies] 1184 | toml = "*" 1185 | 1186 | [[package]] 1187 | name = "watchdog" 1188 | version = "2.2.0" 1189 | description = "Filesystem events monitoring" 1190 | category = "main" 1191 | optional = false 1192 | python-versions = ">=3.6" 1193 | 1194 | [package.extras] 1195 | watchmedo = ["PyYAML (>=3.10)"] 1196 | 1197 | [[package]] 1198 | name = "wcwidth" 1199 | version = "0.2.5" 1200 | description = "Measures the displayed width of unicode strings in a terminal" 1201 | category = "dev" 1202 | optional = false 1203 | python-versions = "*" 1204 | 1205 | [[package]] 1206 | name = "yaspin" 1207 | version = "1.5.0" 1208 | description = "Yet Another Terminal Spinner" 1209 | category = "main" 1210 | optional = false 1211 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 1212 | 1213 | [[package]] 1214 | name = "zipp" 1215 | version = "3.11.0" 1216 | description = "Backport of pathlib-compatible object wrapper for zip files" 1217 | category = "main" 1218 | optional = false 1219 | python-versions = ">=3.7" 1220 | 1221 | [package.extras] 1222 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] 1223 | testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] 1224 | 1225 | [metadata] 1226 | lock-version = "1.1" 1227 | python-versions = ">=3.8" 1228 | content-hash = "9ee8b130d7088fe4d14c4c4dbe02971777d763a79126d22dbf82b921b690fdd7" 1229 | 1230 | [metadata.files] 1231 | appnope = [ 1232 | {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, 1233 | {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, 1234 | ] 1235 | arrow = [ 1236 | {file = "arrow-1.2.3-py3-none-any.whl", hash = "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2"}, 1237 | {file = "arrow-1.2.3.tar.gz", hash = "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1"}, 1238 | ] 1239 | asttokens = [ 1240 | {file = "asttokens-2.2.1-py2.py3-none-any.whl", hash = "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c"}, 1241 | {file = "asttokens-2.2.1.tar.gz", hash = "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3"}, 1242 | ] 1243 | attrs = [ 1244 | {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, 1245 | {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, 1246 | ] 1247 | backcall = [ 1248 | {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, 1249 | {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, 1250 | ] 1251 | bandit = [ 1252 | {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, 1253 | {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, 1254 | ] 1255 | binaryornot = [ 1256 | {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, 1257 | {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, 1258 | ] 1259 | black = [ 1260 | {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, 1261 | {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, 1262 | {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, 1263 | {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, 1264 | {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, 1265 | {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, 1266 | {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, 1267 | {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, 1268 | {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, 1269 | {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, 1270 | {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, 1271 | {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, 1272 | ] 1273 | certifi = [ 1274 | {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, 1275 | {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, 1276 | ] 1277 | chardet = [ 1278 | {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"}, 1279 | {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"}, 1280 | ] 1281 | charset-normalizer = [ 1282 | {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, 1283 | {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, 1284 | ] 1285 | click = [ 1286 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 1287 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 1288 | ] 1289 | colorama = [ 1290 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 1291 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 1292 | ] 1293 | cookiecutter = [ 1294 | {file = "cookiecutter-2.1.1-py2.py3-none-any.whl", hash = "sha256:9f3ab027cec4f70916e28f03470bdb41e637a3ad354b4d65c765d93aad160022"}, 1295 | {file = "cookiecutter-2.1.1.tar.gz", hash = "sha256:f3982be8d9c53dac1261864013fdec7f83afd2e42ede6f6dd069c5e149c540d5"}, 1296 | ] 1297 | coverage = [ 1298 | {file = "coverage-7.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2569682d6ea9628da8d6ba38579a48b1e53081226ec7a6c82b5024b3ce5009f"}, 1299 | {file = "coverage-7.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ec256a592b497f26054195f7d7148892aca8c4cdcc064a7cc66ef7a0455b811"}, 1300 | {file = "coverage-7.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5885a4ceb6dde34271bb0adafa4a248a7f589c89821e9da3110c39f92f41e21b"}, 1301 | {file = "coverage-7.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d43d406a4d73aa7f855fa44fa77ff47e739b565b2af3844600cdc016d01e46b9"}, 1302 | {file = "coverage-7.0.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18df11efa615b79b9ecc13035a712957ff6283f7b244e57684e1c092869f541"}, 1303 | {file = "coverage-7.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f6a4bf5bdee93f6817797beba7086292c2ebde6df0d5822e0c33f8b05415c339"}, 1304 | {file = "coverage-7.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:33efe89cd0efef016db19d8d05aa46631f76793de90a61b6717acb202b36fe60"}, 1305 | {file = "coverage-7.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96b5b1f1079e48f56bfccf103bcf44d48b9eb5163f1ea523fad580f15d3fe5e0"}, 1306 | {file = "coverage-7.0.0-cp310-cp310-win32.whl", hash = "sha256:fb85b7a7a4b204bd59d6d0b0c8d87d9ffa820da225e691dfaffc3137dc05b5f6"}, 1307 | {file = "coverage-7.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:793dcd9d42035746fc7637df4336f7581df19d33c5c5253cf988c99d8e93a8ba"}, 1308 | {file = "coverage-7.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d564142a03d3bc8913499a458e931b52ddfe952f69b6cd4b24d810fd2959044a"}, 1309 | {file = "coverage-7.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0a8b0e86bede874bf5da566b02194fbb12dd14ce3585cabd58452007f272ba81"}, 1310 | {file = "coverage-7.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e645c73cbfc4577d93747d3f793115acf6f907a7eb9208fa807fdcf2da1964a4"}, 1311 | {file = "coverage-7.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de06e7585abe88c6d38c1b73ce4c3cb4c1a79fbb0da0d0f8e8689ef5729ec60d"}, 1312 | {file = "coverage-7.0.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a30b646fbdd5bc52f506e149fa4fbdef82432baf6b81774e61ec4e3b43b9cbde"}, 1313 | {file = "coverage-7.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:db8141856dc9be0917413df7200f53accf1d84c8b156868e6af058a1ea8e903a"}, 1314 | {file = "coverage-7.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:59e71912c7fc78d08a567ee65656123878f49ca1b5672e660ea70bf8dfbebf8f"}, 1315 | {file = "coverage-7.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b8f7cd942dda3795fc9eadf303cc53a422ac057e3b70c2ad6d4276ec6a83a541"}, 1316 | {file = "coverage-7.0.0-cp311-cp311-win32.whl", hash = "sha256:bf437a04b9790d3c9cd5b48e9ce9aa84229040e3ae7d6c670a55118906113c5a"}, 1317 | {file = "coverage-7.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7e1bb36b4e57a2d304322021b35d4e4a25fa0d501ba56e8e51efaebf4480556"}, 1318 | {file = "coverage-7.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:215f40ef86f1958a1151fa7fad2b4f2f99534c4e10a34a1e065eba3f19ef8868"}, 1319 | {file = "coverage-7.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae088eb1cbdad8206931b1bf3f11dee644e038a9300be84d3e705e29356e5b1d"}, 1320 | {file = "coverage-7.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9071e197faa24837b967bc9aa0b9ef961f805a75f1ee3ea1f3367f55cd46c3c"}, 1321 | {file = "coverage-7.0.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f1e6d9c70d45a960d3f3d781ea62b167fdf2e0e1f6bb282b96feea653adb923"}, 1322 | {file = "coverage-7.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9fadd15f9fcfd7b16d9cccce9f5e6ec6f9b8df860633ad9aa62c2b14c259560f"}, 1323 | {file = "coverage-7.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:10b6246cae61896ab4c7568e498e492cbb73a2dfa4c3af79141c43cf806f929a"}, 1324 | {file = "coverage-7.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a8785791c2120af114ea7a06137f7778632e568a5aa2bbfc3b46c573b702af74"}, 1325 | {file = "coverage-7.0.0-cp37-cp37m-win32.whl", hash = "sha256:30220518dd89c4878908d73f5f3d1269f86e9e045354436534587a18c7b9da85"}, 1326 | {file = "coverage-7.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bc904aa96105d73357de03de76336b1e3db28e2b12067d36625fd9646ab043fd"}, 1327 | {file = "coverage-7.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2331b7bd84a1be79bd17ca8e103ce38db8cbf7cb354dc56e651ba489cf849212"}, 1328 | {file = "coverage-7.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e907db8bdd0ad1253a33c20fdc5f0f6209d271114a9c6f1fcdf96617343f7ca0"}, 1329 | {file = "coverage-7.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0deee68e0dae1d6e3fe6943c76d7e66fbeb6519bd08e4e5366bcc28a8a9aca"}, 1330 | {file = "coverage-7.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fff0f08bc5ffd0d78db821971472b4adc2ee876b86f743e46d634fb8e3c22f"}, 1331 | {file = "coverage-7.0.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a290b7921c1c05787b953e5854d394e887df40696f21381cc33c4e2179bf50ac"}, 1332 | {file = "coverage-7.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:100546219af59d2ad82d4575de03a303eb27b75ea36ffbd1677371924d50bcbc"}, 1333 | {file = "coverage-7.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c1ba6e63b831112b9484ff5905370d89e43d4316bac76d403031f60d61597466"}, 1334 | {file = "coverage-7.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c685fc17d6f4f1a3833e9dac27d0b931f7ccb52be6c30d269374203c7d0204a2"}, 1335 | {file = "coverage-7.0.0-cp38-cp38-win32.whl", hash = "sha256:8938f3a10f45019b502020ba9567b97b6ecc8c76b664b421705c5406d4f92fe8"}, 1336 | {file = "coverage-7.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:c4b63888bef2928d0eca12cbce0760cfb696acb4fe226eb55178b6a2a039328a"}, 1337 | {file = "coverage-7.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cda63459eb20652b22e038729a8f5063862c189a3963cb042a764b753172f75e"}, 1338 | {file = "coverage-7.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e06abac1a4aec1ff989131e43ca917fc7bd296f34bf0cfe86cbf74343b21566d"}, 1339 | {file = "coverage-7.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b94ad926e933976627f040f96dd1d9b0ac91f8d27e868c30a28253b9b6ac2d"}, 1340 | {file = "coverage-7.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6b4af31fb49a2ae8de1cd505fa66c403bfcc5066e845ac19d8904dcfc9d40da"}, 1341 | {file = "coverage-7.0.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36b62f0220459e528ad5806cc7dede71aa716e067d2cb10cb4a09686b8791fba"}, 1342 | {file = "coverage-7.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:43ec1935c6d6caab4f3bc126d20bd709c0002a175d62208ebe745be37a826a41"}, 1343 | {file = "coverage-7.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8593c9baf1f0f273afa22f5b45508b76adc7b8e94e17e7d98fbe1e3cd5812af2"}, 1344 | {file = "coverage-7.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fee283cd36c3f14422d9c1b51da24ddbb5e1eed89ad2480f6a9f115df38b5df8"}, 1345 | {file = "coverage-7.0.0-cp39-cp39-win32.whl", hash = "sha256:97c0b001ff15b8e8882995fc07ac0a08c8baf8b13c1145f3f12e0587bbb0e335"}, 1346 | {file = "coverage-7.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:8dbf83a4611c591b5de65069b6fd4dd3889200ed270cd2f7f5ac765d3842889f"}, 1347 | {file = "coverage-7.0.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:bcaf18e46668057051a312c714a4548b81f7e8fb3454116ad97be7562d2a99e4"}, 1348 | {file = "coverage-7.0.0.tar.gz", hash = "sha256:9a175da2a7320e18fc3ee1d147639a2b3a8f037e508c96aa2da160294eb50e17"}, 1349 | ] 1350 | cruft = [ 1351 | {file = "cruft-2.11.1-py3-none-any.whl", hash = "sha256:36026426d1d602656fe894e027dcd502ccf48da4b30927e071928e35465c2676"}, 1352 | {file = "cruft-2.11.1.tar.gz", hash = "sha256:deae3ee097f091462d7c872df22d6ad0ac89ce870bd1665e716e0d08fd21357b"}, 1353 | ] 1354 | decorator = [ 1355 | {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, 1356 | {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, 1357 | ] 1358 | docstring-parser = [ 1359 | {file = "docstring_parser-0.13.tar.gz", hash = "sha256:66dd7eac7232202bf220fd98a5f11491863c01f958a75fdd535c7eccac9ced78"}, 1360 | ] 1361 | dparse = [ 1362 | {file = "dparse-0.6.2-py3-none-any.whl", hash = "sha256:8097076f1dd26c377f30d4745e6ec18fef42f3bf493933b842ac5bafad8c345f"}, 1363 | {file = "dparse-0.6.2.tar.gz", hash = "sha256:d45255bda21f998bc7ddf2afd5e62505ba6134756ba2d42a84c56b0826614dfe"}, 1364 | ] 1365 | exceptiongroup = [ 1366 | {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, 1367 | {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, 1368 | ] 1369 | executing = [ 1370 | {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"}, 1371 | {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"}, 1372 | ] 1373 | falcon = [ 1374 | {file = "falcon-2.0.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:733033ec80c896e30a43ab3e776856096836787197a44eb21022320a61311983"}, 1375 | {file = "falcon-2.0.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f93351459f110b4c1ee28556aef9a791832df6f910bea7b3f616109d534df06b"}, 1376 | {file = "falcon-2.0.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e9efa0791b5d9f9dd9689015ea6bce0a27fcd5ecbcd30e6d940bffa4f7f03389"}, 1377 | {file = "falcon-2.0.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:59d1e8c993b9a37ea06df9d72cf907a46cc8063b30717cdac2f34d1658b6f936"}, 1378 | {file = "falcon-2.0.0-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:a5ebb22a04c9cc65081938ee7651b4e3b4d2a28522ea8ec04c7bdd2b3e9e8cd8"}, 1379 | {file = "falcon-2.0.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:95bf6ce986c1119aef12c9b348f4dee9c6dcc58391bdd0bc2b0bf353c2b15986"}, 1380 | {file = "falcon-2.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:aa184895d1ad4573fbfaaf803563d02f019ebdf4790e41cc568a330607eae439"}, 1381 | {file = "falcon-2.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:74cf1d18207381c665b9e6292d65100ce146d958707793174b03869dc6e614f4"}, 1382 | {file = "falcon-2.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24adcd2b29a8ffa9d552dc79638cd21736a3fb04eda7d102c6cebafdaadb88ad"}, 1383 | {file = "falcon-2.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:18157af2a4fc3feedf2b5dcc6196f448639acf01c68bc33d4d5a04c3ef87f494"}, 1384 | {file = "falcon-2.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e3782b7b92fefd46a6ad1fd8fe63fe6c6f1b7740a95ca56957f48d1aee34b357"}, 1385 | {file = "falcon-2.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:9712975adcf8c6e12876239085ad757b8fdeba223d46d23daef82b47658f83a9"}, 1386 | {file = "falcon-2.0.0-py2.py3-none-any.whl", hash = "sha256:54f2cb4b687035b2a03206dbfc538055cc48b59a953187b0458aa1b574d47b53"}, 1387 | {file = "falcon-2.0.0.tar.gz", hash = "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc"}, 1388 | ] 1389 | flake8 = [ 1390 | {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, 1391 | {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, 1392 | ] 1393 | flake8-bugbear = [ 1394 | {file = "flake8-bugbear-22.12.6.tar.gz", hash = "sha256:4cdb2c06e229971104443ae293e75e64c6107798229202fbe4f4091427a30ac0"}, 1395 | {file = "flake8_bugbear-22.12.6-py3-none-any.whl", hash = "sha256:b69a510634f8a9c298dfda2b18a8036455e6b19ecac4fe582e4d7a0abfa50a30"}, 1396 | ] 1397 | ghp-import = [ 1398 | {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, 1399 | {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, 1400 | ] 1401 | gitdb = [ 1402 | {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"}, 1403 | {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"}, 1404 | ] 1405 | gitpython = [ 1406 | {file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"}, 1407 | {file = "GitPython-3.1.29.tar.gz", hash = "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd"}, 1408 | ] 1409 | hug = [ 1410 | {file = "hug-2.6.1-py2.py3-none-any.whl", hash = "sha256:31c8fc284f81377278629a4b94cbb619ae9ce829cdc2da9564ccc66a121046b4"}, 1411 | {file = "hug-2.6.1.tar.gz", hash = "sha256:b0edace2acb618873779c9ce6ecf9165db54fef95c22262f5700fcdd9febaec9"}, 1412 | ] 1413 | hypothesis = [ 1414 | {file = "hypothesis-6.61.0-py3-none-any.whl", hash = "sha256:7bb22d22e35db99d5724bbf5bdc686b46add94a0f228bf1be249c47ec46b9c7f"}, 1415 | {file = "hypothesis-6.61.0.tar.gz", hash = "sha256:fbf7da30aea839d88898f74bcc027f0f997060498a8a7605880688c8a2166215"}, 1416 | ] 1417 | hypothesis-auto = [ 1418 | {file = "hypothesis-auto-1.1.4.tar.gz", hash = "sha256:5e2c2fb09dc09842512d80630bb792359a1d33d2c0473ad47ee23da0be9e32b1"}, 1419 | {file = "hypothesis_auto-1.1.4-py3-none-any.whl", hash = "sha256:fea8560c4522c0fd490ed8cc17e420b95dabebb11b9b334c59bf2d768839015f"}, 1420 | ] 1421 | idna = [ 1422 | {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, 1423 | {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, 1424 | ] 1425 | importlib-metadata = [ 1426 | {file = "importlib_metadata-5.2.0-py3-none-any.whl", hash = "sha256:0eafa39ba42bf225fc00e67f701d71f85aead9f878569caf13c3724f704b970f"}, 1427 | {file = "importlib_metadata-5.2.0.tar.gz", hash = "sha256:404d48d62bba0b7a77ff9d405efd91501bef2e67ff4ace0bed40a0cf28c3c7cd"}, 1428 | ] 1429 | iniconfig = [ 1430 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 1431 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 1432 | ] 1433 | ipython = [ 1434 | {file = "ipython-8.7.0-py3-none-any.whl", hash = "sha256:352042ddcb019f7c04e48171b4dd78e4c4bb67bf97030d170e154aac42b656d9"}, 1435 | {file = "ipython-8.7.0.tar.gz", hash = "sha256:882899fe78d5417a0aa07f995db298fa28b58faeba2112d2e3a4c95fe14bb738"}, 1436 | ] 1437 | isort = [ 1438 | {file = "isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"}, 1439 | {file = "isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"}, 1440 | ] 1441 | jedi = [ 1442 | {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, 1443 | {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, 1444 | ] 1445 | jinja2 = [ 1446 | {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 1447 | {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 1448 | ] 1449 | jinja2-time = [ 1450 | {file = "jinja2-time-0.2.0.tar.gz", hash = "sha256:d14eaa4d315e7688daa4969f616f226614350c48730bfa1692d2caebd8c90d40"}, 1451 | {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa"}, 1452 | ] 1453 | livereload = [ 1454 | {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, 1455 | ] 1456 | mako = [ 1457 | {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, 1458 | {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, 1459 | ] 1460 | markdown = [ 1461 | {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, 1462 | {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, 1463 | ] 1464 | markupsafe = [ 1465 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, 1466 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, 1467 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, 1468 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, 1469 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, 1470 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, 1471 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, 1472 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, 1473 | {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, 1474 | {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, 1475 | {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, 1476 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, 1477 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, 1478 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, 1479 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, 1480 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, 1481 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, 1482 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, 1483 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, 1484 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, 1485 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, 1486 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, 1487 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, 1488 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, 1489 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, 1490 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, 1491 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, 1492 | {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, 1493 | {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, 1494 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, 1495 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, 1496 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, 1497 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, 1498 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, 1499 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, 1500 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, 1501 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, 1502 | {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, 1503 | {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, 1504 | {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, 1505 | ] 1506 | matplotlib-inline = [ 1507 | {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, 1508 | {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, 1509 | ] 1510 | mccabe = [ 1511 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 1512 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 1513 | ] 1514 | mergedeep = [ 1515 | {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, 1516 | {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, 1517 | ] 1518 | mkdocs = [ 1519 | {file = "mkdocs-1.3.1-py3-none-any.whl", hash = "sha256:fda92466393127d2da830bc6edc3a625a14b436316d1caf347690648e774c4f0"}, 1520 | {file = "mkdocs-1.3.1.tar.gz", hash = "sha256:a41a2ff25ce3bbacc953f9844ba07d106233cd76c88bac1f59cb1564ac0d87ed"}, 1521 | ] 1522 | mkdocs-material = [ 1523 | {file = "mkdocs_material-8.5.4-py3-none-any.whl", hash = "sha256:aec2f0f2143109f8388aadf76e6fff749a2b74ebe730d0f674c65b53da89d19d"}, 1524 | {file = "mkdocs_material-8.5.4.tar.gz", hash = "sha256:70dc47820d4765b77968b9119f2957d09b4d8d328d950bee4544ff224d5c7b36"}, 1525 | ] 1526 | mkdocs-material-extensions = [ 1527 | {file = "mkdocs_material_extensions-1.1.1-py3-none-any.whl", hash = "sha256:e41d9f38e4798b6617ad98ca8f7f1157b1e4385ac1459ca1e4ea219b556df945"}, 1528 | {file = "mkdocs_material_extensions-1.1.1.tar.gz", hash = "sha256:9c003da71e2cc2493d910237448c672e00cefc800d3d6ae93d2fc69979e3bd93"}, 1529 | ] 1530 | mypy = [ 1531 | {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, 1532 | {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, 1533 | {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, 1534 | {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, 1535 | {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, 1536 | {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, 1537 | {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, 1538 | {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, 1539 | {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, 1540 | {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, 1541 | {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, 1542 | {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, 1543 | {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, 1544 | {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, 1545 | {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, 1546 | {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, 1547 | {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, 1548 | {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, 1549 | {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, 1550 | {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, 1551 | {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, 1552 | {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, 1553 | {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, 1554 | {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, 1555 | {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, 1556 | {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, 1557 | {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, 1558 | {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, 1559 | {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, 1560 | {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, 1561 | ] 1562 | mypy-extensions = [ 1563 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 1564 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 1565 | ] 1566 | packaging = [ 1567 | {file = "packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"}, 1568 | {file = "packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"}, 1569 | ] 1570 | parso = [ 1571 | {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, 1572 | {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, 1573 | ] 1574 | pathspec = [ 1575 | {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, 1576 | {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, 1577 | ] 1578 | pbr = [ 1579 | {file = "pbr-5.11.0-py2.py3-none-any.whl", hash = "sha256:db2317ff07c84c4c63648c9064a79fe9d9f5c7ce85a9099d4b6258b3db83225a"}, 1580 | {file = "pbr-5.11.0.tar.gz", hash = "sha256:b97bc6695b2aff02144133c2e7399d5885223d42b7912ffaec2ca3898e673bfe"}, 1581 | ] 1582 | pdocs = [ 1583 | {file = "pdocs-1.2.0-py3-none-any.whl", hash = "sha256:caca2829415e62d31eaab46c53cd77a2a14f132c73e48e25187331c3a9fc2459"}, 1584 | {file = "pdocs-1.2.0.tar.gz", hash = "sha256:996ad4d5039b59a9a112d29abfb3995ec4ed8d8415ddce6947c3a5248adb428b"}, 1585 | ] 1586 | pep8-naming = [ 1587 | {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, 1588 | {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, 1589 | ] 1590 | pexpect = [ 1591 | {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, 1592 | {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, 1593 | ] 1594 | pickleshare = [ 1595 | {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, 1596 | {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, 1597 | ] 1598 | platformdirs = [ 1599 | {file = "platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"}, 1600 | {file = "platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"}, 1601 | ] 1602 | pluggy = [ 1603 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 1604 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 1605 | ] 1606 | pprofile = [ 1607 | {file = "pprofile-2.1.0.tar.gz", hash = "sha256:b2bb56603dadf40c0bc0f61621f22c20e41638425f729945d9b7f8e4ae8cdd4a"}, 1608 | ] 1609 | prompt-toolkit = [ 1610 | {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, 1611 | {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, 1612 | ] 1613 | ptyprocess = [ 1614 | {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, 1615 | {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, 1616 | ] 1617 | pure-eval = [ 1618 | {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, 1619 | {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, 1620 | ] 1621 | pycodestyle = [ 1622 | {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, 1623 | {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, 1624 | ] 1625 | pydantic = [ 1626 | {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"}, 1627 | {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"}, 1628 | {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"}, 1629 | {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"}, 1630 | {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"}, 1631 | {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"}, 1632 | {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"}, 1633 | {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"}, 1634 | {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"}, 1635 | {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"}, 1636 | {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"}, 1637 | {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"}, 1638 | {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"}, 1639 | {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"}, 1640 | {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"}, 1641 | {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"}, 1642 | {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"}, 1643 | {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"}, 1644 | {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"}, 1645 | {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"}, 1646 | {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"}, 1647 | {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"}, 1648 | {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"}, 1649 | {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"}, 1650 | {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"}, 1651 | {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"}, 1652 | {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"}, 1653 | {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"}, 1654 | {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"}, 1655 | {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"}, 1656 | {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"}, 1657 | {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"}, 1658 | {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"}, 1659 | {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"}, 1660 | {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"}, 1661 | {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"}, 1662 | ] 1663 | pyflakes = [ 1664 | {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, 1665 | {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, 1666 | ] 1667 | pygments = [ 1668 | {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, 1669 | {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, 1670 | ] 1671 | pymdown-extensions = [ 1672 | {file = "pymdown_extensions-9.9-py3-none-any.whl", hash = "sha256:ac698c15265680db5eb13cd4342abfcde2079ac01e5486028f47a1b41547b859"}, 1673 | {file = "pymdown_extensions-9.9.tar.gz", hash = "sha256:0f8fb7b74a37a61cc34e90b2c91865458b713ec774894ffad64353a5fce85cfc"}, 1674 | ] 1675 | pytest = [ 1676 | {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, 1677 | {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, 1678 | ] 1679 | pytest-cov = [ 1680 | {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, 1681 | {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, 1682 | ] 1683 | pytest-mock = [ 1684 | {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, 1685 | {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, 1686 | ] 1687 | python-dateutil = [ 1688 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 1689 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 1690 | ] 1691 | python-slugify = [ 1692 | {file = "python-slugify-7.0.0.tar.gz", hash = "sha256:7a0f21a39fa6c1c4bf2e5984c9b9ae944483fd10b54804cb0e23a3ccd4954f0b"}, 1693 | {file = "python_slugify-7.0.0-py2.py3-none-any.whl", hash = "sha256:003aee64f9fd955d111549f96c4b58a3f40b9319383c70fad6277a4974bbf570"}, 1694 | ] 1695 | pyyaml = [ 1696 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, 1697 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, 1698 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, 1699 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, 1700 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, 1701 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, 1702 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, 1703 | {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, 1704 | {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, 1705 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, 1706 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, 1707 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, 1708 | {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, 1709 | {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, 1710 | {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, 1711 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, 1712 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, 1713 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, 1714 | {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, 1715 | {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, 1716 | {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, 1717 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, 1718 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, 1719 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, 1720 | {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, 1721 | {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, 1722 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, 1723 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, 1724 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, 1725 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, 1726 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, 1727 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, 1728 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, 1729 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, 1730 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, 1731 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, 1732 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, 1733 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, 1734 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, 1735 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, 1736 | ] 1737 | pyyaml-env-tag = [ 1738 | {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, 1739 | {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, 1740 | ] 1741 | requests = [ 1742 | {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, 1743 | {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, 1744 | ] 1745 | ruamel-yaml = [ 1746 | {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, 1747 | {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, 1748 | ] 1749 | ruamel-yaml-clib = [ 1750 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"}, 1751 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"}, 1752 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80"}, 1753 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab"}, 1754 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"}, 1755 | {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"}, 1756 | {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, 1757 | {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"}, 1758 | {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"}, 1759 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"}, 1760 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"}, 1761 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"}, 1762 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763"}, 1763 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win32.whl", hash = "sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e"}, 1764 | {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win_amd64.whl", hash = "sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646"}, 1765 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f"}, 1766 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0"}, 1767 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282"}, 1768 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7"}, 1769 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win32.whl", hash = "sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93"}, 1770 | {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b"}, 1771 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb"}, 1772 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307"}, 1773 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697"}, 1774 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b"}, 1775 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win32.whl", hash = "sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac"}, 1776 | {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f"}, 1777 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9"}, 1778 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1"}, 1779 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640"}, 1780 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b"}, 1781 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win32.whl", hash = "sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8"}, 1782 | {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5"}, 1783 | {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, 1784 | ] 1785 | ruff = [ 1786 | {file = "ruff-0.0.191-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:77aa90ab83f6ef663ad002708eec13b7931193dfa418c09564ab34df4766a11d"}, 1787 | {file = "ruff-0.0.191-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:75e8d628f4d1b0db216e8cb26a10f36a6c3db572e839aaad8ac0a51e0bd1c0ef"}, 1788 | {file = "ruff-0.0.191-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ce09e5c6253854ce907a42640c316f97ff49b8a3e35ce3e44524e1fc67bf3f3"}, 1789 | {file = "ruff-0.0.191-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a230dad3805d70d4c2c9716e1cfe0939695979c8ef7b7b4791e3f5c26c5d965"}, 1790 | {file = "ruff-0.0.191-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2727d02f9cacca0d945d1a4fa443ff9469a131c7791df1f8824b9c89721bd138"}, 1791 | {file = "ruff-0.0.191-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:20b52da4ff1008f4401e4681c6b8552133aa0553cde17dfcd4d42d992242e578"}, 1792 | {file = "ruff-0.0.191-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1084aa3a448c5f98c567547de79eef2dba6f022232fd9c89876881d74de3d3cd"}, 1793 | {file = "ruff-0.0.191-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d76b88b0dbd6dfa6ac3ceb08bd519e140ad6e4f7cf9cf2d88310a084af35e92"}, 1794 | {file = "ruff-0.0.191-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:614106b64fefd70750a2eb3e50bfce7c9b56585d7be96f925fb3c13108552984"}, 1795 | {file = "ruff-0.0.191-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:304b83f1ab6f91245a800b3c60e38083b89ee240cbdb3d1d8a98671265292c11"}, 1796 | {file = "ruff-0.0.191-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4bf38f1419b5d8b3bc299a2cba183a43ade4858af2160033fb94aa9e5a85c700"}, 1797 | {file = "ruff-0.0.191-py3-none-musllinux_1_2_i686.whl", hash = "sha256:97c22a00daad210f03458e8993c62bee38ec98d2f0b8d50a671b31a96256cd5f"}, 1798 | {file = "ruff-0.0.191-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c2a5b7e2b7aa33e5ece2291dc655ad2023626114179fc166d382e3902f319dd5"}, 1799 | {file = "ruff-0.0.191-py3-none-win32.whl", hash = "sha256:195da807f65e1b153379b7258086cbfb3b10143ecf93683275dfef561593c485"}, 1800 | {file = "ruff-0.0.191-py3-none-win_amd64.whl", hash = "sha256:a25a7b9d56732df7f4887f3bd4a66dd54924be8c4d359e6a73bd5b1a8082f1bf"}, 1801 | {file = "ruff-0.0.191.tar.gz", hash = "sha256:d698c4d5e3b2963cbbb7c2728f404091d5c47cdf8d94db3eb2f335e2a93a6b1b"}, 1802 | ] 1803 | safety = [ 1804 | {file = "safety-2.3.4-py3-none-any.whl", hash = "sha256:6224dcd9b20986a2b2c5e7acfdfba6bca42bb11b2783b24ed04f32317e5167ea"}, 1805 | {file = "safety-2.3.4.tar.gz", hash = "sha256:b9e74e794e82f54d11f4091c5d820c4d2d81de9f953bf0b4f33ac8bc402ae72c"}, 1806 | ] 1807 | setuptools = [ 1808 | {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, 1809 | {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, 1810 | ] 1811 | six = [ 1812 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1813 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1814 | ] 1815 | smmap = [ 1816 | {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, 1817 | {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, 1818 | ] 1819 | sortedcontainers = [ 1820 | {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, 1821 | {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, 1822 | ] 1823 | stack-data = [ 1824 | {file = "stack_data-0.6.2-py3-none-any.whl", hash = "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8"}, 1825 | {file = "stack_data-0.6.2.tar.gz", hash = "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815"}, 1826 | ] 1827 | stevedore = [ 1828 | {file = "stevedore-4.1.1-py3-none-any.whl", hash = "sha256:aa6436565c069b2946fe4ebff07f5041e0c8bf18c7376dd29edf80cf7d524e4e"}, 1829 | {file = "stevedore-4.1.1.tar.gz", hash = "sha256:7f8aeb6e3f90f96832c301bff21a7eb5eefbe894c88c506483d355565d88cc1a"}, 1830 | ] 1831 | text-unidecode = [ 1832 | {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, 1833 | {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, 1834 | ] 1835 | toml = [ 1836 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1837 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1838 | ] 1839 | tomli = [ 1840 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 1841 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 1842 | ] 1843 | tornado = [ 1844 | {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, 1845 | {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, 1846 | {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, 1847 | {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, 1848 | {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, 1849 | {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, 1850 | {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, 1851 | {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, 1852 | {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, 1853 | {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, 1854 | {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, 1855 | ] 1856 | traitlets = [ 1857 | {file = "traitlets-5.8.0-py3-none-any.whl", hash = "sha256:c864831efa0ba6576d09b44884b34e41defc18c0d7e720b4a2d6698c842cab3e"}, 1858 | {file = "traitlets-5.8.0.tar.gz", hash = "sha256:6cc57d6dc28c85d5365961726ffd19b538739347749e13ebe34e03323a0e8f84"}, 1859 | ] 1860 | typer = [ 1861 | {file = "typer-0.6.1-py3-none-any.whl", hash = "sha256:54b19e5df18654070a82f8c2aa1da456a4ac16a2a83e6dcd9f170e291c56338e"}, 1862 | {file = "typer-0.6.1.tar.gz", hash = "sha256:2d5720a5e63f73eaf31edaa15f6ab87f35f0690f8ca233017d7d23d743a91d73"}, 1863 | ] 1864 | types-toml = [ 1865 | {file = "types-toml-0.10.8.1.tar.gz", hash = "sha256:171bdb3163d79a520560f24ba916a9fc9bff81659c5448a9fea89240923722be"}, 1866 | {file = "types_toml-0.10.8.1-py3-none-any.whl", hash = "sha256:b7b5c4977f96ab7b5ac06d8a6590d17c0bf252a96efc03b109c2711fb3e0eafd"}, 1867 | ] 1868 | typing-extensions = [ 1869 | {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, 1870 | {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, 1871 | ] 1872 | urllib3 = [ 1873 | {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"}, 1874 | {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"}, 1875 | ] 1876 | vulture = [ 1877 | {file = "vulture-2.6-py2.py3-none-any.whl", hash = "sha256:e792e903ccc063ec4873a8979dcf11b51ea3d65a2d3b31c113d47be48f0cdcae"}, 1878 | {file = "vulture-2.6.tar.gz", hash = "sha256:2515fa848181001dc8a73aba6a01a1a17406f5d372f24ec7f7191866f9f4997e"}, 1879 | ] 1880 | watchdog = [ 1881 | {file = "watchdog-2.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ed91c3ccfc23398e7aa9715abf679d5c163394b8cad994f34f156d57a7c163dc"}, 1882 | {file = "watchdog-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:76a2743402b794629a955d96ea2e240bd0e903aa26e02e93cd2d57b33900962b"}, 1883 | {file = "watchdog-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:920a4bda7daa47545c3201a3292e99300ba81ca26b7569575bd086c865889090"}, 1884 | {file = "watchdog-2.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ceaa9268d81205876bedb1069f9feab3eccddd4b90d9a45d06a0df592a04cae9"}, 1885 | {file = "watchdog-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1893d425ef4fb4f129ee8ef72226836619c2950dd0559bba022b0818c63a7b60"}, 1886 | {file = "watchdog-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e99c1713e4436d2563f5828c8910e5ff25abd6ce999e75f15c15d81d41980b6"}, 1887 | {file = "watchdog-2.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a5bd9e8656d07cae89ac464ee4bcb6f1b9cecbedc3bf1334683bed3d5afd39ba"}, 1888 | {file = "watchdog-2.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a048865c828389cb06c0bebf8a883cec3ae58ad3e366bcc38c61d8455a3138f"}, 1889 | {file = "watchdog-2.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e722755d995035dd32177a9c633d158f2ec604f2a358b545bba5bed53ab25bca"}, 1890 | {file = "watchdog-2.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:af4b5c7ba60206759a1d99811b5938ca666ea9562a1052b410637bb96ff97512"}, 1891 | {file = "watchdog-2.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:619d63fa5be69f89ff3a93e165e602c08ed8da402ca42b99cd59a8ec115673e1"}, 1892 | {file = "watchdog-2.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f2b0665c57358ce9786f06f5475bc083fea9d81ecc0efa4733fd0c320940a37"}, 1893 | {file = "watchdog-2.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:441024df19253bb108d3a8a5de7a186003d68564084576fecf7333a441271ef7"}, 1894 | {file = "watchdog-2.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a410dd4d0adcc86b4c71d1317ba2ea2c92babaf5b83321e4bde2514525544d5"}, 1895 | {file = "watchdog-2.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28704c71afdb79c3f215c90231e41c52b056ea880b6be6cee035c6149d658ed1"}, 1896 | {file = "watchdog-2.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ac0bd7c206bb6df78ef9e8ad27cc1346f2b41b1fef610395607319cdab89bc1"}, 1897 | {file = "watchdog-2.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:27e49268735b3c27310883012ab3bd86ea0a96dcab90fe3feb682472e30c90f3"}, 1898 | {file = "watchdog-2.2.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:2af1a29fd14fc0a87fb6ed762d3e1ae5694dcde22372eebba50e9e5be47af03c"}, 1899 | {file = "watchdog-2.2.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:c7bd98813d34bfa9b464cf8122e7d4bec0a5a427399094d2c17dd5f70d59bc61"}, 1900 | {file = "watchdog-2.2.0-py3-none-manylinux2014_i686.whl", hash = "sha256:56fb3f40fc3deecf6e518303c7533f5e2a722e377b12507f6de891583f1b48aa"}, 1901 | {file = "watchdog-2.2.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:74535e955359d79d126885e642d3683616e6d9ab3aae0e7dcccd043bd5a3ff4f"}, 1902 | {file = "watchdog-2.2.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:cf05e6ff677b9655c6e9511d02e9cc55e730c4e430b7a54af9c28912294605a4"}, 1903 | {file = "watchdog-2.2.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:d6ae890798a3560688b441ef086bb66e87af6b400a92749a18b856a134fc0318"}, 1904 | {file = "watchdog-2.2.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5aed2a700a18c194c39c266900d41f3db0c1ebe6b8a0834b9995c835d2ca66e"}, 1905 | {file = "watchdog-2.2.0-py3-none-win32.whl", hash = "sha256:d0fb5f2b513556c2abb578c1066f5f467d729f2eb689bc2db0739daf81c6bb7e"}, 1906 | {file = "watchdog-2.2.0-py3-none-win_amd64.whl", hash = "sha256:1f8eca9d294a4f194ce9df0d97d19b5598f310950d3ac3dd6e8d25ae456d4c8a"}, 1907 | {file = "watchdog-2.2.0-py3-none-win_ia64.whl", hash = "sha256:ad0150536469fa4b693531e497ffe220d5b6cd76ad2eda474a5e641ee204bbb6"}, 1908 | {file = "watchdog-2.2.0.tar.gz", hash = "sha256:83cf8bc60d9c613b66a4c018051873d6273d9e45d040eed06d6a96241bd8ec01"}, 1909 | ] 1910 | wcwidth = [ 1911 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, 1912 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, 1913 | ] 1914 | yaspin = [ 1915 | {file = "yaspin-1.5.0-py2.py3-none-any.whl", hash = "sha256:10348ecc3b9c1f5a0c83390e0b892b62d3f3dae300d0c74e01ce72f8b032406c"}, 1916 | {file = "yaspin-1.5.0.tar.gz", hash = "sha256:d8fc9bc1c8be225877ea6e2e08fec96c2b52e233525a5c40b92d373f015439c6"}, 1917 | ] 1918 | zipp = [ 1919 | {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, 1920 | {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, 1921 | ] 1922 | -------------------------------------------------------------------------------- /portray/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/portray/f8b6d664f0c4d6923958f805fce1a1f94c5eaa6f/portray/__init__.py -------------------------------------------------------------------------------- /portray/__main__.py: -------------------------------------------------------------------------------- 1 | from portray.cli import __hug__ # type: ignore 2 | 3 | __hug__.cli() 4 | -------------------------------------------------------------------------------- /portray/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.8.0" 2 | -------------------------------------------------------------------------------- /portray/api.py: -------------------------------------------------------------------------------- 1 | """This module defines the programmatic API that can be used to interact with `portray` 2 | to generate and view documentation. 3 | 4 | If you want to extend `portray` or use it directly from within Python - this is the place 5 | to start. 6 | """ 7 | import os 8 | import webbrowser 9 | from typing import Dict, Union 10 | 11 | import mkdocs.commands.gh_deploy 12 | from livereload import Server 13 | from portray import config, logo, render 14 | 15 | 16 | def as_html( 17 | directory: str = "", 18 | config_file: str = "pyproject.toml", 19 | output_dir: str = "site", 20 | overwrite: bool = False, 21 | modules: list = None, # type: ignore 22 | ) -> None: 23 | """Produces HTML documentation for a Python project placing it into output_dir. 24 | 25 | - *directory*: The root folder of your project. 26 | - *config_file*: The [TOML](https://github.com/toml-lang/toml#toml) 27 | formatted config file you wish to use. 28 | - *output_dir*: The directory to place the generated HTML into. 29 | - *overwrite*: If set to `True` any existing documentation output will be removed 30 | before generating new documentation. Otherwise, if documentation exists in the 31 | specified `output_dir` the command will fail with a `DocumentationAlreadyExists` 32 | exception. 33 | - *modules*: One or more modules to render reference documentation for 34 | """ 35 | directory = directory if directory else os.getcwd() 36 | render.documentation( 37 | project_configuration(directory, config_file, modules=modules, output_dir=output_dir), 38 | overwrite=overwrite, 39 | ) 40 | print(logo.ascii_art) 41 | print(f"Documentation successfully generated into `{os.path.abspath(output_dir)}` !") 42 | 43 | 44 | def in_browser( 45 | directory: str = "", 46 | config_file: str = "pyproject.toml", 47 | port: int = None, # type: ignore 48 | host: str = None, # type: ignore 49 | modules: list = None, # type: ignore 50 | reload: bool = False, 51 | ) -> None: 52 | """Opens your default webbrowser pointing to a locally started development webserver enabling 53 | you to browse documentation locally 54 | 55 | - *directory*: The root folder of your project. 56 | - *config_file*: The [TOML](https://github.com/toml-lang/toml#toml) formatted 57 | config file you wish to use. 58 | - *port*: The port to expose your documentation on (defaults to: `8000`) 59 | - *host*: The host to expose your documentation on (defaults to `"127.0.0.1"`) 60 | - *modules*: One or more modules to render reference documentation for 61 | - *reload*: If true the server will live load any changes 62 | """ 63 | directory = directory if directory else os.getcwd() 64 | server( 65 | directory=directory, 66 | config_file=config_file, 67 | open_browser=True, 68 | port=port, 69 | host=host, 70 | modules=modules, 71 | reload=reload, 72 | ) 73 | 74 | 75 | def server( 76 | directory: str = "", 77 | config_file: str = "pyproject.toml", 78 | open_browser: bool = False, 79 | port: int = None, # type: ignore 80 | host: str = None, # type: ignore 81 | modules: list = None, # type: ignore 82 | reload: bool = False, 83 | ) -> None: 84 | """Runs a development webserver enabling you to browse documentation locally. 85 | 86 | - *directory*: The root folder of your project. 87 | - *config_file*: The [TOML](https://github.com/toml-lang/toml#toml) formatted 88 | config file you wish to use. 89 | - *open_browser*: If true a browser will be opened pointing at the documentation server 90 | - *port*: The port to expose your documentation on (defaults to: `8000`) 91 | - *host*: The host to expose your documentation on (defaults to `"127.0.0.1"`) 92 | - *modules*: One or more modules to render reference documentation for 93 | - *reload*: If true the server will live load any changes 94 | """ 95 | directory = directory if directory else os.getcwd() 96 | project_config = project_configuration(directory, config_file, modules=modules) 97 | host = host or project_config["host"] 98 | port = port or project_config["port"] 99 | 100 | with render.documentation_in_temp_folder(project_config) as (sources_folder, docs_folder): 101 | 102 | print(logo.ascii_art) 103 | 104 | live_server = Server() 105 | 106 | if reload: 107 | 108 | def reloader(): # pragma: no cover 109 | sources_old = sources_folder + ".old" 110 | docs_old = docs_folder + ".old" 111 | with render.documentation_in_temp_folder(project_config) as (sources_new, docs_new): 112 | # cause as little churn as possible to the server watchers 113 | os.rename(sources_folder, sources_old) 114 | os.rename(sources_new, sources_folder) 115 | os.rename(sources_old, sources_new) 116 | os.rename(docs_folder, docs_old) 117 | os.rename(docs_new, docs_folder) 118 | os.rename(docs_old, docs_new) 119 | 120 | # all directories that feed documentation_in_temp_folder 121 | watch_dirs = { 122 | project_config["directory"], 123 | project_config["docs_dir"], 124 | *project_config["extra_dirs"], 125 | } 126 | if "docs_dir" in project_config["mkdocs"]: 127 | watch_dirs.add(project_config["mkdocs"]["docs_dir"]) 128 | if "site_dir" in project_config["mkdocs"]: 129 | watch_dirs.add(project_config["mkdocs"]["site_dir"]) 130 | for watch_dir in watch_dirs.difference({sources_folder, docs_folder}): 131 | live_server.watch(watch_dir, reloader) 132 | 133 | if open_browser: 134 | webbrowser.open_new(f"http://{host}:{port}") 135 | 136 | live_server.serve(root=docs_folder, host=host, port=port, restart_delay=0) 137 | 138 | 139 | def project_configuration( 140 | directory: str = "", 141 | config_file: str = "pyproject.toml", 142 | modules: list = None, # type: ignore 143 | output_dir: str = None, # type: ignore 144 | ) -> dict: 145 | """Returns the configuration associated with a project. 146 | 147 | - *directory*: The root folder of your project. 148 | - *config_file*: The [TOML](https://github.com/toml-lang/toml#toml) formatted 149 | config file you wish to use. 150 | - *modules*: One or more modules to include in the configuration for reference rendering 151 | """ 152 | overrides: Dict[str, Union[str, list]] = {} 153 | if modules: 154 | overrides["modules"] = modules 155 | if output_dir: 156 | overrides["output_dir"] = output_dir 157 | directory = directory if directory else os.getcwd() 158 | return config.project(directory=directory, config_file=config_file, **overrides) 159 | 160 | 161 | def on_github_pages( 162 | directory: str = "", 163 | config_file: str = "pyproject.toml", 164 | message: str = None, # type: ignore 165 | force: bool = False, 166 | ignore_version: bool = False, 167 | modules: list = None, # type: ignore 168 | ) -> None: 169 | """Regenerates and deploys the documentation to GitHub pages. 170 | 171 | - *directory*: The root folder of your project. 172 | - *config_file*: The [TOML](https://github.com/toml-lang/toml#toml) formatted 173 | config file you wish to use. 174 | - *message*: The commit message to use when uploading your documentation. 175 | - *force*: Force the push to the repository. 176 | - *ignore_version*: Ignore check that build is not being deployed with an old version. 177 | - *modules*: One or more modules to render reference documentation for 178 | """ 179 | directory = directory if directory else os.getcwd() 180 | project_config = project_configuration(directory, config_file, modules) 181 | with render.documentation_in_temp_folder(project_config) as (_, site_dir): 182 | project_config["mkdocs"]["site_dir"] = site_dir 183 | conf = render._mkdocs_config(project_config["mkdocs"]) 184 | conf.config_file_path = directory 185 | mkdocs.commands.gh_deploy.gh_deploy( 186 | conf, message=message, force=force, ignore_version=ignore_version 187 | ) 188 | print(logo.ascii_art) 189 | print("Documentation successfully generated and pushed!") 190 | -------------------------------------------------------------------------------- /portray/cli.py: -------------------------------------------------------------------------------- 1 | """This module defines CLI interaction when using `portray`. 2 | 3 | This is powered by [hug](https://github.com/hugapi/hug) which means unless necessary 4 | it should maintain 1:1 compatibility with the programmatic API definition in the 5 | [API module](/reference/portray/api) 6 | 7 | - `portray as_html`: Renders the project as HTML into the `site` or other specified output directory 8 | - `portray in_browser`: Runs a server with the rendered documentation pointing a browser to it 9 | - `portray server`: Starts a local development server (by default at localhost:8000) 10 | - `portray project_configuration`: Returns back the project configuration as determined by` portray` 11 | """ 12 | from pprint import pprint 13 | 14 | import hug 15 | from portray import api, logo 16 | 17 | cli = hug.cli(api=hug.API(__name__, doc=logo.ascii_art)) 18 | cli(api.as_html) 19 | cli.output(pprint)(api.project_configuration) 20 | cli(api.server) 21 | cli(api.in_browser) 22 | cli(api.on_github_pages) 23 | -------------------------------------------------------------------------------- /portray/config.py: -------------------------------------------------------------------------------- 1 | """Defines the configuration defaults and load functions used by `portray`""" 2 | import _ast 3 | import ast 4 | import os 5 | import re 6 | import warnings 7 | from pathlib import Path 8 | from typing import Any, Dict, Optional 9 | 10 | import mkdocs.config as _mkdocs_config # noqa 11 | import mkdocs.exceptions as _mkdocs_exceptions # noqa 12 | from git import Repo 13 | from portray.exceptions import NoProjectFound 14 | from toml import load as toml_load 15 | 16 | PORTRAY_DEFAULTS = { 17 | "docs_dir": "docs", 18 | "extra_dirs": ["art", "images", "media"], 19 | "output_dir": "site", 20 | "port": 8000, 21 | "host": "127.0.0.1", 22 | "append_directory_to_python_path": True, 23 | "include_reference_documentation": True, 24 | "labels": {"Cli": "CLI", "Api": "API", "Http": "HTTP", "Pypi": "PyPI"}, 25 | "extra_markdown_extensions": [], 26 | } 27 | 28 | MKDOCS_DEFAULTS: Dict[str, Any] = { 29 | "site_name": os.path.basename(os.getcwd()), 30 | "config_file_path": os.getcwd(), 31 | "theme": { 32 | "name": "material", 33 | "palette": {"primary": "green", "accent": "lightgreen"}, 34 | "custom_dir": os.path.join(os.path.dirname(__file__), "mkdocs_templates"), 35 | }, 36 | "markdown_extensions": [ 37 | "admonition", 38 | "codehilite", 39 | "extra", 40 | "pymdownx.details", 41 | "pymdownx.highlight", 42 | ], 43 | } 44 | 45 | PDOCS_DEFAULTS: Dict = {"overwrite": True, "exclude_source": False} 46 | 47 | 48 | def project(directory: str, config_file: str, **overrides) -> dict: 49 | """Returns back the complete configuration - including all sub configuration components 50 | defined below that `portray` was able to determine for the project 51 | """ 52 | if not ( 53 | os.path.isfile(os.path.join(directory, config_file)) 54 | or os.path.isfile(os.path.join(directory, "setup.py")) 55 | or "modules" in overrides 56 | ): 57 | raise NoProjectFound(directory) 58 | 59 | project_config: Dict[str, Any] = {**PORTRAY_DEFAULTS, "directory": directory} 60 | if os.path.isfile(os.path.join(directory, "setup.py")): 61 | project_config.update(setup_py(os.path.join(directory, "setup.py"))) 62 | 63 | project_config.update(toml(os.path.join(directory, config_file))) 64 | project_config.update(overrides) 65 | 66 | project_config.setdefault("modules", [os.path.basename(os.getcwd()).replace("-", "_")]) 67 | project_config.setdefault("pdocs", {}).setdefault("modules", project_config["modules"]) 68 | 69 | mkdocs_config = project_config.get("mkdocs", {}) 70 | mkdocs_config.setdefault( 71 | "extra_markdown_extensions", project_config.get("extra_markdown_extensions", []) 72 | ) 73 | project_config["mkdocs"] = mkdocs(directory, **mkdocs_config) 74 | if "pdoc3" in project_config: 75 | warnings.warn( 76 | "pdoc3 config usage is deprecated in favor of pdocs. " 77 | "pdoc3 section will be ignored. ", 78 | DeprecationWarning, 79 | ) 80 | project_config["pdocs"] = pdocs(directory, **project_config.get("pdocs", {})) 81 | return project_config 82 | 83 | 84 | def setup_py(location: str) -> dict: 85 | """Returns back any configuration info we are able to determine from a setup.py file""" 86 | setup_config = {} 87 | try: 88 | with open(location) as setup_py_file: 89 | for node in ast.walk(ast.parse(setup_py_file.read())): 90 | if ( 91 | type(node) == _ast.Call 92 | and type(getattr(node, "func", None)) == _ast.Name 93 | and node.func.id == "setup" # type: ignore 94 | ): 95 | for keyword in node.keywords: # type: ignore 96 | if keyword.arg == "packages": 97 | setup_config["modules"] = ast.literal_eval(keyword.value) 98 | break 99 | break 100 | except Exception as error: 101 | warnings.warn(f"Error ({error}) occurred trying to parse setup.py file: {location}") 102 | 103 | return setup_config 104 | 105 | 106 | def toml(location: str) -> dict: 107 | """Returns back the configuration found within the projects 108 | [TOML](https://github.com/toml-lang/toml#toml) config (if there is one). 109 | 110 | Generally this is a `pyproject.toml` file at the root of the project 111 | with a `[tool.portray]` section defined. 112 | """ 113 | try: 114 | location_exists = os.path.exists(location) 115 | if not location_exists: 116 | warnings.warn(f'\nNo config file found at location: "{location}"') 117 | return {} 118 | except Exception as detection_error: # pragma: no cover 119 | warnings.warn(f'\nUnable to check config at "{location}" due to error: {detection_error}') 120 | 121 | try: 122 | toml_config = toml_load(location) 123 | tools = toml_config.get("tool", {}) 124 | 125 | config = tools.get("portray", {}) 126 | config["file"] = location 127 | 128 | if "modules" not in config: 129 | if "poetry" in tools and "name" in tools["poetry"]: 130 | config["modules"] = [tools["poetry"]["name"]] 131 | elif ( 132 | "flit" in tools 133 | and "metadata" in tools["flit"] 134 | and "module" in tools["flit"]["metadata"] 135 | ): 136 | config["modules"] = [tools["flit"]["metadata"]["module"]] 137 | 138 | return config 139 | except Exception as load_config_error: 140 | warnings.warn(f'\nConfig file at "{location}" has errors: {load_config_error}') 141 | 142 | return {} 143 | 144 | 145 | def repository( 146 | directory: str, 147 | repo_url: Optional[str] = None, 148 | repo_name: Optional[str] = None, 149 | edit_uri: Optional[str] = None, 150 | normalize_repo_url: bool = True, 151 | **kwargs, 152 | ) -> Dict[str, Optional[str]]: 153 | """Returns back any information that can be determined by introspecting the projects git repo 154 | (if there is one). 155 | """ 156 | try: 157 | if repo_url is None: 158 | repo_url = Repo(directory).remotes.origin.url 159 | if repo_name is None: 160 | match = re.search(r"(:(//)?)([\w\.@\:/\-~]+)(\.git)?(/)?", repo_url) 161 | if match: 162 | path = match.groups()[2] 163 | else: 164 | path = repo_url 165 | repo_name = path.split("/")[-1] 166 | if repo_name.endswith(".git"): 167 | repo_name = repo_name[: -len(".git")] 168 | if edit_uri is None: 169 | if "github" in repo_url or "gitlab" in repo_url: 170 | edit_uri = "edit/main/" 171 | elif "bitbucket" in repo_url: 172 | edit_uri = "src/default/docs/" 173 | 174 | if normalize_repo_url: 175 | if repo_url.startswith("git@") and ":" in repo_url: 176 | tld, path = repo_url[4:].split(":") 177 | repo_url = f"https://{tld}/{path}" 178 | elif repo_url.startswith("https://") and "@" in repo_url: 179 | repo_url = f"https://{repo_url.split('@')[1]}" 180 | 181 | if repo_url and "github" in repo_url or "gitlab" in repo_url or "bitbucket" in repo_url: 182 | repo_url = repo_url.replace(".git", "") 183 | 184 | return { 185 | key: value 186 | for key, value in { 187 | "repo_url": repo_url, 188 | "repo_name": repo_name, 189 | "edit_uri": edit_uri, 190 | }.items() 191 | if value 192 | } 193 | 194 | except Exception: 195 | warnings.warn("Unable to identify `repo_name`, `repo_url`, and `edit_uri` automatically.") 196 | return {} 197 | 198 | 199 | def mkdocs(directory: str, **overrides) -> dict: 200 | """Returns back the configuration that will be used when running mkdocs""" 201 | mkdocs_config: Dict[str, Any] = { 202 | **MKDOCS_DEFAULTS, 203 | **repository(directory, **overrides), 204 | **overrides, 205 | } 206 | theme = mkdocs_config["theme"] 207 | if theme["name"].lower() == "material": 208 | if "custom_dir" in theme: 209 | theme["custom_dir"] = Path(theme["custom_dir"]).absolute().as_posix() 210 | else: 211 | theme["custom_dir"] = MKDOCS_DEFAULTS["theme"]["custom_dir"] 212 | 213 | nav = mkdocs_config.get("nav", None) 214 | if nav and hasattr(nav[0], "copy"): 215 | mkdocs_config["nav"] = [nav_item.copy() for nav_item in nav] 216 | 217 | mkdocs_config["markdown_extensions"] = mkdocs_config["markdown_extensions"] + mkdocs_config.pop( 218 | "extra_markdown_extensions", [] 219 | ) 220 | 221 | return mkdocs_config 222 | 223 | 224 | def pdocs(directory: str, **overrides) -> dict: 225 | """Returns back the configuration that will be used when running pdocs""" 226 | defaults = {**PDOCS_DEFAULTS} 227 | defaults.update(overrides) 228 | return defaults 229 | -------------------------------------------------------------------------------- /portray/exceptions.py: -------------------------------------------------------------------------------- 1 | """All portray specific exception classes should be defined here""" 2 | 3 | 4 | class PortrayError(Exception): 5 | """Base class for all exceptions returned from portray""" 6 | 7 | pass 8 | 9 | 10 | class NoProjectFound(PortrayError): 11 | """Thrown when portray is ran in a directory with no Python project""" 12 | 13 | def __init__(self, directory: str): 14 | super().__init__( 15 | self, 16 | f"No Python project found in the given directory: '{directory}'" 17 | + " See: https://timothycrosley.github.io/portray/TROUBLESHOOTING/#noprojectfound", 18 | ) 19 | self.directory = directory 20 | 21 | 22 | class DocumentationAlreadyExists(PortrayError): 23 | """Thrown when portray has been told to output documentation where it already exists""" 24 | 25 | def __init__(self, directory: str): 26 | super().__init__( 27 | self, f"Documentation already exists in '{directory}'. Use --overwrite to ignore" 28 | ) 29 | self.directory = directory 30 | -------------------------------------------------------------------------------- /portray/logo.py: -------------------------------------------------------------------------------- 1 | from portray._version import __version__ 2 | 3 | ascii_art = rf""" 4 | __ 5 | /\ \__ 6 | _____ ___ _ __\ \ ,_\ _ __ __ __ __ 7 | /\ '__`\ / __`\/\`'__\ \ \/ /\`'__\/'__`\ /\ \/\ \ 8 | \ \ \L\ \/\ \L\ \ \ \/ \ \ \_\ \ \//\ \L\.\_\ \ \_\ \ 9 | \ \ ,__/\ \____/\ \_\ \ \__\\ \_\\ \__/.\_\\/`____ \ 10 | \ \ \/ \/___/ \/_/ \/__/ \/_/ \/__/\/_/ `/___/> \ 11 | \ \_\ /\___/ 12 | \/_/ \/__/ 13 | Your Project with Great Documentation. 14 | 15 | Version: {__version__} 16 | Copyright Timothy Edmund Crosley 2019 MIT License 17 | """ 18 | 19 | __doc__ = f""" 20 | ```python 21 | {ascii_art} 22 | ``` 23 | """ 24 | -------------------------------------------------------------------------------- /portray/mkdocs_templates/partials/footer.html: -------------------------------------------------------------------------------- 1 | {% import "partials/language.html" as lang with context %} 2 | -------------------------------------------------------------------------------- /portray/render.py: -------------------------------------------------------------------------------- 1 | """Defines how to render the current project and project_config using the 2 | included documentation generation utilities. 3 | """ 4 | import os 5 | import shutil 6 | import sys 7 | import tempfile 8 | from contextlib import contextmanager 9 | from glob import glob 10 | from typing import Dict, Iterator, Tuple 11 | 12 | import mkdocs.config as mkdocs_config 13 | import mkdocs.exceptions as _mkdocs_exceptions 14 | from mkdocs.commands.build import build as mkdocs_build 15 | from mkdocs.config.defaults import get_schema as mkdocs_schema 16 | from mkdocs.utils import is_markdown_file 17 | from pdocs import as_markdown as pdocs_as_markdown 18 | from portray.exceptions import DocumentationAlreadyExists 19 | from yaspin import yaspin 20 | 21 | NO_HOME_PAGE = """ 22 | # Nothing here 23 | 24 | `portray` uses README.md as your projects home page. 25 | It appears you do not yet have a README.md file created. 26 | """ 27 | 28 | 29 | def documentation(config: dict, overwrite: bool = False) -> None: 30 | """Renders the entire project given the project config into the config's 31 | specified output directory. 32 | 33 | Behind the scenes: 34 | 35 | - A temporary directory is created and your code is copy and pasted there 36 | - pdoc is ran over your code with the output sent into the temporary directory 37 | as Markdown documents 38 | - MkDocs is ran over all of your projects Markdown documents including those 39 | generated py pdoc. MkDocs outputs an HTML representation to a new temporary 40 | directory. 41 | - The html temporary directory is copied into your specified output location 42 | - Both temporary directories are deleted. 43 | """ 44 | if os.path.exists(config["output_dir"]): 45 | if overwrite: 46 | shutil.rmtree(config["output_dir"]) 47 | else: 48 | raise DocumentationAlreadyExists(config["output_dir"]) 49 | 50 | with documentation_in_temp_folder(config) as (_, documentation_output): 51 | shutil.copytree(documentation_output, config["output_dir"]) 52 | 53 | 54 | def pdocs(config: dict) -> None: 55 | """Render this project using the specified pdoc config passed into pdoc. 56 | 57 | This rendering is from code definition to Markdown so that 58 | it will be compatible with MkDocs. 59 | """ 60 | pdocs_as_markdown(**config) 61 | 62 | 63 | def mkdocs(config: dict): 64 | """Render the project's associated Markdown documentation using the specified 65 | MkDocs config passed into the MkDocs `build` command. 66 | 67 | This rendering is from `.md` Markdown documents into HTML 68 | """ 69 | config_instance = _mkdocs_config(config) 70 | return mkdocs_build(config_instance) 71 | 72 | 73 | @contextmanager 74 | def documentation_in_temp_folder(config: dict) -> Iterator[Tuple[str, str]]: 75 | """Build documentation within a temp folder, returning that folder name before it is deleted.""" 76 | if config["append_directory_to_python_path"] and config["directory"] not in sys.path: 77 | sys.path.append(config["directory"]) 78 | 79 | with tempfile.TemporaryDirectory() as input_dir: 80 | input_dir = os.path.join(input_dir, "input") 81 | os.mkdir(input_dir) 82 | with tempfile.TemporaryDirectory() as temp_output_dir: 83 | 84 | with yaspin( 85 | text="Copying source documentation to temporary compilation directory" 86 | ) as spinner: 87 | for root_file in os.listdir(config["directory"]): 88 | root_file_absolute = os.path.join(config["directory"], root_file) 89 | if os.path.isfile(root_file_absolute) and is_markdown_file(root_file_absolute): 90 | shutil.copyfile(root_file_absolute, os.path.join(input_dir, root_file)) 91 | 92 | for source_directory in [config["docs_dir"]] + config["extra_dirs"]: 93 | directory_absolute = os.path.join(config["directory"], source_directory) 94 | if os.path.isdir(directory_absolute): 95 | shutil.copytree( 96 | directory_absolute, os.path.join(input_dir, source_directory) 97 | ) 98 | 99 | spinner.ok("Done") 100 | 101 | if "docs_dir" not in config["mkdocs"]: 102 | config["mkdocs"]["docs_dir"] = input_dir 103 | if "site_dir" not in config["mkdocs"]: 104 | config["mkdocs"]["site_dir"] = temp_output_dir 105 | if "nav" not in config["mkdocs"]: 106 | nav = config["mkdocs"]["nav"] = [] 107 | 108 | root_docs = sorted(glob(os.path.join(input_dir, "*.md"))) 109 | readme_doc = os.path.join(input_dir, "README.md") 110 | if readme_doc in root_docs: 111 | root_docs.remove(readme_doc) 112 | else: 113 | with open(readme_doc, "w") as readme_doc_file: 114 | readme_doc_file.write(NO_HOME_PAGE) 115 | 116 | nav.append({"Home": "README.md"}) 117 | 118 | nav.extend(_doc(doc, input_dir, config) for doc in root_docs) 119 | 120 | nav.extend( 121 | _nested_docs(os.path.join(input_dir, config["docs_dir"]), input_dir, config) 122 | ) 123 | else: 124 | nav = config["mkdocs"]["nav"] 125 | if nav: 126 | index_nav = nav[0] 127 | index_page: str = "" 128 | if index_nav and isinstance(index_nav, dict): 129 | index_page = tuple(index_nav.values())[0] 130 | elif isinstance(index_nav, str): # pragma: no cover 131 | index_page = index_nav 132 | 133 | if index_page: 134 | 135 | destination_index_page = os.path.join(input_dir, "index.md") 136 | if ( 137 | index_page != "README.md" 138 | and index_page != "index.md" 139 | and not os.path.exists(destination_index_page) 140 | ): 141 | shutil.copyfile( 142 | os.path.join(input_dir, index_page), destination_index_page 143 | ) 144 | 145 | if config["include_reference_documentation"] and ( 146 | config["include_reference_documentation"] not in ("false", "False") 147 | or config["include_reference_documentation"] 148 | ): 149 | with yaspin(text="Auto generating reference documentation using pdocs") as spinner: 150 | if "output_dir" not in config["pdocs"]: 151 | config["pdocs"]["output_dir"] = os.path.join(input_dir, "reference") 152 | pdocs(config["pdocs"]) 153 | reference_docs = _nested_docs(config["pdocs"]["output_dir"], input_dir, config) 154 | nav.append({"Reference": reference_docs}) # type: ignore 155 | spinner.ok("Done") 156 | 157 | with yaspin(text="Rendering complete website from Markdown using MkDocs") as spinner: 158 | mkdocs(config["mkdocs"]) 159 | spinner.ok("Done") 160 | 161 | # remove any settings pointing to the temp dirs 162 | if config["mkdocs"]["docs_dir"].startswith(input_dir): 163 | del config["mkdocs"]["docs_dir"] 164 | if config["mkdocs"]["site_dir"].startswith(temp_output_dir): 165 | del config["mkdocs"]["site_dir"] 166 | if config["pdocs"].get("output_dir") and config["pdocs"]["output_dir"].startswith( 167 | input_dir 168 | ): 169 | del config["pdocs"]["output_dir"] 170 | if config["include_reference_documentation"]: 171 | nav.pop() 172 | 173 | yield input_dir, temp_output_dir 174 | 175 | 176 | def _mkdocs_config(config: dict) -> mkdocs_config.Config: 177 | config_instance = mkdocs_config.Config(schema=mkdocs_schema()) 178 | config_instance.load_dict(config) 179 | 180 | errors, warnings = config_instance.validate() 181 | if errors: 182 | print(errors) 183 | raise _mkdocs_exceptions.ConfigurationError( 184 | f"Aborted with {len(errors)} Configuration Errors!" 185 | ) 186 | elif config.get("strict", False) and warnings: # pragma: no cover 187 | print(warnings) 188 | raise _mkdocs_exceptions.ConfigurationError( 189 | f"Aborted with {len(warnings)} Configuration Warnings in 'strict' mode!" 190 | ) 191 | 192 | config_instance.config_file_path = config["config_file_path"] 193 | return config_instance 194 | 195 | 196 | def _nested_docs(directory: str, root_directory: str, config: dict) -> list: 197 | nav = [ 198 | _doc(doc, root_directory, config) for doc in sorted(glob(os.path.join(directory, "*.md"))) 199 | ] 200 | 201 | nested_dirs = sorted(glob(os.path.join(directory, "*/"))) 202 | for nested_dir in nested_dirs: 203 | if ( 204 | len(glob(os.path.join(nested_dir, "*.md")) + glob(os.path.join(nested_dir, "**/*.md"))) 205 | > 0 206 | ): 207 | dir_nav = { 208 | _label(nested_dir[:-1], config): _nested_docs(nested_dir, root_directory, config) 209 | } 210 | nav.append(dir_nav) # type: ignore 211 | 212 | return nav 213 | 214 | 215 | def _label(path: str, config: Dict) -> str: 216 | label = os.path.basename(path) 217 | if "." in label: 218 | label = ".".join(label.split(".")[:-1]) 219 | label = label.replace("-", " ").replace("_", " ").title() 220 | return config["labels"].get(label, label) 221 | 222 | 223 | def _doc(path: str, root_path: str, config: dict) -> Dict[str, str]: 224 | path = os.path.relpath(path, root_path) 225 | return {_label(path, config): path} 226 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "portray" 3 | version = "1.8.0" 4 | description = "Your Project with Great Documentation" 5 | authors = ["Timothy Crosley "] 6 | license = "MIT" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = ">=3.8" 11 | hug = ">=2.6" 12 | mkdocs = ">=1.3.0,<1.4.0" 13 | pdocs = ">=1.2.0" 14 | toml = ">=0.10.0" 15 | mkdocs-material = ">=7.0" 16 | GitPython = ">=3.0" 17 | pymdown-extensions = ">=7.0" 18 | yaspin = ">=0.15.0,<3" 19 | livereload = ">=2.6.3" 20 | 21 | [tool.poetry.dev-dependencies] 22 | bandit = ">=1.6" 23 | pytest = ">=5.1" 24 | safety = ">=1.8" 25 | black = {version = ">1.0.0", python = ">=3.7.0,<4.0"} 26 | mypy = ">0.730.0" 27 | types-toml = ">0.0" 28 | ipython = ">=7.7" 29 | pytest-cov = ">=2.7" 30 | pytest-mock = ">=1.10" 31 | hypothesis-auto = {version = ">=0.0.4", python = ">=3.7.0,<4.0"} 32 | pprofile = ">=2.0" 33 | ruff = ">=0.0.191" 34 | cruft = {version = ">=2.2", python = ">=3.7.0,<4.0"} 35 | 36 | [tool.poetry.scripts] 37 | portray = "portray.cli:__hug__.cli" 38 | 39 | [tool.portray.mkdocs.theme] 40 | favicon = "art/logo_small.png" 41 | logo = "art/logo_small.png" 42 | name = "material" 43 | palette = {primary = "blue grey", accent = "pink"} 44 | 45 | [build-system] 46 | requires = ["poetry>=0.12"] 47 | build-backend = "poetry.masonry.api" 48 | 49 | [tool.ruff] 50 | line-length = 100 51 | select = [ 52 | "E", # pycodestyle errors 53 | "W", # pycodestyle warnings 54 | "F", # pyflakes 55 | "I", # isort 56 | "C", # flake8-comprehensions 57 | "B", # flake8-bugbear 58 | ] 59 | ignore = [ 60 | "E501", # line too long, handled by black 61 | "B008", # do not perform function calls in argument defaults 62 | "C901", # too complex 63 | ] 64 | 65 | [tool.ruff.isort] 66 | known-third-party = ["portray"] 67 | 68 | [tool.black] 69 | line-length = 100 70 | -------------------------------------------------------------------------------- /scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | poetry run ruff . --fix 5 | poetry run black portray/ tests/ 6 | -------------------------------------------------------------------------------- /scripts/done.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | ./scripts/clean.sh 5 | ./scripts/test.sh 6 | -------------------------------------------------------------------------------- /scripts/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | poetry run cruft check 5 | poetry run mypy --ignore-missing-imports portray/ 6 | poetry run ruff . 7 | poetry run black --check portray/ tests/ 8 | poetry run safety check -i 51457 9 | poetry run bandit -r portray/ 10 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | ./scripts/lint.sh 5 | poetry run pytest -s --cov=portray/ --cov=tests --cov-report=term-missing ${@-} --cov-report html 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 100 3 | extend-ignore = 4 | E203 # https://github.com/psf/black/blob/master/docs/the_black_code_style.md#slices 5 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timothycrosley/portray/f8b6d664f0c4d6923958f805fce1a1f94c5eaa6f/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import os 3 | import tempfile 4 | 5 | import pytest 6 | 7 | 8 | @contextlib.contextmanager 9 | def chdir_manager(directory): 10 | old_directory = os.getcwd() 11 | os.chdir(directory) 12 | yield directory 13 | os.chdir(old_directory) 14 | 15 | 16 | @pytest.fixture(scope="function") 17 | def chdir(): 18 | return chdir_manager 19 | 20 | 21 | @pytest.fixture(scope="function") 22 | def temporary_dir(): 23 | with tempfile.TemporaryDirectory() as temp_dir: 24 | yield temp_dir 25 | 26 | 27 | @pytest.fixture() 28 | def project_dir(): 29 | yield os.path.abspath(os.path.join(__file__, os.pardir, os.pardir)) 30 | -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import sys 4 | import tempfile 5 | 6 | import mkdocs.commands.gh_deploy 7 | import pytest 8 | import toml 9 | from portray import api, exceptions 10 | 11 | CUSTOM_NAV = """ 12 | [[tool.portray.mkdocs.nav]] 13 | Changelog = "CHANGELOG.md" 14 | 15 | [[tool.portray.mkdocs.nav]] 16 | Troubleshooting = "TROUBLESHOOTING.md" 17 | 18 | [[tool.portray.mkdocs.nav]] 19 | [[tool.portray.mkdocs.nav.Contributing]] 20 | "1. Contributing Guide" = "docs/contributing/1.-contributing-guide.md" 21 | 22 | [[tool.portray.mkdocs.nav.Contributing]] 23 | "2. Coding Standard" = "docs/contributing/2.-coding-standard.md" 24 | 25 | [[tool.portray.mkdocs.nav.Contributing]] 26 | "3. Code of Conduct" = "docs/contributing/3.-code-of-conduct.md" 27 | 28 | [[tool.portray.mkdocs.nav.Contributing]] 29 | "4. Acknowledgements" = "docs/contributing/4.-acknowledgements.md" 30 | 31 | [[tool.portray.mkdocs.nav]] 32 | [[tool.portray.mkdocs.nav."Quick Start"]] 33 | "1. Installation" = "docs/quick_start/1.-installation.md" 34 | 35 | [[tool.portray.mkdocs.nav."Quick Start"]] 36 | "2. CLI" = "docs/quick_start/2.-cli.md" 37 | 38 | [[tool.portray.mkdocs.nav."Quick Start"]] 39 | "3. API" = "docs/quick_start/3.-api.md" 40 | 41 | [[tool.portray.mkdocs.nav."Quick Start"]] 42 | "4. Configuration" = "docs/quick_start/4.-configuration.md" 43 | """ 44 | 45 | FAKE_PYPROJECT_TOML_BASIC = """ 46 | [tool.portray] 47 | modules = ["my_module"] 48 | output_dir = "docs_output" 49 | """ 50 | 51 | 52 | @pytest.mark.skipif( 53 | sys.version_info < (3, 8) and sys.platform == "win32", 54 | reason="Tests fails on CI with this combination due to CI specific permission issues on.", 55 | ) 56 | def test_as_html(temporary_dir, project_dir, chdir): 57 | with chdir(temporary_dir): 58 | # Directory with no project 59 | with pytest.raises(exceptions.NoProjectFound): 60 | api.as_html() 61 | 62 | # Rendering should succeed as soon as a project is within the directory 63 | temp_project_dir = os.path.join(temporary_dir, "portray") 64 | shutil.copytree(project_dir, temp_project_dir) 65 | with chdir(temp_project_dir): 66 | api.as_html() 67 | 68 | # Rendering a second time should fail 69 | with pytest.raises(exceptions.DocumentationAlreadyExists): 70 | api.as_html() 71 | 72 | # Unless we enable overwritting destination 73 | api.as_html(overwrite=True) 74 | 75 | # Or, we output to a different location 76 | with tempfile.TemporaryDirectory() as new_temp_directory: 77 | api.as_html(output_dir=os.path.join(new_temp_directory, "site")) 78 | 79 | 80 | @pytest.mark.skipif( 81 | sys.version_info < (3, 8) and sys.platform == "win32", 82 | reason="Tests fails on CI with this combination due to CI specific permission issues on.", 83 | ) 84 | def test_as_html_custom_nav(temporary_dir, project_dir, chdir): 85 | with chdir(temporary_dir): 86 | temp_project_dir = os.path.join(temporary_dir, "portray") 87 | shutil.copytree(project_dir, temp_project_dir) 88 | with chdir(temp_project_dir): 89 | with open(os.path.join(temp_project_dir, "pyproject.toml"), "w+") as pyproject: 90 | pyproject.write(CUSTOM_NAV) 91 | 92 | api.as_html() 93 | 94 | 95 | def test_server(mocker, project_dir, chdir): 96 | with chdir(project_dir): 97 | mocker.patch("portray.api.Server") 98 | api.server() 99 | api.Server.assert_called_once() 100 | api.Server.return_value.serve.assert_called_once() 101 | 102 | 103 | def test_reloading_server(mocker, project_dir, chdir): 104 | with chdir(project_dir): 105 | mocker.patch("portray.api.Server") 106 | api.server(reload=True) 107 | server_instance = api.Server.return_value 108 | server_instance.serve.assert_called_once() 109 | assert len(server_instance.watch.call_args_list) == 5 110 | 111 | server_instance.reset_mock() 112 | with tempfile.TemporaryDirectory(dir=project_dir) as test_dir: 113 | test_config = os.path.join(test_dir, "test.toml") 114 | test_docs_dir = os.path.join(test_dir, "docs_dir") 115 | test_site_dir = os.path.join(test_dir, "site_dir") 116 | os.mkdir(test_docs_dir) 117 | os.mkdir(test_site_dir) 118 | with open(test_config, "w") as test_cfg: 119 | toml.dump( 120 | { 121 | "tool": { 122 | "portray": { 123 | "mkdocs": {"docs_dir": test_docs_dir, "site_dir": test_site_dir} 124 | } 125 | } 126 | }, 127 | test_cfg, 128 | ) 129 | api.server(config_file=test_config, reload=True) 130 | server_instance.watch.assert_called() 131 | assert len(server_instance.watch.call_args_list) == 7 132 | 133 | 134 | def project_configuration(project_dir, chdir): 135 | with chdir(project_dir): 136 | config = api.project_configuration() 137 | assert config 138 | assert isinstance(config, dict) 139 | 140 | 141 | def test_in_browser(mocker, project_dir, chdir): 142 | with chdir(project_dir): 143 | mocker.patch("portray.api.Server") 144 | mocker.patch("webbrowser.open_new") 145 | api.in_browser() 146 | server_instance = api.Server.return_value 147 | server_instance.serve.assert_called_once() 148 | 149 | server_instance.reset_mock() 150 | api.in_browser(port=9999, host="localhost") 151 | call_args = server_instance.serve.call_args_list[0][1] 152 | assert call_args["host"] == "localhost" 153 | assert call_args["port"] == 9999 154 | 155 | 156 | def test_on_github_pages(mocker, project_dir, chdir): 157 | with chdir(project_dir): 158 | mocker.patch("mkdocs.commands.gh_deploy.gh_deploy") 159 | api.on_github_pages() 160 | mkdocs.commands.gh_deploy.gh_deploy.assert_called_once() 161 | 162 | 163 | def test_module_no_path(temporary_dir, chdir): 164 | """Test handling of python modules specified in root project directory but not in path""" 165 | with chdir(temporary_dir): 166 | with open(os.path.join(temporary_dir, "my_module.py"), "w") as pathless_module: 167 | pathless_module.write("def my_method():\n pass\n") 168 | 169 | # Rendering with no module identification should fail 170 | with pytest.raises(Exception): 171 | api.as_html() 172 | 173 | # With module specification, even without path should succeed 174 | api.as_html(modules=["my_module"]) 175 | 176 | # Unless path auto inclusion is turned off 177 | with open(os.path.join(temporary_dir, "pyproject.toml"), "w") as pyproject: 178 | pyproject.write("[tool.portray]\nappend_directory_to_python_path = false") 179 | with pytest.raises(Exception): 180 | api.as_html() 181 | 182 | 183 | def test_setting_output_dir_in_pyproject_overwrites_default(temporary_dir): 184 | config_file = os.path.join(temporary_dir, "pyproject.toml") 185 | with open(config_file, "w") as pyproject: 186 | pyproject.write(FAKE_PYPROJECT_TOML_BASIC) 187 | config = api.project_configuration(directory=temporary_dir, config_file=config_file) 188 | assert config["output_dir"] == "docs_output" 189 | -------------------------------------------------------------------------------- /tests/test_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from hypothesis_auto import auto_test 4 | from portray import config, exceptions 5 | 6 | REPO_URL = "https://github.com/timothycrosley/portray" 7 | 8 | FAKE_SETUP_FILE = """ 9 | from setuptools import setup 10 | 11 | setup(name='fake', 12 | version='0.1', 13 | description='Fake package for tesitng pourposes', 14 | author='Flying Circus', 15 | author_email='flyingcircus@example.com', 16 | license='MIT', 17 | packages=['fake']) 18 | """ 19 | 20 | FAKE_PYPROJECT_TOML_FLIT = """ 21 | [tool.flit.metadata] 22 | module = "preconvert" 23 | author = "Timothy Edmund Crosley" 24 | author-email = "timothy.crosley@gmail.com" 25 | home-page = "https://github.com/timothycrosley/preconvert" 26 | requires-python=">=3.5" 27 | description-file="README.md" 28 | classifiers=[ 29 | "Intended Audience :: Developers", 30 | "License :: OSI Approved :: MIT License", 31 | "Programming Language :: Python :: 3", 32 | "Topic :: Software Development :: Libraries :: Python Modules", 33 | ] 34 | 35 | [tool.portray.pdoc3] 36 | just = "kidding" 37 | 38 | [tool.portray] 39 | extra_markdown_extensions = ["smarty"] 40 | """ 41 | 42 | 43 | def test_project_properties(project_dir): 44 | auto_test(config.project, auto_allow_exceptions_=(exceptions.NoProjectFound,)) 45 | auto_test( 46 | config.project, directory=project_dir, auto_allow_exceptions_=(exceptions.NoProjectFound,) 47 | ) 48 | 49 | 50 | def test_project_setup_py(temporary_dir): 51 | with open(os.path.join(temporary_dir, "setup.py"), "w") as setup_file: 52 | setup_file.write(FAKE_SETUP_FILE) 53 | 54 | project_config = config.project(directory=temporary_dir, config_file="") 55 | assert project_config["modules"] == ["fake"] 56 | 57 | 58 | def test_project_flit_setup(temporary_dir): 59 | with open(os.path.join(temporary_dir, "pyproject.toml"), "w") as setup_file: 60 | setup_file.write(FAKE_PYPROJECT_TOML_FLIT) 61 | 62 | project_config = config.project(directory=temporary_dir, config_file="pyproject.toml") 63 | assert project_config["modules"] == ["preconvert"] 64 | 65 | 66 | def test_setup_py_properties(): 67 | auto_test(config.setup_py) 68 | 69 | 70 | def test_toml_properties(): 71 | auto_test(config.toml) 72 | 73 | 74 | def test_mkdocs_properties(): 75 | auto_test(config.mkdocs) 76 | 77 | 78 | def test_pdocs_properties(): 79 | auto_test(config.pdocs) 80 | 81 | 82 | def test_repository_properties(): 83 | auto_test(config.repository) 84 | 85 | 86 | def test_repository_custom_config(project_dir): 87 | assert config.repository(project_dir, repo_url=REPO_URL) == { 88 | "edit_uri": "edit/main/", 89 | "repo_name": "portray", 90 | "repo_url": REPO_URL, 91 | } 92 | 93 | assert config.repository(project_dir, repo_name="different_name", repo_url=REPO_URL) == { 94 | "edit_uri": "edit/main/", 95 | "repo_name": "different_name", 96 | "repo_url": REPO_URL, 97 | } 98 | 99 | assert config.repository(project_dir, edit_uri="edit/develop/", repo_url=REPO_URL) == { 100 | "edit_uri": "edit/develop/", 101 | "repo_name": "portray", 102 | "repo_url": REPO_URL, 103 | } 104 | 105 | assert config.repository( 106 | project_dir, repo_url="https://github.com/timothycrosley/examples" 107 | ) == { 108 | "edit_uri": "edit/main/", 109 | "repo_name": "examples", 110 | "repo_url": "https://github.com/timothycrosley/examples", 111 | } 112 | 113 | assert config.repository( 114 | project_dir, repo_url="https://bitbucket.org/atlassian/stash-example-plugin.git" 115 | ) == { 116 | "edit_uri": "src/default/docs/", 117 | "repo_name": "stash-example-plugin", 118 | "repo_url": "https://bitbucket.org/atlassian/stash-example-plugin", 119 | } 120 | 121 | assert config.repository( 122 | project_dir, repo_url="git@bitbucket.org:atlassian/stash-example-plugin.git" 123 | ) == { 124 | "edit_uri": "src/default/docs/", 125 | "repo_name": "stash-example-plugin", 126 | "repo_url": "https://bitbucket.org/atlassian/stash-example-plugin", 127 | } 128 | 129 | assert config.repository(project_dir, repo_url="not_actually_a_valid_url") == { 130 | "repo_name": "not_actually_a_valid_url", 131 | "repo_url": "not_actually_a_valid_url", 132 | } 133 | 134 | assert config.repository( 135 | project_dir, repo_url="https://gitlab.ci.token:password@gitlab.net/app.git" 136 | ) == {"edit_uri": "edit/main/", "repo_name": "app", "repo_url": "https://gitlab.net/app"} 137 | 138 | 139 | def test_repository_no_config_no_repository(temporary_dir): 140 | assert config.repository(temporary_dir) == {} 141 | -------------------------------------------------------------------------------- /tests/test_render.py: -------------------------------------------------------------------------------- 1 | from hypothesis_auto import auto_test 2 | from portray import render 3 | 4 | 5 | def test_mkdocs_config(): 6 | auto_test( 7 | render._mkdocs_config, 8 | auto_allow_exceptions_=(render._mkdocs_exceptions.ConfigurationError,), 9 | ) 10 | --------------------------------------------------------------------------------