├── .circleci └── config.yml ├── .gitignore ├── Makefile ├── README.md ├── deploy └── vbump.py ├── logx ├── __init__.py ├── gumph.py └── logxintrospect.py ├── logxutil ├── __init__.py └── util.py ├── poetry.lock ├── pyproject.toml ├── requirements.txt ├── tests ├── __init__.py ├── test_env_yaml ├── test_logx.py └── test_logxutil.py └── tox.ini /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/circleci 5 | docker: 6 | - image: circleci/python:3.8.1 7 | steps: 8 | - checkout 9 | - restore_cache: 10 | key: deps2-{{ .Branch }}-{{ checksum "pyproject.toml" }} 11 | - run: 12 | name: Install poetry 13 | command: | 14 | sudo pip3 install poetry>=1.0 15 | poetry config virtualenvs.create false 16 | - run: 17 | command: | 18 | python3 -m venv ~/.venv 19 | . ~/.venv/bin/activate 20 | 21 | poetry install 22 | - save_cache: 23 | key: deps2-{{ .Branch }}-{{ checksum "pyproject.toml" }} 24 | paths: 25 | - "~/.venv" 26 | - run: 27 | name: Check formatting 28 | command: | 29 | . ~/.venv/bin/activate 30 | make lint 31 | - run: 32 | command: | 33 | . ~/.venv/bin/activate 34 | make test 35 | - store_artifacts: 36 | path: test-reports/ 37 | destination: tr1 38 | 39 | publish: 40 | working_directory: ~/circleci 41 | docker: 42 | - image: circleci/python:3.8.1 43 | steps: 44 | - setup_remote_docker 45 | - checkout 46 | - restore_cache: 47 | key: deps2-{{ .Branch }}-{{ checksum "pyproject.toml" }} 48 | - run: 49 | name: Install poetry, deps 50 | command: | 51 | sudo pip3 install poetry>=1.0 52 | poetry config virtualenvs.create false 53 | python3 -m venv ~/.venv 54 | . ~/.venv/bin/activate 55 | poetry install 56 | - run: 57 | name: Bump version, build, install 58 | command: | 59 | . ~/.venv/bin/activate 60 | python deploy/vbump.py 61 | poetry build 62 | poetry publish --username $PYPI_USERNAME --password $PYPI_PASSWORD 63 | 64 | 65 | workflows: 66 | version: 2 67 | build-then-publish: 68 | jobs: 69 | - build 70 | - publish: 71 | requires: 72 | - build 73 | filters: 74 | branches: 75 | only: master 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | wheelhouse 22 | 23 | # Installer logs 24 | pip-log.txt 25 | 26 | # Unit test / coverage reports 27 | .coverage 28 | .tox 29 | nosetests.xml 30 | .pytest_cache 31 | 32 | # Translations 33 | *.mo 34 | 35 | # Mr Developer 36 | .mr.developer.cfg 37 | .project 38 | .pydevproject 39 | 40 | .cache 41 | 42 | scratch 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | targs = --cov-report term-missing --cov logx --cov logxutil 2 | 3 | pip: 4 | pip install -r requirements.txt 5 | 6 | test: 7 | pytest $(targs) 8 | 9 | fmt: 10 | black . 11 | 12 | lint: 13 | flake8 . 14 | 15 | clean: 16 | git clean -fXd 17 | find . -name \*.pyc -delete 18 | rm -rf .cache 19 | 20 | publish: 21 | python setup.py sdist bdist_wheel --universal 22 | twine upload dist/* 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # logx: best practice python logging with zero config 2 | 3 | Configuring logging is tedious. Reading the logging module docs makes me tired. 4 | 5 | Wouldn't it be nice to log as easily as doing a print statement, without any upfront config? 6 | 7 | ## Obligatory example 8 | 9 | Enter `logx`. It's as simple as: 10 | 11 | >>> from logx import log 12 | >>> log.info('hello world') 13 | hello world 14 | >>> log.set_default_format() 15 | >>> log.warn('warning!') 16 | [2018-02-26 21:51:16,971] WARNING [__main__.:1] warning! 17 | 18 | Logs get logged automatically to the logger whose name matches the current module. 19 | 20 | ## List of sweet features 21 | 22 | - Creates loggers lazily/as needed/on demand and **logs to the appropriate logger automatically**. If you're in the "acme" module it'll log to a log called "acme", no need worry about logger names and instances. 23 | - **Shows all log messages by default**, which follows the principle of least surprise and is probably what you want when debugging. 24 | - Included default handler **logs to the appropriate standard output stream by default**: Errors and warnings to stderr, the rest to stdout. 25 | - Allows easy following of best practice when including log statements in a library: **Just call log.create_null_handler() in your module.** 26 | - **Uses the standard logging library**, so you can still customize your setup as much as you want/need. Plays nicely with your existing logging config files. 27 | - **Includes the very useful logging_tree module** for viewing your current logging configuration. `logx.print_diagram()` 28 | 29 | ## Install 30 | 31 | >>> pip install logx 32 | 33 | ## Contribute 34 | 35 | Issues and pull requests welcome, hit me. Am I doing logging completely wrong? Critique welcome, even if very pedantic. 36 | -------------------------------------------------------------------------------- /deploy/vbump.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from time import time 3 | 4 | from toml import dumps, loads 5 | 6 | PYPROJECT = "pyproject.toml" 7 | 8 | p = Path(PYPROJECT) 9 | pyproject = loads(p.read_text()) 10 | 11 | v = pyproject["tool"]["poetry"]["version"] 12 | 13 | parts = v.split(".")[:2] 14 | unix = str(int(time())) 15 | 16 | parts.append(unix) 17 | 18 | v_with_timestamp = ".".join(parts) 19 | pyproject["tool"]["poetry"]["version"] = v_with_timestamp 20 | 21 | p.write_text(dumps(pyproject)) 22 | -------------------------------------------------------------------------------- /logx/__init__.py: -------------------------------------------------------------------------------- 1 | from .gumph import Log, StdHandler, print_diagram # noqa 2 | 3 | log = Log() 4 | -------------------------------------------------------------------------------- /logx/gumph.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import logging 3 | import logging.config 4 | import io 5 | import yaml 6 | 7 | 8 | from .logxintrospect import get_nicest_module_name 9 | 10 | YAML_CONFIG_PATH = "loggers.yaml" 11 | DEFAULT_FORMAT = ( 12 | "[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s" 13 | ) 14 | 15 | 16 | def load_yaml_config(yaml_config_path=YAML_CONFIG_PATH): 17 | with io.open(yaml_config_path) as f: 18 | yaml_config_text = f.read() 19 | yaml_config = yaml.load(yaml_config_text) 20 | logging.config.dictConfig(yaml_config) 21 | 22 | 23 | class StdHandler(logging.Handler): 24 | def emit(self, record): 25 | msg = self.format(record) 26 | if record.levelno >= logging.WARNING: 27 | stream = sys.stderr 28 | else: 29 | stream = sys.stdout 30 | stream.write(msg) 31 | stream.write("\n") 32 | self.flush() 33 | 34 | 35 | def set_basic_config(): 36 | logging.basicConfig(level=logging.DEBUG) 37 | 38 | 39 | def set_root_level(level="DEBUG"): 40 | get_logger("").setLevel(getattr(logging, level)) 41 | 42 | 43 | def get_logger(name): 44 | logger = logging.getLogger(name) 45 | logger.setLevel(logging.DEBUG) 46 | return logger 47 | 48 | 49 | def print_diagram(): 50 | import logging_tree 51 | 52 | logging_tree.printout() 53 | 54 | 55 | class Log: 56 | def __init__(self): 57 | self.handlers = {} 58 | 59 | def set_level(self, level, name=None): 60 | handler = self.get_handler(name) 61 | handler.setLevel(getattr(logging, level.upper())) 62 | 63 | def add_std_handler(self, logger_name, level="DEBUG", top_level=True): 64 | if top_level: 65 | logger_name = logger_name.split(".")[0] 66 | level = level.upper() 67 | logging_level = getattr(logging, level) 68 | logger = logging.getLogger(logger_name) 69 | logger.setLevel(logging_level) 70 | h = StdHandler() 71 | h.setLevel(logging_level) 72 | logger.addHandler(h) 73 | return h 74 | 75 | def current_logger_name(self): 76 | name = get_nicest_module_name() 77 | return name 78 | 79 | def set_default_format(self, name=None): 80 | self.set_format(DEFAULT_FORMAT) 81 | 82 | def get_logger(self, name=None): 83 | name = name or self.current_logger_name() 84 | return logging.getLogger(name) 85 | 86 | def get_handler(self, name=None, top_level=True): 87 | name = name or self.current_logger_name() 88 | if top_level: 89 | name = name.split(".")[0] 90 | logger = self.get_logger(name) 91 | for h in logger.handlers: 92 | if isinstance(h, (StdHandler, logging.NullHandler)): 93 | return h 94 | 95 | raise KeyError("no logx-related handler found on this logger") 96 | 97 | def set_format(self, formatstring, logger_name=None, top_level=True, datefmt=None): 98 | logger_name = logger_name or self.current_logger_name() 99 | if top_level: 100 | logger_name = logger_name.split(".")[0] 101 | try: 102 | handler = self.get_handler(logger_name) 103 | except KeyError: 104 | handler = None 105 | if not handler: 106 | handler = self.add_std_handler(logger_name) 107 | if datefmt: 108 | formatter = logging.Formatter(formatstring, datefmt=datefmt) 109 | else: 110 | formatter = logging.Formatter(formatstring) 111 | handler.setFormatter(formatter) 112 | 113 | def set_null_handler(self, name=None, top_level=True): 114 | name = name or self.current_logger_name() 115 | if top_level: 116 | name = name.split(".")[0] 117 | logger = self.get_logger(name=name) 118 | logger.handlers = [_ for _ in logger.handlers if not isinstance(_, StdHandler)] 119 | logger.addHandler(logging.NullHandler()) 120 | 121 | def clear_null_handler(self, name=None, top_level=True): 122 | name = name or self.current_logger_name() 123 | if top_level: 124 | name = name.split(".")[0] 125 | logger = self.get_logger(name=name) 126 | logger.handlers = [ 127 | _ for _ in logger.handlers if not isinstance(_, logging.NullHandler) 128 | ] 129 | 130 | def get_logger_with_handler(self, logger_name): 131 | """ 132 | Returns the form of logger_name, potentially converted in to top 133 | level, that has a handler 134 | 135 | Args: 136 | logger_name (str): the expected name of the logger 137 | 138 | Return: 139 | str: The name of the logger that has a handler 140 | 141 | Raises: 142 | KeyError: When it can't find a logger with a handler by that name 143 | in any form 144 | """ 145 | try: 146 | self.get_handler(logger_name) 147 | except KeyError: 148 | logger_name = logger_name.split(".")[0] 149 | self.get_handler(logger_name) 150 | return logger_name 151 | 152 | def __getattr__(self, method, *args, **kwargs): 153 | if method.startswith("_"): 154 | raise AttributeError 155 | 156 | logger_name = self.current_logger_name() 157 | try: 158 | logger_name = self.get_logger_with_handler(logger_name) 159 | except KeyError: 160 | self.add_std_handler(logger_name) 161 | _logger = logging.getLogger(logger_name) 162 | return getattr(_logger, method) 163 | -------------------------------------------------------------------------------- /logx/logxintrospect.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | 4 | def get_calling_module(name=None, top_level_only=True): 5 | def top_module(mname): 6 | if top_level_only: 7 | return mname.split(".")[0] 8 | 9 | else: 10 | return mname 11 | 12 | s = inspect.stack() 13 | 14 | def modules_iter(): 15 | for i in range(len(s)): 16 | m = inspect.getmodule(s[i][0]) 17 | # func_name = s[i][3] 18 | yield m 19 | 20 | x = next( 21 | ( 22 | m 23 | for m in modules_iter() 24 | if m and top_module(m.__name__) != top_module(__name__) 25 | ), 26 | None, 27 | ) 28 | return x 29 | 30 | 31 | def get_nicest_module_name(m=None): 32 | if not m: 33 | m = get_calling_module() 34 | if m: 35 | _name = m.__name__ 36 | else: 37 | return "__main__" 38 | 39 | def is_main(name): 40 | return name == "__main__" 41 | 42 | if is_main(_name): 43 | if m.__spec__: 44 | return m.__spec__.name 45 | 46 | else: 47 | return inspect.getmodulename(inspect.getfile(m)) 48 | 49 | else: 50 | return _name 51 | -------------------------------------------------------------------------------- /logxutil/__init__.py: -------------------------------------------------------------------------------- 1 | from .util import load_env_yaml, env # noqa 2 | -------------------------------------------------------------------------------- /logxutil/util.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | import yaml 4 | 5 | from logx import log 6 | 7 | 8 | log.set_null_handler("logxutil") 9 | 10 | ENV_YAML = ".env.yaml" 11 | 12 | 13 | def load_env_yaml(path=None): 14 | path = path or ENV_YAML 15 | 16 | try: 17 | with io.open(path) as r: 18 | log.info(f"loading env vars from yaml: {path}") 19 | env_vars = yaml.load(r, Loader=yaml.FullLoader) 20 | os.environ.update(env_vars) 21 | except FileNotFoundError: 22 | log.info(f"local env file not found at {path}, not loading") 23 | 24 | 25 | def env(name): 26 | return os.environ[name] 27 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | category = "dev" 3 | description = "apipkg: namespace control and lazy-import mechanism" 4 | name = "apipkg" 5 | optional = false 6 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 7 | version = "1.5" 8 | 9 | [[package]] 10 | category = "dev" 11 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 12 | marker = "python_version > \"3.6\"" 13 | name = "appdirs" 14 | optional = false 15 | python-versions = "*" 16 | version = "1.4.3" 17 | 18 | [[package]] 19 | category = "dev" 20 | description = "Atomic file writes." 21 | name = "atomicwrites" 22 | optional = false 23 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 24 | version = "1.3.0" 25 | 26 | [[package]] 27 | category = "dev" 28 | description = "Classes Without Boilerplate" 29 | name = "attrs" 30 | optional = false 31 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 32 | version = "19.3.0" 33 | 34 | [package.extras] 35 | azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] 36 | dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] 37 | docs = ["sphinx", "zope.interface"] 38 | tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] 39 | 40 | [[package]] 41 | category = "dev" 42 | description = "Backport of functools.lru_cache" 43 | marker = "python_version < \"3.2\"" 44 | name = "backports.functools-lru-cache" 45 | optional = false 46 | python-versions = ">=2.6" 47 | version = "1.6.1" 48 | 49 | [package.extras] 50 | docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] 51 | testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov"] 52 | 53 | [[package]] 54 | category = "dev" 55 | description = "The uncompromising code formatter." 56 | marker = "python_version > \"3.6\"" 57 | name = "black" 58 | optional = false 59 | python-versions = ">=3.6" 60 | version = "18.9b0" 61 | 62 | [package.dependencies] 63 | appdirs = "*" 64 | attrs = ">=17.4.0" 65 | click = ">=6.5" 66 | toml = ">=0.9.4" 67 | 68 | [package.extras] 69 | d = ["aiohttp (>=3.3.2)"] 70 | 71 | [[package]] 72 | category = "dev" 73 | description = "An easy safelist-based HTML-sanitizing tool." 74 | name = "bleach" 75 | optional = false 76 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 77 | version = "3.1.0" 78 | 79 | [package.dependencies] 80 | six = ">=1.9.0" 81 | webencodings = "*" 82 | 83 | [[package]] 84 | category = "dev" 85 | description = "Python package for providing Mozilla's CA Bundle." 86 | name = "certifi" 87 | optional = false 88 | python-versions = "*" 89 | version = "2019.11.28" 90 | 91 | [[package]] 92 | category = "dev" 93 | description = "Universal encoding detector for Python 2 and 3" 94 | name = "chardet" 95 | optional = false 96 | python-versions = "*" 97 | version = "3.0.4" 98 | 99 | [[package]] 100 | category = "dev" 101 | description = "Composable command line interface toolkit" 102 | marker = "python_version > \"3.6\"" 103 | name = "click" 104 | optional = false 105 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 106 | version = "7.0" 107 | 108 | [[package]] 109 | category = "dev" 110 | description = "Cross-platform colored terminal text." 111 | marker = "sys_platform == \"win32\" and python_version == \"3.4\"" 112 | name = "colorama" 113 | optional = false 114 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 115 | version = "0.4.1" 116 | 117 | [[package]] 118 | category = "dev" 119 | description = "Cross-platform colored terminal text." 120 | marker = "sys_platform == \"win32\" and python_version != \"3.4\" or platform_system == \"Windows\" or sys_platform == \"win32\"" 121 | name = "colorama" 122 | optional = false 123 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 124 | version = "0.4.3" 125 | 126 | [[package]] 127 | category = "dev" 128 | description = "Updated configparser from Python 3.7 for Python 2.6+." 129 | marker = "python_version < \"3.2\"" 130 | name = "configparser" 131 | optional = false 132 | python-versions = ">=2.6" 133 | version = "4.0.2" 134 | 135 | [package.extras] 136 | docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] 137 | testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2)", "pytest-flake8", "pytest-black-multipy"] 138 | 139 | [[package]] 140 | category = "dev" 141 | description = "Backports and enhancements for the contextlib module" 142 | marker = "python_version < \"3\"" 143 | name = "contextlib2" 144 | optional = false 145 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 146 | version = "0.6.0.post1" 147 | 148 | [[package]] 149 | category = "dev" 150 | description = "Code coverage measurement for Python" 151 | name = "coverage" 152 | optional = false 153 | python-versions = "*" 154 | version = "4.4.2" 155 | 156 | [[package]] 157 | category = "dev" 158 | description = "Code coverage measurement for Python" 159 | name = "coverage" 160 | optional = false 161 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4" 162 | version = "4.5.4" 163 | 164 | [[package]] 165 | category = "dev" 166 | description = "Code coverage measurement for Python" 167 | name = "coverage" 168 | optional = false 169 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 170 | version = "5.0.3" 171 | 172 | [package.extras] 173 | toml = ["toml"] 174 | 175 | [[package]] 176 | category = "dev" 177 | description = "Docutils -- Python Documentation Utilities" 178 | name = "docutils" 179 | optional = false 180 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 181 | version = "0.15.2" 182 | 183 | [[package]] 184 | category = "dev" 185 | description = "Docutils -- Python Documentation Utilities" 186 | name = "docutils" 187 | optional = false 188 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 189 | version = "0.16" 190 | 191 | [[package]] 192 | category = "dev" 193 | description = "Discover and load entry points from installed packages." 194 | name = "entrypoints" 195 | optional = false 196 | python-versions = ">=2.7" 197 | version = "0.3" 198 | 199 | [package.dependencies] 200 | [package.dependencies.configparser] 201 | python = ">=2.7,<2.8" 202 | version = ">=3.5" 203 | 204 | [[package]] 205 | category = "dev" 206 | description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" 207 | marker = "python_version < \"3.4\"" 208 | name = "enum34" 209 | optional = false 210 | python-versions = "*" 211 | version = "1.1.6" 212 | 213 | [[package]] 214 | category = "dev" 215 | description = "execnet: rapid multi-Python deployment" 216 | name = "execnet" 217 | optional = false 218 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 219 | version = "1.7.1" 220 | 221 | [package.dependencies] 222 | apipkg = ">=1.4" 223 | 224 | [package.extras] 225 | testing = ["pre-commit"] 226 | 227 | [[package]] 228 | category = "dev" 229 | description = "A platform independent file lock." 230 | name = "filelock" 231 | optional = false 232 | python-versions = "*" 233 | version = "3.0.12" 234 | 235 | [[package]] 236 | category = "dev" 237 | description = "the modular source code checker: pep8, pyflakes and co" 238 | name = "flake8" 239 | optional = false 240 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 241 | version = "3.7.9" 242 | 243 | [package.dependencies] 244 | entrypoints = ">=0.3.0,<0.4.0" 245 | mccabe = ">=0.6.0,<0.7.0" 246 | pycodestyle = ">=2.5.0,<2.6.0" 247 | pyflakes = ">=2.1.0,<2.2.0" 248 | 249 | [package.dependencies.configparser] 250 | python = "<3.2" 251 | version = "*" 252 | 253 | [package.dependencies.enum34] 254 | python = "<3.4" 255 | version = "*" 256 | 257 | [package.dependencies.functools32] 258 | python = "<3.2" 259 | version = "*" 260 | 261 | [package.dependencies.typing] 262 | python = "<3.5" 263 | version = "*" 264 | 265 | [[package]] 266 | category = "dev" 267 | description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+" 268 | marker = "python_version < \"3.0\"" 269 | name = "funcsigs" 270 | optional = false 271 | python-versions = "*" 272 | version = "1.0.2" 273 | 274 | [[package]] 275 | category = "dev" 276 | description = "Backport of the functools module from Python 3.2.3 for use on 2.7 and PyPy." 277 | marker = "python_version < \"3.2\"" 278 | name = "functools32" 279 | optional = false 280 | python-versions = "*" 281 | version = "3.2.3-2" 282 | 283 | [[package]] 284 | category = "dev" 285 | description = "Backport of the concurrent.futures package from Python 3" 286 | marker = "python_version < \"3.2\"" 287 | name = "futures" 288 | optional = false 289 | python-versions = ">=2.6, <3" 290 | version = "3.3.0" 291 | 292 | [[package]] 293 | category = "dev" 294 | description = "Internationalized Domain Names in Applications (IDNA)" 295 | name = "idna" 296 | optional = false 297 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 298 | version = "2.8" 299 | 300 | [[package]] 301 | category = "dev" 302 | description = "Read metadata from Python packages" 303 | marker = "python_version < \"3.8\"" 304 | name = "importlib-metadata" 305 | optional = false 306 | python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" 307 | version = "0.23" 308 | 309 | [package.dependencies] 310 | zipp = ">=0.5" 311 | 312 | [package.extras] 313 | docs = ["sphinx", "rst.linker"] 314 | testing = ["packaging", "importlib-resources"] 315 | 316 | [[package]] 317 | category = "dev" 318 | description = "Read metadata from Python packages" 319 | marker = "python_version < \"3.8\"" 320 | name = "importlib-metadata" 321 | optional = false 322 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 323 | version = "1.4.0" 324 | 325 | [package.dependencies] 326 | zipp = ">=0.5" 327 | 328 | [package.dependencies.configparser] 329 | python = "<3" 330 | version = ">=3.5" 331 | 332 | [package.dependencies.contextlib2] 333 | python = "<3" 334 | version = "*" 335 | 336 | [package.dependencies.pathlib2] 337 | python = "<3" 338 | version = "*" 339 | 340 | [package.extras] 341 | docs = ["sphinx", "rst.linker"] 342 | testing = ["packaging", "importlib-resources"] 343 | 344 | [[package]] 345 | category = "dev" 346 | description = "A Python utility / library to sort Python imports." 347 | name = "isort" 348 | optional = false 349 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 350 | version = "4.3.21" 351 | 352 | [package.dependencies] 353 | [package.dependencies."backports.functools-lru-cache"] 354 | python = "<3.2" 355 | version = "*" 356 | 357 | [package.dependencies.futures] 358 | python = "<3.2" 359 | version = "*" 360 | 361 | [package.extras] 362 | pipfile = ["pipreqs", "requirementslib"] 363 | pyproject = ["toml"] 364 | requirements = ["pipreqs", "pip-api"] 365 | xdg_home = ["appdirs (>=1.4.0)"] 366 | 367 | [[package]] 368 | category = "main" 369 | description = "Introspect and display the logger tree inside \"logging\"" 370 | name = "logging-tree" 371 | optional = false 372 | python-versions = "*" 373 | version = "1.8" 374 | 375 | [[package]] 376 | category = "dev" 377 | description = "McCabe checker, plugin for flake8" 378 | name = "mccabe" 379 | optional = false 380 | python-versions = "*" 381 | version = "0.6.1" 382 | 383 | [[package]] 384 | category = "dev" 385 | description = "More routines for operating on iterables, beyond itertools" 386 | marker = "python_version < \"3.8\"" 387 | name = "more-itertools" 388 | optional = false 389 | python-versions = "*" 390 | version = "5.0.0" 391 | 392 | [package.dependencies] 393 | six = ">=1.0.0,<2.0.0" 394 | 395 | [[package]] 396 | category = "dev" 397 | description = "More routines for operating on iterables, beyond itertools" 398 | marker = "python_version < \"3.8\" or python_version > \"2.7\"" 399 | name = "more-itertools" 400 | optional = false 401 | python-versions = ">=3.4" 402 | version = "7.2.0" 403 | 404 | [[package]] 405 | category = "dev" 406 | description = "More routines for operating on iterables, beyond itertools" 407 | name = "more-itertools" 408 | optional = false 409 | python-versions = ">=3.5" 410 | version = "8.1.0" 411 | 412 | [[package]] 413 | category = "dev" 414 | description = "Core utilities for Python packages" 415 | name = "packaging" 416 | optional = false 417 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 418 | version = "20.0" 419 | 420 | [package.dependencies] 421 | pyparsing = ">=2.0.2" 422 | six = "*" 423 | 424 | [[package]] 425 | category = "dev" 426 | description = "Object-oriented filesystem paths" 427 | name = "pathlib" 428 | optional = false 429 | python-versions = "*" 430 | version = "1.0.1" 431 | 432 | [[package]] 433 | category = "dev" 434 | description = "Object-oriented filesystem paths" 435 | marker = "python_version < \"3.6\"" 436 | name = "pathlib2" 437 | optional = false 438 | python-versions = "*" 439 | version = "2.3.5" 440 | 441 | [package.dependencies] 442 | six = "*" 443 | 444 | [package.dependencies.scandir] 445 | python = "<3.5" 446 | version = "*" 447 | 448 | [[package]] 449 | category = "dev" 450 | description = "Query metadatdata from sdists / bdists / installed packages." 451 | name = "pkginfo" 452 | optional = false 453 | python-versions = "*" 454 | version = "1.5.0.1" 455 | 456 | [package.extras] 457 | testing = ["nose", "coverage"] 458 | 459 | [[package]] 460 | category = "dev" 461 | description = "plugin and hook calling mechanisms for python" 462 | name = "pluggy" 463 | optional = false 464 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 465 | version = "0.13.1" 466 | 467 | [package.dependencies] 468 | [package.dependencies.importlib-metadata] 469 | python = "<3.8" 470 | version = ">=0.12" 471 | 472 | [package.extras] 473 | dev = ["pre-commit", "tox"] 474 | 475 | [[package]] 476 | category = "dev" 477 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 478 | name = "py" 479 | optional = false 480 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 481 | version = "1.8.1" 482 | 483 | [[package]] 484 | category = "dev" 485 | description = "Python style guide checker" 486 | name = "pycodestyle" 487 | optional = false 488 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 489 | version = "2.5.0" 490 | 491 | [[package]] 492 | category = "dev" 493 | description = "passive checker of Python programs" 494 | name = "pyflakes" 495 | optional = false 496 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 497 | version = "2.1.1" 498 | 499 | [[package]] 500 | category = "dev" 501 | description = "Pygments is a syntax highlighting package written in Python." 502 | name = "pygments" 503 | optional = false 504 | python-versions = "*" 505 | version = "2.3.1" 506 | 507 | [[package]] 508 | category = "dev" 509 | description = "Pygments is a syntax highlighting package written in Python." 510 | name = "pygments" 511 | optional = false 512 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 513 | version = "2.5.2" 514 | 515 | [[package]] 516 | category = "dev" 517 | description = "Python parsing module" 518 | name = "pyparsing" 519 | optional = false 520 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 521 | version = "2.4.6" 522 | 523 | [[package]] 524 | category = "dev" 525 | description = "pytest: simple powerful testing with Python" 526 | name = "pytest" 527 | optional = false 528 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" 529 | version = "4.6.9" 530 | 531 | [package.dependencies] 532 | atomicwrites = ">=1.0" 533 | attrs = ">=17.4.0" 534 | packaging = "*" 535 | pluggy = ">=0.12,<1.0" 536 | py = ">=1.5.0" 537 | six = ">=1.10.0" 538 | wcwidth = "*" 539 | 540 | [[package.dependencies.colorama]] 541 | python = "<3.4.0 || >=3.5.0" 542 | version = "*" 543 | 544 | [[package.dependencies.colorama]] 545 | python = ">=3.4,<3.5" 546 | version = "<=0.4.1" 547 | 548 | [[package.dependencies.more-itertools]] 549 | python = "<2.8" 550 | version = ">=4.0.0,<6.0.0" 551 | 552 | [[package.dependencies.more-itertools]] 553 | python = ">=2.8" 554 | version = ">=4.0.0" 555 | 556 | [package.dependencies.funcsigs] 557 | python = "<3.0" 558 | version = ">=1.0" 559 | 560 | [package.dependencies.importlib-metadata] 561 | python = "<3.8" 562 | version = ">=0.12" 563 | 564 | [package.dependencies.pathlib2] 565 | python = "<3.6" 566 | version = ">=2.2.0" 567 | 568 | [package.extras] 569 | testing = ["argcomplete", "hypothesis (>=3.56)", "nose", "requests", "mock"] 570 | 571 | [[package]] 572 | category = "dev" 573 | description = "pytest: simple powerful testing with Python" 574 | name = "pytest" 575 | optional = false 576 | python-versions = ">=3.5" 577 | version = "5.3.2" 578 | 579 | [package.dependencies] 580 | atomicwrites = ">=1.0" 581 | attrs = ">=17.4.0" 582 | colorama = "*" 583 | more-itertools = ">=4.0.0" 584 | packaging = "*" 585 | pluggy = ">=0.12,<1.0" 586 | py = ">=1.5.0" 587 | wcwidth = "*" 588 | 589 | [package.dependencies.importlib-metadata] 590 | python = "<3.8" 591 | version = ">=0.12" 592 | 593 | [package.dependencies.pathlib2] 594 | python = "<3.6" 595 | version = ">=2.2.0" 596 | 597 | [package.extras] 598 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 599 | 600 | [[package]] 601 | category = "dev" 602 | description = "Pytest plugin for measuring coverage." 603 | name = "pytest-cov" 604 | optional = false 605 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 606 | version = "2.8.1" 607 | 608 | [package.dependencies] 609 | coverage = ">=4.4" 610 | pytest = ">=3.6" 611 | 612 | [package.extras] 613 | testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "virtualenv"] 614 | 615 | [[package]] 616 | category = "dev" 617 | description = "run tests in isolated forked subprocesses" 618 | name = "pytest-forked" 619 | optional = false 620 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 621 | version = "1.1.3" 622 | 623 | [package.dependencies] 624 | pytest = ">=3.1.0" 625 | 626 | [[package]] 627 | category = "dev" 628 | description = "pytest xdist plugin for distributed testing and loop-on-failing modes" 629 | name = "pytest-xdist" 630 | optional = false 631 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 632 | version = "1.31.0" 633 | 634 | [package.dependencies] 635 | execnet = ">=1.1" 636 | pytest = ">=4.4.0" 637 | pytest-forked = "*" 638 | six = "*" 639 | 640 | [package.extras] 641 | testing = ["filelock"] 642 | 643 | [[package]] 644 | category = "main" 645 | description = "YAML parser and emitter for Python" 646 | name = "pyyaml" 647 | optional = false 648 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 649 | version = "5.2" 650 | 651 | [[package]] 652 | category = "main" 653 | description = "YAML parser and emitter for Python" 654 | name = "pyyaml" 655 | optional = false 656 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 657 | version = "5.3" 658 | 659 | [[package]] 660 | category = "dev" 661 | description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" 662 | name = "readme-renderer" 663 | optional = false 664 | python-versions = "*" 665 | version = "24.0" 666 | 667 | [package.dependencies] 668 | Pygments = "*" 669 | bleach = ">=2.1.0" 670 | docutils = ">=0.13.1" 671 | six = "*" 672 | 673 | [package.extras] 674 | md = ["cmarkgfm (>=0.2.0)"] 675 | 676 | [[package]] 677 | category = "dev" 678 | description = "Python HTTP for Humans." 679 | name = "requests" 680 | optional = false 681 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 682 | version = "2.21.0" 683 | 684 | [package.dependencies] 685 | certifi = ">=2017.4.17" 686 | chardet = ">=3.0.2,<3.1.0" 687 | idna = ">=2.5,<2.9" 688 | urllib3 = ">=1.21.1,<1.25" 689 | 690 | [package.extras] 691 | security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] 692 | socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] 693 | 694 | [[package]] 695 | category = "dev" 696 | description = "Python HTTP for Humans." 697 | name = "requests" 698 | optional = false 699 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 700 | version = "2.22.0" 701 | 702 | [package.dependencies] 703 | certifi = ">=2017.4.17" 704 | chardet = ">=3.0.2,<3.1.0" 705 | idna = ">=2.5,<2.9" 706 | urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" 707 | 708 | [package.extras] 709 | security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] 710 | socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] 711 | 712 | [[package]] 713 | category = "dev" 714 | description = "A utility belt for advanced users of python-requests" 715 | name = "requests-toolbelt" 716 | optional = false 717 | python-versions = "*" 718 | version = "0.9.1" 719 | 720 | [package.dependencies] 721 | requests = ">=2.0.1,<3.0.0" 722 | 723 | [[package]] 724 | category = "dev" 725 | description = "scandir, a better directory iterator and faster os.walk()" 726 | marker = "python_version < \"3.5\"" 727 | name = "scandir" 728 | optional = false 729 | python-versions = "*" 730 | version = "1.10.0" 731 | 732 | [[package]] 733 | category = "dev" 734 | description = "Python 2 and 3 compatibility utilities" 735 | name = "six" 736 | optional = false 737 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 738 | version = "1.14.0" 739 | 740 | [[package]] 741 | category = "dev" 742 | description = "Database Abstraction Library" 743 | name = "sqlalchemy" 744 | optional = false 745 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 746 | version = "1.3.12" 747 | 748 | [package.extras] 749 | mssql = ["pyodbc"] 750 | mssql_pymssql = ["pymssql"] 751 | mssql_pyodbc = ["pyodbc"] 752 | mysql = ["mysqlclient"] 753 | oracle = ["cx-oracle"] 754 | postgresql = ["psycopg2"] 755 | postgresql_pg8000 = ["pg8000"] 756 | postgresql_psycopg2binary = ["psycopg2-binary"] 757 | postgresql_psycopg2cffi = ["psycopg2cffi"] 758 | pymysql = ["pymysql"] 759 | 760 | [[package]] 761 | category = "dev" 762 | description = "various snippets of SQL-related boilerplate" 763 | name = "sqlbag" 764 | optional = false 765 | python-versions = "*" 766 | version = "0.1.1579049654" 767 | 768 | [package.dependencies] 769 | pathlib = "*" 770 | six = "*" 771 | sqlalchemy = "*" 772 | 773 | [package.extras] 774 | maria = ["pymysql"] 775 | pendulum = ["pendulum", "relativedelta"] 776 | pg = ["psycopg2"] 777 | 778 | [[package]] 779 | category = "dev" 780 | description = "Python Library for Tom's Obvious, Minimal Language" 781 | name = "toml" 782 | optional = false 783 | python-versions = "*" 784 | version = "0.10.0" 785 | 786 | [[package]] 787 | category = "dev" 788 | description = "tox is a generic virtualenv management and test command line tool" 789 | name = "tox" 790 | optional = false 791 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 792 | version = "3.14.0" 793 | 794 | [package.dependencies] 795 | filelock = ">=3.0.0,<4" 796 | packaging = ">=14" 797 | pluggy = ">=0.12.0,<1" 798 | py = ">=1.4.17,<2" 799 | six = ">=1.0.0,<2" 800 | toml = ">=0.9.4" 801 | virtualenv = ">=14.0.0" 802 | 803 | [package.dependencies.importlib-metadata] 804 | python = "<3.8" 805 | version = ">=0.12,<1" 806 | 807 | [package.extras] 808 | docs = ["sphinx (>=2.0.0,<3)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"] 809 | testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=4.0.0,<6)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.2.3,<2)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"] 810 | 811 | [[package]] 812 | category = "dev" 813 | description = "tox is a generic virtualenv management and test command line tool" 814 | name = "tox" 815 | optional = false 816 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 817 | version = "3.14.3" 818 | 819 | [package.dependencies] 820 | colorama = ">=0.4.1" 821 | filelock = ">=3.0.0,<4" 822 | packaging = ">=14" 823 | pluggy = ">=0.12.0,<1" 824 | py = ">=1.4.17,<2" 825 | six = ">=1.0.0,<2" 826 | toml = ">=0.9.4" 827 | virtualenv = ">=16.0.0" 828 | 829 | [package.dependencies.importlib-metadata] 830 | python = "<3.8" 831 | version = ">=0.12,<2" 832 | 833 | [package.extras] 834 | docs = ["sphinx (>=2.0.0,<3)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"] 835 | testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=4.0.0,<6)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.0.0,<4)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"] 836 | 837 | [[package]] 838 | category = "dev" 839 | description = "Fast, Extensible Progress Meter" 840 | name = "tqdm" 841 | optional = false 842 | python-versions = ">=2.6, !=3.0.*, !=3.1.*" 843 | version = "4.41.1" 844 | 845 | [package.extras] 846 | dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] 847 | 848 | [[package]] 849 | category = "dev" 850 | description = "Collection of utilities for publishing packages on PyPI" 851 | name = "twine" 852 | optional = false 853 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 854 | version = "1.15.0" 855 | 856 | [package.dependencies] 857 | pkginfo = ">=1.4.2" 858 | readme-renderer = ">=21.0" 859 | requests = ">=2.5.0,<2.15 || >2.15,<2.16 || >2.16" 860 | requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" 861 | setuptools = ">=0.7.0" 862 | tqdm = ">=4.14" 863 | 864 | [package.extras] 865 | keyring = ["keyring"] 866 | with-blake2 = ["pyblake2"] 867 | 868 | [[package]] 869 | category = "dev" 870 | description = "Type Hints for Python" 871 | marker = "python_version < \"3.5\"" 872 | name = "typing" 873 | optional = false 874 | python-versions = "*" 875 | version = "3.7.4.1" 876 | 877 | [[package]] 878 | category = "dev" 879 | description = "HTTP library with thread-safe connection pooling, file post, and more." 880 | name = "urllib3" 881 | optional = false 882 | python-versions = "*" 883 | version = "1.22" 884 | 885 | [package.extras] 886 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 887 | socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] 888 | 889 | [[package]] 890 | category = "dev" 891 | description = "HTTP library with thread-safe connection pooling, file post, and more." 892 | name = "urllib3" 893 | optional = false 894 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" 895 | version = "1.24.3" 896 | 897 | [package.extras] 898 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 899 | socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] 900 | 901 | [[package]] 902 | category = "dev" 903 | description = "HTTP library with thread-safe connection pooling, file post, and more." 904 | name = "urllib3" 905 | optional = false 906 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" 907 | version = "1.25.7" 908 | 909 | [package.extras] 910 | brotli = ["brotlipy (>=0.6.0)"] 911 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 912 | socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] 913 | 914 | [[package]] 915 | category = "dev" 916 | description = "Virtual Python Environment builder" 917 | name = "virtualenv" 918 | optional = false 919 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" 920 | version = "16.7.9" 921 | 922 | [package.extras] 923 | docs = ["sphinx (>=1.8.0,<2)", "towncrier (>=18.5.0)", "sphinx-rtd-theme (>=0.4.2,<1)"] 924 | testing = ["pytest (>=4.0.0,<5)", "coverage (>=4.5.0,<5)", "pytest-timeout (>=1.3.0,<2)", "six (>=1.10.0,<2)", "pytest-xdist", "pytest-localserver", "pypiserver", "mock", "xonsh"] 925 | 926 | [[package]] 927 | category = "dev" 928 | description = "Measures number of Terminal column cells of wide-character codes" 929 | name = "wcwidth" 930 | optional = false 931 | python-versions = "*" 932 | version = "0.1.8" 933 | 934 | [[package]] 935 | category = "dev" 936 | description = "Character encoding aliases for legacy web content" 937 | name = "webencodings" 938 | optional = false 939 | python-versions = "*" 940 | version = "0.5.1" 941 | 942 | [[package]] 943 | category = "dev" 944 | description = "A built-package format for Python." 945 | name = "wheel" 946 | optional = false 947 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 948 | version = "0.33.6" 949 | 950 | [package.extras] 951 | test = ["pytest (>=3.0.0)", "pytest-cov"] 952 | 953 | [[package]] 954 | category = "dev" 955 | description = "Backport of pathlib-compatible object wrapper for zip files" 956 | marker = "python_version < \"3.8\"" 957 | name = "zipp" 958 | optional = false 959 | python-versions = ">=2.7" 960 | version = "1.0.0" 961 | 962 | [package.dependencies] 963 | more-itertools = "*" 964 | 965 | [package.extras] 966 | docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] 967 | testing = ["pathlib2", "contextlib2", "unittest2"] 968 | 969 | [metadata] 970 | content-hash = "d601b8effdd63818ca047a2ab8119faf88143d8802f7a205ae6b6b7d4bd8a293" 971 | python-versions = "*" 972 | 973 | [metadata.files] 974 | apipkg = [ 975 | {file = "apipkg-1.5-py2.py3-none-any.whl", hash = "sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"}, 976 | {file = "apipkg-1.5.tar.gz", hash = "sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6"}, 977 | ] 978 | appdirs = [ 979 | {file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"}, 980 | {file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"}, 981 | ] 982 | atomicwrites = [ 983 | {file = "atomicwrites-1.3.0-py2.py3-none-any.whl", hash = "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"}, 984 | {file = "atomicwrites-1.3.0.tar.gz", hash = "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"}, 985 | ] 986 | attrs = [ 987 | {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, 988 | {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, 989 | ] 990 | "backports.functools-lru-cache" = [ 991 | {file = "backports.functools_lru_cache-1.6.1-py2.py3-none-any.whl", hash = "sha256:0bada4c2f8a43d533e4ecb7a12214d9420e66eb206d54bf2d682581ca4b80848"}, 992 | {file = "backports.functools_lru_cache-1.6.1.tar.gz", hash = "sha256:8fde5f188da2d593bd5bc0be98d9abc46c95bb8a9dde93429570192ee6cc2d4a"}, 993 | ] 994 | black = [ 995 | {file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"}, 996 | {file = "black-18.9b0.tar.gz", hash = "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"}, 997 | ] 998 | bleach = [ 999 | {file = "bleach-3.1.0-py2.py3-none-any.whl", hash = "sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16"}, 1000 | {file = "bleach-3.1.0.tar.gz", hash = "sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"}, 1001 | ] 1002 | certifi = [ 1003 | {file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"}, 1004 | {file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"}, 1005 | ] 1006 | chardet = [ 1007 | {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, 1008 | {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, 1009 | ] 1010 | click = [ 1011 | {file = "Click-7.0-py2.py3-none-any.whl", hash = "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13"}, 1012 | {file = "Click-7.0.tar.gz", hash = "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"}, 1013 | ] 1014 | colorama = [ 1015 | {file = "colorama-0.4.1-py2.py3-none-any.whl", hash = "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"}, 1016 | {file = "colorama-0.4.1.tar.gz", hash = "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d"}, 1017 | {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, 1018 | {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, 1019 | ] 1020 | configparser = [ 1021 | {file = "configparser-4.0.2-py2.py3-none-any.whl", hash = "sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c"}, 1022 | {file = "configparser-4.0.2.tar.gz", hash = "sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df"}, 1023 | ] 1024 | contextlib2 = [ 1025 | {file = "contextlib2-0.6.0.post1-py2.py3-none-any.whl", hash = "sha256:3355078a159fbb44ee60ea80abd0d87b80b78c248643b49aa6d94673b413609b"}, 1026 | {file = "contextlib2-0.6.0.post1.tar.gz", hash = "sha256:01f490098c18b19d2bd5bb5dc445b2054d2fa97f09a4280ba2c5f3c394c8162e"}, 1027 | ] 1028 | coverage = [ 1029 | {file = "coverage-4.4.2-cp26-cp26m-macosx_10_10_x86_64.whl", hash = "sha256:d1ee76f560c3c3e8faada866a07a32485445e16ed2206ac8378bd90dadffb9f0"}, 1030 | {file = "coverage-4.4.2-cp26-cp26m-manylinux1_i686.whl", hash = "sha256:007eeef7e23f9473622f7d94a3e029a45d55a92a1f083f0f3512f5ab9a669b05"}, 1031 | {file = "coverage-4.4.2-cp26-cp26m-manylinux1_x86_64.whl", hash = "sha256:17307429935f96c986a1b1674f78079528833410750321d22b5fb35d1883828e"}, 1032 | {file = "coverage-4.4.2-cp26-cp26mu-manylinux1_i686.whl", hash = "sha256:845fddf89dca1e94abe168760a38271abfc2e31863fbb4ada7f9a99337d7c3dc"}, 1033 | {file = "coverage-4.4.2-cp26-cp26mu-manylinux1_x86_64.whl", hash = "sha256:3f4d0b3403d3e110d2588c275540649b1841725f5a11a7162620224155d00ba2"}, 1034 | {file = "coverage-4.4.2-cp27-cp27m-macosx_10_12_intel.whl", hash = "sha256:4c4f368ffe1c2e7602359c2c50233269f3abe1c48ca6b288dcd0fb1d1c679733"}, 1035 | {file = "coverage-4.4.2-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:f8c55dd0f56d3d618dfacf129e010cbe5d5f94b6951c1b2f13ab1a2f79c284da"}, 1036 | {file = "coverage-4.4.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:cdd92dd9471e624cd1d8c1a2703d25f114b59b736b0f1f659a98414e535ffb3d"}, 1037 | {file = "coverage-4.4.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2ad357d12971e77360034c1596011a03f50c0f9e1ecd12e081342b8d1aee2236"}, 1038 | {file = "coverage-4.4.2-cp27-cp27m-win32.whl", hash = "sha256:700d7579995044dc724847560b78ac786f0ca292867447afda7727a6fbaa082e"}, 1039 | {file = "coverage-4.4.2-cp27-cp27m-win_amd64.whl", hash = "sha256:66f393e10dd866be267deb3feca39babba08ae13763e0fc7a1063cbe1f8e49f6"}, 1040 | {file = "coverage-4.4.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e9a0e1caed2a52f15c96507ab78a48f346c05681a49c5b003172f8073da6aa6b"}, 1041 | {file = "coverage-4.4.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:eea9135432428d3ca7ee9be86af27cb8e56243f73764a9b6c3e0bda1394916be"}, 1042 | {file = "coverage-4.4.2-cp33-cp33m-macosx_10_10_x86_64.whl", hash = "sha256:5ff16548492e8a12e65ff3d55857ccd818584ed587a6c2898a9ebbe09a880674"}, 1043 | {file = "coverage-4.4.2-cp33-cp33m-manylinux1_i686.whl", hash = "sha256:d00e29b78ff610d300b2c37049a41234d48ea4f2d2581759ebcf67caaf731c31"}, 1044 | {file = "coverage-4.4.2-cp33-cp33m-manylinux1_x86_64.whl", hash = "sha256:87d942863fe74b1c3be83a045996addf1639218c2cb89c5da18c06c0fe3917ea"}, 1045 | {file = "coverage-4.4.2-cp34-cp34m-macosx_10_10_x86_64.whl", hash = "sha256:358d635b1fc22a425444d52f26287ae5aea9e96e254ff3c59c407426f44574f4"}, 1046 | {file = "coverage-4.4.2-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:81912cfe276e0069dca99e1e4e6be7b06b5fc8342641c6b472cb2fed7de7ae18"}, 1047 | {file = "coverage-4.4.2-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:079248312838c4c8f3494934ab7382a42d42d5f365f0cf7516f938dbb3f53f3f"}, 1048 | {file = "coverage-4.4.2-cp34-cp34m-win32.whl", hash = "sha256:b0059630ca5c6b297690a6bf57bf2fdac1395c24b7935fd73ee64190276b743b"}, 1049 | {file = "coverage-4.4.2-cp34-cp34m-win_amd64.whl", hash = "sha256:493082f104b5ca920e97a485913de254cbe351900deed72d4264571c73464cd0"}, 1050 | {file = "coverage-4.4.2-cp35-cp35m-macosx_10_10_x86_64.whl", hash = "sha256:e3ba9b14607c23623cf38f90b23f5bed4a3be87cbfa96e2e9f4eabb975d1e98b"}, 1051 | {file = "coverage-4.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:82cbd3317320aa63c65555aa4894bf33a13fb3a77f079059eb5935eea415938d"}, 1052 | {file = "coverage-4.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9721f1b7275d3112dc7ccf63f0553c769f09b5c25a26ee45872c7f5c09edf6c1"}, 1053 | {file = "coverage-4.4.2-cp35-cp35m-win32.whl", hash = "sha256:bd4800e32b4c8d99c3a2c943f1ac430cbf80658d884123d19639bcde90dad44a"}, 1054 | {file = "coverage-4.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:f29841e865590af72c4b90d7b5b8e93fd560f5dea436c1d5ee8053788f9285de"}, 1055 | {file = "coverage-4.4.2-cp36-cp36m-macosx_10_12_x86_64.whl", hash = "sha256:f3a5c6d054c531536a83521c00e5d4004f1e126e2e2556ce399bef4180fbe540"}, 1056 | {file = "coverage-4.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:dd707a21332615108b736ef0b8513d3edaf12d2a7d5fc26cd04a169a8ae9b526"}, 1057 | {file = "coverage-4.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2e1a5c6adebb93c3b175103c2f855eda957283c10cf937d791d81bef8872d6ca"}, 1058 | {file = "coverage-4.4.2-cp36-cp36m-win32.whl", hash = "sha256:f87f522bde5540d8a4b11df80058281ac38c44b13ce29ced1e294963dd51a8f8"}, 1059 | {file = "coverage-4.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a7cfaebd8f24c2b537fa6a271229b051cdac9c1734bb6f939ccfc7c055689baa"}, 1060 | {file = "coverage-4.4.2.tar.gz", hash = "sha256:309d91bd7a35063ec7a0e4d75645488bfab3f0b66373e7722f23da7f5b0f34cc"}, 1061 | {file = "coverage-4.4.2.win-amd64-py2.7.exe", hash = "sha256:b6cebae1502ce5b87d7c6f532fa90ab345cfbda62b95aeea4e431e164d498a3d"}, 1062 | {file = "coverage-4.4.2.win-amd64-py3.4.exe", hash = "sha256:a4497faa4f1c0fc365ba05eaecfb6b5d24e3c8c72e95938f9524e29dadb15e76"}, 1063 | {file = "coverage-4.4.2.win-amd64-py3.5.exe", hash = "sha256:2b4d7f03a8a6632598cbc5df15bbca9f778c43db7cf1a838f4fa2c8599a8691a"}, 1064 | {file = "coverage-4.4.2.win-amd64-py3.6.exe", hash = "sha256:1afccd7e27cac1b9617be8c769f6d8a6d363699c9b86820f40c74cfb3328921c"}, 1065 | {file = "coverage-4.4.2.win32-py2.7.exe", hash = "sha256:0388c12539372bb92d6dde68b4627f0300d948965bbb7fc104924d715fdc0965"}, 1066 | {file = "coverage-4.4.2.win32-py3.4.exe", hash = "sha256:ab3508df9a92c1d3362343d235420d08e2662969b83134f8a97dc1451cbe5e84"}, 1067 | {file = "coverage-4.4.2.win32-py3.5.exe", hash = "sha256:43a155eb76025c61fc20c3d03b89ca28efa6f5be572ab6110b2fb68eda96bfea"}, 1068 | {file = "coverage-4.4.2.win32-py3.6.exe", hash = "sha256:f98b461cb59f117887aa634a66022c0bd394278245ed51189f63a036516e32de"}, 1069 | {file = "coverage-4.5.4-cp26-cp26m-macosx_10_12_x86_64.whl", hash = "sha256:eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28"}, 1070 | {file = "coverage-4.5.4-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c"}, 1071 | {file = "coverage-4.5.4-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce"}, 1072 | {file = "coverage-4.5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe"}, 1073 | {file = "coverage-4.5.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888"}, 1074 | {file = "coverage-4.5.4-cp27-cp27m-win32.whl", hash = "sha256:63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc"}, 1075 | {file = "coverage-4.5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24"}, 1076 | {file = "coverage-4.5.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437"}, 1077 | {file = "coverage-4.5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6"}, 1078 | {file = "coverage-4.5.4-cp33-cp33m-macosx_10_10_x86_64.whl", hash = "sha256:6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5"}, 1079 | {file = "coverage-4.5.4-cp34-cp34m-macosx_10_12_x86_64.whl", hash = "sha256:331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef"}, 1080 | {file = "coverage-4.5.4-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e"}, 1081 | {file = "coverage-4.5.4-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca"}, 1082 | {file = "coverage-4.5.4-cp34-cp34m-win32.whl", hash = "sha256:fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0"}, 1083 | {file = "coverage-4.5.4-cp34-cp34m-win_amd64.whl", hash = "sha256:df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1"}, 1084 | {file = "coverage-4.5.4-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7"}, 1085 | {file = "coverage-4.5.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47"}, 1086 | {file = "coverage-4.5.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025"}, 1087 | {file = "coverage-4.5.4-cp35-cp35m-win32.whl", hash = "sha256:bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e"}, 1088 | {file = "coverage-4.5.4-cp35-cp35m-win_amd64.whl", hash = "sha256:19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d"}, 1089 | {file = "coverage-4.5.4-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9"}, 1090 | {file = "coverage-4.5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755"}, 1091 | {file = "coverage-4.5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9"}, 1092 | {file = "coverage-4.5.4-cp36-cp36m-win32.whl", hash = "sha256:6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f"}, 1093 | {file = "coverage-4.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5"}, 1094 | {file = "coverage-4.5.4-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca"}, 1095 | {file = "coverage-4.5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650"}, 1096 | {file = "coverage-4.5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2"}, 1097 | {file = "coverage-4.5.4-cp37-cp37m-win32.whl", hash = "sha256:93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5"}, 1098 | {file = "coverage-4.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351"}, 1099 | {file = "coverage-4.5.4-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5"}, 1100 | {file = "coverage-4.5.4.tar.gz", hash = "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c"}, 1101 | {file = "coverage-5.0.3-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f"}, 1102 | {file = "coverage-5.0.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc"}, 1103 | {file = "coverage-5.0.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a"}, 1104 | {file = "coverage-5.0.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52"}, 1105 | {file = "coverage-5.0.3-cp27-cp27m-win32.whl", hash = "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c"}, 1106 | {file = "coverage-5.0.3-cp27-cp27m-win_amd64.whl", hash = "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73"}, 1107 | {file = "coverage-5.0.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68"}, 1108 | {file = "coverage-5.0.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691"}, 1109 | {file = "coverage-5.0.3-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301"}, 1110 | {file = "coverage-5.0.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf"}, 1111 | {file = "coverage-5.0.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3"}, 1112 | {file = "coverage-5.0.3-cp35-cp35m-win32.whl", hash = "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0"}, 1113 | {file = "coverage-5.0.3-cp35-cp35m-win_amd64.whl", hash = "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0"}, 1114 | {file = "coverage-5.0.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2"}, 1115 | {file = "coverage-5.0.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894"}, 1116 | {file = "coverage-5.0.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf"}, 1117 | {file = "coverage-5.0.3-cp36-cp36m-win32.whl", hash = "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477"}, 1118 | {file = "coverage-5.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc"}, 1119 | {file = "coverage-5.0.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8"}, 1120 | {file = "coverage-5.0.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987"}, 1121 | {file = "coverage-5.0.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea"}, 1122 | {file = "coverage-5.0.3-cp37-cp37m-win32.whl", hash = "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc"}, 1123 | {file = "coverage-5.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e"}, 1124 | {file = "coverage-5.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb"}, 1125 | {file = "coverage-5.0.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37"}, 1126 | {file = "coverage-5.0.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d"}, 1127 | {file = "coverage-5.0.3-cp38-cp38m-win32.whl", hash = "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954"}, 1128 | {file = "coverage-5.0.3-cp38-cp38m-win_amd64.whl", hash = "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e"}, 1129 | {file = "coverage-5.0.3-cp39-cp39m-win32.whl", hash = "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40"}, 1130 | {file = "coverage-5.0.3-cp39-cp39m-win_amd64.whl", hash = "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af"}, 1131 | {file = "coverage-5.0.3.tar.gz", hash = "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef"}, 1132 | ] 1133 | docutils = [ 1134 | {file = "docutils-0.15.2-py2-none-any.whl", hash = "sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827"}, 1135 | {file = "docutils-0.15.2-py3-none-any.whl", hash = "sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0"}, 1136 | {file = "docutils-0.15.2.tar.gz", hash = "sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"}, 1137 | {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, 1138 | {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, 1139 | ] 1140 | entrypoints = [ 1141 | {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, 1142 | {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, 1143 | ] 1144 | enum34 = [ 1145 | {file = "enum34-1.1.6-py2-none-any.whl", hash = "sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79"}, 1146 | {file = "enum34-1.1.6-py3-none-any.whl", hash = "sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a"}, 1147 | {file = "enum34-1.1.6.tar.gz", hash = "sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1"}, 1148 | {file = "enum34-1.1.6.zip", hash = "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850"}, 1149 | ] 1150 | execnet = [ 1151 | {file = "execnet-1.7.1-py2.py3-none-any.whl", hash = "sha256:d4efd397930c46415f62f8a31388d6be4f27a91d7550eb79bc64a756e0056547"}, 1152 | {file = "execnet-1.7.1.tar.gz", hash = "sha256:cacb9df31c9680ec5f95553976c4da484d407e85e41c83cb812aa014f0eddc50"}, 1153 | ] 1154 | filelock = [ 1155 | {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, 1156 | {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, 1157 | ] 1158 | flake8 = [ 1159 | {file = "flake8-3.7.9-py2.py3-none-any.whl", hash = "sha256:49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca"}, 1160 | {file = "flake8-3.7.9.tar.gz", hash = "sha256:45681a117ecc81e870cbf1262835ae4af5e7a8b08e40b944a8a6e6b895914cfb"}, 1161 | ] 1162 | funcsigs = [ 1163 | {file = "funcsigs-1.0.2-py2.py3-none-any.whl", hash = "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca"}, 1164 | {file = "funcsigs-1.0.2.tar.gz", hash = "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"}, 1165 | ] 1166 | functools32 = [ 1167 | {file = "functools32-3.2.3-2.tar.gz", hash = "sha256:f6253dfbe0538ad2e387bd8fdfd9293c925d63553f5813c4e587745416501e6d"}, 1168 | {file = "functools32-3.2.3-2.zip", hash = "sha256:89d824aa6c358c421a234d7f9ee0bd75933a67c29588ce50aaa3acdf4d403fa0"}, 1169 | ] 1170 | futures = [ 1171 | {file = "futures-3.3.0-py2-none-any.whl", hash = "sha256:49b3f5b064b6e3afc3316421a3f25f66c137ae88f068abbf72830170033c5e16"}, 1172 | {file = "futures-3.3.0.tar.gz", hash = "sha256:7e033af76a5e35f58e56da7a91e687706faf4e7bdfb2cbc3f2cca6b9bcda9794"}, 1173 | ] 1174 | idna = [ 1175 | {file = "idna-2.8-py2.py3-none-any.whl", hash = "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"}, 1176 | {file = "idna-2.8.tar.gz", hash = "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407"}, 1177 | ] 1178 | importlib-metadata = [ 1179 | {file = "importlib_metadata-0.23-py2.py3-none-any.whl", hash = "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"}, 1180 | {file = "importlib_metadata-0.23.tar.gz", hash = "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26"}, 1181 | {file = "importlib_metadata-1.4.0-py2.py3-none-any.whl", hash = "sha256:bdd9b7c397c273bcc9a11d6629a38487cd07154fa255a467bf704cd2c258e359"}, 1182 | {file = "importlib_metadata-1.4.0.tar.gz", hash = "sha256:f17c015735e1a88296994c0697ecea7e11db24290941983b08c9feb30921e6d8"}, 1183 | ] 1184 | isort = [ 1185 | {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, 1186 | {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, 1187 | ] 1188 | logging-tree = [ 1189 | {file = "logging_tree-1.8-py2.py3-none-any.whl", hash = "sha256:668c5c364f4623e81784e6042e82fb87d9423bd291a68933c7a3c03e72f4f343"}, 1190 | {file = "logging_tree-1.8.tar.gz", hash = "sha256:dc6cf63f2f8e7805dad32b3400fb9685c9d3cf83bd0f0e99267e426b9cf72694"}, 1191 | ] 1192 | mccabe = [ 1193 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 1194 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 1195 | ] 1196 | more-itertools = [ 1197 | {file = "more-itertools-5.0.0.tar.gz", hash = "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4"}, 1198 | {file = "more_itertools-5.0.0-py2-none-any.whl", hash = "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc"}, 1199 | {file = "more_itertools-5.0.0-py3-none-any.whl", hash = "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"}, 1200 | {file = "more-itertools-7.2.0.tar.gz", hash = "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832"}, 1201 | {file = "more_itertools-7.2.0-py3-none-any.whl", hash = "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"}, 1202 | {file = "more-itertools-8.1.0.tar.gz", hash = "sha256:c468adec578380b6281a114cb8a5db34eb1116277da92d7c46f904f0b52d3288"}, 1203 | {file = "more_itertools-8.1.0-py3-none-any.whl", hash = "sha256:1a2a32c72400d365000412fe08eb4a24ebee89997c18d3d147544f70f5403b39"}, 1204 | ] 1205 | packaging = [ 1206 | {file = "packaging-20.0-py2.py3-none-any.whl", hash = "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb"}, 1207 | {file = "packaging-20.0.tar.gz", hash = "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8"}, 1208 | ] 1209 | pathlib = [ 1210 | {file = "pathlib-1.0.1.tar.gz", hash = "sha256:6940718dfc3eff4258203ad5021090933e5c04707d5ca8cc9e73c94a7894ea9f"}, 1211 | ] 1212 | pathlib2 = [ 1213 | {file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"}, 1214 | {file = "pathlib2-2.3.5.tar.gz", hash = "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"}, 1215 | ] 1216 | pkginfo = [ 1217 | {file = "pkginfo-1.5.0.1-py2.py3-none-any.whl", hash = "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32"}, 1218 | {file = "pkginfo-1.5.0.1.tar.gz", hash = "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb"}, 1219 | ] 1220 | pluggy = [ 1221 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 1222 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 1223 | ] 1224 | py = [ 1225 | {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, 1226 | {file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, 1227 | ] 1228 | pycodestyle = [ 1229 | {file = "pycodestyle-2.5.0-py2.py3-none-any.whl", hash = "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56"}, 1230 | {file = "pycodestyle-2.5.0.tar.gz", hash = "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"}, 1231 | ] 1232 | pyflakes = [ 1233 | {file = "pyflakes-2.1.1-py2.py3-none-any.whl", hash = "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0"}, 1234 | {file = "pyflakes-2.1.1.tar.gz", hash = "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"}, 1235 | ] 1236 | pygments = [ 1237 | {file = "Pygments-2.3.1-py2.py3-none-any.whl", hash = "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"}, 1238 | {file = "Pygments-2.3.1.tar.gz", hash = "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a"}, 1239 | {file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"}, 1240 | {file = "Pygments-2.5.2.tar.gz", hash = "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"}, 1241 | ] 1242 | pyparsing = [ 1243 | {file = "pyparsing-2.4.6-py2.py3-none-any.whl", hash = "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"}, 1244 | {file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"}, 1245 | ] 1246 | pytest = [ 1247 | {file = "pytest-4.6.9-py2.py3-none-any.whl", hash = "sha256:c77a5f30a90e0ce24db9eaa14ddfd38d4afb5ea159309bdd2dae55b931bc9324"}, 1248 | {file = "pytest-4.6.9.tar.gz", hash = "sha256:19e8f75eac01dd3f211edd465b39efbcbdc8fc5f7866d7dd49fedb30d8adf339"}, 1249 | {file = "pytest-5.3.2-py3-none-any.whl", hash = "sha256:e41d489ff43948babd0fad7ad5e49b8735d5d55e26628a58673c39ff61d95de4"}, 1250 | {file = "pytest-5.3.2.tar.gz", hash = "sha256:6b571215b5a790f9b41f19f3531c53a45cf6bb8ef2988bc1ff9afb38270b25fa"}, 1251 | ] 1252 | pytest-cov = [ 1253 | {file = "pytest-cov-2.8.1.tar.gz", hash = "sha256:cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b"}, 1254 | {file = "pytest_cov-2.8.1-py2.py3-none-any.whl", hash = "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"}, 1255 | ] 1256 | pytest-forked = [ 1257 | {file = "pytest-forked-1.1.3.tar.gz", hash = "sha256:1805699ed9c9e60cb7a8179b8d4fa2b8898098e82d229b0825d8095f0f261100"}, 1258 | {file = "pytest_forked-1.1.3-py2.py3-none-any.whl", hash = "sha256:1ae25dba8ee2e56fb47311c9638f9e58552691da87e82d25b0ce0e4bf52b7d87"}, 1259 | ] 1260 | pytest-xdist = [ 1261 | {file = "pytest-xdist-1.31.0.tar.gz", hash = "sha256:7dc0d027d258cd0defc618fb97055fbd1002735ca7a6d17037018cf870e24011"}, 1262 | {file = "pytest_xdist-1.31.0-py2.py3-none-any.whl", hash = "sha256:0f46020d3d9619e6d17a65b5b989c1ebbb58fc7b1da8fb126d70f4bac4dfeed1"}, 1263 | ] 1264 | pyyaml = [ 1265 | {file = "PyYAML-5.2-cp27-cp27m-win32.whl", hash = "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc"}, 1266 | {file = "PyYAML-5.2-cp27-cp27m-win_amd64.whl", hash = "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"}, 1267 | {file = "PyYAML-5.2-cp35-cp35m-win32.whl", hash = "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15"}, 1268 | {file = "PyYAML-5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075"}, 1269 | {file = "PyYAML-5.2-cp36-cp36m-win32.whl", hash = "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31"}, 1270 | {file = "PyYAML-5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc"}, 1271 | {file = "PyYAML-5.2-cp37-cp37m-win32.whl", hash = "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04"}, 1272 | {file = "PyYAML-5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd"}, 1273 | {file = "PyYAML-5.2-cp38-cp38-win32.whl", hash = "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f"}, 1274 | {file = "PyYAML-5.2-cp38-cp38-win_amd64.whl", hash = "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803"}, 1275 | {file = "PyYAML-5.2.tar.gz", hash = "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c"}, 1276 | {file = "PyYAML-5.3-cp27-cp27m-win32.whl", hash = "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d"}, 1277 | {file = "PyYAML-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6"}, 1278 | {file = "PyYAML-5.3-cp35-cp35m-win32.whl", hash = "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e"}, 1279 | {file = "PyYAML-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689"}, 1280 | {file = "PyYAML-5.3-cp36-cp36m-win32.whl", hash = "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994"}, 1281 | {file = "PyYAML-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e"}, 1282 | {file = "PyYAML-5.3-cp37-cp37m-win32.whl", hash = "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5"}, 1283 | {file = "PyYAML-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf"}, 1284 | {file = "PyYAML-5.3-cp38-cp38-win32.whl", hash = "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811"}, 1285 | {file = "PyYAML-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20"}, 1286 | {file = "PyYAML-5.3.tar.gz", hash = "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"}, 1287 | ] 1288 | readme-renderer = [ 1289 | {file = "readme_renderer-24.0-py2.py3-none-any.whl", hash = "sha256:c8532b79afc0375a85f10433eca157d6b50f7d6990f337fa498c96cd4bfc203d"}, 1290 | {file = "readme_renderer-24.0.tar.gz", hash = "sha256:bb16f55b259f27f75f640acf5e00cf897845a8b3e4731b5c1a436e4b8529202f"}, 1291 | ] 1292 | requests = [ 1293 | {file = "requests-2.21.0-py2.py3-none-any.whl", hash = "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"}, 1294 | {file = "requests-2.21.0.tar.gz", hash = "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e"}, 1295 | {file = "requests-2.22.0-py2.py3-none-any.whl", hash = "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"}, 1296 | {file = "requests-2.22.0.tar.gz", hash = "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4"}, 1297 | ] 1298 | requests-toolbelt = [ 1299 | {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, 1300 | {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, 1301 | ] 1302 | scandir = [ 1303 | {file = "scandir-1.10.0-cp27-cp27m-win32.whl", hash = "sha256:92c85ac42f41ffdc35b6da57ed991575bdbe69db895507af88b9f499b701c188"}, 1304 | {file = "scandir-1.10.0-cp27-cp27m-win_amd64.whl", hash = "sha256:cb925555f43060a1745d0a321cca94bcea927c50114b623d73179189a4e100ac"}, 1305 | {file = "scandir-1.10.0-cp34-cp34m-win32.whl", hash = "sha256:2c712840c2e2ee8dfaf36034080108d30060d759c7b73a01a52251cc8989f11f"}, 1306 | {file = "scandir-1.10.0-cp34-cp34m-win_amd64.whl", hash = "sha256:2586c94e907d99617887daed6c1d102b5ca28f1085f90446554abf1faf73123e"}, 1307 | {file = "scandir-1.10.0-cp35-cp35m-win32.whl", hash = "sha256:2b8e3888b11abb2217a32af0766bc06b65cc4a928d8727828ee68af5a967fa6f"}, 1308 | {file = "scandir-1.10.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8c5922863e44ffc00c5c693190648daa6d15e7c1207ed02d6f46a8dcc2869d32"}, 1309 | {file = "scandir-1.10.0-cp36-cp36m-win32.whl", hash = "sha256:2ae41f43797ca0c11591c0c35f2f5875fa99f8797cb1a1fd440497ec0ae4b022"}, 1310 | {file = "scandir-1.10.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7d2d7a06a252764061a020407b997dd036f7bd6a175a5ba2b345f0a357f0b3f4"}, 1311 | {file = "scandir-1.10.0-cp37-cp37m-win32.whl", hash = "sha256:67f15b6f83e6507fdc6fca22fedf6ef8b334b399ca27c6b568cbfaa82a364173"}, 1312 | {file = "scandir-1.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b24086f2375c4a094a6b51e78b4cf7ca16c721dcee2eddd7aa6494b42d6d519d"}, 1313 | {file = "scandir-1.10.0.tar.gz", hash = "sha256:4d4631f6062e658e9007ab3149a9b914f3548cb38bfb021c64f39a025ce578ae"}, 1314 | ] 1315 | six = [ 1316 | {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, 1317 | {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, 1318 | ] 1319 | sqlalchemy = [ 1320 | {file = "SQLAlchemy-1.3.12.tar.gz", hash = "sha256:bfb8f464a5000b567ac1d350b9090cf081180ec1ab4aa87e7bca12dab25320ec"}, 1321 | ] 1322 | sqlbag = [ 1323 | {file = "sqlbag-0.1.1579049654-py2.py3-none-any.whl", hash = "sha256:4beaffcd9335cb2f71c2f82a0e783da888f1a54c932b6e59d54f300cb062bc39"}, 1324 | {file = "sqlbag-0.1.1579049654.tar.gz", hash = "sha256:bf1aba13a062b0ef61d37ab6f4aa8808bc4bd830e0c14178238ac8185d298f42"}, 1325 | ] 1326 | toml = [ 1327 | {file = "toml-0.10.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"}, 1328 | {file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"}, 1329 | {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"}, 1330 | ] 1331 | tox = [ 1332 | {file = "tox-3.14.0-py2.py3-none-any.whl", hash = "sha256:0bc216b6a2e6afe764476b4a07edf2c1dab99ed82bb146a1130b2e828f5bff5e"}, 1333 | {file = "tox-3.14.0.tar.gz", hash = "sha256:c4f6b319c20ba4913dbfe71ebfd14ff95d1853c4231493608182f66e566ecfe1"}, 1334 | {file = "tox-3.14.3-py2.py3-none-any.whl", hash = "sha256:806d0a9217584558cc93747a945a9d9bff10b141a5287f0c8429a08828a22192"}, 1335 | {file = "tox-3.14.3.tar.gz", hash = "sha256:06ba73b149bf838d5cd25dc30c2dd2671ae5b2757cf98e5c41a35fe449f131b3"}, 1336 | ] 1337 | tqdm = [ 1338 | {file = "tqdm-4.41.1-py2.py3-none-any.whl", hash = "sha256:efab950cf7cc1e4d8ee50b2bb9c8e4a89f8307b49e0b2c9cfef3ec4ca26655eb"}, 1339 | {file = "tqdm-4.41.1.tar.gz", hash = "sha256:4789ccbb6fc122b5a6a85d512e4e41fc5acad77216533a6f2b8ce51e0f265c23"}, 1340 | ] 1341 | twine = [ 1342 | {file = "twine-1.15.0-py2.py3-none-any.whl", hash = "sha256:630fadd6e342e725930be6c696537e3f9ccc54331742b16245dab292a17d0460"}, 1343 | {file = "twine-1.15.0.tar.gz", hash = "sha256:a3d22aab467b4682a22de4a422632e79d07eebd07ff2a7079effb13f8a693787"}, 1344 | ] 1345 | typing = [ 1346 | {file = "typing-3.7.4.1-py2-none-any.whl", hash = "sha256:c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36"}, 1347 | {file = "typing-3.7.4.1-py3-none-any.whl", hash = "sha256:f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714"}, 1348 | {file = "typing-3.7.4.1.tar.gz", hash = "sha256:91dfe6f3f706ee8cc32d38edbbf304e9b7583fb37108fef38229617f8b3eba23"}, 1349 | ] 1350 | urllib3 = [ 1351 | {file = "urllib3-1.22-py2.py3-none-any.whl", hash = "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b"}, 1352 | {file = "urllib3-1.22.tar.gz", hash = "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"}, 1353 | {file = "urllib3-1.24.3-py2.py3-none-any.whl", hash = "sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb"}, 1354 | {file = "urllib3-1.24.3.tar.gz", hash = "sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4"}, 1355 | {file = "urllib3-1.25.7-py2.py3-none-any.whl", hash = "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293"}, 1356 | {file = "urllib3-1.25.7.tar.gz", hash = "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"}, 1357 | ] 1358 | virtualenv = [ 1359 | {file = "virtualenv-16.7.9-py2.py3-none-any.whl", hash = "sha256:55059a7a676e4e19498f1aad09b8313a38fcc0cdbe4fdddc0e9b06946d21b4bb"}, 1360 | {file = "virtualenv-16.7.9.tar.gz", hash = "sha256:0d62c70883c0342d59c11d0ddac0d954d0431321a41ab20851facf2b222598f3"}, 1361 | ] 1362 | wcwidth = [ 1363 | {file = "wcwidth-0.1.8-py2.py3-none-any.whl", hash = "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"}, 1364 | {file = "wcwidth-0.1.8.tar.gz", hash = "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"}, 1365 | ] 1366 | webencodings = [ 1367 | {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, 1368 | {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, 1369 | ] 1370 | wheel = [ 1371 | {file = "wheel-0.33.6-py2.py3-none-any.whl", hash = "sha256:f4da1763d3becf2e2cd92a14a7c920f0f00eca30fdde9ea992c836685b9faf28"}, 1372 | {file = "wheel-0.33.6.tar.gz", hash = "sha256:10c9da68765315ed98850f8e048347c3eb06dd81822dc2ab1d4fde9dc9702646"}, 1373 | ] 1374 | zipp = [ 1375 | {file = "zipp-1.0.0-py2.py3-none-any.whl", hash = "sha256:8dda78f06bd1674bd8720df8a50bb47b6e1233c503a4eed8e7810686bde37656"}, 1376 | {file = "zipp-1.0.0.tar.gz", hash = "sha256:d38fbe01bbf7a3593a32bc35a9c4453c32bc42b98c377f9bff7e9f8da157786c"}, 1377 | ] 1378 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "logx" 3 | packages = [ 4 | { include = "logx" }, 5 | { include = "logxutil" }, 6 | ] 7 | version = "0.1" 8 | authors = [ "Robert Lechte ",] 9 | license = "Unlicense" 10 | readme = "README.md" 11 | description = "best practice python logging with zero config" 12 | 13 | repository = "https://github.com/djrobstep/logx" 14 | homepage = "https://github.com/djrobstep/logx" 15 | 16 | [tool.poetry.dependencies] 17 | python = "*" 18 | pyyaml = "*" 19 | logging_tree = "*" 20 | 21 | [tool.poetry.dev-dependencies] 22 | sqlbag = "*" 23 | pytest = "*" 24 | pytest-cov = "*" 25 | pytest-xdist = "*" 26 | tox = "*" 27 | flake8 = "*" 28 | twine = "*" 29 | wheel = "*" 30 | isort = "*" 31 | black = {python=">3.6", version="^18.9b0"} 32 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-cov 3 | pyyaml 4 | logging_tree 5 | flake8 6 | twine 7 | black 8 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djrobstep/logx/c53fabbc160fb8d70fa878684ea36f0c22fd5caa/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_env_yaml: -------------------------------------------------------------------------------- 1 | ABC: def 2 | -------------------------------------------------------------------------------- /tests/test_logx.py: -------------------------------------------------------------------------------- 1 | from logx import log, print_diagram 2 | import json 3 | from datetime import datetime 4 | 5 | 6 | def load_config(path): 7 | import io 8 | import os 9 | import yaml 10 | import logging 11 | 12 | if path and os.path.exists(path): 13 | with io.open(path) as f: 14 | conf = f.read() 15 | logging.config.dictConfig(yaml.load(conf)) 16 | 17 | 18 | def m(caplog): 19 | return caplog.records[-1].message 20 | 21 | 22 | def test_plain_output(caplog, capsys): 23 | logger_name = log.current_logger_name() 24 | assert logger_name == "tests.test_logx" 25 | FIRST_MESSAGE = "first message" 26 | log.info(FIRST_MESSAGE) 27 | assert m(caplog) == FIRST_MESSAGE 28 | out, err = capsys.readouterr() 29 | assert out == FIRST_MESSAGE + "\n" 30 | assert not err 31 | 32 | 33 | def test_formatted_output(caplog, capsys): 34 | log.set_default_format() 35 | log.debug("debug") 36 | assert m(caplog) == "debug" 37 | out, err = capsys.readouterr() 38 | assert out.endswith("debug\n") 39 | assert "DEBUG [tests.test_logx.test_formatted_output:" in out 40 | assert not err 41 | 42 | 43 | def test_set_format(caplog, capsys): 44 | formatstring = '{ "timestamp": "%(asctime)s", "severity": "%(levelname)s", "name": "%(name)s", "funcName": "%(funcName)s", "lineNo": "%(lineno)d", "message": "%(message)s"}' 45 | datefmt = "%Y-%m-%dT%I:%M:%SZ" 46 | log.set_format(formatstring, datefmt=datefmt) 47 | log.debug("log stuff") 48 | output = json.loads(capsys.readouterr().out) 49 | assert datetime.strptime(output["timestamp"], datefmt) is not None 50 | assert output.keys() == json.loads(formatstring).keys() 51 | 52 | 53 | def test_level_change_output(caplog, capsys): 54 | log.set_default_format() 55 | log.set_level("warn") 56 | log.warn("warn") 57 | log.debug("debug") 58 | out, err = capsys.readouterr() 59 | assert "warn" in err 60 | assert "debug" not in err and "debug" not in out 61 | 62 | 63 | def test_null_handler(caplog, capsys): 64 | log.set_null_handler() 65 | log.warn("warn") 66 | log.debug("debug") 67 | out, err = capsys.readouterr() 68 | assert "warn" not in err and "warn" not in out 69 | assert "debug" not in err and "debug" not in out 70 | log.clear_null_handler() 71 | print_diagram() 72 | log.warn("warn") 73 | log.debug("debug") 74 | out, err = capsys.readouterr() 75 | assert "warn" in err and "warn" not in out 76 | assert "debug" not in err and "debug" in out 77 | -------------------------------------------------------------------------------- /tests/test_logxutil.py: -------------------------------------------------------------------------------- 1 | import logxutil 2 | from logx import log 3 | from pytest import raises 4 | 5 | 6 | def m(caplog): 7 | return caplog.records[-1].message 8 | 9 | 10 | def test_env(caplog, capsys): 11 | log.clear_null_handler(name="logxutil") 12 | logxutil.load_env_yaml("tests/test_env_yaml") 13 | assert "loading env vars" in m(caplog) 14 | assert logxutil.env("ABC") == "def" 15 | 16 | 17 | def test_env_file_not_found(caplog, capsys): 18 | log.clear_null_handler(name="logxutil") 19 | logxutil.load_env_yaml("tests/test_env_yaml_bad") 20 | assert "local env file not found" in m(caplog) 21 | 22 | with raises(KeyError): 23 | logxutil.env("BAD") 24 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (http://tox.testrun.org/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py36 8 | toxworkdir = {homedir}/.toxfiles{toxinidir} 9 | 10 | [testenv] 11 | commands = py.test \ 12 | [] # substitute with tox positional arguments 13 | 14 | deps = 15 | -rrequirements.txt 16 | 17 | [flake8] 18 | ignore = E501 19 | --------------------------------------------------------------------------------