summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Kanakarakis <ivan.kanak@gmail.com>2022-10-01 18:15:12 +0300
committerGitHub <noreply@github.com>2022-10-01 18:15:12 +0300
commit3007eb7231a6f4e4006614643e67354698900aef (patch)
treef81c39612412fc83201855e10ae0a81f1454b8ec
parent15503e13d39b8d0feb9c6164261340ca045914df (diff)
parent8827ab6bbe9ba85da927c9d2aef69bcebc1dc16a (diff)
downloadpysaml2-3007eb7231a6f4e4006614643e67354698900aef.tar.gz
Merge pull request #872 from IdentityPython/feat-poetry
Switch to poetry and add community guidelines
-rw-r--r--.editorconfig13
-rw-r--r--.github/issue_template.md24
-rw-r--r--.github/pull_request_template.md23
-rw-r--r--CONTRIBUTING.md149
-rw-r--r--DEVELOPERS.md102
-rw-r--r--INSTALL31
-rw-r--r--MANIFEST.in18
-rw-r--r--README.md104
-rw-r--r--README.rst64
-rw-r--r--RELEASE.md106
-rw-r--r--SECURITY.md40
-rw-r--r--VERSION1
-rw-r--r--docs/FIXUP_COMMITS.md115
-rw-r--r--poetry.lock1929
-rw-r--r--pyproject.toml237
-rw-r--r--release-howto.rst80
-rw-r--r--setup.cfg111
-rwxr-xr-xsetup.py13
-rw-r--r--src/saml2/tools/make_metadata.py89
-rw-r--r--src/saml2/tools/mdexport.py58
-rw-r--r--[-rwxr-xr-x]src/saml2/tools/mdexport_test.py (renamed from tools/mdexport_test.py)0
-rw-r--r--src/saml2/tools/mdimport.py29
-rw-r--r--src/saml2/tools/merge_metadata.py77
-rw-r--r--[-rwxr-xr-x]src/saml2/tools/parse_xsd2.py (renamed from tools/parse_xsd2.py)426
-rw-r--r--[-rwxr-xr-x]src/saml2/tools/sync_attrmaps.py (renamed from tools/sync_attrmaps.py)0
-rwxr-xr-xsrc/saml2/tools/update_metadata.sh (renamed from tools/update_metadata.sh)0
-rw-r--r--src/saml2/tools/verify_metadata.py66
-rw-r--r--src/saml2/version.py11
-rw-r--r--tests/test-requirements.txt6
-rw-r--r--tools/data/requested_attributes.xsd28
-rw-r--r--tools/data/sp_type.xsd16
-rwxr-xr-xtools/make_metadata.py85
-rwxr-xr-xtools/mdexport.py53
-rwxr-xr-xtools/mdimport.py24
-rwxr-xr-xtools/merge_metadata.py74
-rwxr-xr-xtools/verify_metadata.py65
-rw-r--r--tox.ini20
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")
-
-
-
diff --git a/tox.ini b/tox.ini
index dbaca41e..ad3a44a7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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