├── pyeupi ├── py.typed ├── __init__.py └── api.py ├── mypy.ini ├── MANIFEST.in ├── .readthedocs.yml ├── docs ├── source │ ├── pyeupi.rst │ ├── conf.py │ ├── index.rst │ └── examples.rst └── Makefile ├── .github ├── dependabot.yml └── workflows │ └── mypy.yml ├── README.md ├── setup.py ├── pyproject.toml ├── LICENSE ├── .gitignore └── poetry.lock /pyeupi/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | exclude = docs 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE 3 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | build: 3 | os: "ubuntu-22.04" 4 | tools: 5 | python: "3" 6 | 7 | python: 8 | install: 9 | - method: pip 10 | path: . 11 | extra_requirements: 12 | - docs 13 | 14 | formats: all 15 | -------------------------------------------------------------------------------- /docs/source/pyeupi.rst: -------------------------------------------------------------------------------- 1 | pyeupi package 2 | ============== 3 | 4 | :doc:`Back to main page` 5 | 6 | Submodules 7 | ---------- 8 | 9 | pyeupi.api module 10 | ----------------- 11 | 12 | .. automodule:: pyeupi.api 13 | :members: 14 | :undoc-members: 15 | :show-inheritance: 16 | 17 | Module contents 18 | --------------- 19 | 20 | .. automodule:: pyeupi 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | # Check for updates to GitHub Actions every weekday 17 | interval: "daily" 18 | -------------------------------------------------------------------------------- /.github/workflows/mypy.yml: -------------------------------------------------------------------------------- 1 | name: Python application 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | python-version: ["3.8", "3.9", "3.10", "3.11"] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up Python ${{matrix.python-version}} 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: ${{matrix.python-version}} 23 | 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip poetry 27 | poetry install 28 | 29 | - name: Run MyPy 30 | run: | 31 | poetry run mypy . 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Client API for EUPI 2 | =================== 3 | 4 | [![Build Status](https://travis-ci.org/Rafiot/PyEUPI.svg?branch=master)](https://travis-ci.org/Rafiot/PyEUPI) 5 | [![Documentation Status](https://readthedocs.org/projects/pyeupi/badge/?version=latest)](http://pyeupi.readthedocs.org/en/latest/?badge=latest) 6 | 7 | Client API to query the Phishing Initiative service. 8 | 9 | Installation 10 | ============ 11 | 12 | From the repository: 13 | 14 | ``` 15 | python setup.py install 16 | ``` 17 | 18 | Or via pip: 19 | 20 | ``` 21 | pip install pyeupi 22 | ``` 23 | 24 | Search queries 25 | ============== 26 | 27 | ``` 28 | from pyeupi import PyEUPI 29 | p = PyEUPI('') 30 | p.search(content='circl') 31 | p.search_url(tld='lu') 32 | p.search_submissions() 33 | ``` 34 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from setuptools import setup # type: ignore 4 | 5 | setup( 6 | name='pyeupi', 7 | version='1.1', 8 | author='Raphaël Vinot', 9 | author_email='raphael.vinot@circl.lu', 10 | maintainer='Raphaël Vinot', 11 | url='https://github.com/CIRCL/PyEUPI', 12 | description='Python API for the European Union anti-phishing initiative.', 13 | packages=['pyeupi'], 14 | entry_points={"console_scripts": ["pyeupi = pyeupi:main"]}, 15 | classifiers=[ 16 | 'License :: OSI Approved :: BSD License', 17 | 'Development Status :: 5 - Production/Stable', 18 | 'Environment :: Console', 19 | 'Intended Audience :: Science/Research', 20 | 'Intended Audience :: Telecommunications Industry', 21 | 'Programming Language :: Python :: 3', 22 | 'Topic :: Security', 23 | 'Topic :: Internet', 24 | ], 25 | install_requires=['requests'], 26 | package_data={'': ['*.md', '*.rst', 'LICENSE']}, 27 | ) 28 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "pyeupi" 3 | version = "1.3.0" 4 | description = "Python API for the European Union anti-phishing initiative." 5 | authors = ["Raphaël Vinot "] 6 | license = "BSD-3-Clause" 7 | repository = "https://github.com/CIRCL/PyEUPI" 8 | documentation = "https://pyeupi.readthedocs.io/en/latest/" 9 | 10 | readme = "README.md" 11 | 12 | classifiers = [ 13 | 'License :: OSI Approved :: BSD License', 14 | 'Development Status :: 5 - Production/Stable', 15 | 'Environment :: Console', 16 | 'Operating System :: POSIX :: Linux', 17 | 'Intended Audience :: Science/Research', 18 | 'Intended Audience :: Telecommunications Industry', 19 | 'Intended Audience :: Information Technology', 20 | 'Programming Language :: Python :: 3.8', 21 | 'Programming Language :: Python :: 3.9', 22 | 'Programming Language :: Python :: 3.10', 23 | 'Programming Language :: Python :: 3.11', 24 | 'Topic :: Security', 25 | 'Topic :: Internet', 26 | ] 27 | 28 | [tool.poetry.scripts] 29 | pyeupi = "pyeupi:main" 30 | 31 | [tool.poetry.dependencies] 32 | python = "^3.8" 33 | requests = "^2.31.0" 34 | Sphinx = [ 35 | {version = "<7.2", python = "<3.9", optional = true}, 36 | {version = "^7.2", python = ">=3.9", optional = true} 37 | ] 38 | 39 | [tool.poetry.group.dev.dependencies] 40 | mypy = "^1.9.0" 41 | types-requests = "^2.31.0.20240310" 42 | 43 | [tool.poetry.extras] 44 | docs = ["Sphinx"] 45 | 46 | [build-system] 47 | requires = ["poetry_core"] 48 | build-backend = "poetry.core.masonry.api" 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Raphaël Vinot 2 | Copyright (c) 2015 CIRCL - Computer Incident Response Center Luxembourg 3 | (c/o smile, security made in Lëtzebuerg, Groupement 4 | d'Intérêt Economique) 5 | 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'PyEUPI' 21 | copyright = '2023, CIRCL team' 22 | author = 'CIRCL team' 23 | 24 | # The full version, including alpha/beta/rc tags 25 | release = 'v1.3.0' 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | 'sphinx.ext.autodoc' 35 | ] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # List of patterns, relative to source directory, that match files and 41 | # directories to ignore when looking for source files. 42 | # This pattern also affects html_static_path and html_extra_path. 43 | exclude_patterns = [] 44 | 45 | 46 | # -- Options for HTML output ------------------------------------------------- 47 | 48 | # The theme to use for HTML and HTML Help pages. See the documentation for 49 | # a list of builtin themes. 50 | # 51 | html_theme = 'alabaster' 52 | 53 | html_theme_options = { 54 | 'page_width': '1200', 55 | 'body_min_width': '1200', 56 | } 57 | 58 | # Add any paths that contain custom static files (such as style sheets) here, 59 | # relative to this directory. They are copied after the builtin static files, 60 | # so a file named "default.css" will overwrite the builtin "default.css". 61 | html_static_path = ['_static'] 62 | -------------------------------------------------------------------------------- /pyeupi/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import json 5 | import argparse 6 | 7 | from .api import PyEUPI 8 | 9 | 10 | def main(): 11 | parser = argparse.ArgumentParser(description='Query Phishing Initiative service.') 12 | parser.add_argument("--url", default='https://phishing-initiative.fr', help='URL where the service is running (no path).') 13 | parser.add_argument("--key", required=True, help='Authorization key to query the service.') 14 | parser.add_argument('--debug', action='store_true', help='Enable debug') 15 | parser.add_argument('--not_verify', default=True, action='store_false', help='Verify SSL certificate') 16 | parser.add_argument('-f', '--full_text_search', default=False, action='store_true', help='Full text search on a partial URL') 17 | g = parser.add_mutually_exclusive_group(required=True) 18 | g.add_argument('-u', '--urls', help='Query a URL. Integer means URL ID, a string search the URL in the database. If 0, it returns the first page of urls.') 19 | g.add_argument('-s', '--submissions', type=int, help='Query your submissions (if 0, returns the first page of submissions)') 20 | g.add_argument('-p', '--post', help='URL to submit') 21 | g.add_argument('-t', '--tag', type=int, help='URL status. 0 = unknown, 1 = phishing, 2 = clean.') 22 | args = parser.parse_args() 23 | 24 | p = PyEUPI(args.key, args.url, args.not_verify, args.debug) 25 | if args.urls is not None: 26 | if args.urls.isdigit(): 27 | uid = int(args.urls) 28 | if args.urls == 0: 29 | response = p.get_url('') 30 | else: 31 | response = p.get_url(uid) 32 | elif args.full_text_search: 33 | response = p.search_url(url=args.urls, order_by='-first_seen') 34 | else: 35 | response = p.lookup(url=args.urls) 36 | 37 | elif args.submissions is not None: 38 | if args.submissions == 0: 39 | response = p.get_submission('') 40 | else: 41 | response = p.get_submission(args.submissions) 42 | 43 | elif args.post is not None: 44 | response = p.post_submission(args.post) 45 | elif args.tag is not None: 46 | response = p.search_url(tag=args.tag, order_by='-first_seen') 47 | 48 | print(json.dumps(response, ensure_ascii=False)) 49 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. PyEUPI documentation master file, created by 2 | sphinx-quickstart on Mon Apr 18 11:08:52 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to PyEUPI's documentation! 7 | ================================== 8 | 9 | PyEUPI is the Python API Client to query the `Phishing Initiative `_ service. 10 | 11 | It's used for several purposes, like: 12 | 13 | - Submit phishing URLs 14 | - Retrieve your submissions 15 | - Search for content in the URL's source code 16 | - Query information about the URL itself 17 | - Look for an URL qualification 18 | 19 | 20 | Installation 21 | ============ 22 | 23 | Available for Python3.8+ 24 | 25 | From the repository: 26 | 27 | :: 28 | 29 | pip install -e . 30 | 31 | Or via pip: 32 | 33 | :: 34 | 35 | pip install pyeupi 36 | 37 | Getting Started ! 38 | ================= 39 | 40 | Create your account 41 | -------------------- 42 | 43 | Before querying the API, you first need to create your **Phishing Initiative** account. 44 | 45 | | France: `phishing-initiative.fr `_ 46 | | Luxembourg: `phishing-initiative.lu `_ 47 | | Netherlands: `phishing-initiative.nl `_ 48 | 49 | Get your API key 50 | ---------------- 51 | 52 | Once you've done that, go to `the official API documentation `_ , there you will find your *rights* and your *API auth key*. 53 | 54 | Default users have a limited account. To ask for more rights, you can go to `the contact section `_ . 55 | 56 | Start Querying ! 57 | ---------------- 58 | 59 | :: 60 | 61 | from pyeupi import PyEUPI 62 | 63 | p = PyEUPI('Your API key') 64 | p.search(content='luxembourg') 65 | p.search_url(tld='lu') 66 | p.search_submissions(country='Luxembourg') 67 | 68 | 69 | API Reference 70 | ============= 71 | 72 | Please refer to `the official API documentation `_ (or see the :doc:`pyeupi`) to further information on the functions parameters. 73 | 74 | .. toctree:: 75 | :maxdepth: 2 76 | 77 | examples.rst 78 | pyeupi 79 | 80 | Indices and tables 81 | ================== 82 | 83 | * :ref:`genindex` 84 | * :ref:`modindex` 85 | * :ref:`search` 86 | 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /docs/source/examples.rst: -------------------------------------------------------------------------------- 1 | .. PyEUPI documentation master file, created by 2 | sphinx-quickstart on Fri Apr 15 14:10:29 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | :doc:`Back to main page` 7 | 8 | URLs database 9 | ============= 10 | 11 | Browse the Phishing Initiative URLs database. 12 | 13 | Query and Retrieve URLs 14 | ----------------------- 15 | 16 | GET /api/v1/urls/ 17 | 18 | .. function:: search_url() 19 | 20 | Perform a search on the URL's informations. 21 | 22 | Example: 23 | 24 | :: 25 | 26 | response = p.search_url(tag_label='phishing', 27 | first_seen_after='2015-02-01', 28 | order_by='first_seen' 29 | page_size=15) 30 | 31 | Get a specific URL 32 | ------------------ 33 | 34 | GET /api/v1/urls// 35 | 36 | .. function:: get_url(id) 37 | 38 | Retrieve an URL by its ID. 39 | 40 | Example: 41 | 42 | :: 43 | 44 | response = p.get_url(51325) 45 | 46 | Search an URL from its content 47 | ------------------------------ 48 | 49 | GET /api/v1/urls/search/ 50 | 51 | .. function:: search() 52 | 53 | Search for a string in the URL's source code 54 | 55 | Example: 56 | 57 | :: 58 | 59 | response = p.search(content='Click here', 60 | tag=0) 61 | 62 | 63 | Lookup an URL 64 | ------------- 65 | 66 | GET /api/v1/urls/lookup/ 67 | 68 | .. function:: lookup(url) 69 | 70 | Lookup the actual status of an URL. 71 | 72 | Example: 73 | 74 | :: 75 | 76 | response = p.lookup('http://www.paypal.com') 77 | 78 | 79 | Your submissions 80 | ================ 81 | 82 | Retrieve and Manage your own submissions. 83 | 84 | Retrieve submissions 85 | ------------------------- 86 | 87 | GET /api/v1/submissions/ 88 | 89 | .. function:: search_submissions() 90 | 91 | Return a list of submissions. 92 | 93 | Example: 94 | 95 | :: 96 | 97 | import datetime 98 | 99 | response = p.search_submissions(first_seen_after='2015-3-15', 100 | tld='com', 101 | asn=16276) 102 | 103 | Get a specific submission 104 | ------------------------- 105 | 106 | GET /api/v1/submissions/ 107 | 108 | .. function:: get_submission(id) 109 | 110 | Send back informations about the given submission. 111 | 112 | Example: 113 | 114 | :: 115 | 116 | response = p.get_submission(14527) 117 | 118 | Submit an URL 119 | ------------- 120 | 121 | POST /api/v1/submissions/ 122 | 123 | .. function:: post_submission(url) 124 | 125 | Submit an URL. 126 | 127 | Example: 128 | 129 | :: 130 | 131 | response = p.post_submission('http://lmpot-france.com/gouv/compte/impots-gouv.fr/file/index.php', 132 | comment='Received a spam today with this link!', 133 | notify=True) 134 | 135 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python `which sphinx-build` 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " epub3 to make an epub3" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | @echo " dummy to check syntax errors of document sources" 51 | 52 | .PHONY: clean 53 | clean: 54 | rm -rf $(BUILDDIR)/* 55 | 56 | .PHONY: html 57 | html: 58 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 61 | 62 | .PHONY: dirhtml 63 | dirhtml: 64 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 65 | @echo 66 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 67 | 68 | .PHONY: singlehtml 69 | singlehtml: 70 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 71 | @echo 72 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 73 | 74 | .PHONY: pickle 75 | pickle: 76 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 77 | @echo 78 | @echo "Build finished; now you can process the pickle files." 79 | 80 | .PHONY: json 81 | json: 82 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 83 | @echo 84 | @echo "Build finished; now you can process the JSON files." 85 | 86 | .PHONY: htmlhelp 87 | htmlhelp: 88 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 89 | @echo 90 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 91 | ".hhp project file in $(BUILDDIR)/htmlhelp." 92 | 93 | .PHONY: qthelp 94 | qthelp: 95 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 96 | @echo 97 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 98 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 99 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyEUPI.qhcp" 100 | @echo "To view the help file:" 101 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyEUPI.qhc" 102 | 103 | .PHONY: applehelp 104 | applehelp: 105 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 106 | @echo 107 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 108 | @echo "N.B. You won't be able to view it unless you put it in" \ 109 | "~/Library/Documentation/Help or install it in your application" \ 110 | "bundle." 111 | 112 | .PHONY: devhelp 113 | devhelp: 114 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 115 | @echo 116 | @echo "Build finished." 117 | @echo "To view the help file:" 118 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PyEUPI" 119 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyEUPI" 120 | @echo "# devhelp" 121 | 122 | .PHONY: epub 123 | epub: 124 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 125 | @echo 126 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 127 | 128 | .PHONY: epub3 129 | epub3: 130 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 131 | @echo 132 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 133 | 134 | .PHONY: latex 135 | latex: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo 138 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 139 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 140 | "(use \`make latexpdf' here to do that automatically)." 141 | 142 | .PHONY: latexpdf 143 | latexpdf: 144 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 145 | @echo "Running LaTeX files through pdflatex..." 146 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 147 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 148 | 149 | .PHONY: latexpdfja 150 | latexpdfja: 151 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 152 | @echo "Running LaTeX files through platex and dvipdfmx..." 153 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 154 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 155 | 156 | .PHONY: text 157 | text: 158 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 159 | @echo 160 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 161 | 162 | .PHONY: man 163 | man: 164 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 165 | @echo 166 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 167 | 168 | .PHONY: texinfo 169 | texinfo: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo 172 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 173 | @echo "Run \`make' in that directory to run these through makeinfo" \ 174 | "(use \`make info' here to do that automatically)." 175 | 176 | .PHONY: info 177 | info: 178 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 179 | @echo "Running Texinfo files through makeinfo..." 180 | make -C $(BUILDDIR)/texinfo info 181 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 182 | 183 | .PHONY: gettext 184 | gettext: 185 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 186 | @echo 187 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 188 | 189 | .PHONY: changes 190 | changes: 191 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 192 | @echo 193 | @echo "The overview file is in $(BUILDDIR)/changes." 194 | 195 | .PHONY: linkcheck 196 | linkcheck: 197 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 198 | @echo 199 | @echo "Link check complete; look for any errors in the above output " \ 200 | "or in $(BUILDDIR)/linkcheck/output.txt." 201 | 202 | .PHONY: doctest 203 | doctest: 204 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 205 | @echo "Testing of doctests in the sources finished, look at the " \ 206 | "results in $(BUILDDIR)/doctest/output.txt." 207 | 208 | .PHONY: coverage 209 | coverage: 210 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 211 | @echo "Testing of coverage in the sources finished, look at the " \ 212 | "results in $(BUILDDIR)/coverage/python.txt." 213 | 214 | .PHONY: xml 215 | xml: 216 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 217 | @echo 218 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 219 | 220 | .PHONY: pseudoxml 221 | pseudoxml: 222 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 223 | @echo 224 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 225 | 226 | .PHONY: dummy 227 | dummy: 228 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy 229 | @echo 230 | @echo "Build finished. Dummy builder generates no files." 231 | -------------------------------------------------------------------------------- /pyeupi/api.py: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import json 5 | from typing import List, Dict, Any, Optional, Union, Tuple 6 | from urllib.parse import urljoin, urlencode 7 | 8 | import requests 9 | 10 | 11 | class EUPIException(Exception): 12 | """Catch-all exception for errors emitted by this library.""" 13 | pass 14 | 15 | 16 | class InvalidSearchQuery(EUPIException): 17 | """Catch all for all Invalid search query""" 18 | pass 19 | 20 | 21 | class PyEUPI(object): 22 | 23 | def __init__(self, auth_token: str, url: str='https://phishing-initiative.eu', 24 | verify_ssl: bool=True, debug: bool=False): 25 | self.url: str = url 26 | self.debug: bool = debug 27 | 28 | self.session: requests.Session = requests.Session() 29 | self.session.verify = verify_ssl 30 | self.session.headers.update({'Accept': 'application/json', 31 | 'Authorization': 'Token {}'.format(auth_token)}) 32 | 33 | def _get(self, url: str, path: str, query: List=[]) -> Dict[str, Any]: 34 | to_return: Dict[str, Any] = {} 35 | full_url = urljoin(url, path.format(urlencode(query))) 36 | if self.debug: 37 | to_return.update({'url': full_url}) 38 | response = self.session.get(full_url) 39 | try: 40 | r = response.json() 41 | if isinstance(r, list): 42 | # The output if the API isn't consistant 43 | to_return.update({'results': r}) 44 | to_return.update({'count': len(r)}) 45 | else: 46 | to_return.update(r) 47 | except Exception as e: 48 | # If the key doesn't have the rights 49 | # the API returns a HTML page, normalizing. 50 | to_return.update({"status": 400, 51 | "message": "Probably unauthorized key, enable debug if needed"}) 52 | if self.debug: 53 | to_return.update({'details': response.text}) 54 | to_return.update({'exception': e}) 55 | return to_return 56 | 57 | def _post(self, url: str, path: str, data: Any) -> Dict[str, Any]: 58 | to_return: Dict[str, Any] = {} 59 | full_url = urljoin(url, path) 60 | if self.debug: 61 | to_return.update({'url': full_url}) 62 | to_return.update({'query': data}) 63 | response = self.session.post(full_url, data=json.dumps(data), headers={'Content-Type': 'application/json'}) 64 | try: 65 | to_return.update(response.json()) 66 | except Exception: 67 | # If the key doesn't have the rights 68 | # the API returns a HTML page, normalizing. 69 | to_return.update({"status": 400, 70 | "message": "Probably unauthorized key, enable debug if needed"}) 71 | if self.debug: 72 | to_return.update({'details': response.text}) 73 | return to_return 74 | 75 | def _generic_search_parameters(self, url: Optional[str]=None, 76 | tag: Optional[int]=None, 77 | tag_label: Optional[str]=None, 78 | page: Optional[int]=None, 79 | page_size: Optional[int]=None) -> List[Tuple[str, Union[str, int]]]: 80 | query: List[Tuple[str, Union[str, int]]] = [] 81 | if page: 82 | query.append(('page', page)) 83 | if page_size: 84 | if page_size > 50: 85 | InvalidSearchQuery('Page size must be <= 50') 86 | query.append(('page_size', page_size)) 87 | if url: 88 | query.append(('url', url)) 89 | if tag is not None: 90 | if tag not in [0, 1, 2]: 91 | raise InvalidSearchQuery(f'Tag can only be in 0 (unknown), 1 (phishing), 2 (clean) - {tag}') 92 | query.append(('tag', tag)) 93 | if tag_label: 94 | labels = ["unknown", "phishing", "clean"] 95 | if tag_label not in labels: 96 | raise InvalidSearchQuery('Tag label can only be in {} - {}'.format(', '.join(labels), tag_label)) 97 | query.append(('tag_label', tag_label)) 98 | return query 99 | 100 | def _expanded_search_parameters(self, 101 | tag: Optional[int]=None, 102 | tag_label: Optional[str]=None, 103 | url: Optional[str]=None, 104 | url_exact: Optional[str]=None, 105 | country: Optional[str]=None, 106 | asn: Optional[int]=None, 107 | domain: Optional[str]=None, 108 | redirector: Optional[str]=None, 109 | language: Optional[str]=None, 110 | tld: Optional[str]=None, 111 | ip_address: Optional[str]=None, 112 | ip_range: Optional[str]=None, 113 | first_seen_before: Optional[str]=None, 114 | first_seen_after: Optional[str]=None, 115 | last_tagged_before: Optional[str]=None, 116 | last_tagged_after: Optional[str]=None, 117 | order_by: Optional[str]=None, 118 | page: Optional[int]=None, 119 | page_size: Optional[int]=None) -> List[Tuple[str, Union[str, int]]]: 120 | 121 | query = self._generic_search_parameters(url=url, tag=tag, tag_label=tag_label, page=page, page_size=page_size) 122 | if url_exact: 123 | query.append(('url_exact', url_exact)) 124 | if country: 125 | query.append(('country', country)) 126 | if asn: 127 | query.append(('asn', asn)) 128 | if domain: 129 | query.append(('domain', domain)) 130 | if redirector: 131 | query.append(('redirector', redirector)) 132 | if language: 133 | query.append(('language', language)) 134 | if tld: 135 | query.append(('tld', tld)) 136 | if ip_address: 137 | query.append(('ip_address', ip_address)) 138 | if ip_range: 139 | query.append(('ip_range', ip_range)) 140 | if first_seen_before: 141 | query.append(('first_seen_before', first_seen_before)) 142 | if first_seen_after: 143 | query.append(('first_seen_after', first_seen_after)) 144 | if last_tagged_before: 145 | query.append(('last_tagged_before', last_tagged_before)) 146 | if last_tagged_after: 147 | query.append(('last_tagged_after', last_tagged_after)) 148 | if order_by: 149 | order_opts = ["first_seen", "url", "-first_seen", "-url"] 150 | if order_by not in order_opts: 151 | raise InvalidSearchQuery('order_by can only be in {} - {}'.format(', '.join(order_opts), order_by)) 152 | query.append(('order_by', order_by)) 153 | return query 154 | 155 | def search_url(self, 156 | tag: Optional[int]=None, 157 | tag_label: Optional[str]=None, 158 | url: Optional[str]=None, 159 | url_exact: Optional[str]=None, 160 | country: Optional[str]=None, 161 | asn: Optional[int]=None, 162 | domain: Optional[str]=None, 163 | redirector: Optional[str]=None, 164 | language: Optional[str]=None, 165 | tld: Optional[str]=None, 166 | ip_address: Optional[str]=None, 167 | ip_range: Optional[str]=None, 168 | first_seen_before: Optional[str]=None, 169 | first_seen_after: Optional[str]=None, 170 | last_tagged_before: Optional[str]=None, 171 | last_tagged_after: Optional[str]=None, 172 | order_by: Optional[str]=None, 173 | page: Optional[int]=None, 174 | page_size: int=50) -> Dict[str, Any]: 175 | path = '/api/v1/urls/?{}' 176 | query = self._expanded_search_parameters( 177 | tag=tag, tag_label=tag_label, url=url, url_exact=url_exact, 178 | country=country, asn=asn, domain=domain, redirector=redirector, tld=tld, 179 | language=language, ip_address=ip_address, ip_range=ip_range, 180 | first_seen_before=first_seen_before, 181 | first_seen_after=first_seen_after, 182 | last_tagged_before=last_tagged_before, 183 | last_tagged_after=last_tagged_after, 184 | order_by=order_by, page=page, page_size=page_size) 185 | return self._get(self.url, path, query) 186 | 187 | def get_url(self, itemid: int) -> Dict[str, Any]: 188 | return self._get(self.url, f'/api/v1/urls/{itemid}/') 189 | 190 | def search(self, url: Optional[str]=None, content: Optional[str]=None, 191 | tag_label: Optional[str]=None, tag: Optional[int]=None, 192 | first_seen_after: Optional[str]=None, 193 | first_seen_before: Optional[str]=None, 194 | page: Optional[int]=None, page_size: Optional[int]=50) -> Dict[str, Any]: 195 | path = '/api/v1/urls/search/?{}' 196 | query = self._generic_search_parameters(url=url, tag=tag, tag_label=tag_label, page=page, page_size=page_size) 197 | if first_seen_after: 198 | # TODO: use datetime.isoformat() 199 | query.append(('first_seen_after', first_seen_after)) 200 | if first_seen_before: 201 | # TODO: use datetime.isoformat() 202 | query.append(('first_seen_before', first_seen_before)) 203 | if content: 204 | query.append(('content', content)) 205 | return self._get(self.url, path, query) 206 | 207 | def lookup(self, url: str) -> Dict[str, Any]: 208 | return self._get(self.url, '/api/v1/urls/lookup/?{}', [('url', url)]) 209 | 210 | # ############# Submissions ############# 211 | 212 | def search_submissions(self, submitted_before: Optional[str]=None, 213 | submitted_after: Optional[str]=None, 214 | order_by: Optional[str]=None, 215 | tag: Optional[int]=None, 216 | tag_label: Optional[str]=None, 217 | url: Optional[str]=None, 218 | url_exact: Optional[str]=None, 219 | country: Optional[str]=None, 220 | asn: Optional[int]=None, 221 | domain: Optional[str]=None, 222 | redirector: Optional[str]=None, 223 | language: Optional[str]=None, 224 | tld: Optional[str]=None, 225 | ip_address: Optional[str]=None, 226 | ip_range: Optional[str]=None, 227 | first_seen_before: Optional[str]=None, 228 | first_seen_after: Optional[str]=None, 229 | last_tagged_before: Optional[str]=None, 230 | last_tagged_after: Optional[str]=None, 231 | page: Optional[int]=None, 232 | page_size: int=50) -> Dict[str, Any]: 233 | path = '/api/v1/submissions/?{}' 234 | query = self._expanded_search_parameters( 235 | tag=tag, tag_label=tag_label, url=url, url_exact=url_exact, 236 | country=country, asn=asn, domain=domain, redirector=redirector, 237 | language=language, tld=tld, ip_address=ip_address, ip_range=ip_range, 238 | first_seen_before=first_seen_before, 239 | first_seen_after=first_seen_after, 240 | last_tagged_before=last_tagged_before, 241 | last_tagged_after=last_tagged_after, 242 | order_by=order_by, page=page, page_size=page_size) 243 | if submitted_before: 244 | query.append(('submitted_before', submitted_before)) 245 | if submitted_after: 246 | query.append(('submitted_after', submitted_after)) 247 | return self._get(self.url, path, query) 248 | 249 | def get_submission(self, itemid: int) -> Dict[str, Any]: 250 | return self._get(self.url, f'/api/v1/submissions/{itemid}/') 251 | 252 | def post_submission(self, url: str, comment: str='', notify: bool=False, tag: int=0) -> Dict[str, Any]: 253 | if tag not in [0, 1, 2]: 254 | raise Exception(f'Tag can only be in 0 (unknown), 1 (phishing), 2 (clean) - {tag}') 255 | query = {'url': url, 'comment': comment, 'notify': notify, 'tag': tag} 256 | path = '/api/v1/submissions/' 257 | return self._post(self.url, path, query) 258 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "alabaster" 5 | version = "0.7.13" 6 | description = "A configurable sidebar-enabled Sphinx theme" 7 | optional = true 8 | python-versions = ">=3.6" 9 | files = [ 10 | {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, 11 | {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, 12 | ] 13 | 14 | [[package]] 15 | name = "alabaster" 16 | version = "0.7.16" 17 | description = "A light, configurable Sphinx theme" 18 | optional = true 19 | python-versions = ">=3.9" 20 | files = [ 21 | {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, 22 | {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, 23 | ] 24 | 25 | [[package]] 26 | name = "babel" 27 | version = "2.14.0" 28 | description = "Internationalization utilities" 29 | optional = true 30 | python-versions = ">=3.7" 31 | files = [ 32 | {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, 33 | {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, 34 | ] 35 | 36 | [package.dependencies] 37 | pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} 38 | 39 | [package.extras] 40 | dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] 41 | 42 | [[package]] 43 | name = "certifi" 44 | version = "2024.2.2" 45 | description = "Python package for providing Mozilla's CA Bundle." 46 | optional = false 47 | python-versions = ">=3.6" 48 | files = [ 49 | {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, 50 | {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, 51 | ] 52 | 53 | [[package]] 54 | name = "charset-normalizer" 55 | version = "3.3.2" 56 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 57 | optional = false 58 | python-versions = ">=3.7.0" 59 | files = [ 60 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, 61 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, 62 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, 63 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, 64 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, 65 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, 66 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, 67 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, 68 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, 69 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, 70 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, 71 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, 72 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, 73 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, 74 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, 75 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, 76 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, 77 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, 78 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, 79 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, 80 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, 81 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, 82 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, 83 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, 84 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, 85 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, 86 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, 87 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, 88 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, 89 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, 90 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, 91 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, 92 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, 93 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, 94 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, 95 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, 96 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, 97 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, 98 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, 99 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, 100 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, 101 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, 102 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, 103 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, 104 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, 105 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, 106 | {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, 107 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, 108 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, 109 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, 110 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, 111 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, 112 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, 113 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, 114 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, 115 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, 116 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, 117 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, 118 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, 119 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, 120 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, 121 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, 122 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, 123 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, 124 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, 125 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, 126 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, 127 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, 128 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, 129 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, 130 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, 131 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, 132 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, 133 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, 134 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, 135 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, 136 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, 137 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, 138 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, 139 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, 140 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, 141 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, 142 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, 143 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, 144 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, 145 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, 146 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, 147 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, 148 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, 149 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, 150 | ] 151 | 152 | [[package]] 153 | name = "colorama" 154 | version = "0.4.6" 155 | description = "Cross-platform colored terminal text." 156 | optional = true 157 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 158 | files = [ 159 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 160 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 161 | ] 162 | 163 | [[package]] 164 | name = "docutils" 165 | version = "0.20.1" 166 | description = "Docutils -- Python Documentation Utilities" 167 | optional = true 168 | python-versions = ">=3.7" 169 | files = [ 170 | {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, 171 | {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, 172 | ] 173 | 174 | [[package]] 175 | name = "idna" 176 | version = "3.6" 177 | description = "Internationalized Domain Names in Applications (IDNA)" 178 | optional = false 179 | python-versions = ">=3.5" 180 | files = [ 181 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 182 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 183 | ] 184 | 185 | [[package]] 186 | name = "imagesize" 187 | version = "1.4.1" 188 | description = "Getting image size from png/jpeg/jpeg2000/gif file" 189 | optional = true 190 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 191 | files = [ 192 | {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, 193 | {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, 194 | ] 195 | 196 | [[package]] 197 | name = "importlib-metadata" 198 | version = "7.0.2" 199 | description = "Read metadata from Python packages" 200 | optional = true 201 | python-versions = ">=3.8" 202 | files = [ 203 | {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, 204 | {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, 205 | ] 206 | 207 | [package.dependencies] 208 | zipp = ">=0.5" 209 | 210 | [package.extras] 211 | docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 212 | perf = ["ipython"] 213 | testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] 214 | 215 | [[package]] 216 | name = "jinja2" 217 | version = "3.1.3" 218 | description = "A very fast and expressive template engine." 219 | optional = true 220 | python-versions = ">=3.7" 221 | files = [ 222 | {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, 223 | {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, 224 | ] 225 | 226 | [package.dependencies] 227 | MarkupSafe = ">=2.0" 228 | 229 | [package.extras] 230 | i18n = ["Babel (>=2.7)"] 231 | 232 | [[package]] 233 | name = "markupsafe" 234 | version = "2.1.5" 235 | description = "Safely add untrusted strings to HTML/XML markup." 236 | optional = true 237 | python-versions = ">=3.7" 238 | files = [ 239 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, 240 | {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, 241 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, 242 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, 243 | {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, 244 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, 245 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, 246 | {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, 247 | {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, 248 | {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, 249 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, 250 | {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, 251 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, 252 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, 253 | {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, 254 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, 255 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, 256 | {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, 257 | {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, 258 | {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, 259 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, 260 | {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, 261 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, 262 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, 263 | {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, 264 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, 265 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, 266 | {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, 267 | {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, 268 | {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, 269 | {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, 270 | {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, 271 | {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, 272 | {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, 273 | {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, 274 | {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, 275 | {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, 276 | {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, 277 | {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, 278 | {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, 279 | {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, 280 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, 281 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, 282 | {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, 283 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, 284 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, 285 | {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, 286 | {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, 287 | {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, 288 | {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, 289 | {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, 290 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, 291 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, 292 | {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, 293 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, 294 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, 295 | {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, 296 | {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, 297 | {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, 298 | {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, 299 | ] 300 | 301 | [[package]] 302 | name = "mypy" 303 | version = "1.9.0" 304 | description = "Optional static typing for Python" 305 | optional = false 306 | python-versions = ">=3.8" 307 | files = [ 308 | {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, 309 | {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, 310 | {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, 311 | {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, 312 | {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, 313 | {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, 314 | {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, 315 | {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, 316 | {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, 317 | {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, 318 | {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, 319 | {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, 320 | {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, 321 | {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, 322 | {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, 323 | {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, 324 | {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, 325 | {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, 326 | {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, 327 | {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, 328 | {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, 329 | {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, 330 | {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, 331 | {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, 332 | {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, 333 | {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, 334 | {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, 335 | ] 336 | 337 | [package.dependencies] 338 | mypy-extensions = ">=1.0.0" 339 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 340 | typing-extensions = ">=4.1.0" 341 | 342 | [package.extras] 343 | dmypy = ["psutil (>=4.0)"] 344 | install-types = ["pip"] 345 | mypyc = ["setuptools (>=50)"] 346 | reports = ["lxml"] 347 | 348 | [[package]] 349 | name = "mypy-extensions" 350 | version = "1.0.0" 351 | description = "Type system extensions for programs checked with the mypy type checker." 352 | optional = false 353 | python-versions = ">=3.5" 354 | files = [ 355 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 356 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 357 | ] 358 | 359 | [[package]] 360 | name = "packaging" 361 | version = "24.0" 362 | description = "Core utilities for Python packages" 363 | optional = true 364 | python-versions = ">=3.7" 365 | files = [ 366 | {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, 367 | {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, 368 | ] 369 | 370 | [[package]] 371 | name = "pygments" 372 | version = "2.17.2" 373 | description = "Pygments is a syntax highlighting package written in Python." 374 | optional = true 375 | python-versions = ">=3.7" 376 | files = [ 377 | {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, 378 | {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, 379 | ] 380 | 381 | [package.extras] 382 | plugins = ["importlib-metadata"] 383 | windows-terminal = ["colorama (>=0.4.6)"] 384 | 385 | [[package]] 386 | name = "pytz" 387 | version = "2024.1" 388 | description = "World timezone definitions, modern and historical" 389 | optional = true 390 | python-versions = "*" 391 | files = [ 392 | {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, 393 | {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, 394 | ] 395 | 396 | [[package]] 397 | name = "requests" 398 | version = "2.31.0" 399 | description = "Python HTTP for Humans." 400 | optional = false 401 | python-versions = ">=3.7" 402 | files = [ 403 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 404 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 405 | ] 406 | 407 | [package.dependencies] 408 | certifi = ">=2017.4.17" 409 | charset-normalizer = ">=2,<4" 410 | idna = ">=2.5,<4" 411 | urllib3 = ">=1.21.1,<3" 412 | 413 | [package.extras] 414 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 415 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 416 | 417 | [[package]] 418 | name = "snowballstemmer" 419 | version = "2.2.0" 420 | description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." 421 | optional = true 422 | python-versions = "*" 423 | files = [ 424 | {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, 425 | {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, 426 | ] 427 | 428 | [[package]] 429 | name = "sphinx" 430 | version = "7.1.2" 431 | description = "Python documentation generator" 432 | optional = true 433 | python-versions = ">=3.8" 434 | files = [ 435 | {file = "sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"}, 436 | {file = "sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f"}, 437 | ] 438 | 439 | [package.dependencies] 440 | alabaster = ">=0.7,<0.8" 441 | babel = ">=2.9" 442 | colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} 443 | docutils = ">=0.18.1,<0.21" 444 | imagesize = ">=1.3" 445 | importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} 446 | Jinja2 = ">=3.0" 447 | packaging = ">=21.0" 448 | Pygments = ">=2.13" 449 | requests = ">=2.25.0" 450 | snowballstemmer = ">=2.0" 451 | sphinxcontrib-applehelp = "*" 452 | sphinxcontrib-devhelp = "*" 453 | sphinxcontrib-htmlhelp = ">=2.0.0" 454 | sphinxcontrib-jsmath = "*" 455 | sphinxcontrib-qthelp = "*" 456 | sphinxcontrib-serializinghtml = ">=1.1.5" 457 | 458 | [package.extras] 459 | docs = ["sphinxcontrib-websupport"] 460 | lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] 461 | test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] 462 | 463 | [[package]] 464 | name = "sphinx" 465 | version = "7.2.6" 466 | description = "Python documentation generator" 467 | optional = true 468 | python-versions = ">=3.9" 469 | files = [ 470 | {file = "sphinx-7.2.6-py3-none-any.whl", hash = "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560"}, 471 | {file = "sphinx-7.2.6.tar.gz", hash = "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"}, 472 | ] 473 | 474 | [package.dependencies] 475 | alabaster = ">=0.7,<0.8" 476 | babel = ">=2.9" 477 | colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} 478 | docutils = ">=0.18.1,<0.21" 479 | imagesize = ">=1.3" 480 | importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} 481 | Jinja2 = ">=3.0" 482 | packaging = ">=21.0" 483 | Pygments = ">=2.14" 484 | requests = ">=2.25.0" 485 | snowballstemmer = ">=2.0" 486 | sphinxcontrib-applehelp = "*" 487 | sphinxcontrib-devhelp = "*" 488 | sphinxcontrib-htmlhelp = ">=2.0.0" 489 | sphinxcontrib-jsmath = "*" 490 | sphinxcontrib-qthelp = "*" 491 | sphinxcontrib-serializinghtml = ">=1.1.9" 492 | 493 | [package.extras] 494 | docs = ["sphinxcontrib-websupport"] 495 | lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] 496 | test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"] 497 | 498 | [[package]] 499 | name = "sphinxcontrib-applehelp" 500 | version = "1.0.4" 501 | description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" 502 | optional = true 503 | python-versions = ">=3.8" 504 | files = [ 505 | {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, 506 | {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, 507 | ] 508 | 509 | [package.extras] 510 | lint = ["docutils-stubs", "flake8", "mypy"] 511 | test = ["pytest"] 512 | 513 | [[package]] 514 | name = "sphinxcontrib-applehelp" 515 | version = "1.0.8" 516 | description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" 517 | optional = true 518 | python-versions = ">=3.9" 519 | files = [ 520 | {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, 521 | {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, 522 | ] 523 | 524 | [package.extras] 525 | lint = ["docutils-stubs", "flake8", "mypy"] 526 | standalone = ["Sphinx (>=5)"] 527 | test = ["pytest"] 528 | 529 | [[package]] 530 | name = "sphinxcontrib-devhelp" 531 | version = "1.0.2" 532 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." 533 | optional = true 534 | python-versions = ">=3.5" 535 | files = [ 536 | {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, 537 | {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, 538 | ] 539 | 540 | [package.extras] 541 | lint = ["docutils-stubs", "flake8", "mypy"] 542 | test = ["pytest"] 543 | 544 | [[package]] 545 | name = "sphinxcontrib-devhelp" 546 | version = "1.0.6" 547 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" 548 | optional = true 549 | python-versions = ">=3.9" 550 | files = [ 551 | {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, 552 | {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, 553 | ] 554 | 555 | [package.extras] 556 | lint = ["docutils-stubs", "flake8", "mypy"] 557 | standalone = ["Sphinx (>=5)"] 558 | test = ["pytest"] 559 | 560 | [[package]] 561 | name = "sphinxcontrib-htmlhelp" 562 | version = "2.0.1" 563 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" 564 | optional = true 565 | python-versions = ">=3.8" 566 | files = [ 567 | {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, 568 | {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, 569 | ] 570 | 571 | [package.extras] 572 | lint = ["docutils-stubs", "flake8", "mypy"] 573 | test = ["html5lib", "pytest"] 574 | 575 | [[package]] 576 | name = "sphinxcontrib-htmlhelp" 577 | version = "2.0.5" 578 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" 579 | optional = true 580 | python-versions = ">=3.9" 581 | files = [ 582 | {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, 583 | {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, 584 | ] 585 | 586 | [package.extras] 587 | lint = ["docutils-stubs", "flake8", "mypy"] 588 | standalone = ["Sphinx (>=5)"] 589 | test = ["html5lib", "pytest"] 590 | 591 | [[package]] 592 | name = "sphinxcontrib-jsmath" 593 | version = "1.0.1" 594 | description = "A sphinx extension which renders display math in HTML via JavaScript" 595 | optional = true 596 | python-versions = ">=3.5" 597 | files = [ 598 | {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, 599 | {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, 600 | ] 601 | 602 | [package.extras] 603 | test = ["flake8", "mypy", "pytest"] 604 | 605 | [[package]] 606 | name = "sphinxcontrib-qthelp" 607 | version = "1.0.3" 608 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." 609 | optional = true 610 | python-versions = ">=3.5" 611 | files = [ 612 | {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, 613 | {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, 614 | ] 615 | 616 | [package.extras] 617 | lint = ["docutils-stubs", "flake8", "mypy"] 618 | test = ["pytest"] 619 | 620 | [[package]] 621 | name = "sphinxcontrib-qthelp" 622 | version = "1.0.7" 623 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" 624 | optional = true 625 | python-versions = ">=3.9" 626 | files = [ 627 | {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, 628 | {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, 629 | ] 630 | 631 | [package.extras] 632 | lint = ["docutils-stubs", "flake8", "mypy"] 633 | standalone = ["Sphinx (>=5)"] 634 | test = ["pytest"] 635 | 636 | [[package]] 637 | name = "sphinxcontrib-serializinghtml" 638 | version = "1.1.5" 639 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." 640 | optional = true 641 | python-versions = ">=3.5" 642 | files = [ 643 | {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, 644 | {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, 645 | ] 646 | 647 | [package.extras] 648 | lint = ["docutils-stubs", "flake8", "mypy"] 649 | test = ["pytest"] 650 | 651 | [[package]] 652 | name = "sphinxcontrib-serializinghtml" 653 | version = "1.1.10" 654 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" 655 | optional = true 656 | python-versions = ">=3.9" 657 | files = [ 658 | {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, 659 | {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, 660 | ] 661 | 662 | [package.extras] 663 | lint = ["docutils-stubs", "flake8", "mypy"] 664 | standalone = ["Sphinx (>=5)"] 665 | test = ["pytest"] 666 | 667 | [[package]] 668 | name = "tomli" 669 | version = "2.0.1" 670 | description = "A lil' TOML parser" 671 | optional = false 672 | python-versions = ">=3.7" 673 | files = [ 674 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 675 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 676 | ] 677 | 678 | [[package]] 679 | name = "types-requests" 680 | version = "2.31.0.20240310" 681 | description = "Typing stubs for requests" 682 | optional = false 683 | python-versions = ">=3.8" 684 | files = [ 685 | {file = "types-requests-2.31.0.20240310.tar.gz", hash = "sha256:8a20171e088a0f7893aac490a5f7f37e9f53e14d6b762c5d55623bea022ee06f"}, 686 | {file = "types_requests-2.31.0.20240310-py3-none-any.whl", hash = "sha256:a4ba49353e3f6b3fdb481ba6f8455917e8bc2a0b62b775d80833ec000508e5dc"}, 687 | ] 688 | 689 | [package.dependencies] 690 | urllib3 = ">=2" 691 | 692 | [[package]] 693 | name = "typing-extensions" 694 | version = "4.10.0" 695 | description = "Backported and Experimental Type Hints for Python 3.8+" 696 | optional = false 697 | python-versions = ">=3.8" 698 | files = [ 699 | {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, 700 | {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, 701 | ] 702 | 703 | [[package]] 704 | name = "urllib3" 705 | version = "2.2.1" 706 | description = "HTTP library with thread-safe connection pooling, file post, and more." 707 | optional = false 708 | python-versions = ">=3.8" 709 | files = [ 710 | {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, 711 | {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, 712 | ] 713 | 714 | [package.extras] 715 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 716 | h2 = ["h2 (>=4,<5)"] 717 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 718 | zstd = ["zstandard (>=0.18.0)"] 719 | 720 | [[package]] 721 | name = "zipp" 722 | version = "3.17.0" 723 | description = "Backport of pathlib-compatible object wrapper for zip files" 724 | optional = true 725 | python-versions = ">=3.8" 726 | files = [ 727 | {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, 728 | {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, 729 | ] 730 | 731 | [package.extras] 732 | docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] 733 | testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] 734 | 735 | [extras] 736 | docs = ["Sphinx", "Sphinx"] 737 | 738 | [metadata] 739 | lock-version = "2.0" 740 | python-versions = "^3.8" 741 | content-hash = "5297552341fcd38afb35eeb0534fe374e61de5230f28002a232e3213b2b32c9f" 742 | --------------------------------------------------------------------------------