├── log ├── py.typed ├── tests │ ├── __init__.py │ ├── conftest.py │ ├── test_logger.py │ ├── test_api.py │ ├── test_helpers.py │ └── test_utils.py ├── state.py ├── settings.py ├── __init__.py ├── filters.py ├── logger.py ├── helpers.py └── utils.py ├── docs ├── index.md ├── about │ ├── license.md │ ├── changelog.md │ └── contributing.md ├── requirements.txt ├── logging.md └── options.md ├── .tool-versions ├── notebooks └── profile_default │ ├── .gitignore │ ├── startup │ ├── ipython_startup.py │ └── README │ └── ipython_config.py ├── .gitattributes ├── tests ├── demo.py ├── conftest.py ├── __init__.py ├── other.py └── test_records.py ├── .github ├── dependabot.yml └── workflows │ └── main.yml ├── .readthedocs.yml ├── .scrutinizer.yml ├── .coveragerc ├── .pycodestyle.ini ├── .verchew.ini ├── bin ├── open ├── checksum ├── update └── verchew ├── .pydocstyle.ini ├── mkdocs.yml ├── .appveyor.yml ├── .gitignore ├── LICENSE.md ├── CONTRIBUTING.md ├── .vscode └── settings.json ├── README.md ├── scent.py ├── pyproject.toml ├── CHANGELOG.md ├── Makefile ├── .pylint.ini └── poetry.lock /log/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /docs/about/license.md: -------------------------------------------------------------------------------- 1 | ../../LICENSE.md -------------------------------------------------------------------------------- /docs/about/changelog.md: -------------------------------------------------------------------------------- 1 | ../../CHANGELOG.md -------------------------------------------------------------------------------- /docs/about/contributing.md: -------------------------------------------------------------------------------- 1 | ../../CONTRIBUTING.md -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | python 3.11.11 2 | poetry 2.0.1 3 | -------------------------------------------------------------------------------- /notebooks/profile_default/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | -------------------------------------------------------------------------------- /notebooks/profile_default/startup/ipython_startup.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /log/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit tests for the package.""" 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | CHANGELOG.md merge=union 3 | poetry.lock merge=binary 4 | -------------------------------------------------------------------------------- /tests/demo.py: -------------------------------------------------------------------------------- 1 | import log 2 | 3 | 4 | def greet(name): 5 | log.error("Hello, %s!", name) 6 | -------------------------------------------------------------------------------- /log/state.py: -------------------------------------------------------------------------------- 1 | from typing import Set 2 | 3 | initialized = False 4 | 5 | silenced: Set[str] = set() 6 | -------------------------------------------------------------------------------- /log/settings.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | DEFAULT_LEVEL = logging.INFO 4 | DEFAULT_FORMAT = "%(levelname)s: %(name)s: %(message)s" 5 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """Integration tests configuration file.""" 2 | 3 | # pylint: disable=unused-import 4 | from log.tests.conftest import pytest_configure 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | exclude-paths: 8 | - docs/requirements.txt 9 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Integration tests for the package.""" 2 | 3 | try: 4 | from IPython.terminal.debugger import TerminalPdb as Debugger 5 | except ImportError: 6 | from pdb import Pdb as Debugger 7 | -------------------------------------------------------------------------------- /tests/other.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | log = logging.getLogger("3rd-party") 4 | 5 | 6 | def do_3rd_party_thing(): 7 | log.debug(1) 8 | log.info(2) 9 | log.warning(3) 10 | log.error(4) 11 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs==1.2.3 ; python_version >= "3.8" and python_version < "4.0" 2 | pygments==2.15.0 ; python_version >= "3.8" and python_version < "4.0" 3 | jinja2==3.0.3 ; python_version >= "3.8" and python_version < "4.0" 4 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.11" 7 | 8 | mkdocs: 9 | configuration: mkdocs.yml 10 | 11 | python: 12 | install: 13 | - requirements: docs/requirements.txt 14 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | build: 2 | tests: 3 | override: 4 | - pylint-run --rcfile=.pylint.ini 5 | - py-scrutinizer-run 6 | checks: 7 | python: 8 | code_rating: true 9 | duplicate_code: true 10 | filter: 11 | excluded_paths: 12 | - "*/tests/*" 13 | -------------------------------------------------------------------------------- /log/__init__.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=wildcard-import 2 | 3 | from logging import CRITICAL, DEBUG, ERROR, INFO, WARNING 4 | 5 | from .helpers import * 6 | from .logger import * 7 | 8 | WARN = WARNING 9 | 10 | d = debug 11 | i = info 12 | w = warn = warning 13 | e = error 14 | c = critical 15 | exc = exception 16 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | 3 | branch = true 4 | 5 | data_file = .cache/coverage 6 | 7 | omit = 8 | .venv/* 9 | */tests/* 10 | */__main__.py 11 | 12 | [report] 13 | 14 | exclude_lines = 15 | pragma: no cover 16 | raise NotImplementedError 17 | except DistributionNotFound 18 | TYPE_CHECKING 19 | -------------------------------------------------------------------------------- /log/tests/conftest.py: -------------------------------------------------------------------------------- 1 | """Unit tests configuration file.""" 2 | 3 | import logging 4 | 5 | 6 | def pytest_configure(config): 7 | """Disable verbose output when running tests.""" 8 | logging.basicConfig(level=logging.DEBUG) 9 | 10 | terminal = config.pluginmanager.getplugin("terminal") 11 | terminal.TerminalReporter.showfspath = False 12 | -------------------------------------------------------------------------------- /.pycodestyle.ini: -------------------------------------------------------------------------------- 1 | [pycodestyle] 2 | 3 | # E401 multiple imports on one line (checked by PyLint) 4 | # E402 module level import not at top of file (checked by PyLint) 5 | # E501: line too long (checked by PyLint) 6 | # E711: comparison to None (used to improve test style) 7 | # E712: comparison to True (used to improve test style) 8 | ignore = E401,E402,E501,E711,E712 9 | -------------------------------------------------------------------------------- /.verchew.ini: -------------------------------------------------------------------------------- 1 | [Make] 2 | 3 | cli = make 4 | version = GNU Make 5 | 6 | [Python] 7 | 8 | cli = python 9 | version = 3 10 | 11 | [Poetry] 12 | 13 | cli = poetry 14 | version = 2 15 | 16 | [Graphviz] 17 | 18 | cli = dot 19 | cli_version_arg = -V 20 | version = graphviz 21 | optional = true 22 | message = This is only needed to generate UML diagrams for documentation. 23 | -------------------------------------------------------------------------------- /log/tests/test_logger.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=redefined-outer-name,unused-variable,expression-not-assigned,singleton-comparison 2 | 3 | import logging 4 | 5 | from log import logger 6 | 7 | 8 | def describe_log(): 9 | def it_sets_level_and_message(expect, caplog): 10 | logger.log(logging.DEBUG, "foobar") 11 | expect(caplog.records[-1].levelname) == "DEBUG" 12 | expect(caplog.records[-1].message) == "foobar" 13 | -------------------------------------------------------------------------------- /bin/open: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | 7 | 8 | COMMANDS = { 9 | 'linux': "open", 10 | 'win32': "cmd /c start", 11 | 'cygwin': "cygstart", 12 | 'darwin': "open", 13 | } 14 | 15 | 16 | def run(path): 17 | command = COMMANDS.get(sys.platform, "open") 18 | os.system(command + ' ' + path) 19 | 20 | 21 | if __name__ == '__main__': 22 | run(sys.argv[-1]) 23 | -------------------------------------------------------------------------------- /notebooks/profile_default/startup/README: -------------------------------------------------------------------------------- 1 | This is the IPython startup directory 2 | 3 | .py and .ipy files in this directory will be run *prior* to any code or files specified 4 | via the exec_lines or exec_files configurables whenever you load this profile. 5 | 6 | Files will be run in lexicographical order, so you can control the execution order of files 7 | with a prefix, e.g.:: 8 | 9 | 00-first.py 10 | 50-middle.py 11 | 99-last.ipy 12 | -------------------------------------------------------------------------------- /bin/checksum: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import hashlib 5 | import sys 6 | 7 | 8 | def run(paths): 9 | sha = hashlib.sha1() 10 | 11 | for path in paths: 12 | try: 13 | with open(path, 'rb') as f: 14 | for chunk in iter(lambda: f.read(4096), b''): 15 | sha.update(chunk) 16 | except IOError: 17 | sha.update(path.encode()) 18 | 19 | print(sha.hexdigest()) 20 | 21 | 22 | if __name__ == '__main__': 23 | run(sys.argv[1:]) 24 | -------------------------------------------------------------------------------- /.pydocstyle.ini: -------------------------------------------------------------------------------- 1 | [pydocstyle] 2 | 3 | # D211: No blank lines allowed before class docstring 4 | add_select = D211 5 | 6 | # D100: Missing docstring in public module 7 | # D101: Missing docstring in public class 8 | # D102: Missing docstring in public method 9 | # D103: Missing docstring in public function 10 | # D104: Missing docstring in public package 11 | # D105: Missing docstring in magic method 12 | # D107: Missing docstring in __init__ 13 | # D202: No blank lines allowed after function docstring 14 | add_ignore = D100,D101,D102,D103,D104,D105,D107,D202 15 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: minilog 2 | site_description: Minimalistic wrapper for Python logging. 3 | site_author: Jace Browning 4 | 5 | repo_url: https://github.com/jacebrowning/minilog 6 | edit_uri: https://github.com/jacebrowning/minilog/edit/main/docs 7 | 8 | theme: readthedocs 9 | 10 | markdown_extensions: 11 | - codehilite 12 | 13 | nav: 14 | - Home: index.md 15 | - Logging: logging.md 16 | - Options: options.md 17 | - About: 18 | - Release Notes: about/changelog.md 19 | - Contributor Guide: about/contributing.md 20 | - License: about/license.md 21 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2022 2 | 3 | environment: 4 | matrix: 5 | - PYTHON_MAJOR: 3 6 | PYTHON_MINOR: 9 7 | 8 | cache: 9 | - .venv -> poetry.lock 10 | 11 | install: 12 | # Add Python to the PATH 13 | - set PATH=C:\Python%PYTHON_MAJOR%%PYTHON_MINOR%;%PATH% 14 | - set PATH=C:\Python%PYTHON_MAJOR%%PYTHON_MINOR%\Scripts;%PATH% 15 | # Install system dependencies 16 | - choco install make 17 | - curl -sSL https://install.python-poetry.org | python - 18 | - set PATH=%USERPROFILE%\AppData\Roaming\Python\Scripts;%PATH% 19 | - make doctor 20 | # Install project dependencies 21 | - make install 22 | 23 | build: off 24 | 25 | test_script: 26 | - make check 27 | - make test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary Python files 2 | *.pyc 3 | *.egg-info/ 4 | __pycache__/ 5 | .ipynb_checkpoints/ 6 | setup.py 7 | pip-wheel-metadata/ 8 | 9 | # Temporary OS files 10 | Icon* 11 | 12 | # Temporary virtual environment files 13 | /.cache/ 14 | /.venv/ 15 | tmp/ 16 | 17 | # Temporary server files 18 | .env 19 | *.pid 20 | 21 | # Generated documentation 22 | /docs/gen/ 23 | /docs/apidocs/ 24 | /site/ 25 | /*.html 26 | /docs/*.png 27 | 28 | # Google Drive 29 | *.gdoc 30 | *.gsheet 31 | *.gslides 32 | *.gdraw 33 | 34 | # Testing and coverage results 35 | /.coverage 36 | /.coverage.* 37 | /htmlcov/ 38 | /prof/ 39 | coverage.xml 40 | 41 | # Build and release directories 42 | /build/ 43 | /dist/ 44 | *.spec 45 | 46 | # Sublime Text 47 | *.sublime-workspace 48 | 49 | # Eclipse 50 | .settings 51 | -------------------------------------------------------------------------------- /docs/logging.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | This package intends to be a drop-in replacement for `logging.Logger` objects. It supports the standard logging API: 4 | 5 | ```python 6 | import log 7 | 8 | log.debug(message, *args) 9 | log.info(message, *args) 10 | log.warning(message, *args) 11 | log.error(message, *args) 12 | log.critical(message, *args) 13 | 14 | log.exception(message, *args) 15 | 16 | log.log(level, message, *args) 17 | ``` 18 | 19 | As well as convenience methods: 20 | 21 | ```python 22 | import log 23 | 24 | log.warn(message, *args) # warning 25 | 26 | log.d(message, *args) # debug 27 | log.i(message, *args) # info 28 | log.w(message, *args) # warning 29 | log.e(message, *args) # error 30 | log.c(message, *args) # critical 31 | 32 | log.exc(message, *args) # exception 33 | ``` 34 | -------------------------------------------------------------------------------- /log/tests/test_api.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=redefined-outer-name,unused-variable,expression-not-assigned,singleton-comparison 2 | 3 | import pytest 4 | 5 | import log 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "name, levelname", 10 | [ 11 | ("c", "CRITICAL"), 12 | ("critical", "CRITICAL"), 13 | ("d", "DEBUG"), 14 | ("debug", "DEBUG"), 15 | ("e", "ERROR"), 16 | ("error", "ERROR"), 17 | ("exc", "ERROR"), 18 | ("exception", "ERROR"), 19 | ("i", "INFO"), 20 | ("info", "INFO"), 21 | ("w", "WARNING"), 22 | ("warn", "WARNING"), 23 | ("warning", "WARNING"), 24 | ], 25 | ) 26 | def test_level_mapping(expect, caplog, name, levelname): 27 | getattr(log, name)("message") 28 | expect(caplog.records[-1].levelname) == levelname 29 | -------------------------------------------------------------------------------- /log/filters.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import sys 4 | 5 | 6 | class RelpathFormatFilter(logging.Filter): 7 | """Adds '%(relpath)s' as a 'LogRecord' attribute.""" 8 | 9 | def filter(self, record): 10 | pathname = record.pathname 11 | record.relpath = None 12 | abs_sys_paths = [os.path.abspath(p) for p in sys.path] 13 | for path in sorted(abs_sys_paths, key=len, reverse=True): 14 | if not path.endswith(os.sep): 15 | path += os.sep 16 | if pathname.startswith(path): 17 | record.relpath = os.path.relpath(pathname, path) 18 | break 19 | return True 20 | 21 | 22 | relpath_format_filter = RelpathFormatFilter() 23 | 24 | 25 | def install(logger): 26 | for handler in logger.handlers: 27 | handler.addFilter(relpath_format_filter) 28 | -------------------------------------------------------------------------------- /log/logger.py: -------------------------------------------------------------------------------- 1 | """Replicates some of the `logging.Logger` API.""" 2 | 3 | import logging 4 | import sys 5 | 6 | from . import utils 7 | 8 | __all__ = ["log", "debug", "info", "warning", "error", "critical", "exception"] 9 | 10 | 11 | def log(level, message, *args, **kwargs): 12 | utils.create_logger_record(level, message, *args, **kwargs) 13 | 14 | 15 | def debug(message, *args, **kwargs): 16 | log(logging.DEBUG, message, *args, **kwargs) 17 | 18 | 19 | def info(message, *args, **kwargs): 20 | log(logging.INFO, message, *args, **kwargs) 21 | 22 | 23 | def warning(message, *args, **kwargs): 24 | log(logging.WARNING, message, *args, **kwargs) 25 | 26 | 27 | def error(message, *args, **kwargs): 28 | log(logging.ERROR, message, *args, **kwargs) 29 | 30 | 31 | def critical(message, *args, **kwargs): 32 | log(logging.CRITICAL, message, *args, **kwargs) 33 | 34 | 35 | def exception(message, *args, **kwargs): 36 | kwargs["exc_info"] = kwargs.get("exc_info", sys.exc_info()) 37 | log(logging.ERROR, message, *args, **kwargs) 38 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python-version: ['3.9', '3.10', '3.11'] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | 21 | - uses: Gr1N/setup-poetry@v8 22 | 23 | - name: Check dependencies 24 | run: make doctor 25 | 26 | - uses: actions/cache@v4 27 | with: 28 | path: .venv 29 | key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }} 30 | 31 | - name: Install dependencies 32 | run: make install 33 | 34 | - name: Check code 35 | run: make check 36 | 37 | - name: Test code 38 | run: make test 39 | 40 | - name: Upload coverage 41 | uses: codecov/codecov-action@v4 42 | if: steps.fork-check.outputs.is-fork == 'false' 43 | with: 44 | token: ${{ secrets.CODECOV_TOKEN }} 45 | fail_ci_if_error: true 46 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | **The MIT License (MIT)** 2 | 3 | Copyright © 2018, Jace Browning 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /log/tests/test_helpers.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=unused-variable,expression-not-assigned 2 | 3 | from unittest.mock import call, patch 4 | 5 | from log import helpers 6 | 7 | 8 | def describe_init(): 9 | @patch("logging.basicConfig") 10 | def with_verbosity_0(config, expect): 11 | helpers.init(format="%(message)s", verbosity=0) 12 | expect(config.mock_calls) == [call(format="%(message)s", level=40)] 13 | 14 | @patch("logging.basicConfig") 15 | def with_verbosity_1(config, expect): 16 | helpers.init(format="%(message)s", verbosity=1) 17 | expect(config.mock_calls) == [call(format="%(message)s", level=30)] 18 | 19 | @patch("logging.basicConfig") 20 | def with_verbosity_2(config, expect): 21 | helpers.init(format="%(message)s", verbosity=2) 22 | expect(config.mock_calls) == [call(format="%(message)s", level=20)] 23 | 24 | @patch("logging.basicConfig") 25 | def with_verbosity_3(config, expect): 26 | helpers.init(format="%(message)s", verbosity=3) 27 | expect(config.mock_calls) == [call(format="%(message)s", level=10)] 28 | 29 | @patch("logging.basicConfig") 30 | def with_verbosity_above_3(config, expect): 31 | helpers.init(format="%(message)s", verbosity=4) 32 | expect(config.mock_calls) == [call(format="%(message)s", level=10)] 33 | 34 | @patch("logging.basicConfig") 35 | def with_verbosity_0_and_debug(config, expect): 36 | helpers.init(format="%(message)s", verbosity=0, debug=True) 37 | expect(config.mock_calls) == [call(format="%(message)s", level=10)] 38 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributor Guide 2 | 3 | ## Setup 4 | 5 | ### Requirements 6 | 7 | * Make: 8 | - macOS: `$ xcode-select --install` 9 | - Linux: [https://www.gnu.org](https://www.gnu.org/software/make) 10 | - Windows: `$ choco install make` [https://chocolatey.org](https://chocolatey.org/install) 11 | * Python: `$ asdf install` (https://asdf-vm.com)[https://asdf-vm.com/guide/getting-started.html] 12 | * Poetry: [https://python-poetry.org](https://python-poetry.org/docs/#installation) 13 | * Graphviz: 14 | * macOS: `$ brew install graphviz` 15 | * Linux: [https://graphviz.org/download](https://graphviz.org/download/) 16 | * Windows: [https://graphviz.org/download](https://graphviz.org/download/) 17 | 18 | To confirm these system dependencies are configured correctly: 19 | 20 | ```text 21 | $ make bootstrap 22 | $ make doctor 23 | ``` 24 | 25 | ### Installation 26 | 27 | Install project dependencies into a virtual environment: 28 | 29 | ```text 30 | $ make install 31 | ``` 32 | 33 | ## Development Tasks 34 | 35 | ### Manual 36 | 37 | Run the tests: 38 | 39 | ```text 40 | $ make test 41 | ``` 42 | 43 | Run static analysis: 44 | 45 | ```text 46 | $ make check 47 | ``` 48 | 49 | Build the documentation: 50 | 51 | ```text 52 | $ make docs 53 | ``` 54 | 55 | ### Automatic 56 | 57 | Keep all of the above tasks running on change: 58 | 59 | ```text 60 | $ make dev 61 | ``` 62 | 63 | > In order to have OS X notifications, `brew install terminal-notifier`. 64 | 65 | ### Continuous Integration 66 | 67 | The CI server will report overall build status: 68 | 69 | ```text 70 | $ make all 71 | ``` 72 | 73 | ## Demo Tasks 74 | 75 | Run the program: 76 | 77 | ```text 78 | $ make run 79 | ``` 80 | 81 | Launch an IPython session: 82 | 83 | ```text 84 | $ make shell 85 | ``` 86 | 87 | ## Release Tasks 88 | 89 | Release to PyPI: 90 | 91 | ```text 92 | $ make upload 93 | ``` 94 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | ".cache/": true, 4 | ".venv/": true, 5 | "*.egg-info": true, 6 | "pip-wheel-metadata/": true, 7 | "**/__pycache__": true, 8 | "**/*.pyc": true, 9 | "**/.ipynb_checkpoints": true, 10 | "**/tmp/": true, 11 | "dist/": true, 12 | "htmlcov/": true, 13 | "prof/": true, 14 | "site/": true, 15 | "geckodriver.log": true 16 | }, 17 | "python.defaultInterpreterPath": ".venv/bin/python", 18 | "editor.formatOnSave": true, 19 | "pylint.args": [ 20 | "--rcfile=.pylint.ini" 21 | ], 22 | "cSpell.words": [ 23 | "appex", 24 | "autorefs", 25 | "builtins", 26 | "chanage", 27 | "choco", 28 | "classproperties", 29 | "codehilite", 30 | "completly", 31 | "cookiecutter", 32 | "coveragerc", 33 | "cygstart", 34 | "cygwin", 35 | "dataclass", 36 | "dataclasses", 37 | "datafile", 38 | "ensurepip", 39 | "findstr", 40 | "fontawesome", 41 | "freezegun", 42 | "gethostname", 43 | "getpid", 44 | "getplugin", 45 | "gitman", 46 | "gitsvn", 47 | "Graphviz", 48 | "iglob", 49 | "imac", 50 | "importlib", 51 | "ioreg", 52 | "iphoto", 53 | "ipython", 54 | "levelname", 55 | "logbreak", 56 | "macbook", 57 | "MDEF", 58 | "mkdocs", 59 | "mkdocstrings", 60 | "mrpossoms", 61 | "mylink", 62 | "mypy", 63 | "noclasses", 64 | "nohup", 65 | "pipx", 66 | "pluginmanager", 67 | "preserialization", 68 | "Preserialized", 69 | "Preserializing", 70 | "psutil", 71 | "repr", 72 | "ruamel", 73 | "rustup", 74 | "scalarstring", 75 | "showfspath", 76 | "startfile", 77 | "tomlkit", 78 | "tput", 79 | "tracebackhide", 80 | "Trilean", 81 | "trufflehog", 82 | "udevadm", 83 | "unparseable", 84 | "USERPROFILE", 85 | "venv", 86 | "verchew", 87 | "verchewrc", 88 | "webfonts", 89 | "YORM" 90 | ] 91 | } 92 | -------------------------------------------------------------------------------- /log/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=redefined-outer-name,unused-variable,expression-not-assigned,singleton-comparison,disallowed-name 2 | 3 | import logging 4 | from dataclasses import dataclass 5 | 6 | from log.utils import create_logger_record, format_message, parse_name 7 | 8 | 9 | def describe_create_logger_record(): 10 | def it_uses_the_default_log_level_for_new_loggers(expect, monkeypatch): 11 | monkeypatch.setattr(logging.root, "level", logging.INFO) 12 | 13 | expect(create_logger_record(logging.DEBUG, "hello", name="new")) == False 14 | expect(create_logger_record(logging.INFO, "hello", name="new")) == True 15 | expect(create_logger_record(logging.WARNING, "hello", name="new")) == True 16 | 17 | def it_inherits_the_parent_logging_level(expect): 18 | logger = logging.getLogger("root") 19 | logger.level = logging.WARNING 20 | 21 | expect(create_logger_record(logging.DEBUG, "hello", name="root.new")) == False 22 | expect(create_logger_record(logging.INFO, "hello", name="root.new")) == False 23 | expect(create_logger_record(logging.WARNING, "hello", name="root.new")) == True 24 | 25 | 26 | def describe_parse_name(): 27 | def it_uses_the_filename_when_main(expect): 28 | frame_info = {"__file__": "my_package/my_module.py"} 29 | expect(parse_name("__main__", frame_info)[0]) == "my_package.my_module" 30 | 31 | def it_handles_interactive_sessions(expect): 32 | expect(parse_name("__main__", {})[0]) == "interactive" 33 | 34 | 35 | def describe_format_message(): 36 | def it_formats_structures(expect): 37 | data = {x: x * 10 for x in range(20)} 38 | expect(format_message(data).count("\n")) == 19 39 | 40 | def it_formats_dataclasses(expect): 41 | @dataclass 42 | class Example: 43 | foo: int = 1 44 | bar: str = "abc" 45 | 46 | example = Example() 47 | 48 | expect(format_message(example)) == "{'bar': 'abc', 'foo': 1}" 49 | 50 | def it_preserves_strings(expect): 51 | expect(format_message("foobar")) == "foobar" 52 | -------------------------------------------------------------------------------- /log/helpers.py: -------------------------------------------------------------------------------- 1 | """Wrappers to eliminate boilerplate `logging` activities.""" 2 | 3 | import logging 4 | import sys 5 | from importlib import reload 6 | 7 | from . import filters, settings, state 8 | 9 | __all__ = ["reset", "init", "silence"] 10 | 11 | VERBOSITY_TO_LEVEL = { 12 | 0: logging.ERROR, 13 | 1: logging.WARNING, 14 | 2: logging.INFO, 15 | 3: logging.DEBUG, 16 | } 17 | 18 | 19 | def reset(): 20 | logging.shutdown() 21 | reload(logging) 22 | state.initialized = False 23 | state.silenced.clear() 24 | 25 | 26 | def init(*, debug=False, verbosity=None, **kwargs): 27 | if debug: 28 | settings.DEFAULT_LEVEL = logging.DEBUG 29 | elif verbosity is not None: 30 | try: 31 | settings.DEFAULT_LEVEL = VERBOSITY_TO_LEVEL[verbosity] 32 | except KeyError: 33 | settings.DEFAULT_LEVEL = logging.DEBUG 34 | 35 | kwargs["level"] = kwargs.get("level", settings.DEFAULT_LEVEL) 36 | kwargs["format"] = kwargs.get("format", settings.DEFAULT_FORMAT) 37 | logging.basicConfig(**kwargs) 38 | 39 | custom_format = kwargs.get("format") 40 | if custom_format: 41 | formatter = logging.Formatter( 42 | fmt=custom_format, 43 | datefmt=kwargs.get("datefmt"), 44 | style=kwargs.get("style", "%"), 45 | ) 46 | for handler in logging.root.handlers: 47 | handler.setFormatter(formatter) 48 | 49 | filters.install(logging.root) 50 | state.initialized = True 51 | 52 | 53 | def silence(*names, allow_info=False, allow_warning=False, allow_error=False): 54 | if not state.initialized: 55 | if "pytest" in sys.modules: 56 | filters.install(logging.root) 57 | else: 58 | init() 59 | 60 | if allow_info: 61 | level = logging.INFO 62 | elif allow_warning: 63 | level = logging.WARNING 64 | elif allow_error: 65 | level = logging.ERROR 66 | else: 67 | level = logging.CRITICAL 68 | 69 | for name in names: 70 | logging.getLogger(name).setLevel(level) 71 | state.silenced.add(name) 72 | -------------------------------------------------------------------------------- /docs/options.md: -------------------------------------------------------------------------------- 1 | # Initialization 2 | 3 | `minilog` defaults to INFO level using a simple log format. It supports the same initialization arguments as [`logging.basicConfig`](https://docs.python.org/3/library/logging.html#logging.basicConfig). 4 | 5 | To set the format for all logging handlers: 6 | 7 | ```python 8 | log.init(format="%(levelname)s: %(name)s: %(message)s") 9 | ``` 10 | 11 | To set the level for the root logging handler: 12 | 13 | ```python 14 | log.init(format=…, level=log.WARNING) 15 | ``` 16 | 17 | ### Debug Option 18 | 19 | To simply enable debug-level logging, a convenience option is provided: 20 | 21 | ```python 22 | log.init(format=…, debug=True) 23 | ``` 24 | 25 | ### Verbosity Option 26 | 27 | To work with frameworks that provide a `verbosity` level in their CLI frameworks (such as [Django](https://docs.djangoproject.com/en/4.0/ref/django-admin/#cmdoption-verbosity)), that can be used instead: 28 | 29 | ```python 30 | log.init(format=…, verbosity=verbosity) 31 | ``` 32 | 33 | | Verbosity | Level | 34 | |-----------|-------------| 35 | | `0` | **ERROR** | 36 | | `1` | **WARNING** | 37 | | `2` | **INFO** | 38 | | `3` | **DEBUG** | 39 | 40 | ### Silencing Loggers 41 | 42 | To hide logging for specific named loggers: 43 | 44 | ```python 45 | log.silence('selenium') 46 | log.silence('werkzeug', 'requests', allow_warning=True) 47 | ``` 48 | 49 | ### Reset Loggers 50 | 51 | Finally, if another package has already set the logging format or level, that can be reset so that `minilog` takes over: 52 | 53 | ```python 54 | log.reset() 55 | log.init(…) 56 | ``` 57 | 58 | # Records 59 | 60 | In addition to the standard [`LogRecord`](https://docs.python.org/3/library/logging.html#logrecord-attributes) attributes, the following additional patterns are available: 61 | 62 | | Logging Format | Description | 63 | |----------------|---------------------------------------------------------------------------------------------------------------| 64 | | `%(relpath)s` | Full pathname of the source file where the logging call was issued relative to the current working directory. | 65 | -------------------------------------------------------------------------------- /tests/test_records.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=redefined-outer-name,unused-variable,expression-not-assigned,singleton-comparison 2 | 3 | import os 4 | 5 | import pytest 6 | from freezegun import freeze_time 7 | 8 | import log 9 | 10 | from . import demo, other 11 | 12 | 13 | def describe_text(): 14 | @pytest.mark.first 15 | def it_includes_the_caller_location(expect, caplog): 16 | demo.greet("caller") 17 | 18 | expect(caplog.text) == "ERROR tests.demo:demo.py:5 Hello, caller!\n" 19 | 20 | @freeze_time("2019-01-15") 21 | def it_can_be_formatted_with_init(expect, caplog): 22 | log.init( 23 | level=log.WARNING, 24 | format="%(asctime)s %(relpath)s:%(lineno)s: %(message)s", 25 | datefmt="%Y-%m", 26 | ) 27 | 28 | demo.greet("format") 29 | 30 | if os.name == "nt": 31 | expect(caplog.text) == "2019-01 tests\\demo.py:5: Hello, format!\n" 32 | else: 33 | expect(caplog.text) == "2019-01 tests/demo.py:5: Hello, format!\n" 34 | 35 | def it_can_include_exceptions(expect, caplog): 36 | try: 37 | print(1 / 0) 38 | except ZeroDivisionError: 39 | log.exception("exception") 40 | 41 | expect(caplog.text).contains("Traceback ") 42 | expect(caplog.text).contains('test_records.py", line 37, ') 43 | expect(caplog.text).contains("ZeroDivisionError") 44 | 45 | 46 | def describe_silence(): 47 | def when_off(expect, caplog): 48 | log.silence("3rd-party") 49 | 50 | other.do_3rd_party_thing() 51 | 52 | expect(caplog.records) == [] 53 | 54 | def with_errors(expect, caplog): 55 | log.silence("3rd-party", allow_error=True) 56 | 57 | other.do_3rd_party_thing() 58 | 59 | expect(len(caplog.records)) == 1 60 | 61 | def with_warnings(expect, caplog): 62 | log.silence("3rd-party", allow_warning=True) 63 | 64 | other.do_3rd_party_thing() 65 | 66 | expect(len(caplog.records)) == 2 67 | 68 | def with_infos(expect, caplog): 69 | log.silence("3rd-party", allow_info=True) 70 | 71 | other.do_3rd_party_thing() 72 | 73 | expect(len(caplog.records)) == 3 74 | -------------------------------------------------------------------------------- /log/utils.py: -------------------------------------------------------------------------------- 1 | """Implements the "magic" to create `logging` records for the caller.""" 2 | 3 | import dataclasses 4 | import inspect 5 | import logging 6 | from pprint import pformat 7 | from typing import Dict, Tuple 8 | 9 | from . import state 10 | 11 | 12 | def create_logger_record( 13 | level, message, *args, name: str = "", exc_info=None, **kwargs 14 | ) -> bool: 15 | frame = inspect.currentframe().f_back.f_back.f_back # type: ignore 16 | assert frame 17 | 18 | name, parent_name = parse_name(name, frame.f_globals) 19 | if parent_name in state.silenced: 20 | return False 21 | 22 | logger = get_logger(name, parent_name) 23 | if not logger.isEnabledFor(level): 24 | return False 25 | 26 | record = logger.makeRecord( 27 | name, 28 | level, 29 | fn=parse_filename(frame.f_globals), 30 | lno=frame.f_lineno, 31 | msg=format_message(message), 32 | args=args, 33 | exc_info=exc_info, 34 | extra=kwargs, 35 | sinfo=None, 36 | ) 37 | logger.handle(record) 38 | return True 39 | 40 | 41 | def parse_name(custom_name: str, frame_info: Dict) -> Tuple[str, str]: 42 | module_name = custom_name or frame_info["__name__"] 43 | if module_name == "__main__": 44 | try: 45 | module_name = frame_info["__file__"].split(".")[0].replace("/", ".") 46 | except KeyError: 47 | module_name = "interactive" 48 | parent_module_name = module_name.split(".")[0] 49 | return module_name, parent_module_name 50 | 51 | 52 | def get_logger(name: str, parent_name: str): 53 | logger = logging.getLogger(name) 54 | if not logger.level: 55 | parent_logger = logging.getLogger(parent_name) 56 | logger.level = parent_logger.level 57 | if not logger.level: 58 | logger.level = logging.root.level 59 | return logger 60 | 61 | 62 | def parse_filename(frame_info: Dict) -> str: 63 | return frame_info.get("__file__", "interactive") 64 | 65 | 66 | def format_message(value) -> str: 67 | if dataclasses.is_dataclass(value): 68 | value = dataclasses.asdict(value) 69 | if not isinstance(value, str): 70 | value = pformat(value) 71 | return value 72 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | from contextlib import suppress 6 | import importlib 7 | import tempfile 8 | import shutil 9 | import subprocess 10 | import sys 11 | 12 | 13 | CWD = os.getcwd() 14 | TMP = tempfile.gettempdir() 15 | CONFIG = { 16 | "full_name": "Jace Browning", 17 | "email": "jacebrowning@gmail.com", 18 | "github_username": "jacebrowning", 19 | "github_repo": "minilog", 20 | "default_branch": "main", 21 | "project_name": "minilog", 22 | "package_name": "log", 23 | "project_short_description": "Minimalistic wrapper for Python logging.", 24 | "python_major_version": 3, 25 | "python_minor_version": 6, 26 | } 27 | 28 | 29 | def install(package="cookiecutter"): 30 | try: 31 | importlib.import_module(package) 32 | except ImportError: 33 | print("Installing cookiecutter") 34 | subprocess.check_call([sys.executable, "-m", "pip", "install", package]) 35 | 36 | 37 | def run(): 38 | print("Generating project") 39 | 40 | from cookiecutter.main import cookiecutter 41 | 42 | os.chdir(TMP) 43 | cookiecutter( 44 | "https://github.com/jacebrowning/template-python.git", 45 | no_input=True, 46 | overwrite_if_exists=True, 47 | extra_context=CONFIG, 48 | ) 49 | 50 | 51 | def copy(): 52 | for filename in [ 53 | os.path.join("bin", "update"), 54 | os.path.join("bin", "checksum"), 55 | os.path.join("bin", "open"), 56 | os.path.join("bin", "verchew"), 57 | ".appveyor.yml", 58 | ".coveragerc", 59 | ".gitattributes", 60 | ".gitignore", 61 | ".pydocstyle.ini", 62 | ".pylint.ini", 63 | ".scrutinizer.yml", 64 | ".tool-versions", 65 | ".verchew.ini", 66 | "CONTRIBUTING.md", 67 | "Makefile", 68 | "scent.py", 69 | ]: 70 | src = os.path.join(TMP, CONFIG["project_name"], filename) 71 | dst = os.path.join(CWD, filename) 72 | print("Updating " + filename) 73 | with suppress(FileNotFoundError): 74 | shutil.copy(src, dst) 75 | 76 | 77 | if __name__ == "__main__": 78 | install() 79 | run() 80 | copy() 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # minilog 2 | 3 | A minimalistic logging wrapper for Python. 4 | 5 | [![Linux Build](https://img.shields.io/github/actions/workflow/status/jacebrowning/minilog/main.yml?branch=main&label=linux)](https://github.com/jacebrowning/minilog/actions) 6 | [![Windows Build](https://img.shields.io/appveyor/ci/jacebrowning/minilog/main.svg?label=windows)](https://ci.appveyor.com/project/jacebrowning/minilog) 7 | [![Code Coverage](https://img.shields.io/codecov/c/github/jacebrowning/minilog)](https://codecov.io/gh/jacebrowning/minilog) 8 | [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/jacebrowning/minilog.svg)](https://scrutinizer-ci.com/g/jacebrowning/minilog) 9 | [![PyPI License](https://img.shields.io/pypi/l/minilog.svg)](https://pypi.org/project/minilog) 10 | [![PyPI Version](https://img.shields.io/pypi/v/minilog.svg)](https://pypi.org/project/minilog) 11 | [![PyPI Downloads](https://img.shields.io/pypi/dm/minilog.svg?color=orange)](https://pypistats.org/packages/minilog) 12 | 13 | ## Usage 14 | 15 | Every project should utilize logging, but for simple use cases, this requires a bit too much boilerplate. Instead of including all of this in your modules: 16 | 17 | ```python 18 | import logging 19 | 20 | log = logging.getLogger(__name__) 21 | 22 | def greet(name): 23 | log.info("Hello, %s!", name) 24 | 25 | if __name__ == "__main__": 26 | logging.basicConfig( 27 | level=logging.INFO, 28 | format="%(levelname)s: %(name)s: %(message)s", 29 | ) 30 | ``` 31 | 32 | with this package you can simply: 33 | 34 | ```python 35 | import log 36 | 37 | def greet(name): 38 | log.info("Hello, %s!", name) 39 | 40 | if __name__ == "__main__": 41 | log.init() 42 | ``` 43 | 44 | It will produce the exact same standard library `logging` records behind the scenes with automatic formatting for non-strings. 45 | 46 | ## Installation 47 | 48 | Install this library directly into an activated virtual environment: 49 | 50 | ```text 51 | $ pip install minilog 52 | ``` 53 | 54 | or add it to your [Poetry](https://poetry.eustace.io/) project: 55 | 56 | ```text 57 | $ poetry add minilog 58 | ``` 59 | 60 | ## Documentation 61 | 62 | To view additional options, please consult the [full documentation](https://minilog.readthedocs.io/en/latest/logging/). 63 | -------------------------------------------------------------------------------- /scent.py: -------------------------------------------------------------------------------- 1 | """Configuration file for sniffer.""" 2 | 3 | import time 4 | import subprocess 5 | 6 | from sniffer.api import select_runnable, file_validator, runnable 7 | try: 8 | from pync import Notifier 9 | except ImportError: 10 | notify = None 11 | else: 12 | notify = Notifier.notify 13 | 14 | 15 | watch_paths = ["log", "tests"] 16 | 17 | 18 | class Options: 19 | group = int(time.time()) # unique per run 20 | show_coverage = False 21 | rerun_args = None 22 | 23 | targets = [ 24 | (("make", "test-unit", "DISABLE_COVERAGE=true"), "Unit Tests", True), 25 | (("make", "test-all"), "Integration Tests", False), 26 | (("make", "check"), "Static Analysis", True), 27 | (("make", "docs", "CI=true"), None, True), 28 | ] 29 | 30 | 31 | @select_runnable("run_targets") 32 | @file_validator 33 | def python_files(filename): 34 | return filename.endswith(".py") and ".py." not in filename 35 | 36 | 37 | @select_runnable("run_targets") 38 | @file_validator 39 | def html_files(filename): 40 | return filename.split(".")[-1] in ["html", "css", "js"] 41 | 42 | 43 | @runnable 44 | def run_targets(*args): 45 | """Run targets for Python.""" 46 | Options.show_coverage = "coverage" in args 47 | 48 | count = 0 49 | for count, (command, title, retry) in enumerate(Options.targets, start=1): 50 | 51 | success = call(command, title, retry) 52 | if not success: 53 | message = "✅ " * (count - 1) + "❌" 54 | show_notification(message, title) 55 | 56 | return False 57 | 58 | message = "✅ " * count 59 | title = "All Targets" 60 | show_notification(message, title) 61 | show_coverage() 62 | 63 | return True 64 | 65 | 66 | def call(command, title, retry): 67 | """Run a command-line program and display the result.""" 68 | if Options.rerun_args: 69 | command, title, retry = Options.rerun_args 70 | Options.rerun_args = None 71 | success = call(command, title, retry) 72 | if not success: 73 | return False 74 | 75 | print("") 76 | print("$ %s" % " ".join(command)) 77 | failure = subprocess.call(command) 78 | 79 | if failure and retry: 80 | Options.rerun_args = command, title, retry 81 | 82 | return not failure 83 | 84 | 85 | def show_notification(message, title): 86 | """Show a user notification.""" 87 | if notify and title: 88 | notify(message, title=title, group=Options.group) 89 | 90 | 91 | def show_coverage(): 92 | """Launch the coverage report.""" 93 | if Options.show_coverage: 94 | subprocess.call(["make", "read-coverage"]) 95 | 96 | Options.show_coverage = False 97 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | 3 | name = "minilog" 4 | version = "2.3.1" 5 | description = "Minimalistic wrapper for Python logging." 6 | 7 | license = "MIT" 8 | 9 | authors = ["Jace Browning "] 10 | 11 | readme = "README.md" 12 | packages = [{ include = "log" }] 13 | 14 | homepage = "https://pypi.org/project/minilog" 15 | documentation = "https://minilog.readthedocs.io" 16 | repository = "https://github.com/jacebrowning/minilog" 17 | 18 | keywords = ["logging"] 19 | classifiers = [ 20 | "Development Status :: 5 - Production/Stable", 21 | "Intended Audience :: Developers", 22 | "License :: OSI Approved :: MIT License", 23 | "Natural Language :: English", 24 | "Operating System :: OS Independent", 25 | "Programming Language :: Python :: 3", 26 | "Programming Language :: Python :: 3.9", 27 | "Programming Language :: Python :: 3.10", 28 | "Programming Language :: Python :: 3.11", 29 | "Programming Language :: Python", 30 | "Topic :: Software Development", 31 | "Topic :: System :: Logging", 32 | ] 33 | 34 | [tool.poetry.dependencies] 35 | 36 | python = "^3.8" 37 | 38 | [tool.poetry.group.dev.dependencies] 39 | 40 | # Formatters 41 | black = "^24.3" 42 | isort = "^5.10" 43 | 44 | # Linters 45 | pylint = ">=2.15,<3.3" 46 | pydocstyle = "*" 47 | mypy = "^1.3" 48 | types-freezegun = "*" 49 | 50 | # Testing 51 | pytest = "^7.2" 52 | pytest-describe = "^2.0" 53 | pytest-expecter = "^3.0" 54 | pytest-ordering = "*" 55 | pytest-cov = "^4.1" 56 | pytest-repeat = "*" 57 | pytest-profiling = "*" 58 | freezegun = "*" 59 | 60 | # Reports 61 | coveragespace = "^6.0.1" 62 | 63 | # Documentation 64 | mkdocs = "~1.2" 65 | pygments = "^2.15.0" 66 | jinja2 = ">=3.0,<3.2" 67 | 68 | # Tooling 69 | pyinstaller = "*" 70 | sniffer = "*" 71 | MacFSEvents = { version = "*", platform = "darwin" } 72 | pync = { version = "*", platform = "darwin" } 73 | ipython = "^7.16.3" 74 | 75 | [tool.poetry.requires-plugins] 76 | 77 | poetry-plugin-export = ">=1.8" 78 | 79 | [tool.black] 80 | 81 | quiet = true 82 | 83 | [tool.isort] 84 | 85 | profile = "black" 86 | 87 | [tool.mypy] 88 | 89 | ignore_missing_imports = true 90 | no_implicit_optional = true 91 | check_untyped_defs = true 92 | 93 | cache_dir = ".cache/mypy/" 94 | 95 | [tool.pytest.ini_options] 96 | 97 | addopts = """ 98 | --strict-markers 99 | 100 | -r sxX 101 | --show-capture=log 102 | 103 | --cov-report=html 104 | --cov-report=term-missing:skip-covered 105 | --no-cov-on-fail 106 | """ 107 | 108 | cache_dir = ".cache/pytest/" 109 | 110 | markers = ["first", "last"] 111 | 112 | [build-system] 113 | 114 | requires = ["poetry-core>=1.0.0"] 115 | build-backend = "poetry.core.masonry.api" 116 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## 2.3.1 (2024-10-24) 4 | 5 | - Added `CRITICAL` log level to the package namespace. 6 | 7 | ## 2.3 (2023-12-09) 8 | 9 | - Added automatic formatting for dataclasses. 10 | 11 | ## 2.2 (2023-06-29) 12 | 13 | - Dropped support for Python 3.7. 14 | - Added `py.typed` for better `mypy` support. 15 | 16 | ## 2.1 (2022-03-06) 17 | 18 | - Dropped support for Python 3.6. 19 | - Added automatic formatting of non-string messages. 20 | 21 | ## 2.0.1 (2021-06-03) 22 | 23 | - Fixed logging within interactive sessions. 24 | 25 | ## 2.0 (2020-09-10) 26 | 27 | - Removed automatic call to `init()` when creating the first logging record. 28 | - Removed `reset=True` option for `log.init()`. Use `log.reset()` instead. 29 | 30 | ## 1.6 (2020-05-24) 31 | 32 | - Updated logging to use the source filename when the module is `'__main__'`. 33 | 34 | ## 1.5 (2020-03-28) 35 | 36 | - Fixed `init()` to handle invalid `verbosity` levels and default to **DEBUG**. 37 | 38 | ## 1.4.1 (2020-03-22) 39 | 40 | - Fixed new loggers to inherit the root logging level when their parent has none set. 41 | 42 | ## 1.4 (2020-02-15) 43 | 44 | - Deprecated `reset=True` option for `log.init()` in favor of a separate `log.reset()` function. 45 | 46 | ## 1.3 (2019-11-27) 47 | 48 | - Added support for Python 3.8. 49 | 50 | ## 1.2.6 (2019-11-14) 51 | 52 | - Fixed new loggers to inherit logging level from their parent. 53 | 54 | ## 1.2.5 (2019-10-19) 55 | 56 | - Fixed logging levels to use the default level for new loggers. 57 | 58 | ## 1.2.4 (2019-09-04) 59 | 60 | - Fixed `init()` to pass all arguments to `logging.Formatter`. 61 | 62 | ## 1.2.3 (2019-01-08) 63 | 64 | - Sped up logging by eliminating module lookup. 65 | 66 | ## 1.2.2 (2019-01-08) 67 | 68 | - Sped up logging by eliminating source file loading. 69 | 70 | ## 1.2.1 (2018-12-10) 71 | 72 | - Fixed missing `%(relpath)s` format for `pytest`. 73 | 74 | ## 1.2 (2018-12-09) 75 | 76 | - Fixed bug where logger name is unset when logging during imports. 77 | 78 | ## 1.1 (2018-10-26) 79 | 80 | - Added `%(relpath)s` logging format. 81 | - Added `verbosity` as `init()` option to work with Django admin commands. 82 | 83 | ## 1.0 (2018-09-27) 84 | 85 | - Initial stable release. 86 | 87 | ## 0.5 (2018-09-07) 88 | 89 | - Disabled automatic logging configuration when invoked by `pytest`. 90 | 91 | ## 0.4 (2018-4-28) 92 | 93 | - Added `reset=True` as `init()` option to replace all existing logging handlers. 94 | - Added `exception` logging API. 95 | - Added convenience alias: `log.c`, `log.exc`. 96 | 97 | ## 0.3.1 (2018-03-30) 98 | 99 | - Fixed bug where records were written for disabled levels. 100 | 101 | ## 0.3 (2018-03-15) 102 | 103 | - Exposed `logging` level constants on the `log` package. 104 | - Added `log.WARN` as an alias of `log.WARNING`. 105 | 106 | ## 0.2.1 (2018-03-04) 107 | 108 | - Removed the Python version check on installation. 109 | 110 | ## 0.2 (2018-03-03) 111 | 112 | - Added method to force logging format: `log.init(format="...")` 113 | - Added method to silenced named loggers: `log.silence('requests', allow_error=True)` 114 | - Added convenience aliases: `log.d`, `log.i`, `log.w`, `log.e` 115 | 116 | ## 0.1 (2018-03-03) 117 | 118 | - Initial release. 119 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT := minilog 2 | PACKAGE := log 3 | MODULES := $(wildcard $(PACKAGE)/*.py) 4 | 5 | # MAIN TASKS ################################################################## 6 | 7 | .PHONY: all 8 | all: doctor format check test mkdocs ## Run all tasks that determine CI status 9 | 10 | .PHONY: dev 11 | dev: install .clean-test ## Continuously run CI tasks when files chanage 12 | poetry run sniffer 13 | 14 | # SYSTEM DEPENDENCIES ######################################################### 15 | 16 | .PHONY: bootstrap 17 | bootstrap: ## Attempt to install system dependencies 18 | asdf plugin add python || asdf plugin update python 19 | asdf plugin add poetry || asdf plugin update poetry 20 | asdf install 21 | 22 | .PHONY: doctor 23 | doctor: ## Confirm system dependencies are available 24 | bin/verchew 25 | 26 | # PROJECT DEPENDENCIES ######################################################## 27 | 28 | VIRTUAL_ENV ?= .venv 29 | DEPENDENCIES := $(VIRTUAL_ENV)/.poetry-$(shell bin/checksum pyproject.toml poetry.lock) 30 | 31 | .PHONY: install 32 | install: $(DEPENDENCIES) .cache ## Install project dependencies 33 | 34 | $(DEPENDENCIES): poetry.lock docs/requirements.txt 35 | @ rm -rf $(VIRTUAL_ENV)/.poetry-* 36 | @ rm -rf ~/Library/Preferences/pypoetry 37 | @ poetry config virtualenvs.in-project true 38 | poetry install 39 | @ touch $@ 40 | 41 | ifndef CI 42 | poetry.lock: pyproject.toml 43 | poetry lock 44 | @ touch $@ 45 | docs/requirements.txt: poetry.lock 46 | @ poetry export --all-groups --without-hashes | grep mkdocs > $@ 47 | @ poetry export --all-groups --without-hashes | grep pygments >> $@ 48 | @ poetry export --all-groups --without-hashes | grep jinja2 >> $@ 49 | endif 50 | 51 | .cache: 52 | @ mkdir -p .cache 53 | 54 | # TEST ######################################################################## 55 | 56 | RANDOM_SEED ?= $(shell date +%s) 57 | FAILURES := .cache/pytest/v/cache/lastfailed 58 | 59 | PYTEST_OPTIONS := 60 | ifndef DISABLE_COVERAGE 61 | PYTEST_OPTIONS += --cov=$(PACKAGE) 62 | endif 63 | ifdef CI 64 | PYTEST_OPTIONS += --cov-report=xml 65 | endif 66 | PYTEST_RERUN_OPTIONS := --last-failed --exitfirst 67 | 68 | .PHONY: test 69 | test: test-all ## Run unit and integration tests 70 | 71 | .PHONY: test-unit 72 | test-unit: install 73 | @ ( mv $(FAILURES) $(FAILURES).bak || true ) > /dev/null 2>&1 74 | poetry run pytest $(PACKAGE) $(PYTEST_OPTIONS) 75 | @ ( mv $(FAILURES).bak $(FAILURES) || true ) > /dev/null 2>&1 76 | ifndef DISABLE_COVERAGE 77 | poetry run coveragespace update unit 78 | endif 79 | 80 | .PHONY: test-int 81 | test-int: install 82 | @ if test -e $(FAILURES); then poetry run pytest tests $(PYTEST_RERUN_OPTIONS); fi 83 | @ rm -rf $(FAILURES) 84 | poetry run pytest tests $(PYTEST_OPTIONS) 85 | ifndef DISABLE_COVERAGE 86 | poetry run coveragespace update integration 87 | endif 88 | 89 | .PHONY: test-all 90 | test-all: install 91 | @ if test -e $(FAILURES); then poetry run pytest $(PACKAGE) tests $(PYTEST_RERUN_OPTIONS); fi 92 | @ rm -rf $(FAILURES) 93 | poetry run pytest $(PACKAGE) tests $(PYTEST_OPTIONS) 94 | ifndef DISABLE_COVERAGE 95 | poetry run coveragespace update overall 96 | endif 97 | 98 | .PHONY: read-coverage 99 | read-coverage: 100 | bin/open htmlcov/index.html 101 | 102 | # CHECK ####################################################################### 103 | 104 | .PHONY: format 105 | format: install 106 | poetry run isort $(PACKAGE) tests notebooks 107 | poetry run black $(PACKAGE) tests notebooks 108 | @ echo 109 | 110 | .PHONY: check 111 | check: install format ## Run formatters, linters, and static analysis 112 | ifdef CI 113 | git diff --exit-code 114 | endif 115 | poetry run mypy $(PACKAGE) tests 116 | poetry run pylint $(PACKAGE) tests --rcfile=.pylint.ini 117 | poetry run pydocstyle $(PACKAGE) tests 118 | 119 | # DOCUMENTATION ############################################################### 120 | 121 | MKDOCS_INDEX := site/index.html 122 | 123 | .PHONY: docs 124 | docs: mkdocs uml ## Generate documentation and UML 125 | ifndef CI 126 | @ eval "sleep 3; bin/open http://127.0.0.1:8000" & 127 | poetry run mkdocs serve 128 | endif 129 | 130 | .PHONY: mkdocs 131 | mkdocs: install $(MKDOCS_INDEX) 132 | $(MKDOCS_INDEX): mkdocs.yml docs/*.md 133 | @ mkdir -p docs/about 134 | @ cd docs && ln -sf ../README.md index.md 135 | @ cd docs/about && ln -sf ../../CHANGELOG.md changelog.md 136 | @ cd docs/about && ln -sf ../../CONTRIBUTING.md contributing.md 137 | @ cd docs/about && ln -sf ../../LICENSE.md license.md 138 | poetry run mkdocs build --clean --strict 139 | 140 | .PHONY: uml 141 | uml: install docs/*.png 142 | docs/*.png: $(MODULES) 143 | poetry run pyreverse $(PACKAGE) -p $(PACKAGE) -a 1 -f ALL -o png --ignore tests 144 | - mv -f classes_$(PACKAGE).png docs/classes.png 145 | - mv -f packages_$(PACKAGE).png docs/packages.png 146 | 147 | # DEMO ######################################################################## 148 | 149 | .PHONY: run 150 | run: install ## Start the program 151 | poetry run python $(PACKAGE)/__main__.py 152 | 153 | .PHONY: shell 154 | shell: install ## Launch an IPython session 155 | poetry run ipython --ipython-dir=notebooks 156 | 157 | # BUILD ####################################################################### 158 | 159 | DIST_FILES := dist/*.tar.gz dist/*.whl 160 | EXE_FILES := dist/$(PACKAGE).* 161 | 162 | .PHONY: dist 163 | dist: install $(DIST_FILES) 164 | $(DIST_FILES): $(MODULES) pyproject.toml 165 | rm -f $(DIST_FILES) 166 | poetry build 167 | 168 | .PHONY: exe 169 | exe: install $(EXE_FILES) 170 | $(EXE_FILES): $(MODULES) $(PACKAGE).spec 171 | poetry run pyinstaller $(PACKAGE).spec --noconfirm --clean 172 | 173 | $(PACKAGE).spec: 174 | poetry run pyi-makespec $(PACKAGE)/__main__.py --onefile --windowed --name=$(PACKAGE) 175 | 176 | # RELEASE ##################################################################### 177 | 178 | .PHONY: upload 179 | upload: dist ## Upload the current version to PyPI 180 | git diff --name-only --exit-code 181 | poetry publish 182 | bin/open https://pypi.org/project/$(PROJECT) 183 | 184 | # CLEANUP ##################################################################### 185 | 186 | .PHONY: clean 187 | clean: .clean-build .clean-docs .clean-test .clean-install ## Delete all generated and temporary files 188 | 189 | .PHONY: clean-all 190 | clean-all: clean 191 | rm -rf $(VIRTUAL_ENV) 192 | 193 | .PHONY: .clean-install 194 | .clean-install: 195 | find $(PACKAGE) tests -name '__pycache__' -delete 196 | rm -rf *.egg-info 197 | 198 | .PHONY: .clean-test 199 | .clean-test: 200 | rm -rf .cache .pytest .coverage htmlcov 201 | 202 | .PHONY: .clean-docs 203 | .clean-docs: 204 | rm -rf docs/*.png site 205 | 206 | .PHONY: .clean-build 207 | .clean-build: 208 | rm -rf *.spec dist build 209 | 210 | # HELP ######################################################################## 211 | 212 | .PHONY: help 213 | help: install 214 | @ grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 215 | 216 | .DEFAULT_GOAL := help 217 | -------------------------------------------------------------------------------- /bin/verchew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # The MIT License (MIT) 5 | # Copyright © 2016, Jace Browning 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | # 25 | # Source: https://github.com/jacebrowning/verchew 26 | # Documentation: https://verchew.readthedocs.io 27 | # Package: https://pypi.org/project/verchew 28 | 29 | 30 | from __future__ import unicode_literals 31 | 32 | import argparse 33 | import logging 34 | import os 35 | import re 36 | import sys 37 | from collections import OrderedDict 38 | from subprocess import PIPE, STDOUT, Popen 39 | 40 | 41 | PY2 = sys.version_info[0] == 2 42 | 43 | if PY2: 44 | import ConfigParser as configparser 45 | from urllib import urlretrieve 46 | else: 47 | import configparser 48 | from urllib.request import urlretrieve 49 | 50 | __version__ = '3.4' 51 | 52 | SCRIPT_URL = ( 53 | "https://raw.githubusercontent.com/jacebrowning/verchew/main/verchew/script.py" 54 | ) 55 | WRAPPER_URL = ( 56 | "https://raw.githubusercontent.com/jacebrowning/verchew/main/verchew/wrapper.sh" 57 | ) 58 | 59 | CONFIG_FILENAMES = ['verchew.ini', '.verchew.ini', '.verchewrc', '.verchew'] 60 | 61 | SAMPLE_CONFIG = """ 62 | [Python] 63 | 64 | cli = python 65 | version = Python 3.5 || Python 3.6 66 | 67 | [Legacy Python] 68 | 69 | cli = python2 70 | version = Python 2.7 71 | 72 | [virtualenv] 73 | 74 | cli = virtualenv 75 | version = 15 76 | message = Only required with Python 2. 77 | 78 | [Make] 79 | 80 | cli = make 81 | version = GNU Make 82 | optional = true 83 | 84 | """.strip() 85 | 86 | STYLE = { 87 | "~": "✔", 88 | "?": "▴", 89 | "x": "✘", 90 | "#": "䷉", 91 | } 92 | 93 | COLOR = { 94 | "~": "\033[92m", # green 95 | "?": "\033[93m", # yellow 96 | "x": "\033[91m", # red 97 | "#": "\033[96m", # cyan 98 | None: "\033[0m", # reset 99 | } 100 | 101 | QUIET = False 102 | 103 | log = logging.getLogger(__name__) 104 | 105 | 106 | def main(): 107 | global QUIET 108 | 109 | args = parse_args() 110 | configure_logging(args.verbose) 111 | if args.quiet: 112 | QUIET = True 113 | 114 | log.debug("PWD: %s", os.getenv('PWD')) 115 | log.debug("PATH: %s", os.getenv('PATH')) 116 | 117 | if args.vendor: 118 | vendor_script(SCRIPT_URL, args.vendor) 119 | vendor_script(WRAPPER_URL, args.vendor + "-wrapper") 120 | sys.exit(0) 121 | 122 | path = find_config(args.root, generate=args.init) 123 | config = parse_config(path) 124 | 125 | if not check_dependencies(config) and args.exit_code: 126 | sys.exit(1) 127 | 128 | 129 | def parse_args(): 130 | parser = argparse.ArgumentParser(description="System dependency version checker.",) 131 | 132 | version = "%(prog)s v" + __version__ 133 | parser.add_argument( 134 | '--version', action='version', version=version, 135 | ) 136 | parser.add_argument( 137 | '-r', '--root', metavar='PATH', help="specify a custom project root directory" 138 | ) 139 | parser.add_argument( 140 | '--exit-code', 141 | action='store_true', 142 | help="return a non-zero exit code on failure", 143 | ) 144 | 145 | group_logging = parser.add_mutually_exclusive_group() 146 | group_logging.add_argument( 147 | '-v', '--verbose', action='count', default=0, help="enable verbose logging" 148 | ) 149 | group_logging.add_argument( 150 | '-q', '--quiet', action='store_true', help="suppress all output on success" 151 | ) 152 | 153 | group_commands = parser.add_argument_group('commands') 154 | group_commands.add_argument( 155 | '--init', action='store_true', help="generate a sample configuration file" 156 | ) 157 | 158 | group_commands.add_argument( 159 | '--vendor', metavar='PATH', help="download the program for offline use" 160 | ) 161 | 162 | args = parser.parse_args() 163 | 164 | return args 165 | 166 | 167 | def configure_logging(count=0): 168 | if count == 0: 169 | level = logging.WARNING 170 | elif count == 1: 171 | level = logging.INFO 172 | else: 173 | level = logging.DEBUG 174 | 175 | logging.basicConfig(level=level, format="%(levelname)s: %(message)s") 176 | 177 | 178 | def vendor_script(url, path): 179 | root = os.path.abspath(os.path.join(path, os.pardir)) 180 | if not os.path.isdir(root): 181 | log.info("Creating directory %s", root) 182 | os.makedirs(root) 183 | 184 | log.info("Downloading %s to %s", url, path) 185 | urlretrieve(url, path) 186 | 187 | log.debug("Making %s executable", path) 188 | mode = os.stat(path).st_mode 189 | os.chmod(path, mode | 0o111) 190 | 191 | 192 | def find_config(root=None, filenames=None, generate=False): 193 | root = root or os.getcwd() 194 | filenames = filenames or CONFIG_FILENAMES 195 | 196 | path = None 197 | log.info("Looking for config file in: %s", root) 198 | log.debug("Filename options: %s", ", ".join(filenames)) 199 | for filename in os.listdir(root): 200 | if filename in filenames: 201 | path = os.path.join(root, filename) 202 | log.info("Found config file: %s", path) 203 | return path 204 | 205 | if generate: 206 | path = generate_config(root, filenames) 207 | return path 208 | 209 | msg = "No config file found in: {0}".format(root) 210 | raise RuntimeError(msg) 211 | 212 | 213 | def generate_config(root=None, filenames=None): 214 | root = root or os.getcwd() 215 | filenames = filenames or CONFIG_FILENAMES 216 | 217 | path = os.path.join(root, filenames[0]) 218 | 219 | log.info("Generating sample config: %s", path) 220 | with open(path, 'w') as config: 221 | config.write(SAMPLE_CONFIG + '\n') 222 | 223 | return path 224 | 225 | 226 | def parse_config(path): 227 | data = OrderedDict() # type: ignore 228 | 229 | log.info("Parsing config file: %s", path) 230 | config = configparser.ConfigParser() 231 | config.read(path) 232 | 233 | for section in config.sections(): 234 | data[section] = OrderedDict() 235 | for name, value in config.items(section): 236 | data[section][name] = value 237 | 238 | for name in data: 239 | version = data[name].get('version') or "" 240 | data[name]['version'] = version 241 | data[name]['patterns'] = [v.strip() for v in version.split('||')] 242 | 243 | data[name]['optional'] = data[name].get( 244 | 'optional', 'false' 245 | ).strip().lower() in ('true', 'yes', 'y', True) 246 | 247 | return data 248 | 249 | 250 | def check_dependencies(config): 251 | success = [] 252 | 253 | for name, settings in config.items(): 254 | show("Checking for {0}...".format(name), head=True) 255 | output = get_version(settings['cli'], settings.get('cli_version_arg')) 256 | 257 | for pattern in settings['patterns']: 258 | if match_version(pattern, output): 259 | show(_("~") + " MATCHED: {0}".format(pattern or "")) 260 | success.append(_("~")) 261 | break 262 | else: 263 | if settings.get('optional'): 264 | show(_("?") + " EXPECTED (OPTIONAL): {0}".format(settings['version'])) 265 | success.append(_("?")) 266 | else: 267 | if QUIET: 268 | if "not found" in output: 269 | actual = "Not found" 270 | else: 271 | actual = output.split('\n', maxsplit=1)[0].strip('.') 272 | expected = settings['version'] or "" 273 | print("{0}: {1}, EXPECTED: {2}".format(name, actual, expected)) 274 | show( 275 | _("x") 276 | + " EXPECTED: {0}".format(settings['version'] or "") 277 | ) 278 | success.append(_("x")) 279 | if settings.get('message'): 280 | show(_("#") + " MESSAGE: {0}".format(settings['message'])) 281 | 282 | show("Results: " + " ".join(success), head=True) 283 | 284 | return _("x") not in success 285 | 286 | 287 | def get_version(program, argument=None): 288 | if argument is None: 289 | args = [program, '--version'] 290 | elif argument: 291 | args = [program] + argument.split() 292 | else: 293 | args = [program] 294 | 295 | show("$ {0}".format(" ".join(args))) 296 | output = call(args) 297 | lines = output.splitlines() 298 | 299 | if lines: 300 | for line in lines: 301 | if any(char.isdigit() for char in line): 302 | show(line) 303 | break 304 | else: 305 | show(lines[0]) 306 | else: 307 | show("") 308 | 309 | return output 310 | 311 | 312 | def match_version(pattern, output): 313 | lines = output.splitlines() 314 | if "not found" in lines[0]: 315 | return False 316 | 317 | regex = pattern.replace('.', r'\.') + r'(\b|/)' 318 | 319 | for line in lines: 320 | log.debug("Matching %s: %s", regex, line) 321 | match = re.match(regex, line) 322 | if match is None: 323 | log.debug("Matching %s: %s", regex, line) 324 | match = re.match(r'.*[^\d.]' + regex, line) 325 | if match: 326 | return True 327 | 328 | return False 329 | 330 | 331 | def call(args): 332 | try: 333 | process = Popen(args, stdout=PIPE, stderr=STDOUT) 334 | except OSError: 335 | log.debug("Command not found: %s", args[0]) 336 | output = "sh: command not found: {0}".format(args[0]) 337 | else: 338 | raw = process.communicate()[0] 339 | output = raw.decode('utf-8').strip() 340 | log.debug("Command output: %r", output) 341 | 342 | return output 343 | 344 | 345 | def show(text, start='', end='\n', head=False): 346 | """Python 2 and 3 compatible version of print.""" 347 | if QUIET: 348 | return 349 | 350 | if head: 351 | start = '\n' 352 | end = '\n\n' 353 | 354 | if log.getEffectiveLevel() < logging.WARNING: 355 | log.info(text) 356 | else: 357 | formatted = start + text + end 358 | if PY2: 359 | formatted = formatted.encode('utf-8') 360 | sys.stdout.write(formatted) 361 | sys.stdout.flush() 362 | 363 | 364 | def _(word, is_tty=None, supports_utf8=None, supports_ansi=None): 365 | """Format and colorize a word based on available encoding.""" 366 | formatted = word 367 | 368 | if is_tty is None: 369 | is_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() 370 | if supports_utf8 is None: 371 | supports_utf8 = str(sys.stdout.encoding).lower() == 'utf-8' 372 | if supports_ansi is None: 373 | supports_ansi = sys.platform != 'win32' or 'ANSICON' in os.environ 374 | 375 | style_support = supports_utf8 376 | color_support = is_tty and supports_ansi 377 | 378 | if style_support: 379 | formatted = STYLE.get(word, word) 380 | 381 | if color_support and COLOR.get(word): 382 | formatted = COLOR[word] + formatted + COLOR[None] 383 | 384 | return formatted 385 | 386 | 387 | if __name__ == '__main__': # pragma: no cover 388 | main() 389 | -------------------------------------------------------------------------------- /.pylint.ini: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # A comma-separated list of package or module names from where C extensions may 4 | # be loaded. Extensions are loading into the active Python interpreter and may 5 | # run arbitrary code 6 | extension-pkg-whitelist= 7 | 8 | # Add files or directories to the blacklist. They should be base names, not 9 | # paths. 10 | ignore=CVS 11 | 12 | # Add files or directories matching the regex patterns to the blacklist. The 13 | # regex matches against base names, not paths. 14 | ignore-patterns= 15 | 16 | # Python code to execute, usually for sys.path manipulation such as 17 | # pygtk.require(). 18 | #init-hook= 19 | 20 | # Use multiple processes to speed up Pylint. 21 | jobs=1 22 | 23 | # List of plugins (as comma separated values of python modules names) to load, 24 | # usually to register additional checkers. 25 | load-plugins= 26 | 27 | # Pickle collected data for later comparisons. 28 | persistent=yes 29 | 30 | # Specify a configuration file. 31 | #rcfile= 32 | 33 | # Allow loading of arbitrary C extensions. Extensions are imported into the 34 | # active Python interpreter and may run arbitrary code. 35 | unsafe-load-any-extension=no 36 | 37 | 38 | [MESSAGES CONTROL] 39 | 40 | # Only show warnings with the listed confidence levels. Leave empty to show 41 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED 42 | confidence= 43 | 44 | # Disable the message, report, category or checker with the given id(s). You 45 | # can either give multiple identifiers separated by comma (,) or put this 46 | # option multiple times (only on the command line, not in the configuration 47 | # file where it should appear only once).You can also use "--disable=all" to 48 | # disable everything first and then reenable specific checks. For example, if 49 | # you want to run only the similarities checker, you can use "--disable=all 50 | # --enable=similarities". If you want to run only the classes checker, but have 51 | # no Warning level messages displayed, use"--disable=all --enable=classes 52 | # --disable=W" 53 | disable= 54 | fixme, 55 | global-statement, 56 | invalid-name, 57 | missing-docstring, 58 | redefined-outer-name, 59 | too-few-public-methods, 60 | too-many-locals, 61 | too-many-arguments, 62 | unnecessary-pass, 63 | broad-except, 64 | duplicate-code, 65 | too-many-branches, 66 | too-many-return-statements, 67 | too-many-public-methods, 68 | too-many-ancestors, 69 | too-many-instance-attributes, 70 | too-many-statements, 71 | attribute-defined-outside-init, 72 | unsupported-assignment-operation, 73 | unsupported-delete-operation, 74 | too-many-nested-blocks, 75 | protected-access, 76 | 77 | # Enable the message, report, category or checker with the given id(s). You can 78 | # either give multiple identifier separated by comma (,) or put this option 79 | # multiple time (only on the command line, not in the configuration file where 80 | # it should appear only once). See also the "--disable" option for examples. 81 | enable= 82 | 83 | 84 | [REPORTS] 85 | 86 | # Python expression which should return a note less than 10 (10 is the highest 87 | # note). You have access to the variables errors warning, statement which 88 | # respectively contain the number of errors / warnings messages and the total 89 | # number of statements analyzed. This is used by the global evaluation report 90 | # (RP0004). 91 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 92 | 93 | # Template used to display messages. This is a python new-style format string 94 | # used to format the message information. See doc for all details 95 | #msg-template= 96 | 97 | # Set the output format. Available formats are text, parseable, colorized, json 98 | # and msvs (visual studio).You can also give a reporter class, eg 99 | # mypackage.mymodule.MyReporterClass. 100 | output-format=text 101 | 102 | # Tells whether to display a full report or only the messages 103 | reports=no 104 | 105 | # Activate the evaluation score. 106 | score=no 107 | 108 | 109 | [REFACTORING] 110 | 111 | # Maximum number of nested blocks for function / method body 112 | max-nested-blocks=5 113 | 114 | 115 | [BASIC] 116 | 117 | # Regular expression matching correct argument names 118 | argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ 119 | 120 | # Regular expression matching correct attribute names 121 | attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ 122 | 123 | # Bad variable names which should always be refused, separated by a comma 124 | bad-names=foo,bar,baz,toto,tutu,tata 125 | 126 | # Regular expression matching correct class attribute names 127 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ 128 | 129 | # Regular expression matching correct class names 130 | class-rgx=[A-Z_][a-zA-Z0-9]+$ 131 | 132 | # Regular expression matching correct constant names 133 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ 134 | 135 | # Minimum line length for functions/classes that require docstrings, shorter 136 | # ones are exempt. 137 | docstring-min-length=-1 138 | 139 | # Regular expression matching correct function names 140 | function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ 141 | 142 | # Good variable names which should always be accepted, separated by a comma 143 | good-names=i,j,k,ex,Run,_ 144 | 145 | # Include a hint for the correct naming format with invalid-name 146 | include-naming-hint=no 147 | 148 | # Regular expression matching correct inline iteration names 149 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ 150 | 151 | # Regular expression matching correct method names 152 | method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ 153 | 154 | # Regular expression matching correct module names 155 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 156 | 157 | # Colon-delimited sets of names that determine each other's naming style when 158 | # the name regexes allow several styles. 159 | name-group= 160 | 161 | # Regular expression which should only match function or class names that do 162 | # not require a docstring. 163 | no-docstring-rgx=^_ 164 | 165 | # List of decorators that produce properties, such as abc.abstractproperty. Add 166 | # to this list to register other decorators that produce valid properties. 167 | property-classes=abc.abstractproperty 168 | 169 | # Regular expression matching correct variable names 170 | variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ 171 | 172 | 173 | [FORMAT] 174 | 175 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 176 | expected-line-ending-format= 177 | 178 | # Regexp for a line that is allowed to be longer than the limit. 179 | ignore-long-lines=^.*((https?:)|(pragma:)|(TODO:)).*$ 180 | 181 | # Number of spaces of indent required inside a hanging or continued line. 182 | indent-after-paren=4 183 | 184 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 185 | # tab). 186 | indent-string=' ' 187 | 188 | # Maximum number of characters on a single line. 189 | max-line-length=88 190 | 191 | # Maximum number of lines in a module 192 | max-module-lines=1000 193 | 194 | # Allow the body of a class to be on the same line as the declaration if body 195 | # contains single statement. 196 | single-line-class-stmt=no 197 | 198 | # Allow the body of an if to be on the same line as the test if there is no 199 | # else. 200 | single-line-if-stmt=no 201 | 202 | 203 | [LOGGING] 204 | 205 | # Logging modules to check that the string format arguments are in logging 206 | # function parameter format 207 | logging-modules=logging 208 | 209 | 210 | [MISCELLANEOUS] 211 | 212 | # List of note tags to take in consideration, separated by a comma. 213 | notes=FIXME,XXX,TODO 214 | 215 | 216 | [SIMILARITIES] 217 | 218 | # Ignore comments when computing similarities. 219 | ignore-comments=yes 220 | 221 | # Ignore docstrings when computing similarities. 222 | ignore-docstrings=yes 223 | 224 | # Ignore imports when computing similarities. 225 | ignore-imports=no 226 | 227 | # Minimum lines number of a similarity. 228 | min-similarity-lines=4 229 | 230 | 231 | [SPELLING] 232 | 233 | # Spelling dictionary name. Available dictionaries: none. To make it working 234 | # install python-enchant package. 235 | spelling-dict= 236 | 237 | # List of comma separated words that should not be checked. 238 | spelling-ignore-words= 239 | 240 | # A path to a file that contains private dictionary; one word per line. 241 | spelling-private-dict-file= 242 | 243 | # Tells whether to store unknown words to indicated private dictionary in 244 | # --spelling-private-dict-file option instead of raising a message. 245 | spelling-store-unknown-words=no 246 | 247 | 248 | [TYPECHECK] 249 | 250 | # List of decorators that produce context managers, such as 251 | # contextlib.contextmanager. Add to this list to register other decorators that 252 | # produce valid context managers. 253 | contextmanager-decorators=contextlib.contextmanager 254 | 255 | # List of members which are set dynamically and missed by pylint inference 256 | # system, and so shouldn't trigger E1101 when accessed. Python regular 257 | # expressions are accepted. 258 | generated-members= 259 | 260 | # Tells whether missing members accessed in mixin class should be ignored. A 261 | # mixin class is detected if its name ends with "mixin" (case insensitive). 262 | ignore-mixin-members=yes 263 | 264 | # This flag controls whether pylint should warn about no-member and similar 265 | # checks whenever an opaque object is returned when inferring. The inference 266 | # can return multiple potential results while evaluating a Python object, but 267 | # some branches might not be evaluated, which results in partial inference. In 268 | # that case, it might be useful to still emit no-member and other checks for 269 | # the rest of the inferred objects. 270 | ignore-on-opaque-inference=yes 271 | 272 | # List of class names for which member attributes should not be checked (useful 273 | # for classes with dynamically set attributes). This supports the use of 274 | # qualified names. 275 | ignored-classes=optparse.Values,thread._local,_thread._local 276 | 277 | # List of module names for which member attributes should not be checked 278 | # (useful for modules/projects where namespaces are manipulated during runtime 279 | # and thus existing member attributes cannot be deduced by static analysis. It 280 | # supports qualified module names, as well as Unix pattern matching. 281 | ignored-modules= 282 | 283 | # Show a hint with possible names when a member name was not found. The aspect 284 | # of finding the hint is based on edit distance. 285 | missing-member-hint=yes 286 | 287 | # The minimum edit distance a name should have in order to be considered a 288 | # similar match for a missing member name. 289 | missing-member-hint-distance=1 290 | 291 | # The total number of similar names that should be taken in consideration when 292 | # showing a hint for a missing member. 293 | missing-member-max-choices=1 294 | 295 | 296 | [VARIABLES] 297 | 298 | # List of additional names supposed to be defined in builtins. Remember that 299 | # you should avoid to define new builtins when possible. 300 | additional-builtins= 301 | 302 | # Tells whether unused global variables should be treated as a violation. 303 | allow-global-unused-variables=yes 304 | 305 | # List of strings which can identify a callback function by name. A callback 306 | # name must start or end with one of those strings. 307 | callbacks=cb_,_cb 308 | 309 | # A regular expression matching the name of dummy variables (i.e. expectedly 310 | # not used). 311 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 312 | 313 | # Argument names that match this expression will be ignored. Default to name 314 | # with leading underscore 315 | ignored-argument-names=_.*|^ignored_|^unused_ 316 | 317 | # Tells whether we should check for unused import in __init__ files. 318 | init-import=no 319 | 320 | # List of qualified module names which can have objects that can redefine 321 | # builtins. 322 | redefining-builtins-modules=six.moves,future.builtins 323 | 324 | 325 | [CLASSES] 326 | 327 | # List of method names used to declare (i.e. assign) instance attributes. 328 | defining-attr-methods=__init__,__new__,setUp 329 | 330 | # List of member names, which should be excluded from the protected access 331 | # warning. 332 | exclude-protected=_asdict,_fields,_replace,_source,_make 333 | 334 | # List of valid names for the first argument in a class method. 335 | valid-classmethod-first-arg=cls 336 | 337 | # List of valid names for the first argument in a metaclass class method. 338 | valid-metaclass-classmethod-first-arg=mcs 339 | 340 | 341 | [DESIGN] 342 | 343 | # Maximum number of arguments for function / method 344 | max-args=5 345 | 346 | # Maximum number of attributes for a class (see R0902). 347 | max-attributes=7 348 | 349 | # Maximum number of boolean expressions in a if statement 350 | max-bool-expr=5 351 | 352 | # Maximum number of branch for function / method body 353 | max-branches=12 354 | 355 | # Maximum number of locals for function / method body 356 | max-locals=15 357 | 358 | # Maximum number of parents for a class (see R0901). 359 | max-parents=7 360 | 361 | # Maximum number of public methods for a class (see R0904). 362 | max-public-methods=20 363 | 364 | # Maximum number of return / yield for function / method body 365 | max-returns=6 366 | 367 | # Maximum number of statements in function / method body 368 | max-statements=50 369 | 370 | # Minimum number of public methods for a class (see R0903). 371 | min-public-methods=2 372 | 373 | 374 | [IMPORTS] 375 | 376 | # Allow wildcard imports from modules that define __all__. 377 | allow-wildcard-with-all=no 378 | 379 | # Analyse import fallback blocks. This can be used to support both Python 2 and 380 | # 3 compatible code, which means that the block might have code that exists 381 | # only in one or another interpreter, leading to false positives when analysed. 382 | analyse-fallback-blocks=no 383 | 384 | # Deprecated modules which should not be used, separated by a comma 385 | deprecated-modules=regsub,TERMIOS,Bastion,rexec 386 | 387 | # Create a graph of external dependencies in the given file (report RP0402 must 388 | # not be disabled) 389 | ext-import-graph= 390 | 391 | # Create a graph of every (i.e. internal and external) dependencies in the 392 | # given file (report RP0402 must not be disabled) 393 | import-graph= 394 | 395 | # Create a graph of internal dependencies in the given file (report RP0402 must 396 | # not be disabled) 397 | int-import-graph= 398 | 399 | # Force import order to recognize a module as part of the standard 400 | # compatibility libraries. 401 | known-standard-library= 402 | 403 | # Force import order to recognize a module as part of a third party library. 404 | known-third-party=enchant 405 | 406 | 407 | [EXCEPTIONS] 408 | 409 | # Exceptions that will emit a warning when being caught. Defaults to 410 | # "Exception" 411 | overgeneral-exceptions=Exception 412 | -------------------------------------------------------------------------------- /notebooks/profile_default/ipython_config.py: -------------------------------------------------------------------------------- 1 | # Configuration file for ipython. 2 | 3 | # ------------------------------------------------------------------------------ 4 | # InteractiveShellApp(Configurable) configuration 5 | # ------------------------------------------------------------------------------ 6 | 7 | ## A Mixin for applications that start InteractiveShell instances. 8 | # 9 | # Provides configurables for loading extensions and executing files as part of 10 | # configuring a Shell environment. 11 | # 12 | # The following methods should be called by the :meth:`initialize` method of the 13 | # subclass: 14 | # 15 | # - :meth:`init_path` 16 | # - :meth:`init_shell` (to be implemented by the subclass) 17 | # - :meth:`init_gui_pylab` 18 | # - :meth:`init_extensions` 19 | # - :meth:`init_code` 20 | 21 | ## Execute the given command string. 22 | # c.InteractiveShellApp.code_to_run = '' 23 | 24 | ## Run the file referenced by the PYTHONSTARTUP environment variable at IPython 25 | # startup. 26 | # c.InteractiveShellApp.exec_PYTHONSTARTUP = True 27 | 28 | ## List of files to run at IPython startup. 29 | # c.InteractiveShellApp.exec_files = [] 30 | 31 | ## lines of code to run at IPython startup. 32 | c.InteractiveShellApp.exec_lines = ["%autoreload 2"] 33 | 34 | ## A list of dotted module names of IPython extensions to load. 35 | c.InteractiveShellApp.extensions = ["autoreload"] 36 | 37 | ## dotted module name of an IPython extension to load. 38 | # c.InteractiveShellApp.extra_extension = '' 39 | 40 | ## A file to be run 41 | # c.InteractiveShellApp.file_to_run = '' 42 | 43 | ## Enable GUI event loop integration with any of ('asyncio', 'glut', 'gtk', 44 | # 'gtk2', 'gtk3', 'osx', 'pyglet', 'qt', 'qt4', 'qt5', 'tk', 'wx', 'gtk2', 45 | # 'qt4'). 46 | # c.InteractiveShellApp.gui = None 47 | 48 | ## Should variables loaded at startup (by startup files, exec_lines, etc.) be 49 | # hidden from tools like %who? 50 | # c.InteractiveShellApp.hide_initial_ns = True 51 | 52 | ## Configure matplotlib for interactive use with the default matplotlib backend. 53 | # c.InteractiveShellApp.matplotlib = None 54 | 55 | ## Run the module as a script. 56 | # c.InteractiveShellApp.module_to_run = '' 57 | 58 | ## Pre-load matplotlib and numpy for interactive use, selecting a particular 59 | # matplotlib backend and loop integration. 60 | # c.InteractiveShellApp.pylab = None 61 | 62 | ## If true, IPython will populate the user namespace with numpy, pylab, etc. and 63 | # an ``import *`` is done from numpy and pylab, when using pylab mode. 64 | # 65 | # When False, pylab mode should not import any names into the user namespace. 66 | # c.InteractiveShellApp.pylab_import_all = True 67 | 68 | ## Reraise exceptions encountered loading IPython extensions? 69 | # c.InteractiveShellApp.reraise_ipython_extension_failures = False 70 | 71 | # ------------------------------------------------------------------------------ 72 | # Application(SingletonConfigurable) configuration 73 | # ------------------------------------------------------------------------------ 74 | 75 | ## This is an application. 76 | 77 | ## The date format used by logging formatters for %(asctime)s 78 | # c.Application.log_datefmt = '%Y-%m-%d %H:%M:%S' 79 | 80 | ## The Logging format template 81 | # c.Application.log_format = '[%(name)s]%(highlevel)s %(message)s' 82 | 83 | ## Set the log level by value or name. 84 | # c.Application.log_level = 30 85 | 86 | # ------------------------------------------------------------------------------ 87 | # BaseIPythonApplication(Application) configuration 88 | # ------------------------------------------------------------------------------ 89 | 90 | ## IPython: an enhanced interactive Python shell. 91 | 92 | ## Whether to create profile dir if it doesn't exist 93 | # c.BaseIPythonApplication.auto_create = False 94 | 95 | ## Whether to install the default config files into the profile dir. If a new 96 | # profile is being created, and IPython contains config files for that profile, 97 | # then they will be staged into the new directory. Otherwise, default config 98 | # files will be automatically generated. 99 | # c.BaseIPythonApplication.copy_config_files = False 100 | 101 | ## Path to an extra config file to load. 102 | # 103 | # If specified, load this config file in addition to any other IPython config. 104 | # c.BaseIPythonApplication.extra_config_file = '' 105 | 106 | ## The name of the IPython directory. This directory is used for logging 107 | # configuration (through profiles), history storage, etc. The default is usually 108 | # $HOME/.ipython. This option can also be specified through the environment 109 | # variable IPYTHONDIR. 110 | # c.BaseIPythonApplication.ipython_dir = '' 111 | 112 | ## Whether to overwrite existing config files when copying 113 | # c.BaseIPythonApplication.overwrite = False 114 | 115 | ## The IPython profile to use. 116 | # c.BaseIPythonApplication.profile = 'default' 117 | 118 | ## Create a massive crash report when IPython encounters what may be an internal 119 | # error. The default is to append a short message to the usual traceback 120 | # c.BaseIPythonApplication.verbose_crash = False 121 | 122 | # ------------------------------------------------------------------------------ 123 | # TerminalIPythonApp(BaseIPythonApplication,InteractiveShellApp) configuration 124 | # ------------------------------------------------------------------------------ 125 | 126 | ## Whether to display a banner upon starting IPython. 127 | # c.TerminalIPythonApp.display_banner = True 128 | 129 | ## If a command or file is given via the command-line, e.g. 'ipython foo.py', 130 | # start an interactive shell after executing the file or command. 131 | # c.TerminalIPythonApp.force_interact = False 132 | 133 | ## Class to use to instantiate the TerminalInteractiveShell object. Useful for 134 | # custom Frontends 135 | # c.TerminalIPythonApp.interactive_shell_class = 'IPython.terminal.interactiveshell.TerminalInteractiveShell' 136 | 137 | ## Start IPython quickly by skipping the loading of config files. 138 | # c.TerminalIPythonApp.quick = False 139 | 140 | # ------------------------------------------------------------------------------ 141 | # InteractiveShell(SingletonConfigurable) configuration 142 | # ------------------------------------------------------------------------------ 143 | 144 | ## An enhanced, interactive shell for Python. 145 | 146 | ## 'all', 'last', 'last_expr' or 'none', 'last_expr_or_assign' specifying which 147 | # nodes should be run interactively (displaying output from expressions). 148 | # c.InteractiveShell.ast_node_interactivity = 'last_expr' 149 | 150 | ## A list of ast.NodeTransformer subclass instances, which will be applied to 151 | # user input before code is run. 152 | # c.InteractiveShell.ast_transformers = [] 153 | 154 | ## Automatically run await statement in the top level repl. 155 | # c.InteractiveShell.autoawait = True 156 | 157 | ## Make IPython automatically call any callable object even if you didn't type 158 | # explicit parentheses. For example, 'str 43' becomes 'str(43)' automatically. 159 | # The value can be '0' to disable the feature, '1' for 'smart' autocall, where 160 | # it is not applied if there are no more arguments on the line, and '2' for 161 | # 'full' autocall, where all callable objects are automatically called (even if 162 | # no arguments are present). 163 | # c.InteractiveShell.autocall = 0 164 | 165 | ## Autoindent IPython code entered interactively. 166 | # c.InteractiveShell.autoindent = True 167 | 168 | ## Enable magic commands to be called without the leading %. 169 | # c.InteractiveShell.automagic = True 170 | 171 | ## The part of the banner to be printed before the profile 172 | # c.InteractiveShell.banner1 = "Python 3.8.1 (default, Jan 9 2020, 14:37:22) \nType 'copyright', 'credits' or 'license' for more information\nIPython 7.12.0 -- An enhanced Interactive Python. Type '?' for help.\n" 173 | 174 | ## The part of the banner to be printed after the profile 175 | # c.InteractiveShell.banner2 = '' 176 | 177 | ## Set the size of the output cache. The default is 1000, you can change it 178 | # permanently in your config file. Setting it to 0 completely disables the 179 | # caching system, and the minimum value accepted is 3 (if you provide a value 180 | # less than 3, it is reset to 0 and a warning is issued). This limit is defined 181 | # because otherwise you'll spend more time re-flushing a too small cache than 182 | # working 183 | # c.InteractiveShell.cache_size = 1000 184 | 185 | ## Use colors for displaying information about objects. Because this information 186 | # is passed through a pager (like 'less'), and some pagers get confused with 187 | # color codes, this capability can be turned off. 188 | # c.InteractiveShell.color_info = True 189 | 190 | ## Set the color scheme (NoColor, Neutral, Linux, or LightBG). 191 | # c.InteractiveShell.colors = 'Neutral' 192 | 193 | ## 194 | # c.InteractiveShell.debug = False 195 | 196 | ## Don't call post-execute functions that have failed in the past. 197 | # c.InteractiveShell.disable_failing_post_execute = False 198 | 199 | ## If True, anything that would be passed to the pager will be displayed as 200 | # regular output instead. 201 | # c.InteractiveShell.display_page = False 202 | 203 | ## (Provisional API) enables html representation in mime bundles sent to pagers. 204 | # c.InteractiveShell.enable_html_pager = False 205 | 206 | ## Total length of command history 207 | # c.InteractiveShell.history_length = 10000 208 | 209 | ## The number of saved history entries to be loaded into the history buffer at 210 | # startup. 211 | # c.InteractiveShell.history_load_length = 1000 212 | 213 | ## 214 | # c.InteractiveShell.ipython_dir = '' 215 | 216 | ## Start logging to the given file in append mode. Use `logfile` to specify a log 217 | # file to **overwrite** logs to. 218 | # c.InteractiveShell.logappend = '' 219 | 220 | ## The name of the logfile to use. 221 | # c.InteractiveShell.logfile = '' 222 | 223 | ## Start logging to the default log file in overwrite mode. Use `logappend` to 224 | # specify a log file to **append** logs to. 225 | # c.InteractiveShell.logstart = False 226 | 227 | ## Select the loop runner that will be used to execute top-level asynchronous 228 | # code 229 | # c.InteractiveShell.loop_runner = 'IPython.core.interactiveshell._asyncio_runner' 230 | 231 | ## 232 | # c.InteractiveShell.object_info_string_level = 0 233 | 234 | ## Automatically call the pdb debugger after every exception. 235 | # c.InteractiveShell.pdb = False 236 | 237 | ## Deprecated since IPython 4.0 and ignored since 5.0, set 238 | # TerminalInteractiveShell.prompts object directly. 239 | # c.InteractiveShell.prompt_in1 = 'In [\\#]: ' 240 | 241 | ## Deprecated since IPython 4.0 and ignored since 5.0, set 242 | # TerminalInteractiveShell.prompts object directly. 243 | # c.InteractiveShell.prompt_in2 = ' .\\D.: ' 244 | 245 | ## Deprecated since IPython 4.0 and ignored since 5.0, set 246 | # TerminalInteractiveShell.prompts object directly. 247 | # c.InteractiveShell.prompt_out = 'Out[\\#]: ' 248 | 249 | ## Deprecated since IPython 4.0 and ignored since 5.0, set 250 | # TerminalInteractiveShell.prompts object directly. 251 | # c.InteractiveShell.prompts_pad_left = True 252 | 253 | ## 254 | # c.InteractiveShell.quiet = False 255 | 256 | ## 257 | # c.InteractiveShell.separate_in = '\n' 258 | 259 | ## 260 | # c.InteractiveShell.separate_out = '' 261 | 262 | ## 263 | # c.InteractiveShell.separate_out2 = '' 264 | 265 | ## Show rewritten input, e.g. for autocall. 266 | # c.InteractiveShell.show_rewritten_input = True 267 | 268 | ## Enables rich html representation of docstrings. (This requires the docrepr 269 | # module). 270 | # c.InteractiveShell.sphinxify_docstring = False 271 | 272 | ## 273 | # c.InteractiveShell.wildcards_case_sensitive = True 274 | 275 | ## Switch modes for the IPython exception handlers. 276 | # c.InteractiveShell.xmode = 'Context' 277 | 278 | # ------------------------------------------------------------------------------ 279 | # TerminalInteractiveShell(InteractiveShell) configuration 280 | # ------------------------------------------------------------------------------ 281 | 282 | ## Autoformatter to reformat Terminal code. Can be `'black'` or `None` 283 | # c.TerminalInteractiveShell.autoformatter = None 284 | 285 | ## Set to confirm when you try to exit IPython with an EOF (Control-D in Unix, 286 | # Control-Z/Enter in Windows). By typing 'exit' or 'quit', you can force a 287 | # direct exit without any confirmation. 288 | # c.TerminalInteractiveShell.confirm_exit = True 289 | 290 | ## Options for displaying tab completions, 'column', 'multicolumn', and 291 | # 'readlinelike'. These options are for `prompt_toolkit`, see `prompt_toolkit` 292 | # documentation for more information. 293 | # c.TerminalInteractiveShell.display_completions = 'multicolumn' 294 | 295 | ## Shortcut style to use at the prompt. 'vi' or 'emacs'. 296 | # c.TerminalInteractiveShell.editing_mode = 'emacs' 297 | 298 | ## Set the editor used by IPython (default to $EDITOR/vi/notepad). 299 | # c.TerminalInteractiveShell.editor = 'vim' 300 | 301 | ## Allows to enable/disable the prompt toolkit history search 302 | # c.TerminalInteractiveShell.enable_history_search = True 303 | 304 | ## Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. This is 305 | # in addition to the F2 binding, which is always enabled. 306 | # c.TerminalInteractiveShell.extra_open_editor_shortcuts = False 307 | 308 | ## Provide an alternative handler to be called when the user presses Return. This 309 | # is an advanced option intended for debugging, which may be changed or removed 310 | # in later releases. 311 | # c.TerminalInteractiveShell.handle_return = None 312 | 313 | ## Highlight matching brackets. 314 | # c.TerminalInteractiveShell.highlight_matching_brackets = True 315 | 316 | ## The name or class of a Pygments style to use for syntax highlighting. To see 317 | # available styles, run `pygmentize -L styles`. 318 | # c.TerminalInteractiveShell.highlighting_style = traitlets.Undefined 319 | 320 | ## Override highlighting format for specific tokens 321 | # c.TerminalInteractiveShell.highlighting_style_overrides = {} 322 | 323 | ## 324 | # c.TerminalInteractiveShell.mime_renderers = {} 325 | 326 | ## Enable mouse support in the prompt (Note: prevents selecting text with the 327 | # mouse) 328 | # c.TerminalInteractiveShell.mouse_support = False 329 | 330 | ## Display the current vi mode (when using vi editing mode). 331 | # c.TerminalInteractiveShell.prompt_includes_vi_mode = True 332 | 333 | ## Class used to generate Prompt token for prompt_toolkit 334 | # c.TerminalInteractiveShell.prompts_class = 'IPython.terminal.prompts.Prompts' 335 | 336 | ## Use `raw_input` for the REPL, without completion and prompt colors. 337 | # 338 | # Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. 339 | # Known usage are: IPython own testing machinery, and emacs inferior-shell 340 | # integration through elpy. 341 | # 342 | # This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT` environment 343 | # variable is set, or the current terminal is not a tty. 344 | # c.TerminalInteractiveShell.simple_prompt = False 345 | 346 | ## Number of line at the bottom of the screen to reserve for the completion menu 347 | # c.TerminalInteractiveShell.space_for_menu = 6 348 | 349 | ## Automatically set the terminal title 350 | # c.TerminalInteractiveShell.term_title = True 351 | 352 | ## Customize the terminal title format. This is a python format string. 353 | # Available substitutions are: {cwd}. 354 | # c.TerminalInteractiveShell.term_title_format = 'IPython: {cwd}' 355 | 356 | ## Use 24bit colors instead of 256 colors in prompt highlighting. If your 357 | # terminal supports true color, the following command should print 'TRUECOLOR' 358 | # in orange: printf "\x1b[38;2;255;100;0mTRUECOLOR\x1b[0m\n" 359 | # c.TerminalInteractiveShell.true_color = False 360 | 361 | # ------------------------------------------------------------------------------ 362 | # HistoryAccessor(HistoryAccessorBase) configuration 363 | # ------------------------------------------------------------------------------ 364 | 365 | ## Access the history database without adding to it. 366 | # 367 | # This is intended for use by standalone history tools. IPython shells use 368 | # HistoryManager, below, which is a subclass of this. 369 | 370 | ## Options for configuring the SQLite connection 371 | # 372 | # These options are passed as keyword args to sqlite3.connect when establishing 373 | # database connections. 374 | # c.HistoryAccessor.connection_options = {} 375 | 376 | ## enable the SQLite history 377 | # 378 | # set enabled=False to disable the SQLite history, in which case there will be 379 | # no stored history, no SQLite connection, and no background saving thread. 380 | # This may be necessary in some threaded environments where IPython is embedded. 381 | # c.HistoryAccessor.enabled = True 382 | 383 | ## Path to file to use for SQLite history database. 384 | # 385 | # By default, IPython will put the history database in the IPython profile 386 | # directory. If you would rather share one history among profiles, you can set 387 | # this value in each, so that they are consistent. 388 | # 389 | # Due to an issue with fcntl, SQLite is known to misbehave on some NFS mounts. 390 | # If you see IPython hanging, try setting this to something on a local disk, 391 | # e.g:: 392 | # 393 | # ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite 394 | # 395 | # you can also use the specific value `:memory:` (including the colon at both 396 | # end but not the back ticks), to avoid creating an history file. 397 | # c.HistoryAccessor.hist_file = '' 398 | 399 | # ------------------------------------------------------------------------------ 400 | # HistoryManager(HistoryAccessor) configuration 401 | # ------------------------------------------------------------------------------ 402 | 403 | ## A class to organize all history-related functionality in one place. 404 | 405 | ## Write to database every x commands (higher values save disk access & power). 406 | # Values of 1 or less effectively disable caching. 407 | # c.HistoryManager.db_cache_size = 0 408 | 409 | ## Should the history database include output? (default: no) 410 | # c.HistoryManager.db_log_output = False 411 | 412 | # ------------------------------------------------------------------------------ 413 | # ProfileDir(LoggingConfigurable) configuration 414 | # ------------------------------------------------------------------------------ 415 | 416 | ## An object to manage the profile directory and its resources. 417 | # 418 | # The profile directory is used by all IPython applications, to manage 419 | # configuration, logging and security. 420 | # 421 | # This object knows how to find, create and manage these directories. This 422 | # should be used by any code that wants to handle profiles. 423 | 424 | ## Set the profile location directly. This overrides the logic used by the 425 | # `profile` option. 426 | # c.ProfileDir.location = '' 427 | 428 | # ------------------------------------------------------------------------------ 429 | # BaseFormatter(Configurable) configuration 430 | # ------------------------------------------------------------------------------ 431 | 432 | ## A base formatter class that is configurable. 433 | # 434 | # This formatter should usually be used as the base class of all formatters. It 435 | # is a traited :class:`Configurable` class and includes an extensible API for 436 | # users to determine how their objects are formatted. The following logic is 437 | # used to find a function to format an given object. 438 | # 439 | # 1. The object is introspected to see if it has a method with the name 440 | # :attr:`print_method`. If is does, that object is passed to that method 441 | # for formatting. 442 | # 2. If no print method is found, three internal dictionaries are consulted 443 | # to find print method: :attr:`singleton_printers`, :attr:`type_printers` 444 | # and :attr:`deferred_printers`. 445 | # 446 | # Users should use these dictionaries to register functions that will be used to 447 | # compute the format data for their objects (if those objects don't have the 448 | # special print methods). The easiest way of using these dictionaries is through 449 | # the :meth:`for_type` and :meth:`for_type_by_name` methods. 450 | # 451 | # If no function/callable is found to compute the format data, ``None`` is 452 | # returned and this format type is not used. 453 | 454 | ## 455 | # c.BaseFormatter.deferred_printers = {} 456 | 457 | ## 458 | # c.BaseFormatter.enabled = True 459 | 460 | ## 461 | # c.BaseFormatter.singleton_printers = {} 462 | 463 | ## 464 | # c.BaseFormatter.type_printers = {} 465 | 466 | # ------------------------------------------------------------------------------ 467 | # PlainTextFormatter(BaseFormatter) configuration 468 | # ------------------------------------------------------------------------------ 469 | 470 | ## The default pretty-printer. 471 | # 472 | # This uses :mod:`IPython.lib.pretty` to compute the format data of the object. 473 | # If the object cannot be pretty printed, :func:`repr` is used. See the 474 | # documentation of :mod:`IPython.lib.pretty` for details on how to write pretty 475 | # printers. Here is a simple example:: 476 | # 477 | # def dtype_pprinter(obj, p, cycle): 478 | # if cycle: 479 | # return p.text('dtype(...)') 480 | # if hasattr(obj, 'fields'): 481 | # if obj.fields is None: 482 | # p.text(repr(obj)) 483 | # else: 484 | # p.begin_group(7, 'dtype([') 485 | # for i, field in enumerate(obj.descr): 486 | # if i > 0: 487 | # p.text(',') 488 | # p.breakable() 489 | # p.pretty(field) 490 | # p.end_group(7, '])') 491 | 492 | ## 493 | # c.PlainTextFormatter.float_precision = '' 494 | 495 | ## Truncate large collections (lists, dicts, tuples, sets) to this size. 496 | # 497 | # Set to 0 to disable truncation. 498 | # c.PlainTextFormatter.max_seq_length = 1000 499 | 500 | ## 501 | # c.PlainTextFormatter.max_width = 79 502 | 503 | ## 504 | # c.PlainTextFormatter.newline = '\n' 505 | 506 | ## 507 | # c.PlainTextFormatter.pprint = True 508 | 509 | ## 510 | # c.PlainTextFormatter.verbose = False 511 | 512 | # ------------------------------------------------------------------------------ 513 | # Completer(Configurable) configuration 514 | # ------------------------------------------------------------------------------ 515 | 516 | ## Enable unicode completions, e.g. \alpha . Includes completion of latex 517 | # commands, unicode names, and expanding unicode characters back to latex 518 | # commands. 519 | # c.Completer.backslash_combining_completions = True 520 | 521 | ## Enable debug for the Completer. Mostly print extra information for 522 | # experimental jedi integration. 523 | # c.Completer.debug = False 524 | 525 | ## Activate greedy completion PENDING DEPRECTION. this is now mostly taken care 526 | # of with Jedi. 527 | # 528 | # This will enable completion on elements of lists, results of function calls, 529 | # etc., but can be unsafe because the code is actually evaluated on TAB. 530 | # c.Completer.greedy = False 531 | 532 | ## Experimental: restrict time (in milliseconds) during which Jedi can compute 533 | # types. Set to 0 to stop computing types. Non-zero value lower than 100ms may 534 | # hurt performance by preventing jedi to build its cache. 535 | # c.Completer.jedi_compute_type_timeout = 400 536 | 537 | ## Experimental: Use Jedi to generate autocompletions. Default to True if jedi is 538 | # installed. 539 | # c.Completer.use_jedi = True 540 | 541 | # ------------------------------------------------------------------------------ 542 | # IPCompleter(Completer) configuration 543 | # ------------------------------------------------------------------------------ 544 | 545 | ## Extension of the completer class with IPython-specific features 546 | 547 | ## DEPRECATED as of version 5.0. 548 | # 549 | # Instruct the completer to use __all__ for the completion 550 | # 551 | # Specifically, when completing on ``object.``. 552 | # 553 | # When True: only those names in obj.__all__ will be included. 554 | # 555 | # When False [default]: the __all__ attribute is ignored 556 | # c.IPCompleter.limit_to__all__ = False 557 | 558 | ## Whether to merge completion results into a single list 559 | # 560 | # If False, only the completion results from the first non-empty completer will 561 | # be returned. 562 | # c.IPCompleter.merge_completions = True 563 | 564 | ## Instruct the completer to omit private method names 565 | # 566 | # Specifically, when completing on ``object.``. 567 | # 568 | # When 2 [default]: all names that start with '_' will be excluded. 569 | # 570 | # When 1: all 'magic' names (``__foo__``) will be excluded. 571 | # 572 | # When 0: nothing will be excluded. 573 | # c.IPCompleter.omit__names = 2 574 | 575 | # ------------------------------------------------------------------------------ 576 | # ScriptMagics(Magics) configuration 577 | # ------------------------------------------------------------------------------ 578 | 579 | ## Magics for talking to scripts 580 | # 581 | # This defines a base `%%script` cell magic for running a cell with a program in 582 | # a subprocess, and registers a few top-level magics that call %%script with 583 | # common interpreters. 584 | 585 | ## Extra script cell magics to define 586 | # 587 | # This generates simple wrappers of `%%script foo` as `%%foo`. 588 | # 589 | # If you want to add script magics that aren't on your path, specify them in 590 | # script_paths 591 | # c.ScriptMagics.script_magics = [] 592 | 593 | ## Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' 594 | # 595 | # Only necessary for items in script_magics where the default path will not find 596 | # the right interpreter. 597 | # c.ScriptMagics.script_paths = {} 598 | 599 | # ------------------------------------------------------------------------------ 600 | # LoggingMagics(Magics) configuration 601 | # ------------------------------------------------------------------------------ 602 | 603 | ## Magics related to all logging machinery. 604 | 605 | ## Suppress output of log state when logging is enabled 606 | # c.LoggingMagics.quiet = False 607 | 608 | # ------------------------------------------------------------------------------ 609 | # StoreMagics(Magics) configuration 610 | # ------------------------------------------------------------------------------ 611 | 612 | ## Lightweight persistence for python variables. 613 | # 614 | # Provides the %store magic. 615 | 616 | ## If True, any %store-d variables will be automatically restored when IPython 617 | # starts. 618 | # c.StoreMagics.autorestore = False 619 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.2.0 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "altgraph" 5 | version = "0.17" 6 | description = "Python graph (network) package" 7 | optional = false 8 | python-versions = "*" 9 | groups = ["dev"] 10 | files = [ 11 | {file = "altgraph-0.17-py2.py3-none-any.whl", hash = "sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe"}, 12 | {file = "altgraph-0.17.tar.gz", hash = "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa"}, 13 | ] 14 | 15 | [[package]] 16 | name = "appnope" 17 | version = "0.1.2" 18 | description = "Disable App Nap on macOS >= 10.9" 19 | optional = false 20 | python-versions = "*" 21 | groups = ["dev"] 22 | markers = "sys_platform == \"darwin\"" 23 | files = [ 24 | {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, 25 | {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, 26 | ] 27 | 28 | [[package]] 29 | name = "astroid" 30 | version = "3.2.4" 31 | description = "An abstract syntax tree for Python with inference support." 32 | optional = false 33 | python-versions = ">=3.8.0" 34 | groups = ["dev"] 35 | files = [ 36 | {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, 37 | {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, 38 | ] 39 | 40 | [package.dependencies] 41 | typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} 42 | 43 | [[package]] 44 | name = "attrs" 45 | version = "21.2.0" 46 | description = "Classes Without Boilerplate" 47 | optional = false 48 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 49 | groups = ["dev"] 50 | files = [ 51 | {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, 52 | {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, 53 | ] 54 | 55 | [package.extras] 56 | dev = ["coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] 57 | docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] 58 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] 59 | tests-no-zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] 60 | 61 | [[package]] 62 | name = "backcall" 63 | version = "0.2.0" 64 | description = "Specifications for callback functions passed in to an API" 65 | optional = false 66 | python-versions = "*" 67 | groups = ["dev"] 68 | files = [ 69 | {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, 70 | {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, 71 | ] 72 | 73 | [[package]] 74 | name = "black" 75 | version = "24.8.0" 76 | description = "The uncompromising code formatter." 77 | optional = false 78 | python-versions = ">=3.8" 79 | groups = ["dev"] 80 | markers = "python_version < \"3.11\"" 81 | files = [ 82 | {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, 83 | {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, 84 | {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, 85 | {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, 86 | {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, 87 | {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, 88 | {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, 89 | {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, 90 | {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, 91 | {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, 92 | {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, 93 | {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, 94 | {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, 95 | {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, 96 | {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, 97 | {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, 98 | {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, 99 | {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, 100 | {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, 101 | {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, 102 | {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, 103 | {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, 104 | ] 105 | 106 | [package.dependencies] 107 | click = ">=8.0.0" 108 | mypy-extensions = ">=0.4.3" 109 | packaging = ">=22.0" 110 | pathspec = ">=0.9.0" 111 | platformdirs = ">=2" 112 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 113 | typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} 114 | 115 | [package.extras] 116 | colorama = ["colorama (>=0.4.3)"] 117 | d = ["aiohttp (>=3.7.4) ; sys_platform != \"win32\" or implementation_name != \"pypy\"", "aiohttp (>=3.7.4,!=3.9.0) ; sys_platform == \"win32\" and implementation_name == \"pypy\""] 118 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 119 | uvloop = ["uvloop (>=0.15.2)"] 120 | 121 | [[package]] 122 | name = "black" 123 | version = "24.10.0" 124 | description = "The uncompromising code formatter." 125 | optional = false 126 | python-versions = ">=3.9" 127 | groups = ["dev"] 128 | markers = "python_version >= \"3.11\"" 129 | files = [ 130 | {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, 131 | {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, 132 | {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, 133 | {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, 134 | {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, 135 | {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, 136 | {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, 137 | {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, 138 | {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, 139 | {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, 140 | {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, 141 | {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, 142 | {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, 143 | {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, 144 | {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, 145 | {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, 146 | {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, 147 | {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, 148 | {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, 149 | {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, 150 | {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, 151 | {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, 152 | ] 153 | 154 | [package.dependencies] 155 | click = ">=8.0.0" 156 | mypy-extensions = ">=0.4.3" 157 | packaging = ">=22.0" 158 | pathspec = ">=0.9.0" 159 | platformdirs = ">=2" 160 | 161 | [package.extras] 162 | colorama = ["colorama (>=0.4.3)"] 163 | d = ["aiohttp (>=3.10)"] 164 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 165 | uvloop = ["uvloop (>=0.15.2)"] 166 | 167 | [[package]] 168 | name = "certifi" 169 | version = "2024.7.4" 170 | description = "Python package for providing Mozilla's CA Bundle." 171 | optional = false 172 | python-versions = ">=3.6" 173 | groups = ["dev"] 174 | files = [ 175 | {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, 176 | {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, 177 | ] 178 | 179 | [[package]] 180 | name = "charset-normalizer" 181 | version = "2.1.1" 182 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 183 | optional = false 184 | python-versions = ">=3.6.0" 185 | groups = ["dev"] 186 | files = [ 187 | {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, 188 | {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, 189 | ] 190 | 191 | [package.extras] 192 | unicode-backport = ["unicodedata2"] 193 | 194 | [[package]] 195 | name = "click" 196 | version = "8.0.1" 197 | description = "Composable command line interface toolkit" 198 | optional = false 199 | python-versions = ">=3.6" 200 | groups = ["dev"] 201 | files = [ 202 | {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, 203 | {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, 204 | ] 205 | 206 | [package.dependencies] 207 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 208 | 209 | [[package]] 210 | name = "colorama" 211 | version = "0.4.6" 212 | description = "Cross-platform colored terminal text." 213 | optional = false 214 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 215 | groups = ["dev"] 216 | files = [ 217 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 218 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 219 | ] 220 | 221 | [[package]] 222 | name = "coverage" 223 | version = "7.2.7" 224 | description = "Code coverage measurement for Python" 225 | optional = false 226 | python-versions = ">=3.7" 227 | groups = ["dev"] 228 | files = [ 229 | {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, 230 | {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, 231 | {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, 232 | {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, 233 | {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, 234 | {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, 235 | {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, 236 | {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, 237 | {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, 238 | {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, 239 | {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, 240 | {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, 241 | {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, 242 | {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, 243 | {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, 244 | {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, 245 | {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, 246 | {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, 247 | {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, 248 | {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, 249 | {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, 250 | {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, 251 | {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, 252 | {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, 253 | {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, 254 | {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, 255 | {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, 256 | {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, 257 | {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, 258 | {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, 259 | {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, 260 | {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, 261 | {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, 262 | {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, 263 | {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, 264 | {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, 265 | {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, 266 | {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, 267 | {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, 268 | {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, 269 | {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, 270 | {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, 271 | {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, 272 | {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, 273 | {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, 274 | {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, 275 | {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, 276 | {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, 277 | {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, 278 | {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, 279 | {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, 280 | {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, 281 | {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, 282 | {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, 283 | {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, 284 | {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, 285 | {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, 286 | {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, 287 | {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, 288 | {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, 289 | ] 290 | 291 | [package.dependencies] 292 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 293 | 294 | [package.extras] 295 | toml = ["tomli ; python_full_version <= \"3.11.0a6\""] 296 | 297 | [[package]] 298 | name = "coveragespace" 299 | version = "6.1" 300 | description = "A place to track your code coverage metrics." 301 | optional = false 302 | python-versions = "<4.0,>=3.8" 303 | groups = ["dev"] 304 | files = [ 305 | {file = "coveragespace-6.1-py3-none-any.whl", hash = "sha256:ca6ccd5eb32eb6ce5fe78de6c052353b9fbb378a886fde0838480defe33406a8"}, 306 | {file = "coveragespace-6.1.tar.gz", hash = "sha256:049c0b7b629ad43d72692f0f99b9f8a97936ad596f7f27c1af61323fba90ebef"}, 307 | ] 308 | 309 | [package.dependencies] 310 | colorama = ">=0.4" 311 | coverage = ">=4.0" 312 | docopt = ">=0.6" 313 | minilog = ">=2.0" 314 | requests = ">=2.28,<3.0" 315 | 316 | [[package]] 317 | name = "decorator" 318 | version = "5.0.9" 319 | description = "Decorators for Humans" 320 | optional = false 321 | python-versions = ">=3.5" 322 | groups = ["dev"] 323 | files = [ 324 | {file = "decorator-5.0.9-py3-none-any.whl", hash = "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323"}, 325 | {file = "decorator-5.0.9.tar.gz", hash = "sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5"}, 326 | ] 327 | 328 | [[package]] 329 | name = "dill" 330 | version = "0.4.0" 331 | description = "serialize all of Python" 332 | optional = false 333 | python-versions = ">=3.8" 334 | groups = ["dev"] 335 | files = [ 336 | {file = "dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049"}, 337 | {file = "dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0"}, 338 | ] 339 | 340 | [package.extras] 341 | graph = ["objgraph (>=1.7.2)"] 342 | profile = ["gprof2dot (>=2022.7.29)"] 343 | 344 | [[package]] 345 | name = "docopt" 346 | version = "0.6.2" 347 | description = "Pythonic argument parser, that will make you smile" 348 | optional = false 349 | python-versions = "*" 350 | groups = ["dev"] 351 | files = [ 352 | {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, 353 | ] 354 | 355 | [[package]] 356 | name = "exceptiongroup" 357 | version = "1.0.4" 358 | description = "Backport of PEP 654 (exception groups)" 359 | optional = false 360 | python-versions = ">=3.7" 361 | groups = ["dev"] 362 | markers = "python_version < \"3.11\"" 363 | files = [ 364 | {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, 365 | {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, 366 | ] 367 | 368 | [package.extras] 369 | test = ["pytest (>=6)"] 370 | 371 | [[package]] 372 | name = "freezegun" 373 | version = "1.5.5" 374 | description = "Let your Python tests travel through time" 375 | optional = false 376 | python-versions = ">=3.8" 377 | groups = ["dev"] 378 | files = [ 379 | {file = "freezegun-1.5.5-py3-none-any.whl", hash = "sha256:cd557f4a75cf074e84bc374249b9dd491eaeacd61376b9eb3c423282211619d2"}, 380 | {file = "freezegun-1.5.5.tar.gz", hash = "sha256:ac7742a6cc6c25a2c35e9292dfd554b897b517d2dec26891a2e8debf205cb94a"}, 381 | ] 382 | 383 | [package.dependencies] 384 | python-dateutil = ">=2.7" 385 | 386 | [[package]] 387 | name = "future" 388 | version = "0.18.3" 389 | description = "Clean single-source support for Python 3 and 2" 390 | optional = false 391 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 392 | groups = ["dev"] 393 | markers = "sys_platform == \"win32\"" 394 | files = [ 395 | {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"}, 396 | ] 397 | 398 | [[package]] 399 | name = "ghp-import" 400 | version = "2.0.2" 401 | description = "Copy your docs directly to the gh-pages branch." 402 | optional = false 403 | python-versions = "*" 404 | groups = ["dev"] 405 | files = [ 406 | {file = "ghp-import-2.0.2.tar.gz", hash = "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071"}, 407 | {file = "ghp_import-2.0.2-py3-none-any.whl", hash = "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46"}, 408 | ] 409 | 410 | [package.dependencies] 411 | python-dateutil = ">=2.8.1" 412 | 413 | [package.extras] 414 | dev = ["flake8", "markdown", "twine", "wheel"] 415 | 416 | [[package]] 417 | name = "gprof2dot" 418 | version = "2021.2.21" 419 | description = "Generate a dot graph from the output of several profilers." 420 | optional = false 421 | python-versions = "*" 422 | groups = ["dev"] 423 | files = [ 424 | {file = "gprof2dot-2021.2.21.tar.gz", hash = "sha256:1223189383b53dcc8ecfd45787ac48c0ed7b4dbc16ee8b88695d053eea1acabf"}, 425 | ] 426 | 427 | [[package]] 428 | name = "idna" 429 | version = "3.7" 430 | description = "Internationalized Domain Names in Applications (IDNA)" 431 | optional = false 432 | python-versions = ">=3.5" 433 | groups = ["dev"] 434 | files = [ 435 | {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, 436 | {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, 437 | ] 438 | 439 | [[package]] 440 | name = "importlib-metadata" 441 | version = "4.4.0" 442 | description = "Read metadata from Python packages" 443 | optional = false 444 | python-versions = ">=3.6" 445 | groups = ["dev"] 446 | files = [ 447 | {file = "importlib_metadata-4.4.0-py3-none-any.whl", hash = "sha256:960d52ba7c21377c990412aca380bf3642d734c2eaab78a2c39319f67c6a5786"}, 448 | {file = "importlib_metadata-4.4.0.tar.gz", hash = "sha256:e592faad8de1bda9fe920cf41e15261e7131bcf266c30306eec00e8e225c1dd5"}, 449 | ] 450 | 451 | [package.dependencies] 452 | zipp = ">=0.5" 453 | 454 | [package.extras] 455 | docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] 456 | testing = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\" and python_version < \"3.10\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy ; platform_python_implementation != \"PyPy\" and python_version < \"3.10\""] 457 | 458 | [[package]] 459 | name = "iniconfig" 460 | version = "1.1.1" 461 | description = "iniconfig: brain-dead simple config-ini parsing" 462 | optional = false 463 | python-versions = "*" 464 | groups = ["dev"] 465 | files = [ 466 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 467 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 468 | ] 469 | 470 | [[package]] 471 | name = "ipython" 472 | version = "7.16.3" 473 | description = "IPython: Productive Interactive Computing" 474 | optional = false 475 | python-versions = ">=3.6" 476 | groups = ["dev"] 477 | files = [ 478 | {file = "ipython-7.16.3-py3-none-any.whl", hash = "sha256:c0427ed8bc33ac481faf9d3acf7e84e0010cdaada945e0badd1e2e74cc075833"}, 479 | {file = "ipython-7.16.3.tar.gz", hash = "sha256:5ac47dc9af66fc2f5530c12069390877ae372ac905edca75a92a6e363b5d7caa"}, 480 | ] 481 | 482 | [package.dependencies] 483 | appnope = {version = "*", markers = "sys_platform == \"darwin\""} 484 | backcall = "*" 485 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 486 | decorator = "*" 487 | jedi = ">=0.10,<=0.17.2" 488 | pexpect = {version = "*", markers = "sys_platform != \"win32\""} 489 | pickleshare = "*" 490 | prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" 491 | pygments = "*" 492 | setuptools = ">=18.5" 493 | traitlets = ">=4.2" 494 | 495 | [package.extras] 496 | all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.14)", "pygments", "qtconsole", "requests", "testpath"] 497 | doc = ["Sphinx (>=1.3)"] 498 | kernel = ["ipykernel"] 499 | nbconvert = ["nbconvert"] 500 | nbformat = ["nbformat"] 501 | notebook = ["ipywidgets", "notebook"] 502 | parallel = ["ipyparallel"] 503 | qtconsole = ["qtconsole"] 504 | test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.14)", "pygments", "requests", "testpath"] 505 | 506 | [[package]] 507 | name = "isort" 508 | version = "5.13.2" 509 | description = "A Python utility / library to sort Python imports." 510 | optional = false 511 | python-versions = ">=3.8.0" 512 | groups = ["dev"] 513 | files = [ 514 | {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, 515 | {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, 516 | ] 517 | 518 | [package.extras] 519 | colors = ["colorama (>=0.4.6)"] 520 | 521 | [[package]] 522 | name = "jedi" 523 | version = "0.17.2" 524 | description = "An autocompletion tool for Python that can be used for text editors." 525 | optional = false 526 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 527 | groups = ["dev"] 528 | files = [ 529 | {file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, 530 | {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"}, 531 | ] 532 | 533 | [package.dependencies] 534 | parso = ">=0.7.0,<0.8.0" 535 | 536 | [package.extras] 537 | qa = ["flake8 (==3.7.9)"] 538 | testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] 539 | 540 | [[package]] 541 | name = "jinja2" 542 | version = "3.1.6" 543 | description = "A very fast and expressive template engine." 544 | optional = false 545 | python-versions = ">=3.7" 546 | groups = ["dev"] 547 | files = [ 548 | {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, 549 | {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, 550 | ] 551 | 552 | [package.dependencies] 553 | MarkupSafe = ">=2.0" 554 | 555 | [package.extras] 556 | i18n = ["Babel (>=2.7)"] 557 | 558 | [[package]] 559 | name = "macfsevents" 560 | version = "0.8.4" 561 | description = "Thread-based interface to file system observation primitives." 562 | optional = false 563 | python-versions = "*" 564 | groups = ["dev"] 565 | markers = "sys_platform == \"darwin\"" 566 | files = [ 567 | {file = "MacFSEvents-0.8.4.tar.gz", hash = "sha256:bf7283f1d517764ccdc8195b21631dbbac1c506b920bf9a8ea2956b3127651cb"}, 568 | ] 569 | 570 | [[package]] 571 | name = "macholib" 572 | version = "1.14" 573 | description = "Mach-O header analysis and editing" 574 | optional = false 575 | python-versions = "*" 576 | groups = ["dev"] 577 | markers = "sys_platform == \"darwin\"" 578 | files = [ 579 | {file = "macholib-1.14-py2.py3-none-any.whl", hash = "sha256:c500f02867515e6c60a27875b408920d18332ddf96b4035ef03beddd782d4281"}, 580 | {file = "macholib-1.14.tar.gz", hash = "sha256:0c436bc847e7b1d9bda0560351bf76d7caf930fb585a828d13608839ef42c432"}, 581 | ] 582 | 583 | [package.dependencies] 584 | altgraph = ">=0.15" 585 | 586 | [[package]] 587 | name = "markdown" 588 | version = "3.3.4" 589 | description = "Python implementation of Markdown." 590 | optional = false 591 | python-versions = ">=3.6" 592 | groups = ["dev"] 593 | files = [ 594 | {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, 595 | {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, 596 | ] 597 | 598 | [package.extras] 599 | testing = ["coverage", "pyyaml"] 600 | 601 | [[package]] 602 | name = "markupsafe" 603 | version = "2.0.1" 604 | description = "Safely add untrusted strings to HTML/XML markup." 605 | optional = false 606 | python-versions = ">=3.6" 607 | groups = ["dev"] 608 | files = [ 609 | {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, 610 | {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, 611 | {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, 612 | {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, 613 | {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, 614 | {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, 615 | {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, 616 | {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, 617 | {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, 618 | {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, 619 | {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, 620 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, 621 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, 622 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, 623 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, 624 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, 625 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, 626 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, 627 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, 628 | {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, 629 | {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, 630 | {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, 631 | {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, 632 | {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, 633 | {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, 634 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, 635 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, 636 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, 637 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, 638 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, 639 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, 640 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, 641 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, 642 | {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, 643 | {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, 644 | {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, 645 | {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, 646 | {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, 647 | {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, 648 | {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, 649 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, 650 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, 651 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, 652 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, 653 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, 654 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, 655 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, 656 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, 657 | {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, 658 | {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, 659 | {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, 660 | {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, 661 | {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, 662 | {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, 663 | {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, 664 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, 665 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, 666 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, 667 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, 668 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, 669 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, 670 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, 671 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, 672 | {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, 673 | {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, 674 | {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, 675 | {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, 676 | {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, 677 | {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, 678 | ] 679 | 680 | [[package]] 681 | name = "mccabe" 682 | version = "0.6.1" 683 | description = "McCabe checker, plugin for flake8" 684 | optional = false 685 | python-versions = "*" 686 | groups = ["dev"] 687 | files = [ 688 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 689 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 690 | ] 691 | 692 | [[package]] 693 | name = "mergedeep" 694 | version = "1.3.4" 695 | description = "A deep merge function for 🐍." 696 | optional = false 697 | python-versions = ">=3.6" 698 | groups = ["dev"] 699 | files = [ 700 | {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, 701 | {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, 702 | ] 703 | 704 | [[package]] 705 | name = "mkdocs" 706 | version = "1.2.3" 707 | description = "Project documentation with Markdown." 708 | optional = false 709 | python-versions = ">=3.6" 710 | groups = ["dev"] 711 | files = [ 712 | {file = "mkdocs-1.2.3-py3-none-any.whl", hash = "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072"}, 713 | {file = "mkdocs-1.2.3.tar.gz", hash = "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1"}, 714 | ] 715 | 716 | [package.dependencies] 717 | click = ">=3.3" 718 | ghp-import = ">=1.0" 719 | importlib-metadata = ">=3.10" 720 | Jinja2 = ">=2.10.1" 721 | Markdown = ">=3.2.1" 722 | mergedeep = ">=1.3.4" 723 | packaging = ">=20.5" 724 | PyYAML = ">=3.10" 725 | pyyaml-env-tag = ">=0.1" 726 | watchdog = ">=2.0" 727 | 728 | [package.extras] 729 | i18n = ["babel (>=2.9.0)"] 730 | 731 | [[package]] 732 | name = "mypy" 733 | version = "1.4.1" 734 | description = "Optional static typing for Python" 735 | optional = false 736 | python-versions = ">=3.7" 737 | groups = ["dev"] 738 | files = [ 739 | {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, 740 | {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, 741 | {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, 742 | {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, 743 | {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, 744 | {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, 745 | {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, 746 | {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, 747 | {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, 748 | {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, 749 | {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, 750 | {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, 751 | {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, 752 | {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, 753 | {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, 754 | {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, 755 | {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, 756 | {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, 757 | {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, 758 | {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, 759 | {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, 760 | {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, 761 | {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, 762 | {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, 763 | {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, 764 | {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, 765 | ] 766 | 767 | [package.dependencies] 768 | mypy-extensions = ">=1.0.0" 769 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 770 | typing-extensions = ">=4.1.0" 771 | 772 | [package.extras] 773 | dmypy = ["psutil (>=4.0)"] 774 | install-types = ["pip"] 775 | python2 = ["typed-ast (>=1.4.0,<2)"] 776 | reports = ["lxml"] 777 | 778 | [[package]] 779 | name = "mypy-extensions" 780 | version = "1.0.0" 781 | description = "Type system extensions for programs checked with the mypy type checker." 782 | optional = false 783 | python-versions = ">=3.5" 784 | groups = ["dev"] 785 | files = [ 786 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 787 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 788 | ] 789 | 790 | [[package]] 791 | name = "nose" 792 | version = "1.3.7" 793 | description = "nose extends unittest to make testing easier" 794 | optional = false 795 | python-versions = "*" 796 | groups = ["dev"] 797 | files = [ 798 | {file = "nose-1.3.7-py2-none-any.whl", hash = "sha256:dadcddc0aefbf99eea214e0f1232b94f2fa9bd98fa8353711dacb112bfcbbb2a"}, 799 | {file = "nose-1.3.7-py3-none-any.whl", hash = "sha256:9ff7c6cc443f8c51994b34a667bbcf45afd6d945be7477b52e97516fd17c53ac"}, 800 | {file = "nose-1.3.7.tar.gz", hash = "sha256:f1bffef9cbc82628f6e7d7b40d7e255aefaa1adb6a1b1d26c69a8b79e6208a98"}, 801 | ] 802 | 803 | [[package]] 804 | name = "packaging" 805 | version = "24.0" 806 | description = "Core utilities for Python packages" 807 | optional = false 808 | python-versions = ">=3.7" 809 | groups = ["dev"] 810 | files = [ 811 | {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, 812 | {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, 813 | ] 814 | 815 | [[package]] 816 | name = "parso" 817 | version = "0.7.1" 818 | description = "A Python Parser" 819 | optional = false 820 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 821 | groups = ["dev"] 822 | files = [ 823 | {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, 824 | {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"}, 825 | ] 826 | 827 | [package.extras] 828 | testing = ["docopt", "pytest (>=3.0.7)"] 829 | 830 | [[package]] 831 | name = "pathspec" 832 | version = "0.9.0" 833 | description = "Utility library for gitignore style pattern matching of file paths." 834 | optional = false 835 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 836 | groups = ["dev"] 837 | files = [ 838 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 839 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 840 | ] 841 | 842 | [[package]] 843 | name = "pefile" 844 | version = "2021.5.24" 845 | description = "Python PE parsing module" 846 | optional = false 847 | python-versions = ">=3.6.0" 848 | groups = ["dev"] 849 | markers = "sys_platform == \"win32\"" 850 | files = [ 851 | {file = "pefile-2021.5.24.tar.gz", hash = "sha256:ed79b2353daa58421459abf4d685953bde0adf9f6e188944f97ba9795f100246"}, 852 | ] 853 | 854 | [package.dependencies] 855 | future = "*" 856 | 857 | [[package]] 858 | name = "pexpect" 859 | version = "4.8.0" 860 | description = "Pexpect allows easy control of interactive console applications." 861 | optional = false 862 | python-versions = "*" 863 | groups = ["dev"] 864 | markers = "sys_platform != \"win32\"" 865 | files = [ 866 | {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, 867 | {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, 868 | ] 869 | 870 | [package.dependencies] 871 | ptyprocess = ">=0.5" 872 | 873 | [[package]] 874 | name = "pickleshare" 875 | version = "0.7.5" 876 | description = "Tiny 'shelve'-like database with concurrency support" 877 | optional = false 878 | python-versions = "*" 879 | groups = ["dev"] 880 | files = [ 881 | {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, 882 | {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, 883 | ] 884 | 885 | [[package]] 886 | name = "platformdirs" 887 | version = "2.4.1" 888 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 889 | optional = false 890 | python-versions = ">=3.7" 891 | groups = ["dev"] 892 | files = [ 893 | {file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"}, 894 | {file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"}, 895 | ] 896 | 897 | [package.extras] 898 | docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] 899 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 900 | 901 | [[package]] 902 | name = "pluggy" 903 | version = "0.13.1" 904 | description = "plugin and hook calling mechanisms for python" 905 | optional = false 906 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 907 | groups = ["dev"] 908 | files = [ 909 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 910 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 911 | ] 912 | 913 | [package.extras] 914 | dev = ["pre-commit", "tox"] 915 | 916 | [[package]] 917 | name = "prompt-toolkit" 918 | version = "3.0.3" 919 | description = "Library for building powerful interactive command lines in Python" 920 | optional = false 921 | python-versions = ">=3.6" 922 | groups = ["dev"] 923 | files = [ 924 | {file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"}, 925 | {file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"}, 926 | ] 927 | 928 | [package.dependencies] 929 | wcwidth = "*" 930 | 931 | [[package]] 932 | name = "ptyprocess" 933 | version = "0.7.0" 934 | description = "Run a subprocess in a pseudo terminal" 935 | optional = false 936 | python-versions = "*" 937 | groups = ["dev"] 938 | markers = "sys_platform != \"win32\"" 939 | files = [ 940 | {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, 941 | {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, 942 | ] 943 | 944 | [[package]] 945 | name = "pydocstyle" 946 | version = "6.3.0" 947 | description = "Python docstring style checker" 948 | optional = false 949 | python-versions = ">=3.6" 950 | groups = ["dev"] 951 | files = [ 952 | {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, 953 | {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, 954 | ] 955 | 956 | [package.dependencies] 957 | snowballstemmer = ">=2.2.0" 958 | 959 | [package.extras] 960 | toml = ["tomli (>=1.2.3) ; python_version < \"3.11\""] 961 | 962 | [[package]] 963 | name = "pygments" 964 | version = "2.19.2" 965 | description = "Pygments is a syntax highlighting package written in Python." 966 | optional = false 967 | python-versions = ">=3.8" 968 | groups = ["dev"] 969 | files = [ 970 | {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, 971 | {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, 972 | ] 973 | 974 | [package.extras] 975 | windows-terminal = ["colorama (>=0.4.6)"] 976 | 977 | [[package]] 978 | name = "pyinstaller" 979 | version = "4.5.1" 980 | description = "PyInstaller bundles a Python application and all its dependencies into a single package." 981 | optional = false 982 | python-versions = ">=3.6" 983 | groups = ["dev"] 984 | files = [ 985 | {file = "pyinstaller-4.5.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:ecc2baadeeefd2b6fbf39d13c65d4aa603afdda1c6aaaebc4577ba72893fee9e"}, 986 | {file = "pyinstaller-4.5.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4d848cd782ee0893d7ad9fe2bfe535206a79f0b6760cecc5f2add831258b9322"}, 987 | {file = "pyinstaller-4.5.1-py3-none-manylinux2014_i686.whl", hash = "sha256:8f747b190e6ad30e2d2fd5da9a64636f61aac8c038c0b7f685efa92c782ea14f"}, 988 | {file = "pyinstaller-4.5.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c587da8f521a7ce1b9efb4e3d0117cd63c92dc6cedff24590aeef89372f53012"}, 989 | {file = "pyinstaller-4.5.1-py3-none-win32.whl", hash = "sha256:fed9f5e4802769a416a8f2ca171c6be961d1861cc05a0b71d20dfe05423137e9"}, 990 | {file = "pyinstaller-4.5.1-py3-none-win_amd64.whl", hash = "sha256:aae456205c68355f9597411090576bb31b614e53976b4c102d072bbe5db8392a"}, 991 | {file = "pyinstaller-4.5.1.tar.gz", hash = "sha256:30733baaf8971902286a0ddf77e5499ac5f7bf8e7c39163e83d4f8c696ef265e"}, 992 | ] 993 | 994 | [package.dependencies] 995 | altgraph = "*" 996 | macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} 997 | pefile = {version = ">=2017.8.1", markers = "sys_platform == \"win32\""} 998 | pyinstaller-hooks-contrib = ">=2020.6" 999 | pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} 1000 | setuptools = "*" 1001 | 1002 | [package.extras] 1003 | encryption = ["tinyaes (>=1.0.0)"] 1004 | hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] 1005 | 1006 | [[package]] 1007 | name = "pyinstaller-hooks-contrib" 1008 | version = "2021.1" 1009 | description = "Community maintained hooks for PyInstaller" 1010 | optional = false 1011 | python-versions = "*" 1012 | groups = ["dev"] 1013 | files = [ 1014 | {file = "pyinstaller-hooks-contrib-2021.1.tar.gz", hash = "sha256:892310e6363655838485ee748bf1c5e5cade7963686d9af8650ee218a3e0b031"}, 1015 | {file = "pyinstaller_hooks_contrib-2021.1-py2.py3-none-any.whl", hash = "sha256:27558072021857d89524c42136feaa2ffe4f003f1bdf0278f9b24f6902c1759c"}, 1016 | ] 1017 | 1018 | [[package]] 1019 | name = "pylint" 1020 | version = "3.2.7" 1021 | description = "python code static checker" 1022 | optional = false 1023 | python-versions = ">=3.8.0" 1024 | groups = ["dev"] 1025 | files = [ 1026 | {file = "pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b"}, 1027 | {file = "pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e"}, 1028 | ] 1029 | 1030 | [package.dependencies] 1031 | astroid = ">=3.2.4,<=3.3.0-dev0" 1032 | colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} 1033 | dill = [ 1034 | {version = ">=0.2", markers = "python_version < \"3.11\""}, 1035 | {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, 1036 | {version = ">=0.3.6", markers = "python_version == \"3.11\""}, 1037 | ] 1038 | isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" 1039 | mccabe = ">=0.6,<0.8" 1040 | platformdirs = ">=2.2.0" 1041 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 1042 | tomlkit = ">=0.10.1" 1043 | typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} 1044 | 1045 | [package.extras] 1046 | spelling = ["pyenchant (>=3.2,<4.0)"] 1047 | testutils = ["gitpython (>3)"] 1048 | 1049 | [[package]] 1050 | name = "pync" 1051 | version = "2.0.3" 1052 | description = "Python Wrapper for Mac OS 10.10 Notification Center" 1053 | optional = false 1054 | python-versions = "*" 1055 | groups = ["dev"] 1056 | markers = "sys_platform == \"darwin\"" 1057 | files = [ 1058 | {file = "pync-2.0.3.tar.gz", hash = "sha256:38b9e61735a3161f9211a5773c5f5ea698f36af4ff7f77fa03e8d1ff0caa117f"}, 1059 | ] 1060 | 1061 | [package.dependencies] 1062 | python-dateutil = ">=2.0" 1063 | 1064 | [[package]] 1065 | name = "pytest" 1066 | version = "7.2.0" 1067 | description = "pytest: simple powerful testing with Python" 1068 | optional = false 1069 | python-versions = ">=3.7" 1070 | groups = ["dev"] 1071 | files = [ 1072 | {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, 1073 | {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, 1074 | ] 1075 | 1076 | [package.dependencies] 1077 | attrs = ">=19.2.0" 1078 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 1079 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 1080 | iniconfig = "*" 1081 | packaging = "*" 1082 | pluggy = ">=0.12,<2.0" 1083 | tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 1084 | 1085 | [package.extras] 1086 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 1087 | 1088 | [[package]] 1089 | name = "pytest-cov" 1090 | version = "4.1.0" 1091 | description = "Pytest plugin for measuring coverage." 1092 | optional = false 1093 | python-versions = ">=3.7" 1094 | groups = ["dev"] 1095 | files = [ 1096 | {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, 1097 | {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, 1098 | ] 1099 | 1100 | [package.dependencies] 1101 | coverage = {version = ">=5.2.1", extras = ["toml"]} 1102 | pytest = ">=4.6" 1103 | 1104 | [package.extras] 1105 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] 1106 | 1107 | [[package]] 1108 | name = "pytest-describe" 1109 | version = "2.2.0" 1110 | description = "Describe-style plugin for pytest" 1111 | optional = false 1112 | python-versions = ">=3.7" 1113 | groups = ["dev"] 1114 | files = [ 1115 | {file = "pytest-describe-2.2.0.tar.gz", hash = "sha256:39bb05eb90f2497d9ca342ef9a0b7fa5bada7e58505aec33f66d661d631955b7"}, 1116 | {file = "pytest_describe-2.2.0-py3-none-any.whl", hash = "sha256:bd9e2c73acb4b9522a8400823d98f5b6a081667d3bfd7243a8598336896b544d"}, 1117 | ] 1118 | 1119 | [package.dependencies] 1120 | pytest = ">=4.6,<9" 1121 | 1122 | [[package]] 1123 | name = "pytest-expecter" 1124 | version = "3.0" 1125 | description = "Better testing with expecter and pytest." 1126 | optional = false 1127 | python-versions = ">=3.8,<4.0" 1128 | groups = ["dev"] 1129 | files = [ 1130 | {file = "pytest-expecter-3.0.tar.gz", hash = "sha256:be8f3e9f823af6d6713e3f552ed47560061a2fd243a78952180f5df61a2b76a4"}, 1131 | {file = "pytest_expecter-3.0-py3-none-any.whl", hash = "sha256:98fe65ecc1ddb7ca29084dc68ec07983dbbdb20b566fd14140b0b5f4b7c84cc8"}, 1132 | ] 1133 | 1134 | [[package]] 1135 | name = "pytest-ordering" 1136 | version = "0.6" 1137 | description = "pytest plugin to run your tests in a specific order" 1138 | optional = false 1139 | python-versions = "*" 1140 | groups = ["dev"] 1141 | files = [ 1142 | {file = "pytest-ordering-0.6.tar.gz", hash = "sha256:561ad653626bb171da78e682f6d39ac33bb13b3e272d406cd555adb6b006bda6"}, 1143 | {file = "pytest_ordering-0.6-py2-none-any.whl", hash = "sha256:27fba3fc265f5d0f8597e7557885662c1bdc1969497cd58aff6ed21c3b617de2"}, 1144 | {file = "pytest_ordering-0.6-py3-none-any.whl", hash = "sha256:3f314a178dbeb6777509548727dc69edf22d6d9a2867bf2d310ab85c403380b6"}, 1145 | ] 1146 | 1147 | [package.dependencies] 1148 | pytest = "*" 1149 | 1150 | [[package]] 1151 | name = "pytest-profiling" 1152 | version = "1.8.1" 1153 | description = "Profiling plugin for py.test" 1154 | optional = false 1155 | python-versions = ">=3.6" 1156 | groups = ["dev"] 1157 | files = [ 1158 | {file = "pytest-profiling-1.8.1.tar.gz", hash = "sha256:3f171fa69d5c82fa9aab76d66abd5f59da69135c37d6ae5bf7557f1b154cb08d"}, 1159 | {file = "pytest_profiling-1.8.1-py3-none-any.whl", hash = "sha256:3dd8713a96298b42d83de8f5951df3ada3e61b3e5d2a06956684175529e17aea"}, 1160 | ] 1161 | 1162 | [package.dependencies] 1163 | gprof2dot = "*" 1164 | pytest = "*" 1165 | six = "*" 1166 | 1167 | [[package]] 1168 | name = "pytest-repeat" 1169 | version = "0.9.3" 1170 | description = "pytest plugin for repeating tests" 1171 | optional = false 1172 | python-versions = ">=3.7" 1173 | groups = ["dev"] 1174 | markers = "python_version < \"3.11\"" 1175 | files = [ 1176 | {file = "pytest_repeat-0.9.3-py3-none-any.whl", hash = "sha256:26ab2df18226af9d5ce441c858f273121e92ff55f5bb311d25755b8d7abdd8ed"}, 1177 | {file = "pytest_repeat-0.9.3.tar.gz", hash = "sha256:ffd3836dfcd67bb270bec648b330e20be37d2966448c4148c4092d1e8aba8185"}, 1178 | ] 1179 | 1180 | [package.dependencies] 1181 | pytest = "*" 1182 | 1183 | [[package]] 1184 | name = "pytest-repeat" 1185 | version = "0.9.4" 1186 | description = "pytest plugin for repeating tests" 1187 | optional = false 1188 | python-versions = ">=3.9" 1189 | groups = ["dev"] 1190 | markers = "python_version >= \"3.11\"" 1191 | files = [ 1192 | {file = "pytest_repeat-0.9.4-py3-none-any.whl", hash = "sha256:c1738b4e412a6f3b3b9e0b8b29fcd7a423e50f87381ad9307ef6f5a8601139f3"}, 1193 | {file = "pytest_repeat-0.9.4.tar.gz", hash = "sha256:d92ac14dfaa6ffcfe6917e5d16f0c9bc82380c135b03c2a5f412d2637f224485"}, 1194 | ] 1195 | 1196 | [package.dependencies] 1197 | pytest = "*" 1198 | 1199 | [[package]] 1200 | name = "python-dateutil" 1201 | version = "2.8.1" 1202 | description = "Extensions to the standard Python datetime module" 1203 | optional = false 1204 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 1205 | groups = ["dev"] 1206 | files = [ 1207 | {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, 1208 | {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, 1209 | ] 1210 | 1211 | [package.dependencies] 1212 | six = ">=1.5" 1213 | 1214 | [[package]] 1215 | name = "python-termstyle" 1216 | version = "0.1.10" 1217 | description = "console colouring for python" 1218 | optional = false 1219 | python-versions = "*" 1220 | groups = ["dev"] 1221 | files = [ 1222 | {file = "python-termstyle-0.1.10.tar.gz", hash = "sha256:f42a6bb16fbfc5e2c66d553e7ad46524ea833872f75ee5d827c15115fafc94e2"}, 1223 | {file = "python-termstyle-0.1.10.tgz", hash = "sha256:6faf42ba42f2826c38cf70dacb3ac51f248a418e48afc0e36593df11cf3ab1d2"}, 1224 | ] 1225 | 1226 | [package.dependencies] 1227 | setuptools = "*" 1228 | 1229 | [[package]] 1230 | name = "pywin32-ctypes" 1231 | version = "0.2.0" 1232 | description = "" 1233 | optional = false 1234 | python-versions = "*" 1235 | groups = ["dev"] 1236 | markers = "sys_platform == \"win32\"" 1237 | files = [ 1238 | {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, 1239 | {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "pyyaml" 1244 | version = "6.0.1" 1245 | description = "YAML parser and emitter for Python" 1246 | optional = false 1247 | python-versions = ">=3.6" 1248 | groups = ["dev"] 1249 | files = [ 1250 | {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, 1251 | {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, 1252 | {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, 1253 | {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, 1254 | {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, 1255 | {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, 1256 | {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, 1257 | {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, 1258 | {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, 1259 | {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, 1260 | {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, 1261 | {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, 1262 | {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, 1263 | {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, 1264 | {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, 1265 | {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, 1266 | {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, 1267 | {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, 1268 | {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, 1269 | {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, 1270 | {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, 1271 | {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, 1272 | {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, 1273 | {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, 1274 | {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, 1275 | {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, 1276 | {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, 1277 | {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, 1278 | {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, 1279 | {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, 1280 | {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, 1281 | {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, 1282 | {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, 1283 | {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, 1284 | {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, 1285 | {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, 1286 | {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, 1287 | {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, 1288 | {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, 1289 | {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, 1290 | {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, 1291 | {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, 1292 | {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, 1293 | {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, 1294 | {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, 1295 | {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, 1296 | {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, 1297 | {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, 1298 | {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, 1299 | {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, 1300 | {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, 1301 | ] 1302 | 1303 | [[package]] 1304 | name = "pyyaml-env-tag" 1305 | version = "0.1" 1306 | description = "A custom YAML tag for referencing environment variables in YAML files. " 1307 | optional = false 1308 | python-versions = ">=3.6" 1309 | groups = ["dev"] 1310 | files = [ 1311 | {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, 1312 | {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, 1313 | ] 1314 | 1315 | [package.dependencies] 1316 | pyyaml = "*" 1317 | 1318 | [[package]] 1319 | name = "requests" 1320 | version = "2.32.4" 1321 | description = "Python HTTP for Humans." 1322 | optional = false 1323 | python-versions = ">=3.8" 1324 | groups = ["dev"] 1325 | files = [ 1326 | {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, 1327 | {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, 1328 | ] 1329 | 1330 | [package.dependencies] 1331 | certifi = ">=2017.4.17" 1332 | charset_normalizer = ">=2,<4" 1333 | idna = ">=2.5,<4" 1334 | urllib3 = ">=1.21.1,<3" 1335 | 1336 | [package.extras] 1337 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 1338 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 1339 | 1340 | [[package]] 1341 | name = "setuptools" 1342 | version = "65.6.3" 1343 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 1344 | optional = false 1345 | python-versions = ">=3.7" 1346 | groups = ["dev"] 1347 | files = [ 1348 | {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, 1349 | {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, 1350 | ] 1351 | 1352 | [package.extras] 1353 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] 1354 | testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov ; platform_python_implementation != \"PyPy\"", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] 1355 | testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] 1356 | 1357 | [[package]] 1358 | name = "six" 1359 | version = "1.16.0" 1360 | description = "Python 2 and 3 compatibility utilities" 1361 | optional = false 1362 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 1363 | groups = ["dev"] 1364 | files = [ 1365 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1366 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1367 | ] 1368 | 1369 | [[package]] 1370 | name = "sniffer" 1371 | version = "0.4.1" 1372 | description = "An automatic test runner. Supports nose out of the box." 1373 | optional = false 1374 | python-versions = "*" 1375 | groups = ["dev"] 1376 | files = [ 1377 | {file = "sniffer-0.4.1-py2.py3-none-any.whl", hash = "sha256:f120843fe152d0e380402fc11313b151e2044c47fdd36895de2efedc8624dbb8"}, 1378 | {file = "sniffer-0.4.1.tar.gz", hash = "sha256:b37665053fb83d7790bf9e51d616c11970863d14b5ea5a51155a4e95759d1529"}, 1379 | ] 1380 | 1381 | [package.dependencies] 1382 | colorama = "*" 1383 | nose = "*" 1384 | python-termstyle = "*" 1385 | 1386 | [package.extras] 1387 | growl = ["gntp (==0.7)"] 1388 | libnotify = ["py-notify (==0.3.1)"] 1389 | linux = ["pyinotify (==0.9.0)"] 1390 | osx = ["MacFSEvents (==0.2.8)"] 1391 | 1392 | [[package]] 1393 | name = "snowballstemmer" 1394 | version = "3.0.1" 1395 | description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." 1396 | optional = false 1397 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*" 1398 | groups = ["dev"] 1399 | files = [ 1400 | {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, 1401 | {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, 1402 | ] 1403 | 1404 | [[package]] 1405 | name = "tomli" 1406 | version = "2.0.0" 1407 | description = "A lil' TOML parser" 1408 | optional = false 1409 | python-versions = ">=3.7" 1410 | groups = ["dev"] 1411 | markers = "python_version < \"3.11\"" 1412 | files = [ 1413 | {file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"}, 1414 | {file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"}, 1415 | ] 1416 | 1417 | [[package]] 1418 | name = "tomlkit" 1419 | version = "0.11.8" 1420 | description = "Style preserving TOML library" 1421 | optional = false 1422 | python-versions = ">=3.7" 1423 | groups = ["dev"] 1424 | files = [ 1425 | {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, 1426 | {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, 1427 | ] 1428 | 1429 | [[package]] 1430 | name = "traitlets" 1431 | version = "5.9.0" 1432 | description = "Traitlets Python configuration system" 1433 | optional = false 1434 | python-versions = ">=3.7" 1435 | groups = ["dev"] 1436 | files = [ 1437 | {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, 1438 | {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, 1439 | ] 1440 | 1441 | [package.extras] 1442 | docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] 1443 | test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] 1444 | 1445 | [[package]] 1446 | name = "types-freezegun" 1447 | version = "1.1.6" 1448 | description = "Typing stubs for freezegun" 1449 | optional = false 1450 | python-versions = "*" 1451 | groups = ["dev"] 1452 | files = [ 1453 | {file = "types-freezegun-1.1.6.tar.gz", hash = "sha256:5c70a4b7444b8c7dd2800e0063d6fe721ab11209399264fa0f77af253dd8b14f"}, 1454 | {file = "types_freezegun-1.1.6-py3-none-any.whl", hash = "sha256:eaa4ccac7f4ff92762b6e5d34c3c4e41a7763b6d09a8595e0224ff1f24c9d4e1"}, 1455 | ] 1456 | 1457 | [[package]] 1458 | name = "typing-extensions" 1459 | version = "4.7.0" 1460 | description = "Backported and Experimental Type Hints for Python 3.7+" 1461 | optional = false 1462 | python-versions = ">=3.7" 1463 | groups = ["dev"] 1464 | files = [ 1465 | {file = "typing_extensions-4.7.0-py3-none-any.whl", hash = "sha256:5d8c9dac95c27d20df12fb1d97b9793ab8b2af8a3a525e68c80e21060c161771"}, 1466 | {file = "typing_extensions-4.7.0.tar.gz", hash = "sha256:935ccf31549830cda708b42289d44b6f74084d616a00be651601a4f968e77c82"}, 1467 | ] 1468 | 1469 | [[package]] 1470 | name = "urllib3" 1471 | version = "1.26.18" 1472 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1473 | optional = false 1474 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 1475 | groups = ["dev"] 1476 | files = [ 1477 | {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, 1478 | {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, 1479 | ] 1480 | 1481 | [package.extras] 1482 | brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""] 1483 | secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] 1484 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 1485 | 1486 | [[package]] 1487 | name = "watchdog" 1488 | version = "2.1.6" 1489 | description = "Filesystem events monitoring" 1490 | optional = false 1491 | python-versions = ">=3.6" 1492 | groups = ["dev"] 1493 | files = [ 1494 | {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"}, 1495 | {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b"}, 1496 | {file = "watchdog-2.1.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542"}, 1497 | {file = "watchdog-2.1.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669"}, 1498 | {file = "watchdog-2.1.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660"}, 1499 | {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3"}, 1500 | {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04"}, 1501 | {file = "watchdog-2.1.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b"}, 1502 | {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604"}, 1503 | {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6"}, 1504 | {file = "watchdog-2.1.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9"}, 1505 | {file = "watchdog-2.1.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8"}, 1506 | {file = "watchdog-2.1.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6"}, 1507 | {file = "watchdog-2.1.6-py3-none-manylinux2014_armv7l.whl", hash = "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685"}, 1508 | {file = "watchdog-2.1.6-py3-none-manylinux2014_i686.whl", hash = "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0"}, 1509 | {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65"}, 1510 | {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb"}, 1511 | {file = "watchdog-2.1.6-py3-none-manylinux2014_s390x.whl", hash = "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2"}, 1512 | {file = "watchdog-2.1.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15"}, 1513 | {file = "watchdog-2.1.6-py3-none-win32.whl", hash = "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d"}, 1514 | {file = "watchdog-2.1.6-py3-none-win_amd64.whl", hash = "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5"}, 1515 | {file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"}, 1516 | {file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"}, 1517 | ] 1518 | 1519 | [package.extras] 1520 | watchmedo = ["PyYAML (>=3.10)"] 1521 | 1522 | [[package]] 1523 | name = "wcwidth" 1524 | version = "0.2.5" 1525 | description = "Measures the displayed width of unicode strings in a terminal" 1526 | optional = false 1527 | python-versions = "*" 1528 | groups = ["dev"] 1529 | files = [ 1530 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, 1531 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, 1532 | ] 1533 | 1534 | [[package]] 1535 | name = "zipp" 1536 | version = "3.4.1" 1537 | description = "Backport of pathlib-compatible object wrapper for zip files" 1538 | optional = false 1539 | python-versions = ">=3.6" 1540 | groups = ["dev"] 1541 | files = [ 1542 | {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, 1543 | {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, 1544 | ] 1545 | 1546 | [package.extras] 1547 | docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] 1548 | testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-enabler", "pytest-flake8", "pytest-mypy ; platform_python_implementation != \"PyPy\""] 1549 | 1550 | [metadata] 1551 | lock-version = "2.1" 1552 | python-versions = "^3.8" 1553 | content-hash = "db5459351c8cdacab5ee3303d99f9782d7e91441366180bc8799fbe6c1ffe803" 1554 | --------------------------------------------------------------------------------