├── .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 | [](https://timothycrosley.github.io/portray/)
2 | _________________
3 |
4 | [](http://badge.fury.io/py/portray)
5 | [](https://github.com/timothycrosley/portray/actions?query=workflow%3ATest)
6 | [](https://github.com/timothycrosley/portray/actions?query=workflow%3ALint)
7 | [](https://codecov.io/gh/timothycrosley/portray)
8 | [](https://gitter.im/timothycrosley/portray?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
9 | [](https://pypi.python.org/pypi/portray/)
10 | [](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 | [](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 | 
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 |
--------------------------------------------------------------------------------