diff options
author | Ivan Kanakarakis <ivan.kanak@gmail.com> | 2022-10-01 18:15:12 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-01 18:15:12 +0300 |
commit | 3007eb7231a6f4e4006614643e67354698900aef (patch) | |
tree | f81c39612412fc83201855e10ae0a81f1454b8ec | |
parent | 15503e13d39b8d0feb9c6164261340ca045914df (diff) | |
parent | 8827ab6bbe9ba85da927c9d2aef69bcebc1dc16a (diff) | |
download | pysaml2-3007eb7231a6f4e4006614643e67354698900aef.tar.gz |
Merge pull request #872 from IdentityPython/feat-poetry
Switch to poetry and add community guidelines
37 files changed, 3361 insertions, 926 deletions
diff --git a/.editorconfig b/.editorconfig index a1e1486a..b738a9ea 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,17 +8,8 @@ tab_width = 4 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true -max_line_length = 88 +max_line_length = 120 -[*.y{a,}ml] +[{*.y{a,}ml,*.html,*.xhtml,*.xml,*.xsd}] indent_size = 2 tab_width = 2 - -[*.html] -indent_style = tab - -[*.js] -indent_style = tab - -[*.css] -indent_style = tab diff --git a/.github/issue_template.md b/.github/issue_template.md index 58967f57..b76ed50a 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,20 +1,22 @@ -<!--- Provide a general summary of the issue in the Title above --> +<!-- Provide a general summary of the issue in the Title above --> -## Code Version +### Code Version <!-- What version and/or branch of the code are you running? --> -## Expected Behavior -<!--- Tell us what should happen --> +### Expected Behavior +<!-- Tell us what should happen --> -## Current Behavior -<!--- Tell us what happens instead of the expected behavior --> +### Current Behavior +<!-- Tell us what happens instead of the expected behavior --> -## Possible Solution -<!--- Not obligatory, but suggest a fix/reason for the bug, --> +### Possible Solution +<!-- Not obligatory, but suggest a fix/reason for the bug, --> + +### Steps to Reproduce +<!-- Provide a link to a live example --> +<!-- or, an unambiguous set of steps to reproduce this bug --> +<!-- Include code to reproduce, if relevant --> -## Steps to Reproduce -<!--- Provide a link to a live example, or an unambiguous set of steps to --> -<!--- reproduce this bug. Include code to reproduce, if relevant --> 1. 2. 3. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 94be46df..f388ca27 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,11 +1,20 @@ -### All Submissions: +### Description -* [ ] Have you checked to ensure there aren't other open [Pull Requests](../../pulls) for the same update/change? -* [ ] Have you added an explanation of what problem you are trying to solve with this PR? -* [ ] Have you added information on what your changes do and why you chose this as your solution? -* [ ] Have you written new tests for your changes? -* [ ] Does your submission pass tests? -* [ ] This project follows PEP8 style guide. Have you run your code against the 'flake8' linter? +##### The feature or problem addressed by this PR +<!-- an explaination of the issue that is being resolved with this PR --> +<!-- or, an explaination of the feature that is being added with this PR --> +<!-- or, link to an issue describing the problem --> +##### What your changes do and why you chose this solution + +<!-- description of the technical changes that help resolve the issue --> + + +### Checklist + +* [ ] Checked that no other issues or pull requests exist for the same issue/change +* [ ] Added tests covering the new functionality +* [ ] Updated documentation OR the change is too minor to be documented +* [ ] Updated CHANGELOG.md OR changes are insignificant diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..bcb1de57 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,149 @@ +# Contributing guidelines + + +## Questions or Problems + +Please, do not open issues for general support questions as we want to keep GitHub +issues for bug reports and feature requests. Instead, we recommend using our [mailing +list](https://lists.sunet.se/postorius/lists/idpy-discuss.lists.sunet.se/) or asking +support-related questions on the [Slack workspace](https://identity-python.slack.com/) +([invitation](https://join.slack.com/t/identity-python/shared_invite/enQtNzEyNjU1NDI1MjUyLTM2MWI5ZGNhMTk1ZThiOTIxNWY2OTY1ODVmMWNjMzUzMTYxNTY5MzE5N2RlYjExZTIyM2MwYjBjZGE4MGVlMTM)). + +To save your and our time, we will systematically close all issues that are requests for +general support and redirect people to the channels above. + + +## Issues and Bugs + +If you find a bug in the source code, you can help us by submitting an issue to our +GitHub Repository. Even better, you can submit a Pull Request with a fix. + + +## Feature Requests + +You can request a new feature by submitting an issue to our GitHub Repository. If you +would like to implement a new feature, please consider the size of the change in order +to determine the right steps to proceed: + +- For a Major Feature, first open an issue and outline your proposal so that it can be + discussed. This process allows us to better coordinate our efforts, prevent + duplication of work, and help you to craft the change so that it is successfully + accepted into the project. + +- Small Features can be crafted and directly submitted as a Pull Request. + + +## Improving Documentation + +Should you have a suggestion for the documentation, you can open an issue and outline +the problem or improvement you have - however, creating the doc fix yourself is much +better! + +If you want to help improve the docs, it's a good idea to let others know what you're +working on to minimize duplication of effort. Create a new issue (or comment on a +related existing one) to let others know what you're working on. + +If you're making a small change (typo, phrasing) don't worry about filing an issue +first. Fork the repository in-place and make a quick change on the fly. + +For large fixes, please build and test the documentation before submitting the PR to be +sure you haven't accidentally introduced any layout or formatting issues. + + +## Submission Guidelines + + +### Submitting an Issue + +Before you submit an issue, please search the issue tracker. +An issue for your problem might already exist +and the discussion might inform you of workarounds readily available. + +We want to fix all the issues as soon as possible, but before fixing a bug, we need to +reproduce and confirm it. In order to reproduce bugs, we require that you provide a +minimal reproduction. Having a minimal reproducible scenario gives us a wealth of +important information without going back and forth to you with additional questions. + +A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) +as well as confirm that we are fixing the right problem. + +We require a minimal reproduction to save maintainers' time and ultimately be able to +fix more bugs. Often, developers find coding problems themselves while preparing a +minimal reproduction. We understand that sometimes it might be hard to extract +essential bits of code from a larger codebase, but we really need to isolate the problem +before we can fix it. + +Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, +so if we don't hear back from you, we are going to close an issue that doesn't have +enough information to be reproduced. + + +### Submitting a Pull Request (PR) + +Before you submit your Pull Request (PR) consider the following guidelines: + +1. Search [GitHub](../pulls) for an open or closed PR + that relates to your submission. You don't want to duplicate existing efforts. + +2. Be sure that an issue describes the problem you're fixing, or documents the design + for the feature you'd like to add. Discussing the design upfront helps to ensure that + we're ready to accept your work. + +3. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the repo. + +4. In your forked repository, make your changes in a new git branch: + + ```shell + $ git checkout -b my-fix-branch main + ``` + +5. Create your patch, **including appropriate test cases**. + Remember to follow the [Coding Rules](#coding-rules). + +6. Run the full test suite, as described in the [developer documentation][dev-doc], + and ensure that all tests pass. + +7. Commit your changes using a descriptive commit message. + + ```shell + $ git commit --all + ``` + Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. + +8. Push your branch to GitHub: + + ```shell + $ git push origin my-fix-branch + ``` + +9. In GitHub, create a new pull request. + + +#### Addressing review feedback + +If we ask for changes via code reviews then: + +1. Make the required updates to the code. +2. Re-run the test suite to ensure tests are still passing. +3. Create a fixup commit and push to your GitHub repository. Update your Pull Request: + + ```shell + $ git commit --all --fixup HEAD + $ git push + ``` + + For more info on working with fixup commits see [here](docs/FIXUP_COMMITS.md). + +That's it! Thank you for your contribution! + + +## Coding Rules + +To ensure consistency throughout the source code, +keep these rules in mind as you are working: + +* All features or bug fixes **must be tested** by one or more specs (unit-tests). +* All public API methods **must be documented**. +* We follow [Black's style guide](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html), + and wrap all code at **120 characters**. + Pre-configured tools to automatically lint and format code are available, see [DEVELOPER.md](DEVELOPER.md). diff --git a/DEVELOPERS.md b/DEVELOPERS.md new file mode 100644 index 00000000..ed395cb1 --- /dev/null +++ b/DEVELOPERS.md @@ -0,0 +1,102 @@ +# Developing guidelines + +If you already cloned the repository and you know that you need to deep dive in the code, +here are some guidelines to set up your environment. + + +## Development environment setup + +### Python versions + +To manage multiple Python versions on my system, I use [`pyenv`]. +See the `pyenv` documentation on how to install and configure the tool. + +Install the supported python versions and enable them for this project: + +```shell +$ for v in 3.6 3.7 3.8 3.9 3.10; do pyenv install "${v}:latest"; done +$ pyenv versions --bare | xargs pyenv local +``` + + +### Dependencies + +This project uses [`poetry`] to manage dependencies and virtual environments. +See `poetry`'s [installation instructions] on how to install `poetry` on your system. + +I have opted to use [`pipx`] to install and manage `poerty` itself. +I also use `pipx` to manage other python executables that I want readily available on my system. + +Once `poetry` is available on your system, install the development dependencies: + +```shell +$ poetry install --with dev,test,coverage,docs --sync +``` + +A virtual environment will be created automatically by `poetry`. +To enter a shell with the virtual environment loaded use the `shell` command: + +```shell +$ poetry shell +``` + +Note that `poetry` doesn't need you to load the virtual environment. +It will automatically load the virtual environment as you interact with the `poetry` commands. + + +## Running Tests + +The tests are written with the [`pytest`] test framework. +To run the tests invoke `pytest` through `poetry`: + +```shell +$ poetry run pytest +``` + +However, the above will only run the tests for the latest python version. +To test all python versions invoke the test runner [`tox`]: + +```shell +$ poetry run tox +``` + +This works because different python versions were made available through `pyenv`. + + +## Coding Rules + +Coding style is encoded through the configurations of [`black`] and [`isort`]. +To enforce the rules run: + +```shell +$ poetry run black src/ tests/ example/ +$ poetry run isort src/ tests/ example/ +``` + +Additional rules and suggestions are generated by [`flake8`]. +Check your code with: + +```shell +$ poetry run flake8 src/ +``` + + +## Commit Message Guidelines + +(TODO) + + +## Writing Documentation + +(TODO) + + + [`poetry`]: https://python-poetry.org/ + [installation instructions]: https://python-poetry.org/docs/#installation + [`pipx`]: https://pypa.github.io/pipx/ + [`pyenv`]: https://github.com/pyenv/pyenv + [`pytest`]: https://docs.pytest.org/ + [`tox`]: https://tox.wiki/ + [`black`]: https://black.readthedocs.io/ + [`isort`]: https://pycqa.github.io/isort/ + [`flake8`]: https://flake8.pycqa.org/ diff --git a/INSTALL b/INSTALL deleted file mode 100644 index c846abff..00000000 --- a/INSTALL +++ /dev/null @@ -1,31 +0,0 @@ -You need repoze.who to get the examples working, can be gotten through -easy_install - - easy_install "repoze.who=1.0.16" - -!! 2.0 or newer are missing the form plugin which is used in some instances - -Or from the PyPI site if you prefer to do it that way. -Likewise for pyasn1. - -You should get the latest version, which is right now 1.0.18 . - -You also need xmlsec, which you can find here: - - http://www.aleksey.com/xmlsec/ - -On Ubuntu and Debian: - - sudo apt install xmlsec1 - -You may also need: - - mako - memcached - python-memcache - -Apart from that a normal - - python setup.py install - -will install the package. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 2092bb0a..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,18 +0,0 @@ -include VERSION -include LICENSE -include README.rst -include CHANGELOG.md - -include src/saml2/data/templates/*.xml -include src/saml2/data/schemas/*.xsd - -graft docs -prune docs/build - -prune tests -prune example -prune .github - -global-exclude *.py[cod] -global-exclude __pycache__ -global-exclude *.egg-info diff --git a/README.md b/README.md new file mode 100644 index 00000000..90bbc713 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# PySAML2 - SAML2 for Python + +[![Version](https://img.shields.io/pypi/v/pysaml2)](https://pypi.org/project/pysaml2/) +[![Supported Python versions](https://img.shields.io/pypi/pyversions/pysaml2)](https://pypi.org/project/pysaml2/) +[![Total downloads](https://pepy.tech/badge/pysaml2)](https://pepy.tech/project/pysaml2) +[![Weekly downloads](https://pepy.tech/badge/pysaml2/week)](https://pepy.tech/project/pysaml2) +[![License](https://img.shields.io/github/license/IdentityPython/pysaml2)](https://github.com/IdentityPython/pysaml2/blob/master/LICENSE) + +PySAML2 is a pure python implementation of SAML Version 2 Standard. +It contains all necessary pieces for building a SAML2 service provider +or an identity provider. The distribution contains examples of both. +Originally written to work in a WSGI environment +there are extensions that allow you to use it with other frameworks. + +**Website**: https://idpy.org/ + +**Documentation**: https://pysaml2.readthedocs.io/ + +**Contribution guidelines**: [CONTRIBUTING.md][contributing] + +**Security policies**: [SECURITY.md][sec] + +**Source code**: https://github.com/IdentityPython/pysaml2/ + +**Developer guidelines**: [DEVELOPERS.md][dev] + +**PyPI project**: https://pypi.org/project/pysaml2/ + +**License**: [LICENSE][license] + + +## Installation + +You can install PySAML2 through pip: + +```shell +pip install pysaml2 +``` + +### External dependencies + +PySAML2 works with the [`xmlsec`][xmlsec] binary. +This should be readily available in most Linux distributions: + +```shell +$ apt-get install xmlsec1 +$ dnf install xmlsec1-openssl +$ yum install xmlsec1-openssl +$ pacman -S xmlsec +... +``` + +and on MacOS through [`homebrew`][brew] + +```shell +$ brew install libxmlsec1 +``` + + +## Changelog + +See the [CHANGELOG][clog] to learn about the latest developments. + + +## Contributing + +We've set up a separate document for our [contribution guidelines][contributing]. + + +## Community + +[IdentityPython][idpy] is a community around +a collection of libraries and tools to manage identity related concepts with Python code. +You can interact with the community though the [mailing list](https://lists.sunet.se/postorius/lists/idpy-discuss.lists.sunet.se/) +or on the [Slack workspace](https://identity-python.slack.com/) ([invitation](https://join.slack.com/t/identity-python/shared_invite/enQtNzEyNjU1NDI1MjUyLTM2MWI5ZGNhMTk1ZThiOTIxNWY2OTY1ODVmMWNjMzUzMTYxNTY5MzE5N2RlYjExZTIyM2MwYjBjZGE4MGVlMTM)). + + +## Development + +We've set up a separate document for [developers][dev]. + + +### Releasing + +We've set up a separate document for our [release process][rel]. + + +### Pre-commit + +(TODO) + + + [idpy]: https://idpy.org/ + [docs]: https://pysaml2.readthedocs.io/ + [contributing]: https://github.com/IdentityPython/pysaml2/blob/master/CONTRIBUTING.md + [sec]: https://github.com/IdentityPython/pysaml2/blob/master/SECURITY.md + [repo]: https://github.com/IdentityPython/pysaml2/ + [dev]: https://github.com/IdentityPython/pysaml2/blob/master/DEVELOPERS.md + [pypi]: https://pypi.org/project/pysaml2/ + [license]: https://github.com/IdentityPython/pysaml2/blob/master/LICENSE + [clog]: https://github.com/IdentityPython/pysaml2/blob/master/CHANGELOG.md + [rel]: https://github.com/IdentityPython/pysaml2/blob/master/RELEASE.md + [xmlsec]: http://www.aleksey.com/xmlsec/ + [brew]: https://brew.sh/ diff --git a/README.rst b/README.rst deleted file mode 100644 index 9cd45e21..00000000 --- a/README.rst +++ /dev/null @@ -1,64 +0,0 @@ -************************* -PySAML2 - SAML2 in Python -************************* - -:Version: see VERSION_ -:Documentation: https://pysaml2.readthedocs.io/ - -.. image:: https://api.travis-ci.com/IdentityPython/pysaml2.png?branch=master - :target: https://travis-ci.com/IdentityPython/pysaml2 - -.. image:: https://img.shields.io/pypi/pyversions/pysaml2.svg - :target: https://pypi.org/project/pysaml2/ - -.. image:: https://pepy.tech/badge/pysaml2 - :target: https://pepy.tech/project/pysaml2 - -.. image:: https://pepy.tech/badge/pysaml2/week - :target: https://pepy.tech/project/pysaml2 - -.. image:: https://img.shields.io/pypi/v/pysaml2.svg - :target: https://pypi.org/project/pysaml2/ - - -PySAML2 is a pure python implementation of SAML Version 2 Standard. It contains -all necessary pieces for building a SAML2 service provider or an identity -provider. The distribution contains examples of both. Originally written to -work in a WSGI environment there are extensions that allow you to use it with -other frameworks. - -Install -======= -You can install with :code:`pip install pysaml2` - -Testing -======= - -PySAML2 uses the pytest_ framework for testing. To run the tests on your -system's version of python: - -1. Create and activate a virtualenv_ -2. Inside the virtualenv_, install the dependencies needed for testing - :code:`pip install -r tests/test-requirements.txt` -3. Run the tests :code:`py.test tests` - -To run tests in multiple python environments, you can use pyenv_ with tox_. - - -Please contribute! -================== - -To help out, you could: - -1. Test and report any bugs or other difficulties. -2. Implement missing features. -3. Write more unit tests. - -**If you have the time and inclination I'm looking for Collaborators** - - -.. _VERSION: VERSION -.. _pytest: https://docs.pytest.org/en/latest/ -.. _virtualenv: https://virtualenv.pypa.io/en/stable/ -.. _pyenv: https://github.com/yyuu/pyenv -.. _tox: https://tox.readthedocs.io/en/latest/ diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..57172a00 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,106 @@ +## NOTICE + +this is not accurate anymore and needs to be reworked. + + +## Release instructions + +When releasing a new version, the following steps should be taken: + +1. Make sure all automated tests pass. + +2. Make sure the package metadata in `setup.py` is up-to-date. You can + verify the information by re-generating the egg info: + + ``` + python setup.py egg_info + ``` + + and inspecting `src/pysaml2.egg-info/PKG-INFO`. You should also make sure + that the long description renders as valid reStructuredText. You can + do this by using the `rst2html.py` utility from [docutils]: + + ``` + python setup.py --long-description | rst2html > test.html + ``` + + If this will produce warning or errors, PyPI will be unable to render + the long description nicely. It will treat it as plain text instead. + +3. Update the version in the [VERSION] file and report the changes in + [CHANGELOG.md] and commit the changes.: + + ``` + git add CHANGELOG.md + git add VERSION + git commit -v -s -m "Release version X.Y.Z" + ``` + +4. Create a release [branch]: + + ``` + git branch vX.Y.Z + ``` + +5. Create a release [tag]: + + ``` + git tag -a -s vX.Y.Z -m "Version X.Y.Z" + ``` + +6. Push these changes to Github: + + ``` + git push --follow-tags origin vX.Y.Z + git push --follow-tags origin vX.Y.Z:vX.Y.Z + ``` + +7. Create a source and wheel distribution and upload it to PyPI: + + generate a source and wheel distribution at once + ``` + python setup.py sdist bdist_wheel + ``` + + generated files are under dist/ + ``` + ls dist/ + ``` + + upload release on test.pypi.org + ``` + twine upload --repository-url https://test.pypi.org/legacy/ dist/pysaml2-X.Y.Z* + ``` + + then, upload release on official pypi.org + ``` + twine upload dist/pysaml2-X.Y.Z* + ``` + +8. Upload the documentation to PyPI. First you need to generate the html + version of the documentation: + + ``` + cd docs/ + make clean + make html + cd _build/html + zip -r pysaml2-docs.zip * + ``` + + Submit the generated pysaml2-docs.zip file. + +9. Send an email to the pysaml2 list announcing this release + + +**Important:** Once released to PyPI or any other public download location, +a released egg may *never* be removed, even if it has proven to be a faulty +release ("brown bag release"). In such a case it should simply be superseded +immediately by a new, improved release. + + + [VERSION]: https://github.com/IdentityPython/pysaml2/blob/master/VERSION + [CHANGELOG.md]: https://github.com/IdentityPython/pysaml2/blob/master/CHANGELOG.md + [docutils]: http://docutils.sourceforge.net/ + [branch]: https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell + [tag]: https://git-scm.com/book/en/v2/Git-Basics-Tagging#_annotated_tags diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..bb7c88d8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,40 @@ +# Security Policy + +You can find more information on security incidents +on the [IdPy security webpage](https://idpy.org/security/). + +You read on the [incident response policy](https://github.com/IdentityPython/Governance/blob/master/idpy-incidentresponse.md) +under the [governance documentation](https://github.com/IdentityPython/Governance). + + +## Incident report / Reporting a Vulnerability + +Anyone can submit a potential security vulnerability to `incident-response@idpy.org`. +The incident-response team will verify the issue and contact you on how this will be +handled. + + +## Public Discussions + +When a new vulnerability is reported and verified, a new security advisory is created on +GitHub and the issue is assigned a CVE identifier. Progress on the mitigation is tracked +on a private fork, where the incident-response team and developers communicate to fix +the issue. + +When the fix is ready, a release plan is prepared and all communication channels are +used to notify the community of the presence of a new issue and the expected release +plan. This allows the community time to prepare for a security upgrade. (Notice that +security fixes are not backported at the moment.) + +When the advisory is published, GitHub automatically notifies all associated projects of +the published advisory. Projects that use IdPy projects as dependencies should +automatically get Pull Requests by dependabot. Additionally, all communication channels +are used again, to notify the community of the release of a new version of the affected +software that contains the relevant fixes that mitigate the reported issue. + + +## Supported versions + +Notice, that security fixes are not backported at the moment to older releases than the +latest. The team does not have the capacity to guarantee that these backports will exist. +You are advised to be prepared to upgrade to the latest version once the fix is out. diff --git a/VERSION b/VERSION deleted file mode 100644 index b26a34e4..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -7.2.1 diff --git a/docs/FIXUP_COMMITS.md b/docs/FIXUP_COMMITS.md new file mode 100644 index 00000000..5d7b50a9 --- /dev/null +++ b/docs/FIXUP_COMMITS.md @@ -0,0 +1,115 @@ +# Working with fixup commits + +This document provides information and guidelines for working with fixup commits: +- [What are fixup commits](#about-fixup-commits) +- [Why use fixup commits](#why-fixup-commits) +- [Creating fixup commits](#create-fixup-commits) +- [Squashing fixup commits](#squash-fixup-commits) + +[This blog post](https://thoughtbot.com/blog/autosquashing-git-commits) is also a good resource on the subject. + + +## <a name="about-fixup-commits"></a> What are fixup commits + +At their core, fixup commits are just regular commits with a special commit message: +The first line of their commit message starts with "fixup! " (notice the space +after "!") followed by the first line of the commit message of an earlier +commit (it doesn't have to be the immediately preceding one). + +The purpose of a fixup commit is to modify an earlier commit. +I.e. it allows adding more changes in a new commit, but "marking" them as +belonging to an earlier commit. +`Git` provides tools to make it easy to squash fixup commits into the original +commit at a later time (see [below](#squash-fixup-commits) for details). + +For example, let's assume you have added the following commits to your branch: + +``` +feat: first commit +fix: second commit +``` + +If you want to add more changes to the first commit, you can create a new +commit with the commit message: `fixup! feat: first commit`: + +``` +feat: first commit +fix: second commit +fixup! feat: first commit +``` + + +## <a name="why-fixup-commits"></a> Why use fixup commits + +So, when are fixup commits useful? + +During the life of a Pull Request, a reviewer might request changes. +The Pull Request author can make the requested changes and submit them for another review. +Normally, these changes should be part of one of the original commits of the Pull Request. +However, amending an existing commit with the changes makes it difficult for +the reviewer to know exactly what has changed since the last time they reviewed +the Pull Request. + +Here is where fixup commits come in handy. +By addressing review feedback in fixup commits, you make it very straight +forward for the reviewer to see what are the new changes that need to be +reviewed and verify that their earlier feedback has been addressed. +This can save a lot of effort, especially on larger Pull Requests (where having +to re-review _all_ the changes is pretty wasteful). + +When the time comes to merge the Pull Request into the repository, the merge +script knows how to automatically squash fixup commits with the corresponding +regular commits. + + +## <a name="create-fixup-commits"></a> Creating fixup commits + +As mentioned [above](#about-fixup-commits), the only thing that differentiates +a fixup commit from a regular commit is the commit message. +You can create a fixup commit by specifying an appropriate commit message (i.e. +`fixup! <original-commit-message-subject>`). + +In addition, the `git` command-line tool provides an easy way to create a fixup +commit via [git commit --fixup](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---fixupltcommitgt): + +```sh +# Create a fixup commit to fix up the last commit on the branch: +git commit --fixup HEAD ... + +# Create a fixup commit to fix up commit with SHA <COMMIT_SHA>: +git commit --fixup <COMMIT_SHA> ... +``` + + +## <a name="squash-fixup-commits"></a> Squashing fixup commits + +As mentioned above, the merge script will automatically squash fixup commits. +However, sometimes you might want to manually squash a fixup commit. + + +### Rebasing to squash fixup commits + +The easiest way to re-order and squash any commit is via [rebasing interactively](https://git-scm.com/docs/git-rebase#_interactive_mode). +You move a commit right after the one you want to squash it into in the rebase +TODO list and change the corresponding action from `pick` to `fixup`. + +`Git` can do all these automatically for you if you pass the `--autosquash` +option to `git rebase`. See the [`git` docs](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---autosquash) +for more details. + + +### Additional options + +You may like to consider some optional configurations: + + +#### Configuring `git` to auto-squash by default + +By default, `git` will not automatically squash fixup commits when +interactively rebasing. If you prefer to not have to pass the `--autosquash` +option every time, you can change the default behavior by setting the +`rebase.autoSquash` `git` config option to true. +See the [`git` docs](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt-rebaseautoSquash) for more details. + +If you have `rebase.autoSquash` set to true, you can pass the `--no-autosquash` +option to `git rebase` to override and disable this setting. diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..bf374955 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1929 @@ +[[package]] +name = "alabaster" +version = "0.7.12" +description = "A configurable sidebar-enabled Sphinx theme" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "appnope" +version = "0.1.3" +description = "Disable App Nap on macOS >= 10.9" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "atomicwrites" +version = "1.4.1" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "22.1.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] +docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] +tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] + +[[package]] +name = "Babel" +version = "2.10.3" +description = "Internationalization utilities" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pytz = ">=2015.7" + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "black" +version = "22.8.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=8.0.0" +dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2022.6.15" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "2.0.12" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.0.4" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "colorama" +version = "0.4.5" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "coverage" +version = "6.2" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "cryptography" +version = "37.0.4" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx_rtd_theme"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +sdist = ["setuptools_rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] + +[[package]] +name = "dataclasses" +version = "0.8" +description = "A backport of the dataclasses module for Python 3.6" +category = "dev" +optional = false +python-versions = ">=3.6, <3.7" + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "docutils" +version = "0.17.1" +description = "Docutils -- Python Documentation Utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "elementpath" +version = "2.4.0" +description = "XPath 1.0/2.0 parsers and selectors for ElementTree and lxml" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["Sphinx", "coverage", "flake8", "lxml", "memory-profiler", "mypy (==0.910)", "tox", "xmlschema (>=1.8.0)"] + +[[package]] +name = "filelock" +version = "3.4.1" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] +testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] + +[[package]] +name = "flake8" +version = "5.0.4" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +importlib-metadata = {version = ">=1.1.0,<4.3", markers = "python_version < \"3.8\""} +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.9.0,<2.10.0" +pyflakes = ">=2.5.0,<2.6.0" + +[[package]] +name = "flake8-bugbear" +version = "22.8.23" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +attrs = ">=19.2.0" +flake8 = ">=3.0.0" + +[package.extras] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit"] + +[[package]] +name = "flake8-logging-format" +version = "0.7.5" +description = "" +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +lint = ["flake8"] +test = ["PyHamcrest", "pytest", "pytest-cov"] + +[[package]] +name = "Flake8-pyproject" +version = "1.1.0.post0" +description = "Flake8 plug-in loading the configuration from pyproject.toml" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +Flake8 = ">=5,<6" +TOMLi = [ + {version = "*", markers = "python_version < \"3.11\""}, + {version = "<2", markers = "python_version < \"3.7\""}, +] + +[package.extras] +test = ["pyTest", "pyTest-cov"] + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "importlib-metadata" +version = "4.2.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] + +[[package]] +name = "importlib-resources" +version = "5.4.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "ipdb" +version = "0.13.9" +description = "IPython-enabled pdb" +category = "dev" +optional = false +python-versions = ">=2.7" + +[package.dependencies] +decorator = {version = "*", markers = "python_version >= \"3.6\""} +ipython = [ + {version = ">=7.10.0,<7.17.0", markers = "python_version == \"3.6\""}, + {version = ">=7.17.0", markers = "python_version > \"3.6\""}, +] +setuptools = "*" +toml = {version = ">=0.10.2", markers = "python_version >= \"3.6\""} + +[[package]] +name = "ipython" +version = "7.16.3" +description = "IPython: Productive Interactive Computing" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.10,<=0.17.2" +pexpect = {version = "*", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" +setuptools = ">=18.5" +traitlets = ">=4.2" + +[package.extras] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.14)", "pygments", "qtconsole", "requests", "testpath"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.14)", "pygments", "requests", "testpath"] + +[[package]] +name = "ipython" +version = "7.34.0" +description = "IPython: Productive Interactive Computing" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" +setuptools = ">=18.5" +traitlets = ">=4.2" + +[package.extras] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"] + +[[package]] +name = "ipython_genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "isort" +version = "5.10.1" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0" + +[package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements_deprecated_finder = ["pip-api", "pipreqs"] + +[[package]] +name = "jedi" +version = "0.17.2" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +parso = ">=0.7.0,<0.8.0" + +[package.extras] +qa = ["flake8 (==3.7.9)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] + +[[package]] +name = "jedi" +version = "0.18.1" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +parso = ">=0.8.0,<0.9.0" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "Jinja2" +version = "3.0.3" +description = "A very fast and expressive template engine." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "MarkupSafe" +version = "2.0.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "parso" +version = "0.7.1" +description = "A Python Parser" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +testing = ["docopt", "pytest (>=3.0.7)"] + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "paste" +version = "3.5.2" +description = "Tools for using a Web Server Gateway Interface stack" +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +setuptools = "*" +six = ">=1.4.0" + +[package.extras] +flup = ["flup"] +openid = ["python-openid"] + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "platformdirs" +version = "2.4.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.31" +description = "Library for building powerful interactive command lines in Python" +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyasn1" +version = "0.4.8" +description = "ASN.1 types and codecs" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "pycodestyle" +version = "2.9.1" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyflakes" +version = "2.5.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "Pygments" +version = "2.13.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pymongo" +version = "3.12.3" +description = "Python driver for MongoDB <http://www.mongodb.org>" +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +aws = ["pymongo-auth-aws (<2.0.0)"] +encryption = ["pymongocrypt (>=1.1.0,<2.0.0)"] +gssapi = ["pykerberos"] +ocsp = ["certifi", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"] +snappy = ["python-snappy"] +srv = ["dnspython (>=1.16.0,<1.17.0)"] +tls = ["ipaddress"] +zstd = ["zstandard"] + +[[package]] +name = "pyopenssl" +version = "22.0.0" +description = "Python wrapper module around the OpenSSL library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cryptography = ">=35.0" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +test = ["flaky", "pretend", "pytest (>=3.0.1)"] + +[[package]] +name = "pyparsing" +version = "3.0.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "7.0.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +tomli = ">=1.0.0" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "3.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2022.2.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "repoze.who" +version = "2.4.1" +description = "repoze.who is an identification and authentication framework for WSGI." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +setuptools = "*" +WebOb = "*" +"zope.interface" = "*" + +[package.extras] +docs = ["Sphinx", "WebOb", "repoze.sphinx.autointerface", "zope.interface"] +testing = ["WebOb", "coverage", "nose", "zope.interface"] + +[[package]] +name = "requests" +version = "2.27.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "responses" +version = "0.17.0" +description = "A utility library for mocking out the `requests` Python library." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +requests = ">=2.0" +six = "*" +urllib3 = ">=1.25.10" + +[package.extras] +tests = ["coverage (>=3.7.1,<6.0.0)", "flake8", "mypy", "pytest (>=4.6)", "pytest (>=4.6,<5.0)", "pytest-cov", "pytest-localserver", "types-mock", "types-requests", "types-six"] + +[[package]] +name = "setuptools" +version = "59.6.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=8.2)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-inline-tabs", "sphinxcontrib-towncrier"] +testing = ["flake8-2020", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "paver", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy", "pytest-virtualenv (>=1.2.7)", "pytest-xdist", "sphinx", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "Sphinx" +version = "4.3.2" +description = "Python documentation generator" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=1.3" +colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.18" +imagesize = "*" +Jinja2 = ">=2.3" +packaging = "*" +Pygments = ">=2.0" +requests = ">=2.5.0" +setuptools = "*" +snowballstemmer = ">=1.1" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.920)", "types-pkg-resources", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.2" +description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "1.2.3" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "tox" +version = "3.25.1" +description = "tox is a generic virtualenv management and test command line tool" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} +filelock = ">=3.0.0" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +packaging = ">=14" +pluggy = ">=0.12.0" +py = ">=1.4.17" +six = ">=1.14.0" +toml = ">=0.9.4" +virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" + +[package.extras] +docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] +testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] + +[[package]] +name = "traitlets" +version = "4.3.3" +description = "Traitlets Python config system" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +decorator = "*" +ipython-genutils = "*" +six = "*" + +[package.extras] +test = ["mock", "pytest"] + +[[package]] +name = "traitlets" +version = "5.4.0" +description = "" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pre-commit", "pytest"] + +[[package]] +name = "typed-ast" +version = "1.5.4" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "typing-extensions" +version = "4.1.1" +description = "Backported and Experimental Type Hints for Python 3.6+" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "urllib3" +version = "1.26.12" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.16.2" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +distlib = ">=0.3.1,<1" +filelock = ">=3.2,<4" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} +platformdirs = ">=2,<3" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "webob" +version = "1.8.7" +description = "WSGI request and response object" +category = "main" +optional = true +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" + +[package.extras] +docs = ["Sphinx (>=1.7.5)", "pylons-sphinx-themes"] +testing = ["coverage", "pytest (>=3.1.0)", "pytest-cov", "pytest-xdist"] + +[[package]] +name = "xmlschema" +version = "1.9.2" +description = "An XML Schema validator and decoder" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +elementpath = ">=2.4.0,<3.0.0" + +[package.extras] +codegen = ["elementpath (>=2.4.0,<3.0.0)", "jinja2"] +dev = ["Sphinx", "coverage", "elementpath (>=2.4.0,<3.0.0)", "flake8", "jinja2", "lxml", "lxml-stubs", "memory-profiler", "mypy", "sphinx-rtd-theme", "tox"] +docs = ["Sphinx", "elementpath (>=2.4.0,<3.0.0)", "jinja2", "sphinx-rtd-theme"] + +[[package]] +name = "zipp" +version = "3.6.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] +testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] + +[[package]] +name = "zope.interface" +version = "5.4.0" +description = "Interfaces for Python" +category = "main" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +setuptools = "*" + +[package.extras] +docs = ["Sphinx", "repoze.sphinx.autointerface"] +test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] +testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] + +[extras] +s2repoze = ["paste", "repoze.who", "zope.interface"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.6.2" +content-hash = "f40f4284c1c0599aaa84b827c980fc05ac38c47a169e33f2c7c1c14c615a3e2d" + +[metadata.files] +alabaster = [ + {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, + {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, +] +appnope = [ + {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, + {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, +] +attrs = [ + {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, + {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, +] +Babel = [ + {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, + {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, +] +backcall = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] +black = [ + {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, + {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, + {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, + {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, + {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, + {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, + {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, + {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, + {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, + {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, + {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, + {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, + {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, + {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, + {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, + {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, + {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, + {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, + {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, +] +certifi = [ + {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, + {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, +] +cffi = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, +] +click = [ + {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, + {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, +] +colorama = [ + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, +] +coverage = [ + {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, + {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, + {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, + {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, + {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, + {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, + {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, + {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, + {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, + {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, + {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, + {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, + {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, + {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, + {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, + {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, + {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, + {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, + {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, + {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, +] +cryptography = [ + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"}, + {file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8"}, + {file = "cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3"}, + {file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59"}, + {file = "cryptography-37.0.4-cp36-abi3-win32.whl", hash = "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157"}, + {file = "cryptography-37.0.4-cp36-abi3-win_amd64.whl", hash = "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b"}, + {file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282"}, + {file = "cryptography-37.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a"}, + {file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"}, + {file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"}, +] +dataclasses = [ + {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, + {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, +] +decorator = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] +defusedxml = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] +distlib = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] +docutils = [ + {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, + {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, +] +elementpath = [ + {file = "elementpath-2.4.0-py3-none-any.whl", hash = "sha256:1e98a4f5e8ccb5fe147074adda751c648d4a752973212298b6c9fc18c3a8c3e6"}, + {file = "elementpath-2.4.0.tar.gz", hash = "sha256:5b6801b3be94d48d213beb7b8ebad96addb35c95fc6a9c062c80e033b4a32fe8"}, +] +filelock = [ + {file = "filelock-3.4.1-py3-none-any.whl", hash = "sha256:a4bc51381e01502a30e9f06dd4fa19a1712eab852b6fb0f84fd7cce0793d8ca3"}, + {file = "filelock-3.4.1.tar.gz", hash = "sha256:0f12f552b42b5bf60dba233710bf71337d35494fc8bdd4fd6d9f6d082ad45e06"}, +] +flake8 = [ + {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, + {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, +] +flake8-bugbear = [ + {file = "flake8-bugbear-22.8.23.tar.gz", hash = "sha256:de0717d11124a082118dd08387b34fd86b2721642ec2d8e92be66cfa5ea7c445"}, + {file = "flake8_bugbear-22.8.23-py3-none-any.whl", hash = "sha256:1b0ebe0873d1cd55bf9f1588bfcb930db339018ef44a3981a26532daa9fd14a8"}, +] +flake8-logging-format = [ + {file = "flake8-logging-format-0.7.5.tar.gz", hash = "sha256:54f7e349c934ce5c594f251885bc2240e99f6b48752a672a8fc7e3d1388352bb"}, +] +Flake8-pyproject = [ + {file = "flake8_pyproject-1.1.0.post0-py3-none-any.whl", hash = "sha256:55bc7cbb4272dca45ba42521954452be9d443237fa9b78a09d0424bbd5c94fab"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +imagesize = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, + {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, +] +importlib-resources = [ + {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, + {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +ipdb = [ + {file = "ipdb-0.13.9.tar.gz", hash = "sha256:951bd9a64731c444fd907a5ce268543020086a697f6be08f7cc2c9a752a278c5"}, +] +ipython = [ + {file = "ipython-7.16.3-py3-none-any.whl", hash = "sha256:c0427ed8bc33ac481faf9d3acf7e84e0010cdaada945e0badd1e2e74cc075833"}, + {file = "ipython-7.16.3.tar.gz", hash = "sha256:5ac47dc9af66fc2f5530c12069390877ae372ac905edca75a92a6e363b5d7caa"}, + {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"}, + {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"}, +] +ipython_genutils = [ + {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, + {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, +] +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] +jedi = [ + {file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, + {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"}, + {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, + {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, +] +Jinja2 = [ + {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, + {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, +] +MarkupSafe = [ + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, + {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, +] +matplotlib-inline = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] +mccabe = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +parso = [ + {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, + {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"}, + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] +paste = [ + {file = "Paste-3.5.2-py2.py3-none-any.whl", hash = "sha256:fa0385cd07a50e6c679e735e44afef1e24ab1a0578eea501e45b8c2d38669b77"}, + {file = "Paste-3.5.2.tar.gz", hash = "sha256:d5a7340c30bcdf3023dd0106c8a5c430dd8fe84aeb8113bc7b93f8dd729f4af6"}, +] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +pexpect = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +pickleshare = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] +platformdirs = [ + {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, + {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.31-py3-none-any.whl", hash = "sha256:9696f386133df0fc8ca5af4895afe5d78f5fcfe5258111c2a79a1c3e41ffa96d"}, + {file = "prompt_toolkit-3.0.31.tar.gz", hash = "sha256:9ada952c9d1787f52ff6d5f3484d0b4df8952787c087edf6a1f7c2cb1ea88148"}, +] +ptyprocess = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pyasn1 = [ + {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, + {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, + {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, + {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, + {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, + {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, + {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, + {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, + {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, + {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, + {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, + {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, +] +pycodestyle = [ + {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, + {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, +] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pyflakes = [ + {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, + {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, +] +Pygments = [ + {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, + {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, +] +pymongo = [ + {file = "pymongo-3.12.3-cp27-cp27m-macosx_10_14_intel.whl", hash = "sha256:c164eda0be9048f83c24b9b2656900041e069ddf72de81c17d874d0c32f6079f"}, + {file = "pymongo-3.12.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:a055d29f1302892a9389a382bed10a3f77708bcf3e49bfb76f7712fa5f391cc6"}, + {file = "pymongo-3.12.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:8c7ad5cab282f53b9d78d51504330d1c88c83fbe187e472c07e6908a0293142e"}, + {file = "pymongo-3.12.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a766157b195a897c64945d4ff87b050bb0e763bb78f3964e996378621c703b00"}, + {file = "pymongo-3.12.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c8d6bf6fcd42cde2f02efb8126812a010c297eacefcd090a609639d2aeda6185"}, + {file = "pymongo-3.12.3-cp27-cp27m-win32.whl", hash = "sha256:5fdffb0cfeb4dc8646a5381d32ec981ae8472f29c695bf09e8f7a8edb2db12ca"}, + {file = "pymongo-3.12.3-cp27-cp27m-win_amd64.whl", hash = "sha256:648fcfd8e019b122b7be0e26830a3a2224d57c3e934f19c1e53a77b8380e6675"}, + {file = "pymongo-3.12.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:3f0ac6e0203bd88863649e6ed9c7cfe53afab304bc8225f2597c4c0a74e4d1f0"}, + {file = "pymongo-3.12.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:71c0db2c313ea8a80825fb61b7826b8015874aec29ee6364ade5cb774fe4511b"}, + {file = "pymongo-3.12.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5b779e87300635b8075e8d5cfd4fdf7f46078cd7610c381d956bca5556bb8f97"}, + {file = "pymongo-3.12.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:351a2efe1c9566c348ad0076f4bf541f4905a0ebe2d271f112f60852575f3c16"}, + {file = "pymongo-3.12.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0a02313e71b7c370c43056f6b16c45effbb2d29a44d24403a3d5ba6ed322fa3f"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux1_i686.whl", hash = "sha256:d3082e5c4d7b388792124f5e805b469109e58f1ab1eb1fbd8b998e8ab766ffb7"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:514e78d20d8382d5b97f32b20c83d1d0452c302c9a135f0a9022236eb9940fda"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux2014_i686.whl", hash = "sha256:b1b5be40ebf52c3c67ee547e2c4435ed5bc6352f38d23e394520b686641a6be4"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux2014_ppc64le.whl", hash = "sha256:58db209da08a502ce6948841d522dcec80921d714024354153d00b054571993c"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux2014_s390x.whl", hash = "sha256:5296e5e69243ffd76bd919854c4da6630ae52e46175c804bc4c0e050d937b705"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:51d1d061df3995c2332ae78f036492cc188cb3da8ef122caeab3631a67bb477e"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:463b974b7f49d65a16ca1435bc1c25a681bb7d630509dd23b2e819ed36da0b7f"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e099b79ccf7c40f18b149a64d3d10639980035f9ceb223169dd806ff1bb0d9cc"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27e5ea64332385385b75414888ce9d1a9806be8616d7cef4ef409f4f256c6d06"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed7d11330e443aeecab23866055e08a5a536c95d2c25333aeb441af2dbac38d2"}, + {file = "pymongo-3.12.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93111fd4e08fa889c126aa8baf5c009a941880a539c87672e04583286517450a"}, + {file = "pymongo-3.12.3-cp310-cp310-win32.whl", hash = "sha256:2301051701b27aff2cbdf83fae22b7ca883c9563dfd088033267291b46196643"}, + {file = "pymongo-3.12.3-cp310-cp310-win_amd64.whl", hash = "sha256:c7e8221278e5f9e2b6d3893cfc3a3e46c017161a57bb0e6f244826e4cee97916"}, + {file = "pymongo-3.12.3-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:7b4a9fcd95e978cd3c96cdc2096aa54705266551422cf0883c12a4044def31c6"}, + {file = "pymongo-3.12.3-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:06b64cdf5121f86b78a84e61b8f899b6988732a8d304b503ea1f94a676221c06"}, + {file = "pymongo-3.12.3-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:c8f7dd025cb0bf19e2f60a64dfc24b513c8330e0cfe4a34ccf941eafd6194d9e"}, + {file = "pymongo-3.12.3-cp34-cp34m-win32.whl", hash = "sha256:ab23b0545ec71ea346bf50a5d376d674f56205b729980eaa62cdb7871805014b"}, + {file = "pymongo-3.12.3-cp34-cp34m-win_amd64.whl", hash = "sha256:1b5cb75d2642ff7db823f509641f143f752c0d1ab03166cafea1e42e50469834"}, + {file = "pymongo-3.12.3-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:fc2048d13ff427605fea328cbe5369dce549b8c7657b0e22051a5b8831170af6"}, + {file = "pymongo-3.12.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c5f83bb59d0ff60c6fdb1f8a7b0288fbc4640b1f0fd56f5ae2387749c35d34e3"}, + {file = "pymongo-3.12.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6632b1c63d58cddc72f43ab9f17267354ddce563dd5e11eadabd222dcc808808"}, + {file = "pymongo-3.12.3-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3fedad05147b40ff8a93fcd016c421e6c159f149a2a481cfa0b94bfa3e473bab"}, + {file = "pymongo-3.12.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:208a61db8b8b647fb5b1ff3b52b4ed6dbced01eac3b61009958adb203596ee99"}, + {file = "pymongo-3.12.3-cp35-cp35m-win32.whl", hash = "sha256:3100a2352bdded6232b385ceda0c0a4624598c517d52c2d8cf014b7abbebd84d"}, + {file = "pymongo-3.12.3-cp35-cp35m-win_amd64.whl", hash = "sha256:3492ae1f97209c66af70e863e6420e6301cecb0a51a5efa701058aa73a8ca29e"}, + {file = "pymongo-3.12.3-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:87e18f29bac4a6be76a30e74de9c9005475e27100acf0830679420ce1fd9a6fd"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b3e08aef4ea05afbc0a70cd23c13684e7f5e074f02450964ec5cfa1c759d33d2"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e66b3c9f8b89d4fd58a59c04fdbf10602a17c914fbaaa5e6ea593f1d54b06362"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5d67dbc8da2dac1644d71c1839d12d12aa333e266a9964d5b1a49feed036bc94"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:a351986d6c9006308f163c359ced40f80b6cffb42069f3e569b979829951038d"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:5296669bff390135528001b4e48d33a7acaffcd361d98659628ece7f282f11aa"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:9d5b66d457d2c5739c184a777455c8fde7ab3600a56d8bbebecf64f7c55169e1"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:1c771f1a8b3cd2d697baaf57e9cfa4ae42371cacfbea42ea01d9577c06d92f96"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81a3ebc33b1367f301d1c8eda57eec4868e951504986d5d3fe437479dcdac5b2"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cf113a46d81cff0559d57aa66ffa473d57d1a9496f97426318b6b5b14fdec1c"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64b9122be1c404ce4eb367ad609b590394587a676d84bfed8e03c3ce76d70560"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6c71e198b36f0f0dfe354f06d3655ecfa30d69493a1da125a9a54668aad652"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33ab8c031f788609924e329003088831045f683931932a52a361d4a955b7dce2"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e2b4c95c47fb81b19ea77dc1c50d23af3eba87c9628fcc2e03d44124a3d336ea"}, + {file = "pymongo-3.12.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4e0a3ea7fd01cf0a36509f320226bd8491e0f448f00b8cb89f601c109f6874e1"}, + {file = "pymongo-3.12.3-cp36-cp36m-win32.whl", hash = "sha256:dfec57f15f53d677b8e4535695ff3f37df7f8fe431f2efa8c3c8c4025b53d1eb"}, + {file = "pymongo-3.12.3-cp36-cp36m-win_amd64.whl", hash = "sha256:c22591cff80188dd8543be0b559d0c807f7288bd353dc0bcfe539b4588b3a5cd"}, + {file = "pymongo-3.12.3-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:7738147cd9dbd6d18d5593b3491b4620e13b61de975fd737283e4ad6c255c273"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:be1f10145f7ea76e3e836fdc5c8429c605675bdcddb0bca9725ee6e26874c00c"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:295a5beaecb7bf054c1c6a28749ed72b19f4d4b61edcd8a0815d892424baf780"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:320f8734553c50cffe8a8e1ae36dfc7d7be1941c047489db20a814d2a170d7b5"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:5d20072d81cbfdd8e15e6a0c91fc7e3a4948c71e0adebfc67d3b4bcbe8602711"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:2c46a0afef69d61938a6fe32c3afd75b91dec3ab3056085dc72abbeedcc94166"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:5f530f35e1a57d4360eddcbed6945aecdaee2a491cd3f17025e7b5f2eea88ee7"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:6526933760ee1e6090db808f1690a111ec409699c1990efc96f134d26925c37f"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95d15cf81cd2fb926f2a6151a9f94c7aacc102b415e72bc0e040e29332b6731c"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d52a70350ec3dfc39b513df12b03b7f4c8f8ec6873bbf958299999db7b05eb1"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9252c991e8176b5a2fa574c5ab9a841679e315f6e576eb7cf0bd958f3e39b0ad"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:145d78c345a38011497e55aff22c0f8edd40ee676a6810f7e69563d68a125e83"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8e0a086dbbee406cc6f603931dfe54d1cb2fba585758e06a2de01037784b737"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f6d5443104f89a840250087863c91484a72f254574848e951d1bdd7d8b2ce7c9"}, + {file = "pymongo-3.12.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6f93dbfa5a461107bc3f5026e0d5180499e13379e9404f07a9f79eb5e9e1303d"}, + {file = "pymongo-3.12.3-cp37-cp37m-win32.whl", hash = "sha256:c9d212e2af72d5c8d082775a43eb726520e95bf1c84826440f74225843975136"}, + {file = "pymongo-3.12.3-cp37-cp37m-win_amd64.whl", hash = "sha256:320a1fe403dd83a35709fcf01083d14bc1462e9789b711201349a9158db3a87e"}, + {file = "pymongo-3.12.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a1ba93be779a9b8e5e44f5c133dc1db4313661cead8a2fd27661e6cb8d942ee9"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:4294f2c1cd069b793e31c2e6d7ac44b121cf7cedccd03ebcc30f3fc3417b314a"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:845b178bd127bb074835d2eac635b980c58ec5e700ebadc8355062df708d5a71"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:176fdca18391e1206c32fb1d8265628a84d28333c20ad19468d91e3e98312cd1"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:28bfd5244d32faf3e49b5a8d1fab0631e922c26e8add089312e4be19fb05af50"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:f38b35ecd2628bf0267761ed659e48af7e620a7fcccfccf5774e7308fb18325c"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:cebb3d8bcac4a6b48be65ebbc5c9881ed4a738e27bb96c86d9d7580a1fb09e05"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:80710d7591d579442c67a3bc7ae9dcba9ff95ea8414ac98001198d894fc4ff46"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d7baa847383b9814de640c6f1a8553d125ec65e2761ad146ea2e75a7ad197c"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:602284e652bb56ca8760f8e88a5280636c5b63d7946fca1c2fe0f83c37dffc64"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bfc2d763d05ec7211313a06e8571236017d3e61d5fef97fcf34ec4b36c0b6556"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a6e4dccae8ef5dd76052647d78f02d5d0ffaff1856277d951666c54aeba3ad2"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1fc4d3985868860b6585376e511bb32403c5ffb58b0ed913496c27fd791deea"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e4e5d163e6644c2bc84dd9f67bfa89288c23af26983d08fefcc2cbc22f6e57e6"}, + {file = "pymongo-3.12.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8d92c6bb9174d47c2257528f64645a00bbc6324a9ff45a626192797aff01dc14"}, + {file = "pymongo-3.12.3-cp38-cp38-win32.whl", hash = "sha256:b0db9a4691074c347f5d7ee830ab3529bc5ad860939de21c1f9c403daf1eda9a"}, + {file = "pymongo-3.12.3-cp38-cp38-win_amd64.whl", hash = "sha256:d81047341ab56061aa4b6823c54d4632579c3b16e675089e8f520e9b918a133b"}, + {file = "pymongo-3.12.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07398d8a03545b98282f459f2603a6bb271f4448d484ed7f411121a519a7ea48"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:b7df0d99e189b7027d417d4bfd9b8c53c9c7ed5a0a1495d26a6f547d820eca88"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:a283425e6a474facd73072d8968812d1d9058490a5781e022ccf8895500b83ce"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:2577b8161eeae4dd376d13100b2137d883c10bb457dd08935f60c9f9d4b5c5f6"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:517b09b1dd842390a965a896d1327c55dfe78199c9f5840595d40facbcd81854"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:2567885ff0c8c7c0887ba6cefe4ae4af96364a66a7069f924ce0cd12eb971d04"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:71c5c200fd37a5322706080b09c3ec8907cf01c377a7187f354fc9e9e13abc73"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:14dee106a10b77224bba5efeeb6aee025aabe88eb87a2b850c46d3ee55bdab4a"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f340a2a908644ea6cccd399be0fb308c66e05d2800107345f9f0f0d59e1731c4"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b4c535f524c9d8c86c3afd71d199025daa070859a2bdaf94a298120b0de16db"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8455176fd1b86de97d859fed4ae0ef867bf998581f584c7a1a591246dfec330f"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf254a1a95e95fdf4eaa25faa1ea450a6533ed7a997f9f8e49ab971b61ea514d"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a3540e21213cb8ce232e68a7d0ee49cdd35194856c50b8bd87eeb572fadd42"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0e7a5d0b9077e8c3e57727f797ee8adf12e1d5e7534642230d98980d160d1320"}, + {file = "pymongo-3.12.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0be605bfb8461384a4cb81e80f51eb5ca1b89851f2d0e69a75458c788a7263a4"}, + {file = "pymongo-3.12.3-cp39-cp39-win32.whl", hash = "sha256:2157d68f85c28688e8b723bbe70c8013e0aba5570e08c48b3562f74d33fc05c4"}, + {file = "pymongo-3.12.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfa217bf8cf3ff6b30c8e6a89014e0c0e7b50941af787b970060ae5ba04a4ce5"}, + {file = "pymongo-3.12.3-py2.7-macosx-10.14-intel.egg", hash = "sha256:d81299f63dc33cc172c26faf59cc54dd795fc6dd5821a7676cca112a5ee8bbd6"}, + {file = "pymongo-3.12.3.tar.gz", hash = "sha256:0a89cadc0062a5e53664dde043f6c097172b8c1c5f0094490095282ff9995a5f"}, +] +pyopenssl = [ + {file = "pyOpenSSL-22.0.0-py2.py3-none-any.whl", hash = "sha256:ea252b38c87425b64116f808355e8da644ef9b07e429398bfece610f893ee2e0"}, + {file = "pyOpenSSL-22.0.0.tar.gz", hash = "sha256:660b1b1425aac4a1bea1d94168a85d99f0b3144c869dd4390d27629d0087f1bf"}, +] +pyparsing = [ + {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, + {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, +] +pytest = [ + {file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"}, + {file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"}, +] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] +pytz = [ + {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"}, + {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"}, +] +"repoze.who" = [ + {file = "repoze.who-2.4.1-py3-none-any.whl", hash = "sha256:5d422e060311452f841ba21a70e8f96d65af8a7e10004dc366f7e0eb9843d062"}, + {file = "repoze.who-2.4.1.tar.gz", hash = "sha256:8a4c9b9268b4ff0edd906e97c2eed7ce83eb9f62e4250bfe03bcdbba6d31a237"}, +] +requests = [ + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +] +responses = [ + {file = "responses-0.17.0-py2.py3-none-any.whl", hash = "sha256:e4fc472fb7374fb8f84fcefa51c515ca4351f198852b4eb7fc88223780b472ea"}, + {file = "responses-0.17.0.tar.gz", hash = "sha256:ec675e080d06bf8d1fb5e5a68a1e5cd0df46b09c78230315f650af5e4036bec7"}, +] +setuptools = [ + {file = "setuptools-59.6.0-py3-none-any.whl", hash = "sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e"}, + {file = "setuptools-59.6.0.tar.gz", hash = "sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +snowballstemmer = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] +Sphinx = [ + {file = "Sphinx-4.3.2-py3-none-any.whl", hash = "sha256:6a11ea5dd0bdb197f9c2abc2e0ce73e01340464feaece525e64036546d24c851"}, + {file = "Sphinx-4.3.2.tar.gz", hash = "sha256:0a8836751a68306b3fe97ecbe44db786f8479c3bf4b80e3a7f5c838657b4698c"}, +] +sphinxcontrib-applehelp = [ + {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, + {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, +] +sphinxcontrib-devhelp = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] +sphinxcontrib-htmlhelp = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] +sphinxcontrib-jsmath = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] +sphinxcontrib-qthelp = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] +sphinxcontrib-serializinghtml = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +tomli = [ + {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, + {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, +] +tox = [ + {file = "tox-3.25.1-py2.py3-none-any.whl", hash = "sha256:c38e15f4733683a9cc0129fba078633e07eb0961f550a010ada879e95fb32632"}, + {file = "tox-3.25.1.tar.gz", hash = "sha256:c138327815f53bc6da4fe56baec5f25f00622ae69ef3fe4e1e385720e22486f9"}, +] +traitlets = [ + {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, + {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, + {file = "traitlets-5.4.0-py3-none-any.whl", hash = "sha256:93663cc8236093d48150e2af5e2ed30fc7904a11a6195e21bab0408af4e6d6c8"}, + {file = "traitlets-5.4.0.tar.gz", hash = "sha256:3f2c4e435e271592fe4390f1746ea56836e3a080f84e7833f0f801d9613fec39"}, +] +typed-ast = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] +typing-extensions = [ + {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, + {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, +] +urllib3 = [ + {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, + {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, +] +virtualenv = [ + {file = "virtualenv-20.16.2-py2.py3-none-any.whl", hash = "sha256:635b272a8e2f77cb051946f46c60a54ace3cb5e25568228bd6b57fc70eca9ff3"}, + {file = "virtualenv-20.16.2.tar.gz", hash = "sha256:0ef5be6d07181946891f5abc8047fda8bc2f0b4b9bf222c64e6e8963baee76db"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +webob = [ + {file = "WebOb-1.8.7-py2.py3-none-any.whl", hash = "sha256:73aae30359291c14fa3b956f8b5ca31960e420c28c1bec002547fb04928cf89b"}, + {file = "WebOb-1.8.7.tar.gz", hash = "sha256:b64ef5141be559cfade448f044fa45c2260351edcb6a8ef6b7e00c7dcef0c323"}, +] +xmlschema = [ + {file = "xmlschema-1.9.2-py3-none-any.whl", hash = "sha256:a7ba52b774a87b59c6428cd9e3601210cbb226552208015bd40800698a6500ad"}, + {file = "xmlschema-1.9.2.tar.gz", hash = "sha256:3ce6fe408a8c0a0ca5917cbe6181a933dfb5cfade9714eeb07b6335f9aff7b10"}, +] +zipp = [ + {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, + {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, +] +"zope.interface" = [ + {file = "zope.interface-5.4.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:7df1e1c05304f26faa49fa752a8c690126cf98b40b91d54e6e9cc3b7d6ffe8b7"}, + {file = "zope.interface-5.4.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2c98384b254b37ce50eddd55db8d381a5c53b4c10ee66e1e7fe749824f894021"}, + {file = "zope.interface-5.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:08f9636e99a9d5410181ba0729e0408d3d8748026ea938f3b970a0249daa8192"}, + {file = "zope.interface-5.4.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ea1d73b7c9dcbc5080bb8aaffb776f1c68e807767069b9ccdd06f27a161914a"}, + {file = "zope.interface-5.4.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:273f158fabc5ea33cbc936da0ab3d4ba80ede5351babc4f577d768e057651531"}, + {file = "zope.interface-5.4.0-cp27-cp27m-win32.whl", hash = "sha256:a1e6e96217a0f72e2b8629e271e1b280c6fa3fe6e59fa8f6701bec14e3354325"}, + {file = "zope.interface-5.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:877473e675fdcc113c138813a5dd440da0769a2d81f4d86614e5d62b69497155"}, + {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263"}, + {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:b0297b1e05fd128d26cc2460c810d42e205d16d76799526dfa8c8ccd50e74959"}, + {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:af310ec8335016b5e52cae60cda4a4f2a60a788cbb949a4fbea13d441aa5a09e"}, + {file = "zope.interface-5.4.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:9a9845c4c6bb56e508651f005c4aeb0404e518c6f000d5a1123ab077ab769f5c"}, + {file = "zope.interface-5.4.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0b465ae0962d49c68aa9733ba92a001b2a0933c317780435f00be7ecb959c702"}, + {file = "zope.interface-5.4.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:5dd9ca406499444f4c8299f803d4a14edf7890ecc595c8b1c7115c2342cadc5f"}, + {file = "zope.interface-5.4.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:469e2407e0fe9880ac690a3666f03eb4c3c444411a5a5fddfdabc5d184a79f05"}, + {file = "zope.interface-5.4.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:52de7fc6c21b419078008f697fd4103dbc763288b1406b4562554bd47514c004"}, + {file = "zope.interface-5.4.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:3dd4952748521205697bc2802e4afac5ed4b02909bb799ba1fe239f77fd4e117"}, + {file = "zope.interface-5.4.0-cp35-cp35m-win32.whl", hash = "sha256:dd93ea5c0c7f3e25335ab7d22a507b1dc43976e1345508f845efc573d3d779d8"}, + {file = "zope.interface-5.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:3748fac0d0f6a304e674955ab1365d515993b3a0a865e16a11ec9d86fb307f63"}, + {file = "zope.interface-5.4.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:66c0061c91b3b9cf542131148ef7ecbecb2690d48d1612ec386de9d36766058f"}, + {file = "zope.interface-5.4.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d0c1bc2fa9a7285719e5678584f6b92572a5b639d0e471bb8d4b650a1a910920"}, + {file = "zope.interface-5.4.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2876246527c91e101184f63ccd1d716ec9c46519cc5f3d5375a3351c46467c46"}, + {file = "zope.interface-5.4.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:334701327f37c47fa628fc8b8d28c7d7730ce7daaf4bda1efb741679c2b087fc"}, + {file = "zope.interface-5.4.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:71aace0c42d53abe6fc7f726c5d3b60d90f3c5c055a447950ad6ea9cec2e37d9"}, + {file = "zope.interface-5.4.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5bb3489b4558e49ad2c5118137cfeaf59434f9737fa9c5deefc72d22c23822e2"}, + {file = "zope.interface-5.4.0-cp36-cp36m-win32.whl", hash = "sha256:1c0e316c9add0db48a5b703833881351444398b04111188069a26a61cfb4df78"}, + {file = "zope.interface-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f0c02cbb9691b7c91d5009108f975f8ffeab5dff8f26d62e21c493060eff2a1"}, + {file = "zope.interface-5.4.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:7d97a4306898b05404a0dcdc32d9709b7d8832c0c542b861d9a826301719794e"}, + {file = "zope.interface-5.4.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:867a5ad16892bf20e6c4ea2aab1971f45645ff3102ad29bd84c86027fa99997b"}, + {file = "zope.interface-5.4.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5f931a1c21dfa7a9c573ec1f50a31135ccce84e32507c54e1ea404894c5eb96f"}, + {file = "zope.interface-5.4.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:194d0bcb1374ac3e1e023961610dc8f2c78a0f5f634d0c737691e215569e640d"}, + {file = "zope.interface-5.4.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8270252effc60b9642b423189a2fe90eb6b59e87cbee54549db3f5562ff8d1b8"}, + {file = "zope.interface-5.4.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:15e7d1f7a6ee16572e21e3576d2012b2778cbacf75eb4b7400be37455f5ca8bf"}, + {file = "zope.interface-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:8892f89999ffd992208754851e5a052f6b5db70a1e3f7d54b17c5211e37a98c7"}, + {file = "zope.interface-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2e5a26f16503be6c826abca904e45f1a44ff275fdb7e9d1b75c10671c26f8b94"}, + {file = "zope.interface-5.4.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0f91b5b948686659a8e28b728ff5e74b1be6bf40cb04704453617e5f1e945ef3"}, + {file = "zope.interface-5.4.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:4de4bc9b6d35c5af65b454d3e9bc98c50eb3960d5a3762c9438df57427134b8e"}, + {file = "zope.interface-5.4.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf68f4b2b6683e52bec69273562df15af352e5ed25d1b6641e7efddc5951d1a7"}, + {file = "zope.interface-5.4.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:63b82bb63de7c821428d513607e84c6d97d58afd1fe2eb645030bdc185440120"}, + {file = "zope.interface-5.4.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:db1fa631737dab9fa0b37f3979d8d2631e348c3b4e8325d6873c2541d0ae5a48"}, + {file = "zope.interface-5.4.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4"}, + {file = "zope.interface-5.4.0-cp38-cp38-win32.whl", hash = "sha256:a9506a7e80bcf6eacfff7f804c0ad5350c8c95b9010e4356a4b36f5322f09abb"}, + {file = "zope.interface-5.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:3c02411a3b62668200910090a0dff17c0b25aaa36145082a5a6adf08fa281e54"}, + {file = "zope.interface-5.4.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:0cee5187b60ed26d56eb2960136288ce91bcf61e2a9405660d271d1f122a69a4"}, + {file = "zope.interface-5.4.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a8156e6a7f5e2a0ff0c5b21d6bcb45145efece1909efcbbbf48c56f8da68221d"}, + {file = "zope.interface-5.4.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:205e40ccde0f37496904572035deea747390a8b7dc65146d30b96e2dd1359a83"}, + {file = "zope.interface-5.4.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:3f24df7124c323fceb53ff6168da70dbfbae1442b4f3da439cd441681f54fe25"}, + {file = "zope.interface-5.4.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:5208ebd5152e040640518a77827bdfcc73773a15a33d6644015b763b9c9febc1"}, + {file = "zope.interface-5.4.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:17776ecd3a1fdd2b2cd5373e5ef8b307162f581c693575ec62e7c5399d80794c"}, + {file = "zope.interface-5.4.0-cp39-cp39-win32.whl", hash = "sha256:d4d9d6c1a455d4babd320203b918ccc7fcbefe308615c521062bc2ba1aa4d26e"}, + {file = "zope.interface-5.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:0cba8477e300d64a11a9789ed40ee8932b59f9ee05f85276dbb4b59acee5dd09"}, + {file = "zope.interface-5.4.0.tar.gz", hash = "sha256:5dba5f530fec3f0988d83b78cc591b58c0b6eb8431a85edd1569a0539a8a5a0e"}, +] diff --git a/pyproject.toml b/pyproject.toml index 385c7952..140febb3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,237 @@ +[tool.poetry] +name = "pysaml2" +version = "7.3.0-alpha" +description = "Python implementation of SAML Version 2 Standard" +license = "Apache-2.0" +authors = ["IdentityPython <discuss@idpy.org>"] +maintainers = ["IdentityPython <discuss@idpy.org>"] +readme = "README.rst" +homepage = "https://idpy.org" +repository = "https://github.com/IdentityPython/pysaml2" +documentation = "https://pysaml2.readthedocs.io" +keywords = [ + "saml", + "saml2", + "standard", + "federation", + "identity", + "idpy", + "IdentityPython", +] +classifiers = [ + "Topic :: Software Development :: Libraries :: Python Modules", +] +packages = [ + { include = "saml2", from = "src" }, + { include = "saml2test", from = "src" }, + { include = "utility", from = "src" }, +] + +[tool.poetry.urls] +"Bug Tracker" = "https://github.com/IdentityPython/pysaml2/issues" + +[tool.poetry.scripts] +make_metadata = "saml2.tools.make_metadata:main" +mdexport = "saml2.tools.mdexport:main" +merge_metadata = "saml2.tools.merge_metadata:main" +parse_xsd2 = "saml2.tools.parse_xsd2:main" + +[tool.poetry.dependencies] +python = "^3.6.2" +cryptography = ">=3.1" +defusedxml = "*" +importlib-metadata = {version = ">=1.7.0", python = "<3.8"} +importlib-resources = {python = "<3.9", version = "*"} +paste = {optional = true, version = "*"} +pyopenssl = "*" +python-dateutil = "*" +pytz = "*" +"repoze.who" = {optional = true, version = "*"} +requests = "^2" +six = "*" +xmlschema = ">=1.2.1" +"zope.interface" = {optional = true, version = "*"} + +[tool.poetry.extras] +s2repoze = ["paste", "repoze-who", "zope-interface"] + +[tool.poetry.group.dev] +optional = true + +[tool.poetry.group.dev.dependencies] +black = "*" +isort = {version = "^5.10.1", extras = ["pyproject"]} +tox = "^3.25.1" +flake8 = ">=4" +Flake8-pyproject = "^1.1.0.post0" +flake8-bugbear = "^22.8.23" +flake8-logging-format = "^0.7.5" +ipdb = "^0.13.9" + +[tool.poetry.group.test] +optional = true + +[tool.poetry.group.test.dependencies] +pyasn1 = "*" +pymongo = "^3" +pytest = ">=6.0" +responses = "*" + +[tool.poetry.group.coverage] +optional = true + +[tool.poetry.group.coverage.dependencies] +coverage = "*" +pytest-cov = "*" + +[tool.poetry.group.docs] +optional = true + +[tool.poetry.group.docs.dependencies] +sphinx = "*" + [build-system] -requires = ["setuptools>=40.0.0", "wheel"] +requires = ["poetry_core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-ra -vvv" +testpaths = [ + "tests", +] +pythonpath = [ + "tests", +] + +[tool.coverage.run] +branch = true +source = ["saml2"] + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "def __str__", + "raise AssertionError", + "raise NotImplementedError", + "if __name__ == .__main__.:", + "if TYPE_CHECKING:", + "if typing.TYPE_CHECKING:", +] + +[tool.coverage.html] +directory = "cov_html" + +[tool.flake8] +max-line-length = 120 +max-complexity = 18 +count = true +show-source = true +statistics = true +disable-noqa = false +enable-extensions = [ + 'G', # flake8-logging-format +] +# 'ignore' defaults to: E121,E123,E126,E226,E24,E704,W503,W504 +extend-ignore = [ + 'E501', # line too long; instead, use B950 + 'E203', # whitespace before ‘,’, ‘;’, or ‘:’; may conflict with black + 'W503', # line break before binary operator +] +per-file-ignores = [ + '__init__.py:F401', +] +# 'select' defaults to: E,F,W,C90 +extend-select = [ + # * Default warnings reported by flake8-bugbear (B) - + # https://github.com/PyCQA/flake8-bugbear#list-of-warnings + 'B', + # * The B950 flake8-bugbear opinionated warnings - + # https://github.com/PyCQA/flake8-bugbear#opinionated-warnings + 'B9', + # + # * Complexity violations reported by mccabe (C) - + # http://flake8.pycqa.org/en/latest/user/error-codes.html#error-violation-codes + # mccabe only ever reports one violation - C901 based on the complexity value + # provided by the user. + # This is selected by default. + #'C90', + # + # * Documentation conventions compliance reported by pydocstyle (D) - + # http://www.pydocstyle.org/en/stable/error_codes.html + #'D', # FIXME TODO + # + # * Default errors reported by pycodestyle (E) - + # https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes + # This is selected by default. + #'E', + # + # * Default errors reported by pyflakes (F) - + # http://flake8.pycqa.org/en/latest/user/error-codes.html + # This is selected by default. + #'F', + # + # * flake8-mypy - enable limited type checking as a linter + # http://flake8.pycqa.org/en/latest/user/error-codes.html + #'T4', # FIXME TODO + # + # * Default warnings reported by pycodestyle (W) - + # https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes + # This is selected by default. + #'W', +] +extend-exclude = [ + '.github', '.gitlab', + '.Python', '.*.pyc', '.*.pyo', '.*.pyd', '.*.py.class', '*.egg-info', + 'venv*', '.venv*', '.*_cache', + 'lib', 'lib64', '.*.so', + 'build', 'dist', 'sdist', 'wheels', +] + +[tool.black] +line-length = 120 +extend-exclude = ''' +# A regex preceded with ^/ will apply only to files and directories +# in the root of the project. +( + \.pytest_cache +) +''' + +[tool.isort] +profile = 'black' +# The 'black' profile means: +# multi_line_output = 3 +# include_trailing_comma = true +# force_grid_wrap = 0 +# use_parentheses = true +# ensure_newline_before_comments = true +# line_length = 88 +line_length = 120 # override black provile line_length +force_single_line = true # override black profile multi_line_output +star_first = true +group_by_package = true +force_sort_within_sections = true +lines_after_imports = 2 +honor_noqa = true +atomic = true +ignore_comments = true +skip_gitignore = true +src_paths = [ + 'src', + 'test', +] + +# XXX TODO +#[tool.mypy] +#pretty = true +#check_untyped_defs = true +#ignore_errors = false +#ignore_missing_imports = true +#show_error_codes = true +#strict_optional = true +#warn_unused_ignores = true +#warn_redundant_casts = true +#warn_unused_configs = true +#warn_unreachable = true diff --git a/release-howto.rst b/release-howto.rst deleted file mode 100644 index 1ea60ba9..00000000 --- a/release-howto.rst +++ /dev/null @@ -1,80 +0,0 @@ -Releasing software -------------------- - -When releasing a new version, the following steps should be taken: - -1. Make sure all automated tests pass. - -2. Make sure the package metadata in ``setup.py`` is up-to-date. You can - verify the information by re-generating the egg info:: - - python setup.py egg_info - - and inspecting ``src/pysaml2.egg-info/PKG-INFO``. You should also make sure - that the long description renders as valid reStructuredText. You can - do this by using the ``rst2html.py`` utility from docutils_:: - - python setup.py --long-description | rst2html > test.html - - If this will produce warning or errors, PyPI will be unable to render - the long description nicely. It will treat it as plain text instead. - -3. Update the version in the VERSION_ file and report the changes in - CHANGELOG.md_ and commit the changes.:: - - git add CHANGELOG.md - git add VERSION - git commit -v -s -m "Release version X.Y.Z" - -4. Create a release branch_:: - - git branch vX.Y.Z - -5. Create a release tag_:: - - git tag -a -s vX.Y.Z -m "Version X.Y.Z" - -6. Push these changes to Github:: - - git push --follow-tags origin vX.Y.Z - git push --follow-tags origin vX.Y.Z:vX.Y.Z - -7. Create a source and wheel distribution and upload it to PyPI:: - - # generate a source and wheel distribution at once - python setup.py sdist bdist_wheel - - # generated files are under dist/ - ls dist/ - - # upload release on test.pypi.org - twine upload --repository-url https://test.pypi.org/legacy/ dist/pysaml2-X.Y.Z* - - # then, upload release on official pypi.org - twine upload dist/pysaml2-X.Y.Z* - -8. Upload the documentation to PyPI. First you need to generate the html - version of the documentation:: - - cd docs/ - make clean - make html - cd _build/html - zip -r pysaml2-docs.zip * - - Submit the generated pysaml2-docs.zip file. - -9. Send an email to the pysaml2 list announcing this release - - -**Important:** Once released to PyPI or any other public download location, -a released egg may *never* be removed, even if it has proven to be a faulty -release ("brown bag release"). In such a case it should simply be superseded -immediately by a new, improved release. - - -.. _VERSION: https://github.com/IdentityPython/pysaml2/blob/master/VERSION -.. _CHANGELOG.md: https://github.com/IdentityPython/pysaml2/blob/master/CHANGELOG.md -.. _docutils: http://docutils.sourceforge.net/ -.. _branch: https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell -.. _tag: https://git-scm.com/book/en/v2/Git-Basics-Tagging#_annotated_tags diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 4d47ee35..00000000 --- a/setup.cfg +++ /dev/null @@ -1,111 +0,0 @@ -[metadata] -name = pysaml2 -version = file:VERSION -author = IdentityPython -author-email = discuss@idpy.org -maintainer = IdentityPython -maintainer-email = discuss@idpy.org -license = Apache License Version 2.0 -license-file = LICENSE -description = Python implementation of SAML Version 2 Standard -long-description = file:README.rst -long_description_content_type = text/x-rst; charset=UTF-8 -home-page = https://idpy.org -project_urls = - Bug Tracker = https://github.com/IdentityPython/pysaml2/issues - Documentation = https://pysaml2.readthedocs.io - Source Code = https://github.com/IdentityPython/pysaml2 -classifier = - Development Status :: 4 - Beta - License :: OSI Approved :: Apache Software License - Topic :: Software Development :: Libraries :: Python Modules - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 -requires-dist = setuptools -keywords = - saml - saml2 - standard - federation - idpy - IdentityPython - - -[options] -zip_safe = False -include_package_data = True -package_dir = - = src -packages = find: -scripts = - tools/make_metadata.py - tools/mdexport.py - tools/merge_metadata.py - tools/parse_xsd2.py -python_requires = >=3.6, <4 -install_requires = - cryptography >= 3.1 - defusedxml - pyOpenSSL - python-dateutil - pytz - requests >= 1.0.0 - setuptools - six - importlib_resources;python_version<'3.9' - xmlschema >= 1.2.1 - - -[options.packages.find] -where = src -include = - saml2 - saml2.* - - -[options.package_data] -* = - *.xml - *.xsd - - -[options.extras_require] -s2repoze = - paste - zope.interface - repoze.who - - -[bdist_wheel] -universal = 1 - - -[tool:pytest] -markers = - mongo: marks tests that need mongodb - - -[flake8] -max-line-length = 120 -author-attribute = forbidden -no-accept-encodings = True -assertive-snakecase = True -# assertive-test-pattern = <fnmatch> -inline-quotes = " -multiline-quotes = """ -docstring-quotes = """ -application-import-names = saml2 - -hang_closing = false -doctests = false -max-complexity = 10 -exclude = - .git - __pycache__ - docs/source/conf.py - build - dist diff --git a/setup.py b/setup.py deleted file mode 100755 index 996b404f..00000000 --- a/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Setup.py entry point for package. - -Configuration is handled by setuptools>30.3.0 through setup.cfg. -https://setuptools.readthedocs.io/en/latest/setuptools.html#metadata -https://setuptools.readthedocs.io/en/latest/setuptools.html#options -""" - -import setuptools - - -setuptools.setup( - package_dir={'': 'src'}, -) diff --git a/src/saml2/tools/make_metadata.py b/src/saml2/tools/make_metadata.py new file mode 100644 index 00000000..a4d9670d --- /dev/null +++ b/src/saml2/tools/make_metadata.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +import argparse +import os +import sys +from saml2.metadata import entity_descriptor, metadata_tostring_fix +from saml2.metadata import entities_descriptor +from saml2.metadata import sign_entity_descriptor + +from saml2.sigver import security_context +from saml2.validate import valid_instance +from saml2.config import Config + +# ============================================================================= +# Script that creates a SAML2 metadata file from a pysaml2 entity configuration +# file +# ============================================================================= + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-v', dest='valid', + help="How long, in days, the metadata is valid from the time of creation") + parser.add_argument('-c', dest='cert', help='certificate') + parser.add_argument('-e', dest='ed', action='store_true', + help="Wrap the whole thing in an EntitiesDescriptor") + parser.add_argument('-i', dest='id', + help="The ID of the entities descriptor") + parser.add_argument('-k', dest='keyfile', + help="A file with a key to sign the metadata with") + parser.add_argument('-n', dest='name', default="") + parser.add_argument('-p', dest='path', + help="path to the configuration file") + parser.add_argument('-s', dest='sign', action='store_true', + help="sign the metadata") + parser.add_argument('-x', dest='xmlsec', + help="xmlsec binaries to be used for the signing") + parser.add_argument('-w', dest='wellknown', + help="Use wellknown namespace prefixes") + parser.add_argument(dest="config", nargs="+") + args = parser.parse_args() + + valid_for = 0 + nspair = {"xs": "http://www.w3.org/2001/XMLSchema"} + paths = [".", "/opt/local/bin"] + + if args.valid: + # translate into hours + valid_for = int(args.valid) * 24 + + eds = [] + for filespec in args.config: + bas, fil = os.path.split(filespec) + if bas != "": + sys.path.insert(0, bas) + if fil.endswith(".py"): + fil = fil[:-3] + cnf = Config().load_file(fil) + if valid_for: + cnf.valid_for = valid_for + eds.append(entity_descriptor(cnf)) + + conf = Config() + conf.key_file = args.keyfile + conf.cert_file = args.cert + conf.debug = 1 + conf.xmlsec_binary = args.xmlsec + secc = security_context(conf) + + if args.id: + desc, xmldoc = entities_descriptor(eds, valid_for, args.name, args.id, + args.sign, secc) + valid_instance(desc) + xmldoc = metadata_tostring_fix(desc, nspair, xmldoc) + print(xmldoc.decode("utf-8")) + else: + for eid in eds: + if args.sign: + assert conf.key_file + assert conf.cert_file + eid, xmldoc = sign_entity_descriptor(eid, args.id, secc) + else: + xmldoc = None + + valid_instance(eid) + xmldoc = metadata_tostring_fix(eid, nspair, xmldoc) + print(xmldoc.decode("utf-8")) + + +if __name__ == "__main__": + main() diff --git a/src/saml2/tools/mdexport.py b/src/saml2/tools/mdexport.py new file mode 100644 index 00000000..7ecae391 --- /dev/null +++ b/src/saml2/tools/mdexport.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +from saml2.sigver import _get_xmlsec_cryptobackend +from saml2.sigver import SecurityContext +from saml2.httpbase import HTTPBase + +from saml2 import saml +from saml2 import md +from saml2.attribute_converter import ac_factory +from saml2 import xmldsig +from saml2 import xmlenc + +import argparse + +from saml2.mdstore import MetaDataFile, MetaDataExtern, load_extensions + +__author__ = 'rolandh' + +""" +A script that imports and verifies metadata and then dumps it in a basic +dictionary format. +""" + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-t', dest='type') + parser.add_argument('-u', dest='url') + parser.add_argument('-c', dest='cert') + parser.add_argument('-a', dest='attrsmap') + parser.add_argument('-o', dest='output') + parser.add_argument('-x', dest='xmlsec') + parser.add_argument(dest="item") + args = parser.parse_args() + + + metad = None + + if args.type == "local": + metad = MetaDataFile(args.item, args.item) + elif args.type == "external": + ATTRCONV = ac_factory(args.attrsmap) + httpc = HTTPBase() + crypto = _get_xmlsec_cryptobackend(args.xmlsec) + sc = SecurityContext(crypto) + metad = MetaDataExtern(ATTRCONV, args.url, sc, cert=args.cert, http=httpc) + + if metad is not None: + metad.load() + txt = metad.dumps() + if args.output: + f = open(args.output, "w") + f.write(txt) + f.close() + else: + print(txt) + + +if __name__ == '__main__': + main() diff --git a/tools/mdexport_test.py b/src/saml2/tools/mdexport_test.py index c8b4e49f..c8b4e49f 100755..100644 --- a/tools/mdexport_test.py +++ b/src/saml2/tools/mdexport_test.py diff --git a/src/saml2/tools/mdimport.py b/src/saml2/tools/mdimport.py new file mode 100644 index 00000000..310e61ad --- /dev/null +++ b/src/saml2/tools/mdimport.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +import time +from saml2.attribute_converter import ac_factory +from saml2.mdstore import MetaDataMD, MetaDataFile + +__author__ = 'rolandh' + +def main(): + start = time.time() + for i in range(1, 10): + mdmd = MetaDataMD(ac_factory("../tests/attributemaps"), "swamid2.md") + mdmd.load() + + _ = mdmd.keys() + + print(time.time() - start) + + start = time.time() + for i in range(1, 10): + mdf = MetaDataFile(ac_factory("../tests/attributemaps"), + "../tests/swamid-2.0.xml") + mdf.load() + _ = mdf.keys() + + print(time.time() - start) + + +if __name__ == '__main__': + main() diff --git a/src/saml2/tools/merge_metadata.py b/src/saml2/tools/merge_metadata.py new file mode 100644 index 00000000..dbd93e5a --- /dev/null +++ b/src/saml2/tools/merge_metadata.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +from saml2.sigver import _get_xmlsec_cryptobackend, SecurityContext +from saml2.httpbase import HTTPBase +from saml2.attribute_converter import ac_factory +import argparse + +from saml2.mdstore import MetaDataFile, MetaDataExtern, MetadataStore + +__author__ = 'rolandh' + +""" +A script that imports and verifies metadata. +""" + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-a', dest='attrsmap') + parser.add_argument('-o', dest='output', default="local") + parser.add_argument('-x', dest='xmlsec') + parser.add_argument('-i', dest='ignore_valid', action='store_true') + parser.add_argument(dest="conf") + args = parser.parse_args() + + metad = None + + # config file format + # + # local <local file name> + # remote <url> <local file name for certificate use to verify signature> + # + # for instance + # + #local metadata_sp_1.xml + #local InCommon-metadata.xml + #remote https://kalmar2.org/simplesaml/module.php/aggregator/?id=kalmarcentral2&set=saml2 kalmar2.pem + # + + ATTRCONV = ac_factory(args.attrsmap) + + mds = MetadataStore(None, None) + + for line in open(args.conf).readlines(): + line = line.strip() + if len(line) == 0: + continue + elif line[0] == "#": + continue + spec = line.split(" ") + + if args.ignore_valid: + kwargs = {"check_validity": False} + else: + kwargs = {} + + if spec[0] == "local": + metad = MetaDataFile(spec[1], spec[1], **kwargs) + elif spec[0] == "remote": + ATTRCONV = ac_factory(args.attrsmap) + httpc = HTTPBase() + crypto = _get_xmlsec_cryptobackend(args.xmlsec) + sc = SecurityContext(crypto, key_type="", cert_type="") + metad = MetaDataExtern(ATTRCONV, spec[1], sc, cert=spec[2], http=httpc, + **kwargs) + + if metad is not None: + try: + metad.load() + except: + raise + + mds.metadata[spec[1]] = metad + + print(mds.dumps(args.output)) + + +if __name__ == '__main__': + main() diff --git a/tools/parse_xsd2.py b/src/saml2/tools/parse_xsd2.py index 0fc252a3..e5616da3 100755..100644 --- a/tools/parse_xsd2.py +++ b/src/saml2/tools/parse_xsd2.py @@ -19,13 +19,13 @@ DEBUG = False XMLSCHEMA = "http://www.w3.org/2001/XMLSchema" XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace' -CLASS_PROP = [("c_children", ".copy()"), +CLASS_PROP = [("c_children", ".copy()"), ("c_attributes", ".copy()"), ("c_child_order", "[:]"), ("c_cardinality", ".copy()")] - + BASE_ELEMENT = ["text", "extension_elements", "extension_attributes"] - + class MissingPrerequisite(Exception): pass @@ -34,7 +34,7 @@ def sd_copy(arg): return arg.copy() except AttributeError: return {} - + # ------------------------------------------------------------------------ def class_pyify(ref): @@ -73,7 +73,7 @@ def def_init(imports, attributes): line.append("%sextension_attributes=None," % indent3) line.append("%s):" % indent) return line - + def base_init(imports): line = [] @@ -101,7 +101,7 @@ def base_init(imports): line.append("%s%s=%s," % (indent4, _name, _name)) line.append("%s)" % indent4) return line - + def initialize(attributes): indent = INDENT+INDENT @@ -116,7 +116,7 @@ def initialize(attributes): _vname = val +"_" else: _vname = val - + line.append("%sself.%s=%s" % (indent, _name, _vname)) return line @@ -136,7 +136,7 @@ def _mod_typ(prop): else: typ = prop.ref mod = None - + return mod, typ @@ -153,7 +153,7 @@ def _mod_cname(prop, cdict): cname = cdict[class_pyify(typ)].class_name else: cname = typ - + return mod, cname @@ -199,7 +199,7 @@ def klass_namn(obj): return obj.class_name else: return obj.name - + class PyObj(object): def __init__(self, name=None, pyname=None, root=None): @@ -212,7 +212,7 @@ class PyObj(object): self.properties = ([], []) self.abstract = False self.class_name = "" - + if pyname: self.pyname = pyname elif name: @@ -221,7 +221,7 @@ class PyObj(object): self.pyname = name self.type = None - + def child_spec(self, target_namespace, prop, mod, typ, lista): if mod: namesp = external_namespace(self.root.modul[mod]) @@ -236,7 +236,7 @@ class PyObj(object): else: return "c_children['%s'] = ('%s', %s)" % ( pkey, prop.pyname, typ) - + def knamn(self, sup, cdict): cname = cdict[sup].class_name if not cname: @@ -247,7 +247,7 @@ class PyObj(object): else: cname = tag + "_" return cname - + def _do_properties(self, line, cdict, ignore, target_namespace): args = [] child = [] @@ -256,10 +256,10 @@ class PyObj(object): (own, inh) = self.properties except AttributeError: (own, inh) = ([], []) - + for prop in own: if isinstance(prop, PyAttribute): - line.append("%sc_attributes['%s'] = %s" % (INDENT, + line.append("%sc_attributes['%s'] = %s" % (INDENT, prop.name, prop.spec())) if prop.fixed: args.append((prop.pyname, prop.fixed, None)) @@ -268,18 +268,18 @@ class PyObj(object): args.append((prop.pyname, prop.pyname, prop.default)) else: args.append((prop.pyname, prop.pyname, None)) - + elif isinstance(prop, PyElement): (mod, cname) = _mod_cname(prop, cdict) - + if prop.max == "unbounded": lista = True pmax = 0 # just has to be different from 1 else: pmax = int(prop.max) lista = False - + if prop.name in ignore: pass else: @@ -302,13 +302,13 @@ class PyObj(object): child.append(prop.pyname) if lista: - args.append((prop.pyname, "%s or []" % (prop.pyname,), + args.append((prop.pyname, "%s or []" % (prop.pyname,), None)) else: args.append((prop.pyname, prop.pyname, None)) - + return args, child, inh - + def _superiors(self, cdict): imps = {} @@ -318,17 +318,17 @@ class PyObj(object): for sup in superior: klass = self.knamn(sup, cdict) sups.append(klass) - + imps[klass] = [] for cla in cdict[sup].properties[0]: - if cla.pyname and cla.pyname not in imps[klass]: + if cla.pyname and cla.pyname not in imps[klass]: imps[klass].append(cla.pyname) except AttributeError: superior = [] sups = [] - + return superior, sups, imps - + def class_definition(self, target_namespace, cdict=None, ignore=None): line = [] @@ -339,7 +339,7 @@ class PyObj(object): (superior, sups, imps) = self._superiors(cdict) c_name = klass_namn(self) - + if not superior: line.append("class %s(SamlBase):" % (c_name,)) else: @@ -348,7 +348,7 @@ class PyObj(object): if hasattr(self, 'scoped'): pass else: - line.append("%s\"\"\"The %s:%s element \"\"\"" % (INDENT, + line.append("%s\"\"\"The %s:%s element \"\"\"" % (INDENT, target_namespace, self.name)) line.append("") @@ -357,10 +357,10 @@ class PyObj(object): try: if self.value_type: if isinstance(self.value_type, six.string_types): - line.append("%sc_value_type = '%s'" % (INDENT, + line.append("%sc_value_type = '%s'" % (INDENT, self.value_type)) else: - line.append("%sc_value_type = %s" % (INDENT, + line.append("%sc_value_type = %s" % (INDENT, self.value_type)) except AttributeError: pass @@ -371,16 +371,16 @@ class PyObj(object): else: for sup in sups: for var, cps in CLASS_PROP: - line.append("%s%s = %s.%s%s" % (INDENT, var, sup, var, + line.append("%s%s = %s.%s%s" % (INDENT, var, sup, var, cps)) - (args, child, inh) = self._do_properties(line, cdict, ignore, + (args, child, inh) = self._do_properties(line, cdict, ignore, target_namespace) - + if child: line.append("%sc_child_order.extend([%s])" % (INDENT, "'"+"', '".join(child)+"'")) - + if args: if inh: cname = self.knamn(self.superior[0], cdict) @@ -389,7 +389,7 @@ class PyObj(object): line.extend(def_init(imps, args)) line.extend(base_init(imps)) line.extend(initialize(args)) - + line.append("") if not self.abstract or not self.class_name.endswith("_"): line.append("def %s_from_string(xml_string):" % pyify( @@ -398,17 +398,17 @@ class PyObj(object): "%sreturn saml2.create_class_from_xml_string(%s, xml_string)" % ( INDENT, self.class_name)) line.append("") - + self.done = True return "\n".join(line) - + def prepend(add, orig): # return a list which is the lists concatenated with the second list first res = [add] if orig: res.extend(orig) return res - + def pyobj_factory(name, value_type, elms=None): pyobj = PyObj(name, pyify(name)) pyobj.value_type = value_type @@ -424,7 +424,7 @@ def pyelement_factory(name, value_type, elms=None): if name not in [c.name for c in elms]: elms.append(obj) return obj - + def expand_groups(properties, cdict): res = [] for prop in properties: @@ -434,9 +434,9 @@ def expand_groups(properties, cdict): res.extend(cdict[cname].properties[0]) else: res.append(prop) - + return res - + class PyElement(PyObj): def __init__(self, name=None, pyname=None, root=None, parent=""): PyObj.__init__(self, name, pyname, root) @@ -449,7 +449,7 @@ class PyElement(PyObj): self.max = 1 self.definition = None self.orig = None - + # def prereq(self, prop): # prtext = prop.text(target_namespace, cdict) # if prtext == None: @@ -457,7 +457,7 @@ class PyElement(PyObj): # else: # prop.done = True # return prtext - + def undefined(self, cdict): try: (mod, typ) = self.type @@ -477,7 +477,7 @@ class PyElement(PyObj): if not cdict[cname].done: return [cdict[cname]], [] return [], [] - + def _local_class(self, typ, cdict, child, target_namespace, ignore): if typ in cdict and not cdict[typ].done: raise MissingPrerequisite(typ) @@ -488,31 +488,31 @@ class PyElement(PyObj): except AttributeError: self.orig["superior"] = [] self.superior = [typ] - req = self.class_definition(target_namespace, cdict, + req = self.class_definition(target_namespace, cdict, ignore) if not child: req = [req] - + if not hasattr(self, 'scoped'): cdict[self.name] = self cdict[self.name].done = True if child: cdict[self.name].local = True self.type = (None, self.name) - + return req - - def _external_class(self, mod, typ, cdict, child, target_namespace, + + def _external_class(self, mod, typ, cdict, child, target_namespace, ignore): # Will raise exception if class can't be found cname = self.root.modul[mod].factory(typ).__class__.__name__ imp_name = "%s.%s" % (mod, cname) - + if imp_name not in cdict: - # create import object so I can get the properties from it + # create import object so I can get the properties from it # later impo = pyelement_factory(imp_name, None, None) - impo.properties = [_import_attrs(self.root.modul[mod], typ, + impo.properties = [_import_attrs(self.root.modul[mod], typ, self.root),[]] impo.class_name = imp_name cdict[imp_name] = impo @@ -521,11 +521,11 @@ class PyElement(PyObj): impo.local = True # and now for this object self.superior = [imp_name] - text = self.class_definition(target_namespace, cdict, + text = self.class_definition(target_namespace, cdict, ignore=ignore) - + return text - + def text(self, target_namespace, cdict, child=True, ignore=None): if ignore is None: ignore = [] @@ -538,19 +538,19 @@ class PyElement(PyObj): try: (mod, typ) = self.type if not mod: - req = self._local_class(typ, cdict, child, + req = self._local_class(typ, cdict, child, target_namespace, ignore) else: - text = self._external_class(mod, typ, cdict, child, + text = self._external_class(mod, typ, cdict, child, target_namespace, ignore) except ValueError: # Simple type element if self.type: - text = self.class_definition(target_namespace, cdict, + text = self.class_definition(target_namespace, cdict, ignore=ignore) if child: self.local = True self.done = True - + except TypeError: # could be a ref then or a PyObj instance if isinstance(self.type, PyObj): pyobj = self.type @@ -576,16 +576,16 @@ class PyElement(PyObj): else: if not cdict[class_pyify(self.ref)].done: raise MissingPrerequisite(self.ref) - + self.done = True return req, text - + def _do(obj, target_namespace, cdict, prep): try: (req, text) = obj.text(target_namespace, cdict) except MissingPrerequisite: return [], None - + if text is None: if req: #prep = prepend(req, prep) @@ -612,9 +612,9 @@ def reqursive_superior(supc, cdict): else: properties.extend(reqursive_superior(rsup, cdict)) return properties - + class PyType(PyObj): - def __init__(self, name=None, pyname=None, root=None, superior=None, + def __init__(self, name=None, pyname=None, root=None, superior=None, internal=True, namespace=None): PyObj.__init__(self, name, pyname, root) self.class_name = leading_uppercase(self.name + '_') @@ -645,29 +645,29 @@ class PyType(PyObj): (mod, typ) = sup.split('.') supc = pyobj_factory(sup, None, None) if mod: - supc.properties = [_import_attrs(self.root.modul[mod], + supc.properties = [_import_attrs(self.root.modul[mod], typ, self.root),[]] cdict[sup] = supc supc.done = True - + if not supc.done: res = _do(supc, target_namespace, cdict, req) if isinstance(res, tuple): return res - + if not self.properties[1]: inherited_properties = reqursive_superior(supc, cdict) - + if inherited_properties: - self.properties = (self.properties[0], + self.properties = (self.properties[0], rm_duplicates(inherited_properties)) - + (own, inh) = self.properties own = rm_duplicates(expand_groups(own, cdict)) self.properties = (own, inh) for prop in own: if not prop.name: # Ignore - continue + continue if not prop.done: if prop.name in ignore: continue @@ -690,9 +690,9 @@ class PyType(PyObj): res = (req, None) if isinstance(res, tuple): return res - + return req, self.class_definition(target_namespace, cdict, ignore) - + def undefined(self, cdict): undef = ([], []) @@ -704,7 +704,7 @@ class PyType(PyObj): (own, _) = self.properties for prop in own: if not prop.name: # Ignore - continue + continue if isinstance(prop, PyAttribute): continue if not prop.done: @@ -712,7 +712,7 @@ class PyType(PyObj): return undef class PyAttribute(PyObj): - def __init__(self, name=None, pyname=None, root=None, external=False, + def __init__(self, name=None, pyname=None, root=None, external=False, namespace="", required=False, typ=""): PyObj.__init__(self, name, pyname, root) @@ -728,21 +728,21 @@ class PyAttribute(PyObj): if isinstance(self.type, PyObj): if not cdict[self.type.name].done: raise MissingPrerequisite(self.type.name) - + return [], [] # Means this elements definition is empty - + def spec(self): if isinstance(self.type, PyObj): - return "('%s', %s, %s)" % (self.pyname, self.type.class_name, + return "('%s', %s, %s)" % (self.pyname, self.type.class_name, self.required) else: if self.type: - return "('%s', '%s', %s)" % (self.pyname, self.type, + return "('%s', '%s', %s)" % (self.pyname, self.type, self.required) else: - return "('%s', '%s', %s)" % (self.pyname, self.base, + return "('%s', '%s', %s)" % (self.pyname, self.base, self.required) - + class PyAny(PyObj): def __init__(self, name=None, pyname=None, _external=False, _namespace=""): PyObj.__init__(self, name, pyname) @@ -761,21 +761,21 @@ class PyGroup(object): self.properties = [] self.done = False self.ref = [] - + def text(self, _target_namespace, _dict, _child, _ignore): return [], [] - + def undefined(self, _cdict): undef = ([], []) (own, _) = self.properties for prop in own: if not prop.name: # Ignore - continue + continue if not prop.done: undef[1].append(prop) return undef - + # ----------------------------------------------------------------------------- def verify_import(modul, tag): try: @@ -783,7 +783,7 @@ def verify_import(modul, tag): return True except Exception: return False - + def external_namespace(modul): return modul.NAMESPACE @@ -799,7 +799,7 @@ def _import_attrs(modul, tag, top): mul = mul[0] maximum = "unbounded" if pyn == child: - cpy = PyElement(name=mul.c_tag, pyname=pyn, root=top) + cpy = PyElement(name=mul.c_tag, pyname=pyn, root=top) # internal=False, ns=obj.c_namespace) cpy.max = maximum properties.append(cpy) @@ -823,7 +823,7 @@ def _spec(elem): pass return txt - + # def _klass(elem, _namespace, sup, top): # if elem.name in top.py_elements: # return None @@ -833,7 +833,7 @@ def _spec(elem): # if sup != "SamlBase": # kl.superior.append(sup) # return kl - + def _do_from_string(name): print print("def %s_from_string(xml_string):" % pyify(name)) @@ -851,7 +851,7 @@ def _namespace_and_tag(obj, param, top): # tag = obj.name return namespace, tag - + # ----------------------------------------------------------------------------- class Simple(object): @@ -865,7 +865,7 @@ class Simple(object): self.ref = None self.scoped = False self.itemType = None - + for attribute, value in iter(elem.attrib.items()): self.__setattr__(attribute, value) @@ -879,16 +879,16 @@ class Simple(object): def repr(self, _top=None, _sup=None, _argv=None, _child=True, _parent=""): return None - + def elements(self, _top): return [] - + class Any(Simple): - + def repr(self, _top=None, _sup=None, _argv=None, _child=True, _parent=""): return PyAny() - + class AnyAttribute(Simple): def repr(self, _top=None, _sup=None, _argv=None, _child=True, _parent=""): @@ -897,7 +897,7 @@ class AnyAttribute(Simple): class Attribute(Simple): def repr(self, top=None, sup=None, _argv=None, _child=True, _parent=""): # default, fixed, use, type - + if DEBUG: print("#ATTR", self.__dict__) @@ -927,9 +927,9 @@ class Attribute(Simple): pyname = pyify(name) else: # referering to what raise Exception("Strange reference: %s" % self.ref) - + objekt = PyAttribute(name, pyname, external=external, root=top) - + # Initial declaration if not ref: try: @@ -948,7 +948,7 @@ class Attribute(Simple): ctyp = get_type_def(self.type.replace("-","_"), top.parts) if not ctyp.py_class: ctyp.repr(top, sup) - objekt.type = ctyp.py_class + objekt.type = ctyp.py_class else: objekt.type = self.type except AttributeError: @@ -958,45 +958,45 @@ class Attribute(Simple): objekt.required = True except AttributeError: pass - + # in init try: objekt.default = self.default except AttributeError: pass - + # attr def try: objekt.fixed = self.fixed except AttributeError: pass - + if DEBUG: print("#--ATTR py_attr:%s" % (objekt,)) - + return objekt - + class Enumeration(Simple): pass - + class Union(Simple): pass - + class Import(Simple): pass - + class Documentation(Simple): pass - + class MaxLength(Simple): pass class Length(Simple): pass - + class MinInclusive(Simple): pass - + class MaxInclusive(Simple): pass @@ -1005,7 +1005,7 @@ class MinExclusive(Simple): class MaxExclusive(Simple): pass - + class List(Simple): pass @@ -1046,7 +1046,7 @@ class Complex(object): self.base = None self.scoped = False self.abstract = False - + for attribute, value in iter(elem.attrib.items()): self.__setattr__(attribute, value) @@ -1057,7 +1057,7 @@ class Complex(object): pass self.do_child(elem) - + try: self.name = self.name.replace("-","_") except AttributeError: @@ -1072,19 +1072,19 @@ class Complex(object): own[0].base = base self._own.extend(own) self._inherited.extend(inh) - + def collect(self, top, sup, argv=None, parent=""): if self._own or self._inherited: return self._own, self._inherited - + if DEBUG: print(self.__dict__) print("#-- %d parts" % len(self.parts)) - + self._extend(top, sup, argv, parent) - + return self._own, self._inherited - + def do_child(self, elem): for child in elem: self.parts.append(evaluate(child.tag, child)) @@ -1116,9 +1116,9 @@ class Complex(object): continue else: res.append(p) - + return res - + def min_max(cls, objekt, argv): try: objekt.max = argv["maxOccurs"] @@ -1133,8 +1133,8 @@ def min_max(cls, objekt, argv): objekt.min = cls.minOccurs except (KeyError, TypeError): objekt.min = cls.minOccurs - - + + class Element(Complex): def __str__(self): return "%s" % (self.__dict__,) @@ -1163,9 +1163,9 @@ class Element(Complex): return namespace, name, ctyp, xns, ref def collect(self, top, sup, argv=None, parent=""): - """ means this element is part of a larger object, hence a property of + """ means this element is part of a larger object, hence a property of that object """ - + try: argv_copy = sd_copy(argv) return [self.repr(top, sup, argv_copy, parent=parent)], [] @@ -1173,7 +1173,7 @@ class Element(Complex): print("#!!!!", exc) return [], [] - def elements(self, top): + def elements(self, top): (_namespace, name, ctyp, xns, _) = self.klass(top) if ctyp: return ctyp.elements(top) @@ -1191,7 +1191,7 @@ class Element(Complex): if self.py_class: return self.py_class - + try: myname = self.name except AttributeError: @@ -1203,7 +1203,7 @@ class Element(Complex): self.py_class = objekt = PyElement(myname, root=top) min_max(self, objekt, argv) - + try: (namespace, superkl) = _namespace_and_tag(self, self.ref, top) # internal or external reference @@ -1211,9 +1211,9 @@ class Element(Complex): objekt.name = superkl objekt.pyname = pyify(superkl) if self.xmlns_map[namespace] == top.target_namespace: - objekt.ref = superkl + objekt.ref = superkl else: - objekt.ref = (namespace, superkl) + objekt.ref = (namespace, superkl) except AttributeError as exc: if DEBUG: print("#===>", exc) @@ -1241,31 +1241,31 @@ class Element(Complex): if isinstance(self.parts[0], ComplexType) or \ isinstance(self.parts[0], SimpleType): self.parts[0].name = self.name - objekt.type = self.parts[0].repr(top, sup, + objekt.type = self.parts[0].repr(top, sup, parent=self.name) objekt.scoped = True elif len(self.parts) == 2:# One child might be Annotation if isinstance(self.parts[0], Annotation): self.parts[1].name = self.name - objekt.type = self.parts[1].repr(top, sup, + objekt.type = self.parts[1].repr(top, sup, parent=self.name) objekt.scoped = True elif isinstance(self.parts[1], Annotation): self.parts[0].name = self.name - objekt.type = self.parts[0].repr(top, sup, + objekt.type = self.parts[0].repr(top, sup, parent=self.name) objekt.scoped = True else: if DEBUG: print("$", self) - raise + raise if parent: objekt.class_name = "%s_%s" % ( leading_uppercase(parent), objekt.name) objekt.scoped = True - + return objekt @@ -1273,7 +1273,7 @@ class SimpleType(Complex): def repr(self, top=None, _sup=None, _argv=None, _child=True, parent=""): if self.py_class: return self.py_class - + obj = PyType(self.name, root=top) try: if len(self.parts) == 1: @@ -1297,10 +1297,10 @@ class SimpleType(Complex): obj.value_type = {"base":"list", "member":part.itemType} except ValueError: pass - + self.py_class = obj return obj - + class Sequence(Complex): def collect(self, top, sup, argv=None, parent=""): @@ -1308,7 +1308,7 @@ class Sequence(Complex): for key, val in self.__dict__.items(): if key not in ['xmlns_map'] and not key.startswith("_"): argv_copy[key] = val - + if DEBUG: print("#Sequence: %s" % argv) return Complex.collect(self, top, sup, argv_copy, parent) @@ -1334,14 +1334,14 @@ class Extension(Complex): def collect(self, top, sup, argv=None, parent=""): if self._own or self._inherited: return self._own, self._inherited - + if DEBUG: print("#!!!", self.__dict__) try: base = self.base (namespace, tag) = _namespace_and_tag(self, base, top) - + if self.xmlns_map[namespace] == top.target_namespace: cti = get_type_def(tag, top.parts) if not cti.py_class: @@ -1349,7 +1349,7 @@ class Extension(Complex): #print("#EXT..",ct._collection) self._inherited = cti.py_class.properties[0][:] self._inherited.extend(cti.py_class.properties[1]) - elif self.xmlns_map[namespace] == XMLSCHEMA: + elif self.xmlns_map[namespace] == XMLSCHEMA: base = tag else: iattr = _import_attrs(top.modul[namespace], tag, top) @@ -1371,7 +1371,7 @@ class Choice(Complex): # A choice means each element may not be part of the choice argv_copy["minOccurs"] = 0 - + if DEBUG: print("#Choice: %s" % argv) return Complex.collect(self, top, sup, argv_copy, parent=parent) @@ -1385,7 +1385,7 @@ class ComplexType(Complex): def repr(self, top=None, sup=None, _argv=None, _child=True, parent=""): if self.py_class: return self.py_class - + # looking for a pattern here significant_parts = self.significant_parts() value_type = "" @@ -1397,7 +1397,7 @@ class ComplexType(Complex): if isinstance(cci.parts[0], Extension): ext = cci.parts[0] - (namespace, name) = _namespace_and_tag(ext, ext.base, + (namespace, name) = _namespace_and_tag(ext, ext.base, top) if ext.xmlns_map[namespace] == top.target_namespace: @@ -1410,42 +1410,42 @@ class ComplexType(Complex): value_type = name else: new_sup = "%s.%s" % (namespace, name) - + #print("#Superior: %s" % new_sup) if new_sup: sup = new_sup else: #print("#>>", self.parts[0].__class__) pass - + try: - self.py_class = PyType(self.name, superior=sup, + self.py_class = PyType(self.name, superior=sup, namespace=top.target_namespace, root=top) - except AttributeError: # No name - self.py_class = PyType("", superior=sup, + except AttributeError: # No name + self.py_class = PyType("", superior=sup, namespace=top.target_namespace, root=top) try: self.py_class.abstract = self.abstract except AttributeError: pass - + if value_type: self.py_class.value_type = {"base": value_type} - + try: if not parent: try: parent = self.name except AttributeError: parent = "" - + self.py_class.properties = self.collect(top, sup, parent=parent) except ValueError: pass - - return self.py_class - + + return self.py_class + class Annotation(Complex): pass @@ -1454,25 +1454,25 @@ class All(Complex): class Group(Complex): def collect(self, top, sup, argv=None, parent=""): - """ means this element is part of a larger object, hence a property of + """ means this element is part of a larger object, hence a property of that object """ - + try: #objekt = PyGroup("", root=top) (namespace, tag) = _namespace_and_tag(self, self.ref, top) - + try: if self.xmlns_map[namespace] == top.target_namespace: cti = get_type_def(tag, top.parts) try: return cti.py_class.properties except ValueError: - return cti.collect(top, sup) + return cti.collect(top, sup) else: raise Exception( "Reference to group in other XSD file, not supported") except KeyError: - raise Exception("Missing namespace definition") + raise Exception("Missing namespace definition") except AttributeError as exc: print("#!!!!", exc) return [], [] @@ -1484,13 +1484,13 @@ class Group(Complex): self.py_class = objekt = PyGroup(self.name, root=top) min_max(self, objekt, argv) - + try: self._extend(top, sup, argv) objekt.properties = (self._own, self._inherited) except ValueError: pass - + return objekt class Unique(Complex): @@ -1512,9 +1512,9 @@ class AttributeGroup(Complex): except AttributeError: if self._own or self._inherited: return self._own, self._inherited - + argv_copy = sd_copy(argv) - + for prop in self.parts: if isinstance(prop, Attribute): self._own.append(prop.repr(top, sup, argv_copy, parent)) @@ -1524,15 +1524,15 @@ class AttributeGroup(Complex): def repr(self, top=None, sup=None, _argv=None, _child=True, parent=""): if self.py_class: return self.py_class - + self.py_class = PyAttributeGroup(self.name, root=top) try: self.py_class.properties = self.collect(top, sup) except ValueError: pass - - return self.py_class + + return self.py_class def pyify_0(name): res = "" @@ -1545,7 +1545,7 @@ def pyify_0(name): res += "_"+match.group(num+1).lower()+match.group(num)[1:] except AttributeError: break - + res = res.replace("-","_") if res in ["class"]: res += "_" @@ -1555,7 +1555,7 @@ def pyify_0(name): def pyify(name): # AssertionIDRef res = [] - + upc = [] pre = "" for char in name: @@ -1573,7 +1573,7 @@ def pyify(name): for uch in upc[:-1]: res.append(uch.lower()) res.append("_"+upc[-1].lower()) - + upc = [] res.append(char) pre = "_" @@ -1582,7 +1582,7 @@ def pyify(name): return name.lower() else: res.append("_"+("".join(upc).lower())) - + return "".join(res) @@ -1594,18 +1594,18 @@ def get_type_def( typ, defs): except AttributeError: pass return None - + def sort_elements(els): res = [] - + diff = False for key, val in els.items(): if not val: res.append(key) del els[key] diff = True - + res.sort() while diff: diff = False @@ -1624,7 +1624,7 @@ def sort_elements(els): diff = True partres.sort() res.extend(partres) - + return res, els @@ -1633,14 +1633,14 @@ def output(elem, target_namespace, eldict, ignore=None): if ignore is None: ignore = [] - + try: (preps, text) = elem.text(target_namespace, eldict, False, ignore) except TypeError: return done except MissingPrerequisite: return done - + for prep in preps: if prep: done = 1 @@ -1657,9 +1657,9 @@ def output(elem, target_namespace, eldict, ignore=None): elem.done = True print(text) print() - + return done - + def intro(): print("""#!/usr/bin/env python @@ -1673,7 +1673,7 @@ from saml2 import SamlBase """ % (time.ctime(), __version__)) #NAMESPACE = 'http://www.w3.org/2000/09/xmldsig#' - + def block_items(objekt, block, eldict): if objekt not in block: @@ -1682,14 +1682,14 @@ def block_items(objekt, block, eldict): block.append(objekt.type) block.append(objekt) if isinstance(objekt, PyType): - others = [p for p in eldict.values() if isinstance(p, + others = [p for p in eldict.values() if isinstance(p, PyElement) and p.type[1] == objekt.name] for item in others: if item not in block: block.append(item) return block - + def find_parent(elm, eldict): if isinstance(elm, PyElement): if elm.type: @@ -1706,9 +1706,9 @@ def find_parent(elm, eldict): if sup.done: return elm return find_parent(sup, eldict) - + return elm - + class Schema(Complex): @@ -1739,14 +1739,14 @@ class Schema(Complex): else: lista = False - spec = objekt.child_spec(self.target_namespace, - prop, mod, cname, + spec = objekt.child_spec(self.target_namespace, + prop, mod, cname, lista) lines = ["%s.%s" % (objekt.class_name, spec)] tup.append((prop, lines, spec)) - + return tup - + def adjust(self, eldict, block): udict = {} for elem in self.elems: @@ -1813,7 +1813,7 @@ class Schema(Complex): alla.append(elem) tup = self._mk_list(parent, alla, eldict) res = (parent, tup) - + if res[0]: break else: @@ -1832,7 +1832,7 @@ class Schema(Complex): undone += 1 not_done += output(elem, self.target_namespace, eldict) return undone - + def _element_from_string(self): print("ELEMENT_FROM_STRING = {") for elem in self.elems: @@ -1840,11 +1840,11 @@ class Schema(Complex): continue if elem.abstract: continue - print("%s%s.c_tag: %s_from_string," % (INDENT, elem.class_name, + print("%s%s.c_tag: %s_from_string," % (INDENT, elem.class_name, pyify(elem.class_name))) print("}") print() - + def _element_by_tag(self): print("ELEMENT_BY_TAG = {") listed = [] @@ -1865,27 +1865,27 @@ class Schema(Complex): listed.append(lcen) print("}") print - + def out(self): for part in self.parts: if isinstance(part, Import): continue if part is None: continue - + elem = part.repr(self, "", {}, False) if elem: if isinstance(elem, PyAttributeGroup): self.attrgrp.append(elem) else: self.elems.append(elem) - + eldict = {} for elem in self.elems: eldict[elem.name] = elem #print(eldict.keys()) - + intro() for modul in self.add: print("from %s import *" % modul) @@ -1899,7 +1899,7 @@ class Schema(Complex): for defs in self.defs: print(defs) print - + exceptions = [] block = [] while self._do(eldict): @@ -1922,7 +1922,7 @@ class Schema(Complex): print(line) print("#", 70*'+') print - + for attrgrp in self.attrgrp: print("AG_%s = [" % attrgrp.name) for prop in attrgrp.properties[0]: @@ -1935,22 +1935,22 @@ class Schema(Complex): prop.type, prop.required)) print("]") print() - - self._element_from_string() + + self._element_from_string() self._element_by_tag() print print("def factory(tag, **kwargs):") print(" return ELEMENT_BY_TAG[tag](**kwargs)") print - - + + # ----------------------------------------------------------------------------- NAMESPACE_BASE = ["http://www.w3.org/2001/XMLSchema", "http://www.w3.org/2000/10/XMLSchema"] -_MAP = { +_MAP = { "element": Element, "complexType": ComplexType, "sequence": Sequence, @@ -1980,21 +1980,21 @@ _MAP = { "include": Include, "redefine": Redefine } - + ELEMENTFUNCTION = {} for nsp in NAMESPACE_BASE: for nskey, func in _MAP.items(): ELEMENTFUNCTION["{%s}%s" % (nsp, nskey)] = func - + def evaluate(typ, elem): try: return ELEMENTFUNCTION[typ](elem) except KeyError: print("Unknown type", typ) - - + + NS_MAP = "xmlns_map" def parse_nsmap(fil): @@ -2017,7 +2017,7 @@ def parse_nsmap(fil): def usage(): print("Usage: parse_xsd [-i <module:as>] xsd.file > module.py") - + def recursive_find_module(name, path=None): parts = name.split(".") @@ -2027,7 +2027,7 @@ def recursive_find_module(name, path=None): try: (fil, pathname, desc) = imp.find_module(part, path) except ImportError: - raise + raise mod_a = imp.load_module(name, fil, pathname, desc) sys.modules[name] = mod_a @@ -2141,7 +2141,8 @@ def read_schema(doc, add, defs, impo, modul, ignore, sdir): _schema.parts.extend(med) return _schema -def main(argv): +def main(): + argv = sys.argv[1:] try: opts, args = getopt.getopt(argv, "a:d:hi:I:s:", ["add=", "help", "import=", "defs="]) @@ -2186,5 +2187,6 @@ def main(argv): #print(schema.__dict__) schema.out() -if __name__ == "__main__": - main(sys.argv[1:]) + +if __name__ == "__main__": + main() diff --git a/tools/sync_attrmaps.py b/src/saml2/tools/sync_attrmaps.py index 4f0d3b67..4f0d3b67 100755..100644 --- a/tools/sync_attrmaps.py +++ b/src/saml2/tools/sync_attrmaps.py diff --git a/tools/update_metadata.sh b/src/saml2/tools/update_metadata.sh index cb1f0dff..cb1f0dff 100755 --- a/tools/update_metadata.sh +++ b/src/saml2/tools/update_metadata.sh diff --git a/src/saml2/tools/verify_metadata.py b/src/saml2/tools/verify_metadata.py new file mode 100644 index 00000000..d7102548 --- /dev/null +++ b/src/saml2/tools/verify_metadata.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +import argparse + +from saml2.attribute_converter import ac_factory +from saml2.httpbase import HTTPBase + +from saml2.sigver import _get_xmlsec_cryptobackend +from saml2.sigver import SecurityContext + +from saml2.mdstore import MetaDataFile +from saml2.mdstore import MetaDataExtern + +__author__ = 'rolandh' + +""" +A script that imports and verifies metadata. +""" + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-t', dest='type') + parser.add_argument('-u', dest='url') + parser.add_argument('-c', dest='cert') + parser.add_argument('-a', dest='attrsmap') + parser.add_argument('-o', dest='output') + parser.add_argument('-x', dest='xmlsec') + parser.add_argument('-i', dest='ignore_valid', action='store_true') + parser.add_argument(dest="item") + args = parser.parse_args() + + + metad = None + + if args.ignore_valid: + kwargs = {"check_validity": False} + else: + kwargs = {} + + if args.type == "local": + if args.cert and args.xmlsec: + crypto = _get_xmlsec_cryptobackend(args.xmlsec) + sc = SecurityContext(crypto) + metad = MetaDataFile(args.item, args.item, cert=args.cert, security=sc, + **kwargs) + else: + metad = MetaDataFile(args.item, args.item, **kwargs) + elif args.type == "external": + ATTRCONV = ac_factory(args.attrsmap) + httpc = HTTPBase() + crypto = _get_xmlsec_cryptobackend(args.xmlsec) + sc = SecurityContext(crypto) + metad = MetaDataExtern(ATTRCONV, args.url, sc, cert=args.cert, http=httpc, + **kwargs) + + if metad: + try: + metad.load() + except: + raise + else: + print("OK") + + +if __name__ == '__main__': + main() diff --git a/src/saml2/version.py b/src/saml2/version.py index 9b87aa11..fa5670a9 100644 --- a/src/saml2/version.py +++ b/src/saml2/version.py @@ -1,11 +1,12 @@ -import pkg_resources as _pkg_resources +try: + from importlib.metadata import version as _resolve_package_version +except ImportError: + from importlib_metadata import version as _resolve_package_version def _parse_version(): - data = _pkg_resources.get_distribution('pysaml2') - value = _pkg_resources.parse_version(data.version) + value = _resolve_package_version("pysaml2") return value -version_info = _parse_version() -version = str(version_info) +version = _parse_version() diff --git a/tests/test-requirements.txt b/tests/test-requirements.txt deleted file mode 100644 index e932ad7f..00000000 --- a/tests/test-requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -coverage -pyasn1 -pymongo >=3,<4 -pytest -pytest-cov -responses diff --git a/tools/data/requested_attributes.xsd b/tools/data/requested_attributes.xsd deleted file mode 100644 index b796f3d3..00000000 --- a/tools/data/requested_attributes.xsd +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xsd:schema - xmlns="http://eidas.europa.eu/saml-extensions" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" - xmlns:eidas="http://eidas.europa.eu/saml-extensions" - targetNamespace="http://eidas.europa.eu/saml-extensions" - elementFormDefault="qualified" - attributeFormDefault="unqualified" - version="1"> - <xsd:element name="RequestedAttributes" type="eidas:RequestedAttributesType"/> - <xsd:complexType name="RequestedAttributesType"> - <xsd:sequence> - <xsd:element minOccurs="0" maxOccurs="unbounded" ref="eidas:RequestedAttribute"/> - </xsd:sequence> - </xsd:complexType> - <xsd:element name="RequestedAttribute" type="eidas:RequestedAttributeType"/> - <xsd:complexType name="RequestedAttributeType"> - <xsd:sequence> - <xsd:element minOccurs="0" maxOccurs="unbounded" ref="saml2:AttributeValue" type="anyType"/> - </xsd:sequence> - <xsd:attribute name="Name" type="string" use="required"/> - <xsd:attribute name="NameFormat" type="anyURI" use="required"/> - <xsd:attribute name="FriendlyName" type="string" use="optional"/> - <xsd:anyAttribute namespace="##other" processContents="lax"/> - <xsd:attribute name="isRequired" type="boolean" use="optional"/> - </xsd:complexType> -</xsd:schema> diff --git a/tools/data/sp_type.xsd b/tools/data/sp_type.xsd deleted file mode 100644 index dbb1418d..00000000 --- a/tools/data/sp_type.xsd +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xsd:schema - xmlns="http://eidas.europa.eu/saml-extensions" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - targetNamespace="http://eidas.europa.eu/saml-extensions" - elementFormDefault="qualified" - attributeFormDefault="unqualified" - version="1"> - <xsd:element name="SPType" type="SPTypeType"/> - <xsd:simpleType name="SPTypeType"> - <xsd:restriction base="xsd:string"> - <xsd:enumeration value="public"/> - <xsd:enumeration value="private"/> - </xsd:restriction> - </xsd:simpleType> -</xsd:schema> diff --git a/tools/make_metadata.py b/tools/make_metadata.py deleted file mode 100755 index 147425fe..00000000 --- a/tools/make_metadata.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python -import argparse -import os -import sys -from saml2.metadata import entity_descriptor, metadata_tostring_fix -from saml2.metadata import entities_descriptor -from saml2.metadata import sign_entity_descriptor - -from saml2.sigver import security_context -from saml2.validate import valid_instance -from saml2.config import Config - -# ============================================================================= -# Script that creates a SAML2 metadata file from a pysaml2 entity configuration -# file -# ============================================================================= - -parser = argparse.ArgumentParser() -parser.add_argument('-v', dest='valid', - help="How long, in days, the metadata is valid from the time of creation") -parser.add_argument('-c', dest='cert', help='certificate') -parser.add_argument('-e', dest='ed', action='store_true', - help="Wrap the whole thing in an EntitiesDescriptor") -parser.add_argument('-i', dest='id', - help="The ID of the entities descriptor") -parser.add_argument('-k', dest='keyfile', - help="A file with a key to sign the metadata with") -parser.add_argument('-n', dest='name', default="") -parser.add_argument('-p', dest='path', - help="path to the configuration file") -parser.add_argument('-s', dest='sign', action='store_true', - help="sign the metadata") -parser.add_argument('-x', dest='xmlsec', - help="xmlsec binaries to be used for the signing") -parser.add_argument('-w', dest='wellknown', - help="Use wellknown namespace prefixes") -parser.add_argument(dest="config", nargs="+") -args = parser.parse_args() - -valid_for = 0 -nspair = {"xs": "http://www.w3.org/2001/XMLSchema"} -paths = [".", "/opt/local/bin"] - -if args.valid: - # translate into hours - valid_for = int(args.valid) * 24 - - -eds = [] -for filespec in args.config: - bas, fil = os.path.split(filespec) - if bas != "": - sys.path.insert(0, bas) - if fil.endswith(".py"): - fil = fil[:-3] - cnf = Config().load_file(fil) - if valid_for: - cnf.valid_for = valid_for - eds.append(entity_descriptor(cnf)) - -conf = Config() -conf.key_file = args.keyfile -conf.cert_file = args.cert -conf.debug = 1 -conf.xmlsec_binary = args.xmlsec -secc = security_context(conf) - -if args.id: - desc, xmldoc = entities_descriptor(eds, valid_for, args.name, args.id, - args.sign, secc) - valid_instance(desc) - xmldoc = metadata_tostring_fix(desc, nspair, xmldoc) - print(xmldoc.decode("utf-8")) -else: - for eid in eds: - if args.sign: - assert conf.key_file - assert conf.cert_file - eid, xmldoc = sign_entity_descriptor(eid, args.id, secc) - else: - xmldoc = None - - valid_instance(eid) - xmldoc = metadata_tostring_fix(eid, nspair, xmldoc) - print(xmldoc.decode("utf-8")) diff --git a/tools/mdexport.py b/tools/mdexport.py deleted file mode 100755 index a427af77..00000000 --- a/tools/mdexport.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -from saml2.sigver import _get_xmlsec_cryptobackend -from saml2.sigver import SecurityContext -from saml2.httpbase import HTTPBase - -from saml2 import saml -from saml2 import md -from saml2.attribute_converter import ac_factory -from saml2 import xmldsig -from saml2 import xmlenc - -import argparse - -from saml2.mdstore import MetaDataFile, MetaDataExtern, load_extensions - -__author__ = 'rolandh' - -""" -A script that imports and verifies metadata and then dumps it in a basic -dictionary format. -""" - -parser = argparse.ArgumentParser() -parser.add_argument('-t', dest='type') -parser.add_argument('-u', dest='url') -parser.add_argument('-c', dest='cert') -parser.add_argument('-a', dest='attrsmap') -parser.add_argument('-o', dest='output') -parser.add_argument('-x', dest='xmlsec') -parser.add_argument(dest="item") -args = parser.parse_args() - - -metad = None - -if args.type == "local": - metad = MetaDataFile(args.item, args.item) -elif args.type == "external": - ATTRCONV = ac_factory(args.attrsmap) - httpc = HTTPBase() - crypto = _get_xmlsec_cryptobackend(args.xmlsec) - sc = SecurityContext(crypto) - metad = MetaDataExtern(ATTRCONV, args.url, sc, cert=args.cert, http=httpc) - -if metad is not None: - metad.load() - txt = metad.dumps() - if args.output: - f = open(args.output, "w") - f.write(txt) - f.close() - else: - print(txt) diff --git a/tools/mdimport.py b/tools/mdimport.py deleted file mode 100755 index 4434b4ab..00000000 --- a/tools/mdimport.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -import time -from saml2.attribute_converter import ac_factory -from saml2.mdstore import MetaDataMD, MetaDataFile - -__author__ = 'rolandh' - -start = time.time() -for i in range(1, 10): - mdmd = MetaDataMD(ac_factory("../tests/attributemaps"), "swamid2.md") - mdmd.load() - - _ = mdmd.keys() - -print(time.time() - start) - -start = time.time() -for i in range(1, 10): - mdf = MetaDataFile(ac_factory("../tests/attributemaps"), - "../tests/swamid-2.0.xml") - mdf.load() - _ = mdf.keys() - -print(time.time() - start) diff --git a/tools/merge_metadata.py b/tools/merge_metadata.py deleted file mode 100755 index fc8430bf..00000000 --- a/tools/merge_metadata.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python -from saml2.sigver import _get_xmlsec_cryptobackend, SecurityContext -from saml2.httpbase import HTTPBase -from saml2.attribute_converter import ac_factory -import argparse - -from saml2.mdstore import MetaDataFile, MetaDataExtern, MetadataStore - -__author__ = 'rolandh' - -""" -A script that imports and verifies metadata. -""" - -parser = argparse.ArgumentParser() -parser.add_argument('-a', dest='attrsmap') -parser.add_argument('-o', dest='output', default="local") -parser.add_argument('-x', dest='xmlsec') -parser.add_argument('-i', dest='ignore_valid', action='store_true') -parser.add_argument(dest="conf") -args = parser.parse_args() - -metad = None - -# config file format -# -# local <local file name> -# remote <url> <local file name for certificate use to verify signature> -# -# for instance -# -#local metadata_sp_1.xml -#local InCommon-metadata.xml -#remote https://kalmar2.org/simplesaml/module.php/aggregator/?id=kalmarcentral2&set=saml2 kalmar2.pem -# - -ATTRCONV = ac_factory(args.attrsmap) - -mds = MetadataStore(None, None) - -for line in open(args.conf).readlines(): - line = line.strip() - if len(line) == 0: - continue - elif line[0] == "#": - continue - spec = line.split(" ") - - if args.ignore_valid: - kwargs = {"check_validity": False} - else: - kwargs = {} - - if spec[0] == "local": - metad = MetaDataFile(spec[1], spec[1], **kwargs) - elif spec[0] == "remote": - ATTRCONV = ac_factory(args.attrsmap) - httpc = HTTPBase() - crypto = _get_xmlsec_cryptobackend(args.xmlsec) - sc = SecurityContext(crypto, key_type="", cert_type="") - metad = MetaDataExtern(ATTRCONV, spec[1], sc, cert=spec[2], http=httpc, - **kwargs) - - if metad is not None: - try: - metad.load() - except: - raise - - mds.metadata[spec[1]] = metad - -print(mds.dumps(args.output)) - - diff --git a/tools/verify_metadata.py b/tools/verify_metadata.py deleted file mode 100755 index c13b7acc..00000000 --- a/tools/verify_metadata.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python - -import argparse - -from saml2.attribute_converter import ac_factory -from saml2.httpbase import HTTPBase - -from saml2.sigver import _get_xmlsec_cryptobackend -from saml2.sigver import SecurityContext - -from saml2.mdstore import MetaDataFile -from saml2.mdstore import MetaDataExtern - -__author__ = 'rolandh' - -""" -A script that imports and verifies metadata. -""" - - -parser = argparse.ArgumentParser() -parser.add_argument('-t', dest='type') -parser.add_argument('-u', dest='url') -parser.add_argument('-c', dest='cert') -parser.add_argument('-a', dest='attrsmap') -parser.add_argument('-o', dest='output') -parser.add_argument('-x', dest='xmlsec') -parser.add_argument('-i', dest='ignore_valid', action='store_true') -parser.add_argument(dest="item") -args = parser.parse_args() - - -metad = None - -if args.ignore_valid: - kwargs = {"check_validity": False} -else: - kwargs = {} - -if args.type == "local": - if args.cert and args.xmlsec: - crypto = _get_xmlsec_cryptobackend(args.xmlsec) - sc = SecurityContext(crypto) - metad = MetaDataFile(args.item, args.item, cert=args.cert, security=sc, - **kwargs) - else: - metad = MetaDataFile(args.item, args.item, **kwargs) -elif args.type == "external": - ATTRCONV = ac_factory(args.attrsmap) - httpc = HTTPBase() - crypto = _get_xmlsec_cryptobackend(args.xmlsec) - sc = SecurityContext(crypto) - metad = MetaDataExtern(ATTRCONV, args.url, sc, cert=args.cert, http=httpc, - **kwargs) - -if metad: - try: - metad.load() - except: - raise - else: - print("OK") - - - @@ -1,22 +1,22 @@ [tox] +isolated_build = true envlist = py36 py37 py38 py39 py310 - pypy3 [testenv] -deps = -r tests/test-requirements.txt +skip_install = true whitelist_externals = - tox xmlsec1 -commands = + poetry +commands_pre = + poetry install --with test,coverage xmlsec1 --version - python --version - pytest --version - tox --version - pip --version - pip freeze - pytest --cov={envsitepackagesdir}/saml2 -vvv -ra {posargs:tests/} + poetry run python --version + poetry run pytest --version + poetry run tox --version +commands = + poetry run pytest --import-mode=importlib --cov=saml2 --cov-report=term-missing |