diff --git a/.github/workflows/qencode-ci.yml b/.github/workflows/qencode-ci.yml new file mode 100644 index 0000000..e3fc3ee --- /dev/null +++ b/.github/workflows/qencode-ci.yml @@ -0,0 +1,34 @@ +name: qencode CI + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ '3.x' ] + steps: + - uses: actions/checkout@v1 + - name: Configure git + run: | + git config --global user.name 'travis-ci' + git config --global user.email 'travis@nowhere.edu' + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade poetry + poetry install + - name: Lint with flake8 + run: | + poetry run flake8 + - name: Test with pytest + run: | + poetry run py.test --cov=./ --cov-report=xml + - uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 84d0c8b..8ce2566 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,130 @@ -.env .DS_Store -*.log *.sql -*.pyc -.cache/ .vscode/ .idea/ +.vim/ + .eggs/ build/ dist/ qencode.egg-info/ sample-code/drm/buydrm/keys/user_private_key.pem sample-code/drm/buydrm/keys/user_public_cert.pem + +LONG_DESCRIPTION.md + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# Pip +pip-wheel-metadata/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9ccd720 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +PY_FILES= find . -type f -not -path '*/\.*' | grep -i '.*[.]py$$' 2> /dev/null + + +entr_warn: + @echo "----------------------------------------------------------" + @echo " ! File watching functionality non-operational ! " + @echo " " + @echo "Install entr(1) to automatically run tasks on file change." + @echo "See http://entrproject.org/ " + @echo "----------------------------------------------------------" + +isort: + isort `${PY_FILES}` + +black: + black `${PY_FILES}` --skip-string-normalization + +test: + py.test $(test) + +watch_test: + if command -v entr > /dev/null; then ${PY_FILES} | entr -c $(MAKE) test; else $(MAKE) test entr_warn; fi + +build_docs: + cd doc && $(MAKE) html + +watch_docs: + cd doc && $(MAKE) watch_docs + +flake8: + flake8 setup.py sample-code qencode tests + +watch_flake8: + if command -v entr > /dev/null; then ${PY_FILES} | entr -c $(MAKE) flake8; else $(MAKE) flake8 entr_warn; fi diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..659864b --- /dev/null +++ b/poetry.lock @@ -0,0 +1,971 @@ +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.2.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] + +[[package]] +name = "black" +version = "21.12b0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=7.1.2" +dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0,<1" +platformdirs = ">=2" +tomli = ">=0.2.6,<2.0.0" +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = [ + {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, + {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""}, +] + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +python2 = ["typed-ast (>=1.4.3)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "bleach" +version = "4.1.0" +description = "An easy safelist-based HTML-sanitizing tool." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +packaging = "*" +six = ">=1.9.0" +webencodings = "*" + +[[package]] +name = "certifi" +version = "2021.10.8" +description = "Python package for providing Mozilla's CA Bundle." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "cffi" +version = "1.15.0" +description = "Foreign Function Interface for Python calling C code." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "2.0.9" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "dev" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.0.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "codecov" +version = "2.1.12" +description = "Hosted coverage reports for GitHub, Bitbucket and Gitlab" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +coverage = "*" +requests = ">=2.7.9" + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "coverage" +version = "6.2" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "cryptography" +version = "36.0.0" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +sdist = ["setuptools_rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] + +[[package]] +name = "dataclasses" +version = "0.8" +description = "A backport of the dataclasses module for Python 3.6" +category = "dev" +optional = false +python-versions = ">=3.6, <3.7" + +[[package]] +name = "docutils" +version = "0.18.1" +description = "Docutils -- Python Documentation Utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "flake8" +version = "3.9.2" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "importlib-metadata" +version = "4.8.2" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "isort" +version = "4.3.21" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +pipfile = ["pipreqs", "requirementslib"] +pyproject = ["toml"] +requirements = ["pipreqs", "pip-api"] +xdg_home = ["appdirs (>=1.4.0)"] + +[[package]] +name = "jeepney" +version = "0.7.1" +description = "Low-level, pure Python DBus protocol wrapper." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio", "async-timeout"] +trio = ["trio", "async-generator"] + +[[package]] +name = "keyring" +version = "23.4.0" +description = "Store and access your passwords safely." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = ">=3.6" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "pkginfo" +version = "1.8.2" +description = "Query metadatdata from sdists / bdists / installed packages." +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +testing = ["coverage", "nose"] + +[[package]] +name = "platformdirs" +version = "2.4.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pycodestyle" +version = "2.7.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyflakes" +version = "2.3.1" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pygments" +version = "2.10.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "pyparsing" +version = "3.0.6" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "6.2.5" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "3.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.6.1" +description = "Thin-wrapper around the mock package for easier use with pytest" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "tox", "pytest-asyncio"] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.0" +description = "" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "readme-renderer" +version = "31.0" +description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +bleach = ">=2.1.0" +docutils = ">=0.13.1" +Pygments = ">=2.5.1" + +[package.extras] +md = ["cmarkgfm (>=0.5.0,<0.7.0)"] + +[[package]] +name = "requests" +version = "2.26.0" +description = "Python HTTP for Humans." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "requests-toolbelt" +version = "0.9.1" +description = "A utility belt for advanced users of python-requests" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "secretstorage" +version = "3.3.1" +description = "Python bindings to FreeDesktop.org Secret Service API" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "1.2.2" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "tqdm" +version = "4.62.3" +description = "Fast, Extensible Progress Meter" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +notebook = ["ipywidgets (>=6)"] +telegram = ["requests"] + +[[package]] +name = "twine" +version = "3.7.1" +description = "Collection of utilities for publishing packages on PyPI" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = ">=0.4.3" +importlib-metadata = ">=3.6" +keyring = ">=15.1" +pkginfo = ">=1.8.1" +readme-renderer = ">=21.0" +requests = ">=2.20" +requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" +rfc3986 = ">=1.4.0" +tqdm = ">=4.14" + +[[package]] +name = "typed-ast" +version = "1.5.1" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "typing-extensions" +version = "4.0.1" +description = "Backported and Experimental Type Hints for Python 3.6+" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "urllib3" +version = "1.22" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "zipp" +version = "3.6.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[metadata] +lock-version = "1.1" +python-versions = ">=3.6.2" +content-hash = "05b565e73cde7139f91957afb652095f24e40fd0190924a8097518b515c6d68c" + +[metadata.files] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, +] +black = [ + {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, + {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, +] +bleach = [ + {file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"}, + {file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"}, +] +certifi = [ + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] +cffi = [ + {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, + {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, + {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, + {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, + {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, + {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, + {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, + {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, + {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, + {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, + {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, + {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, + {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, + {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, + {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, + {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, + {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"}, + {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"}, +] +click = [ + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, +] +codecov = [ + {file = "codecov-2.1.12-py2.py3-none-any.whl", hash = "sha256:585dc217dc3d8185198ceb402f85d5cb5dbfa0c5f350a5abcdf9e347776a5b47"}, + {file = "codecov-2.1.12-py3.8.egg", hash = "sha256:782a8e5352f22593cbc5427a35320b99490eb24d9dcfa2155fd99d2b75cfb635"}, + {file = "codecov-2.1.12.tar.gz", hash = "sha256:a0da46bb5025426da895af90938def8ee12d37fcbcbbbc15b6dc64cf7ebc51c1"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +coverage = [ + {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, + {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, + {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, + {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, + {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, + {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, + {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, + {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, + {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, + {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, + {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, + {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, + {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, + {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, + {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, + {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, + {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, + {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, + {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, + {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, +] +cryptography = [ + {file = "cryptography-36.0.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:9511416e85e449fe1de73f7f99b21b3aa04fba4c4d335d30c486ba3756e3a2a6"}, + {file = "cryptography-36.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:97199a13b772e74cdcdb03760c32109c808aff7cd49c29e9cf4b7754bb725d1d"}, + {file = "cryptography-36.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:494106e9cd945c2cadfce5374fa44c94cfadf01d4566a3b13bb487d2e6c7959e"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6fbbbb8aab4053fa018984bb0e95a16faeb051dd8cca15add2a27e267ba02b58"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:684993ff6f67000a56454b41bdc7e015429732d65a52d06385b6e9de6181c71e"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c702855cd3174666ef0d2d13dcc879090aa9c6c38f5578896407a7028f75b9f"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d91bc9f535599bed58f6d2e21a2724cb0c3895bf41c6403fe881391d29096f1d"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b17d83b3d1610e571fedac21b2eb36b816654d6f7496004d6a0d32f99d1d8120"}, + {file = "cryptography-36.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8982c19bb90a4fa2aad3d635c6d71814e38b643649b4000a8419f8691f20ac44"}, + {file = "cryptography-36.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:24469d9d33217ffd0ce4582dfcf2a76671af115663a95328f63c99ec7ece61a4"}, + {file = "cryptography-36.0.0-cp36-abi3-win32.whl", hash = "sha256:f6a5a85beb33e57998dc605b9dbe7deaa806385fdf5c4810fb849fcd04640c81"}, + {file = "cryptography-36.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:2deab5ec05d83ddcf9b0916319674d3dae88b0e7ee18f8962642d3cde0496568"}, + {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2049f8b87f449fc6190350de443ee0c1dd631f2ce4fa99efad2984de81031681"}, + {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a776bae1629c8d7198396fd93ec0265f8dd2341c553dc32b976168aaf0e6a636"}, + {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:aa94d617a4cd4cdf4af9b5af65100c036bce22280ebb15d8b5262e8273ebc6ba"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5c49c9e8fb26a567a2b3fa0343c89f5d325447956cc2fc7231c943b29a973712"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef216d13ac8d24d9cd851776662f75f8d29c9f2d05cdcc2d34a18d32463a9b0b"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:231c4a69b11f6af79c1495a0e5a85909686ea8db946935224b7825cfb53827ed"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f92556f94e476c1b616e6daec5f7ddded2c082efa7cee7f31c7aeda615906ed8"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d73e3a96c38173e0aa5646c31bf8473bc3564837977dd480f5cbeacf1d7ef3a3"}, + {file = "cryptography-36.0.0.tar.gz", hash = "sha256:52f769ecb4ef39865719aedc67b4b7eae167bafa48dbc2a26dd36fa56460507f"}, +] +dataclasses = [ + {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, + {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, +] +docutils = [ + {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, + {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, +] +flake8 = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.8.2-py3-none-any.whl", hash = "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100"}, + {file = "importlib_metadata-4.8.2.tar.gz", hash = "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +isort = [ + {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, + {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, +] +jeepney = [ + {file = "jeepney-0.7.1-py3-none-any.whl", hash = "sha256:1b5a0ea5c0e7b166b2f5895b91a08c14de8915afda4407fb5022a195224958ac"}, + {file = "jeepney-0.7.1.tar.gz", hash = "sha256:fa9e232dfa0c498bd0b8a3a73b8d8a31978304dcef0515adc859d4e096f96f4f"}, +] +keyring = [ + {file = "keyring-23.4.0-py3-none-any.whl", hash = "sha256:3dc0f66062a4f8f6f2ce30d6a516e6e623e6c3c2e76864204ceaf64695408f07"}, + {file = "keyring-23.4.0.tar.gz", hash = "sha256:88f206024295e3c6fb16bb0a60fb4bb7ec1185629dc5a729f12aa7c236d01387"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +pkginfo = [ + {file = "pkginfo-1.8.2-py2.py3-none-any.whl", hash = "sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"}, + {file = "pkginfo-1.8.2.tar.gz", hash = "sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff"}, +] +platformdirs = [ + {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, + {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pycodestyle = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pyflakes = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] +pygments = [ + {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, + {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, +] +pyparsing = [ + {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, + {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, +] +pytest = [ + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] +pytest-mock = [ + {file = "pytest-mock-3.6.1.tar.gz", hash = "sha256:40217a058c52a63f1042f0784f62009e976ba824c418cced42e88d5f40ab0e62"}, + {file = "pytest_mock-3.6.1-py3-none-any.whl", hash = "sha256:30c2f2cc9759e76eee674b81ea28c9f0b94f8f0445a1b87762cadf774f0df7e3"}, +] +pywin32-ctypes = [ + {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, + {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, +] +readme-renderer = [ + {file = "readme_renderer-31.0-py3-none-any.whl", hash = "sha256:03b3cca3d1a8ef9d2200258c282eae4b8b60bc53b674c0588cb214785465213a"}, + {file = "readme_renderer-31.0.tar.gz", hash = "sha256:8ceeb608a1bc6f474430db890a4b9b6006e16d9d26b81a84a1e2e2aae4454f9b"}, +] +requests = [ + {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, + {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, +] +requests-toolbelt = [ + {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, + {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, +] +rfc3986 = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] +secretstorage = [ + {file = "SecretStorage-3.3.1-py3-none-any.whl", hash = "sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f"}, + {file = "SecretStorage-3.3.1.tar.gz", hash = "sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +tomli = [ + {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, + {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, +] +tqdm = [ + {file = "tqdm-4.62.3-py2.py3-none-any.whl", hash = "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c"}, + {file = "tqdm-4.62.3.tar.gz", hash = "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d"}, +] +twine = [ + {file = "twine-3.7.1-py3-none-any.whl", hash = "sha256:8c120845fc05270f9ee3e9d7ebbed29ea840e41f48cd059e04733f7e1d401345"}, + {file = "twine-3.7.1.tar.gz", hash = "sha256:28460a3db6b4532bde6a5db6755cf2dce6c5020bada8a641bb2c5c7a9b1f35b8"}, +] +typed-ast = [ + {file = "typed_ast-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d8314c92414ce7481eee7ad42b353943679cf6f30237b5ecbf7d835519e1212"}, + {file = "typed_ast-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b53ae5de5500529c76225d18eeb060efbcec90ad5e030713fe8dab0fb4531631"}, + {file = "typed_ast-1.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:24058827d8f5d633f97223f5148a7d22628099a3d2efe06654ce872f46f07cdb"}, + {file = "typed_ast-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a6d495c1ef572519a7bac9534dbf6d94c40e5b6a608ef41136133377bba4aa08"}, + {file = "typed_ast-1.5.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:de4ecae89c7d8b56169473e08f6bfd2df7f95015591f43126e4ea7865928677e"}, + {file = "typed_ast-1.5.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:256115a5bc7ea9e665c6314ed6671ee2c08ca380f9d5f130bd4d2c1f5848d695"}, + {file = "typed_ast-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:7c42707ab981b6cf4b73490c16e9d17fcd5227039720ca14abe415d39a173a30"}, + {file = "typed_ast-1.5.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:71dcda943a471d826ea930dd449ac7e76db7be778fcd722deb63642bab32ea3f"}, + {file = "typed_ast-1.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4f30a2bcd8e68adbb791ce1567fdb897357506f7ea6716f6bbdd3053ac4d9471"}, + {file = "typed_ast-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ca9e8300d8ba0b66d140820cf463438c8e7b4cdc6fd710c059bfcfb1531d03fb"}, + {file = "typed_ast-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9caaf2b440efb39ecbc45e2fabde809cbe56272719131a6318fd9bf08b58e2cb"}, + {file = "typed_ast-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c9bcad65d66d594bffab8575f39420fe0ee96f66e23c4d927ebb4e24354ec1af"}, + {file = "typed_ast-1.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:591bc04e507595887160ed7aa8d6785867fb86c5793911be79ccede61ae96f4d"}, + {file = "typed_ast-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:a80d84f535642420dd17e16ae25bb46c7f4c16ee231105e7f3eb43976a89670a"}, + {file = "typed_ast-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:38cf5c642fa808300bae1281460d4f9b7617cf864d4e383054a5ef336e344d32"}, + {file = "typed_ast-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b6ab14c56bc9c7e3c30228a0a0b54b915b1579613f6e463ba6f4eb1382e7fd4"}, + {file = "typed_ast-1.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2b8d7007f6280e36fa42652df47087ac7b0a7d7f09f9468f07792ba646aac2d"}, + {file = "typed_ast-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:b6d17f37f6edd879141e64a5db17b67488cfeffeedad8c5cec0392305e9bc775"}, + {file = "typed_ast-1.5.1.tar.gz", hash = "sha256:484137cab8ecf47e137260daa20bafbba5f4e3ec7fda1c1e69ab299b75fa81c5"}, +] +typing-extensions = [ + {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, + {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, +] +urllib3 = [ + {file = "urllib3-1.22-py2.py3-none-any.whl", hash = "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b"}, + {file = "urllib3-1.22.tar.gz", hash = "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"}, +] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] +zipp = [ + {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, + {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, +] diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 0000000..ab1033b --- /dev/null +++ b/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/pyproject.toml b/pyproject.toml index 0097e9f..eb04f1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,27 @@ [tool.black] skip-string-normalization = true + +[tool.poetry] +name = "qencode-api-python-client" +version = "1.0.0" +description = "Python bindings for the Qencode API" +authors = ["Qencode Team "] +license = "MIT" + +[tool.poetry.dependencies] +python = ">=3.6.2" + +[tool.poetry.dev-dependencies] +black = {version="==21.12b0", python="^3.6"} +flake8 = "*" +isort = "*" +pytest = "*" +twine = "*" +codecov = "*" +coverage = "*" +pytest-cov = "*" +pytest-mock = "*" + +[build-system] +requires = ["poetry_core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/qencode/__init__.py b/qencode/__init__.py index f11b550..d4aab36 100644 --- a/qencode/__init__.py +++ b/qencode/__init__.py @@ -41,8 +41,7 @@ def x265_video_codec(): from exeptions import QencodeClientException, QencodeTaskException - -from tools import generate_aws_signed_url, fps_drm, cenc_drm +from tools import cenc_drm, fps_drm, generate_aws_signed_url __version__ = "1.0.4" __status__ = "Production/Stable" diff --git a/qencode/client.py b/qencode/client.py index 69d5869..fc711ff 100644 --- a/qencode/client.py +++ b/qencode/client.py @@ -1,6 +1,6 @@ from httptools import Http -from task import Task from metadata import Metadata +from task import Task class QencodeApiClient(object): diff --git a/qencode/custom_params.py b/qencode/custom_params.py index f23aef5..f643123 100644 --- a/qencode/custom_params.py +++ b/qencode/custom_params.py @@ -1,5 +1,6 @@ import json from json import JSONEncoder + from utils import rm_attributes_if_null diff --git a/qencode/drm/buydrm.py b/qencode/drm/buydrm.py index 8ad9fb9..c7f8514 100644 --- a/qencode/drm/buydrm.py +++ b/qencode/drm/buydrm.py @@ -1,6 +1,7 @@ +import os + from lxml import etree from signxml import XMLSigner -import os NSMAP = { 'cpix': 'urn:dashif:org:cpix', diff --git a/qencode/httptools.py b/qencode/httptools.py index 30f11ff..7cb7b4a 100644 --- a/qencode/httptools.py +++ b/qencode/httptools.py @@ -1,8 +1,8 @@ import json +import ssl import urllib import urllib2 from urlparse import urljoin -import ssl class Http(object): diff --git a/qencode/metadata.py b/qencode/metadata.py index fe0ca70..ffd2da1 100644 --- a/qencode/metadata.py +++ b/qencode/metadata.py @@ -1,6 +1,8 @@ +import urllib2 + from task import * + from qencode import QencodeTaskException -import urllib2 class Metadata(Task): diff --git a/qencode/task.py b/qencode/task.py index 194a9cb..04e6cbf 100644 --- a/qencode/task.py +++ b/qencode/task.py @@ -1,7 +1,8 @@ -from custom_params import Query, CustomTranscodingParams -from const import * -import time import json +import time + +from const import * +from custom_params import CustomTranscodingParams, Query from utils import is_json, rm_key_if_null diff --git a/qencode/tools.py b/qencode/tools.py index 3f0a7af..e77107e 100644 --- a/qencode/tools.py +++ b/qencode/tools.py @@ -1,12 +1,13 @@ # -from const import * import datetime import hashlib import hmac +import uuid +import xml.etree.cElementTree as et + import requests +from const import * from requests.utils import quote -import xml.etree.cElementTree as et -import uuid def generate_aws_signed_url( diff --git a/qencode/tus_uploader.py b/qencode/tus_uploader.py index 748094a..3f0cefb 100644 --- a/qencode/tus_uploader.py +++ b/qencode/tus_uploader.py @@ -1,4 +1,5 @@ import sys + from tusclient import client from utils import get_tus_from_url diff --git a/qencode/utils.py b/qencode/utils.py index 58cdcf7..193c825 100644 --- a/qencode/utils.py +++ b/qencode/utils.py @@ -1,6 +1,6 @@ -import sys -import logging import json +import logging +import sys def is_number(s): diff --git a/sample-code/drm/buydrm/buydrm_widevine_playready.py b/sample-code/drm/buydrm/buydrm_widevine_playready.py index 36ef352..460ac40 100644 --- a/sample-code/drm/buydrm/buydrm_widevine_playready.py +++ b/sample-code/drm/buydrm/buydrm_widevine_playready.py @@ -1,10 +1,11 @@ -import uuid -import time -import json import base64 +import json +import time +import uuid + import qencode -from qencode.drm.buydrm import create_cpix_user_request from qencode import QencodeClientException, QencodeTaskException +from qencode.drm.buydrm import create_cpix_user_request # replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' @@ -14,13 +15,12 @@ USER_PUB_CERT_PATH = './keys/user_public_cert.pem' key_ids = [ - { 'kid': str(uuid.uuid4()), 'track_type': 'SD' }, - { 'kid': str(uuid.uuid4()), 'track_type': 'HD' } + {'kid': str(uuid.uuid4()), 'track_type': 'SD'}, + {'kid': str(uuid.uuid4()), 'track_type': 'HD'}, ] media_id = 'my first stream' - QUERY = """ { "query": { @@ -48,40 +48,44 @@ def start_encode(): - # this creates signed request to BuyDRM - cpix_request = create_cpix_user_request( - key_ids, media_id, USER_PVT_KEY_PATH, USER_PUB_CERT_PATH, - use_playready=True, use_widevine=True - ) + # this creates signed request to BuyDRM + cpix_request = create_cpix_user_request( + key_ids, + media_id, + USER_PVT_KEY_PATH, + USER_PUB_CERT_PATH, + use_playready=True, + use_widevine=True, + ) - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) - print 'The client created. Expire date: %s' % client.expire + print 'The client created. Expire date: %s' % client.expire - task = client.create_task() + task = client.create_task() - if task.error: - raise QencodeTaskException(task.message) + if task.error: + raise QencodeTaskException(task.message) - query = QUERY.replace('{cpix_request}', base64.b64encode(cpix_request)) + query = QUERY.replace('{cpix_request}', base64.b64encode(cpix_request)) - task.custom_start(query) + task.custom_start(query) - if task.error: - raise QencodeTaskException(task.message) + if task.error: + raise QencodeTaskException(task.message) - print 'Start encode. Task: %s' % task.task_token + print 'Start encode. Task: %s' % task.task_token - while True: - status = task.status() - # print status - print json.dumps(status, indent=2, sort_keys=True) - if status['error'] or status['status'] == 'completed': - break - time.sleep(5) + while True: + status = task.status() + # print status + print json.dumps(status, indent=2, sort_keys=True) + if status['error'] or status['status'] == 'completed': + break + time.sleep(5) if __name__ == '__main__': - start_encode() + start_encode() diff --git a/sample-code/metadata.py b/sample-code/metadata.py index e1b7246..a8402ea 100644 --- a/sample-code/metadata.py +++ b/sample-code/metadata.py @@ -1,13 +1,17 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) +import sys + import qencode from qencode import QencodeClientException -#replace with your API KEY (can be found in your Project settings on Qencode portal) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) + +# replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' VIDEO_URL = 'https://nyc3.s3.qencode.com/qencode/bbb_30s.mp4' @@ -18,7 +22,4 @@ print 'The client created. Expire date: %s' % client.expire metadata = client.get_metadata(VIDEO_URL) -print('Metadata: ' + metadata) - - - +print ('Metadata: ' + metadata) diff --git a/sample-code/start_custom_hls.py b/sample-code/start_custom_hls.py index 3d9728d..8f699b1 100644 --- a/sample-code/start_custom_hls.py +++ b/sample-code/start_custom_hls.py @@ -1,15 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys +import json import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) -import qencode +import sys import time -import json + +import qencode from qencode import QencodeClientException, QencodeTaskException -#replace with your API KEY (can be found in your Project settings on Qencode portal) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) + +# replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' params = qencode.custom_params() @@ -35,47 +39,47 @@ FORMAT.output = "advanced_hls" FORMAT.destination = DESTINATION -#replace with a link to your input video +# replace with a link to your input video params.source = 'https://qencode.com/static/1.mp4' params.format = [FORMAT] def start_encode(): - """ + """ Create client object :param api_key: string. required :param api_url: string. not required :param api_version: int. not required. default 'v1' :return: task object - """ + """ - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) - print 'The client created. Expire date: %s' % client.expire + print 'The client created. Expire date: %s' % client.expire - task = client.create_task() + task = client.create_task() - if task.error: - raise QencodeTaskException(task.message) + if task.error: + raise QencodeTaskException(task.message) - task.custom_start(params) + task.custom_start(params) - if task.error: - raise QencodeTaskException(task.message) + if task.error: + raise QencodeTaskException(task.message) - print 'Start encode. Task: %s' % task.task_token + print 'Start encode. Task: %s' % task.task_token - while True: - status = task.status() - # print status - print json.dumps(status, indent=2, sort_keys=True) - if status['error'] or status['status'] == 'completed': - break - time.sleep(5) + while True: + status = task.status() + # print status + print json.dumps(status, indent=2, sort_keys=True) + if status['error'] or status['status'] == 'completed': + break + time.sleep(5) if __name__ == '__main__': - start_encode() + start_encode() diff --git a/sample-code/start_custom_mp4.py b/sample-code/start_custom_mp4.py index 9541e4c..47fd2aa 100644 --- a/sample-code/start_custom_mp4.py +++ b/sample-code/start_custom_mp4.py @@ -1,15 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys +import json import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) -import qencode +import sys import time -import json + +import qencode from qencode import QencodeClientException, QencodeTaskException -#replace with your API KEY (can be found in your Project settings on Qencode portal) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) + +# replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' params = qencode.custom_params() @@ -28,47 +32,47 @@ FORMAT.output = "mp4" FORMAT.destination = DESTINATION -#replace with a link to your input video +# replace with a link to your input video params.source = 'https://qencode.com/static/1.mp4' params.format = [FORMAT] - def start_encode(): - """ + """ Create client object :param api_key: string. required :param api_url: string. not required :param api_version: int. not required. default 'v1' :return: task object - """ + """ + + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + print 'The client created. Expire date: %s' % client.expire - print 'The client created. Expire date: %s' % client.expire + task = client.create_task() - task = client.create_task() + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + task.custom_start(params) - task.custom_start(params) + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + print 'Start encode. Task: %s' % task.task_token - print 'Start encode. Task: %s' % task.task_token + while True: + status = task.status() + # print status + print json.dumps(status, indent=2, sort_keys=True) + if status['error'] or status['status'] == 'completed': + break + time.sleep(5) - while True: - status = task.status() - # print status - print json.dumps(status, indent=2, sort_keys=True) - if status['error'] or status['status'] == 'completed': - break - time.sleep(5) if __name__ == '__main__': - start_encode() + start_encode() diff --git a/sample-code/start_custom_tus_upload.py b/sample-code/start_custom_tus_upload.py index cf97b82..56aa1f3 100644 --- a/sample-code/start_custom_tus_upload.py +++ b/sample-code/start_custom_tus_upload.py @@ -1,15 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys +import json import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) -import qencode +import sys import time -import json + +import qencode from qencode import QencodeClientException, QencodeTaskException, tus_uploader -#replace with your API KEY (can be found in your Project settings on Qencode portal) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) + +# replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' file_path = '/path/to/file/for/upload' @@ -31,49 +35,53 @@ def start_encode(): - """ + """ Create client object :param api_key: string. required :param api_url: string. not required :param api_version: int. not required. default 'v1' :return: task object - """ + """ - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) - print 'The client created. Expire date: %s' % client.expire + print 'The client created. Expire date: %s' % client.expire - task = client.create_task() + task = client.create_task() - if task.error: - raise QencodeTaskException(task.message) + if task.error: + raise QencodeTaskException(task.message) - #get upload url from endpoint returned with /v1/create_task and task_token value - uploadUrl = task.upload_url + '/' + task.task_token + # get upload url from endpoint returned with /v1/create_task and task_token value + uploadUrl = task.upload_url + '/' + task.task_token - #do upload and get uploaded file URI - uploadedFile = tus_uploader.upload(file_path=file_path, url=uploadUrl, log_func=log_upload, chunk_size=2000000) + # do upload and get uploaded file URI + uploadedFile = tus_uploader.upload( + file_path=file_path, url=uploadUrl, log_func=log_upload, chunk_size=2000000 + ) - params = query % uploadedFile.url - task.custom_start(params) + params = query % uploadedFile.url + task.custom_start(params) - if task.error: - raise QencodeTaskException(task.message) + if task.error: + raise QencodeTaskException(task.message) - print 'Start encode. Task: %s' % task.task_token + print 'Start encode. Task: %s' % task.task_token + + while True: + status = task.status() + # print status + print json.dumps(status, indent=2, sort_keys=True) + if status['error'] or status['status'] == 'completed': + break + time.sleep(5) - while True: - status = task.status() - # print status - print json.dumps(status, indent=2, sort_keys=True) - if status['error'] or status['status'] == 'completed': - break - time.sleep(5) def log_upload(msg): - print(msg) + print (msg) + if __name__ == '__main__': - start_encode() + start_encode() diff --git a/sample-code/start_custom_with_dict.py b/sample-code/start_custom_with_dict.py index 32601c3..b262705 100644 --- a/sample-code/start_custom_with_dict.py +++ b/sample-code/start_custom_with_dict.py @@ -1,78 +1,72 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys +import json import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) -import qencode +import sys import time -import json + +import qencode from qencode import QencodeClientException, QencodeTaskException +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) + -#replace with your API KEY (can be found in your Project settings on Qencode portal) +# replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' -#replace with a link to your input video +# replace with a link to your input video source_url = "https://qencode.com/static/1.mp4" -format_240 = dict( - output="mp4", - size="320x240", - video_codec="libx264" -) +format_240 = dict(output="mp4", size="320x240", video_codec="libx264") -format_720 = dict( - output="mp4", - size="1280x720", - video_codec="libx264" -) +format_720 = dict(output="mp4", size="1280x720", video_codec="libx264") format = [format_240, format_720] -query = dict( - source=source_url, - format=format -) +query = dict(source=source_url, format=format) params = dict(query=query) def start_encode(): - """ + """ Create client object :param api_key: string. required :param api_url: string. not required :param api_version: int. not required. default 'v1' :return: task object - """ + """ + + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + print 'The client created. Expire date: %s' % client.expire - print 'The client created. Expire date: %s' % client.expire + task = client.create_task() - task = client.create_task() + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + task.custom_start(params) - task.custom_start(params) + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + print 'Start encode. Task: %s' % task.task_token - print 'Start encode. Task: %s' % task.task_token + while True: + status = task.status() + # print status + print json.dumps(status, indent=2, sort_keys=True) + if status['error'] or status['status'] == 'completed': + break + time.sleep(5) - while True: - status = task.status() - # print status - print json.dumps(status, indent=2, sort_keys=True) - if status['error'] or status['status'] == 'completed': - break - time.sleep(5) if __name__ == '__main__': - start_encode() + start_encode() diff --git a/sample-code/start_custom_with_file.py b/sample-code/start_custom_with_file.py index 1133904..a0092c2 100644 --- a/sample-code/start_custom_with_file.py +++ b/sample-code/start_custom_with_file.py @@ -1,66 +1,75 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys +import json import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) -import qencode +import sys import time -import json + +import qencode from qencode import QencodeClientException, QencodeTaskException -#replace with your API KEY (can be found in your Project settings on Qencode portal) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) + +# replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' + def get_query_template(): - try: - with open(os.path.abspath(os.path.join(os.path.dirname(__file__), 'query.json'))) as data: - try: - file_data = json.load(data) - return json.dumps(file_data) - except ValueError as e: + try: + with open( + os.path.abspath(os.path.join(os.path.dirname(__file__), 'query.json')) + ) as data: + try: + file_data = json.load(data) + return json.dumps(file_data) + except ValueError as e: + print e + except IOError as e: print e - except IOError as e: - print e + params = get_query_template() def start_encode(): - """ + """ Create client object :param api_key: string. required :param api_url: string. not required :param api_version: int. not required. default 'v1' :return: task object - """ + """ + + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + print 'The client created. Expire date: %s' % client.expire - print 'The client created. Expire date: %s' % client.expire + task = client.create_task() - task = client.create_task() + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + task.custom_start(params) - task.custom_start(params) + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + print 'Start encode. Task: %s' % task.task_token - print 'Start encode. Task: %s' % task.task_token + while True: + status = task.status() + # print status + print json.dumps(status, indent=2, sort_keys=True) + if status['error'] or status['status'] == 'completed': + break + time.sleep(5) - while True: - status = task.status() - # print status - print json.dumps(status, indent=2, sort_keys=True) - if status['error'] or status['status'] == 'completed': - break - time.sleep(5) if __name__ == '__main__': - start_encode() + start_encode() diff --git a/sample-code/start_custom_with_json.py b/sample-code/start_custom_with_json.py index 11d8f01..36300f0 100644 --- a/sample-code/start_custom_with_json.py +++ b/sample-code/start_custom_with_json.py @@ -1,15 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys +import json import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) -import qencode +import sys import time -import json + +import qencode from qencode import QencodeClientException, QencodeTaskException -#replace with your API KEY (can be found in your Project settings on Qencode portal) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) + +# replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' params = """ @@ -29,39 +33,40 @@ def start_encode(): - """ + """ Create client object :param api_key: string. required :param api_url: string. not required :param api_version: int. not required. default 'v1' :return: task object - """ + """ + + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + print 'The client created. Expire date: %s' % client.expire - print 'The client created. Expire date: %s' % client.expire + task = client.create_task() - task = client.create_task() + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + task.custom_start(params) - task.custom_start(params) + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + print 'Start encode. Task: %s' % task.task_token - print 'Start encode. Task: %s' % task.task_token + while True: + status = task.status() + # print status + print json.dumps(status, indent=2, sort_keys=True) + if status['error'] or status['status'] == 'completed': + break + time.sleep(5) - while True: - status = task.status() - # print status - print json.dumps(status, indent=2, sort_keys=True) - if status['error'] or status['status'] == 'completed': - break - time.sleep(5) if __name__ == '__main__': - start_encode() + start_encode() diff --git a/sample-code/start_with_aws_signed_url.py b/sample-code/start_with_aws_signed_url.py index a77cfeb..0f9ce2c 100644 --- a/sample-code/start_with_aws_signed_url.py +++ b/sample-code/start_with_aws_signed_url.py @@ -1,14 +1,21 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys +import json import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) -import qencode +import sys import time -import json -from qencode import QencodeClientException, QencodeTaskException -from qencode import generate_aws_signed_url + +import qencode +from qencode import ( + QencodeClientException, + QencodeTaskException, + generate_aws_signed_url, +) + +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) # replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' @@ -22,68 +29,60 @@ secret_key = 'your-AWS-secret-key' # generate AWS signed url -source_url = generate_aws_signed_url(region, bucket, object_key, access_key, secret_key, expiration) +source_url = generate_aws_signed_url( + region, bucket, object_key, access_key, secret_key, expiration +) print(source_url) -format_240 = dict( - output="mp4", - size="320x240", - video_codec="libx264" -) +format_240 = dict(output="mp4", size="320x240", video_codec="libx264") -format_720 = dict( - output="mp4", - size="1280x720", - video_codec="libx264" -) +format_720 = dict(output="mp4", size="1280x720", video_codec="libx264") format = [format_240, format_720] -query = dict( - source=source_url, - format=format -) +query = dict(source=source_url, format=format) params = dict(query=query) + def start_encode(): - """ + """ Create client object :param api_key: string. required :param api_url: string. not required :param api_version: int. not required. default 'v1' :return: task object - """ + """ + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + print('The client created. Expire date: {0}'.format(client.expire)) - print('The client created. Expire date: {0}'.format(client.expire)) + task = client.create_task() - task = client.create_task() + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + task.custom_start(params) - task.custom_start(params) + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + print('Start encode. Task: {0}'.format(task.task_token)) - print('Start encode. Task: {0}'.format(task.task_token)) + line = "-" * 80 + while True: + print(line) + status = task.status() + # print status + print(json.dumps(status, indent=2, sort_keys=True)) + if status['error'] or status['status'] == 'completed': + break + time.sleep(5) - line = "-"*80 - while True: - print(line) - status = task.status() - # print status - print(json.dumps(status, indent=2, sort_keys=True)) - if status['error'] or status['status'] == 'completed': - break - time.sleep(5) if __name__ == '__main__': - start_encode() + start_encode() diff --git a/sample-code/start_with_callback.py b/sample-code/start_with_callback.py index 35e3cfc..ef9388b 100644 --- a/sample-code/start_with_callback.py +++ b/sample-code/start_with_callback.py @@ -1,15 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys +import json import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) -import qencode +import sys import time -import json + +import qencode from qencode import QencodeClientException, QencodeTaskException -#replace with your API KEY (can be found in your Project settings on Qencode portal) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) + +# replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' params = """ @@ -26,47 +30,49 @@ } """ + def progress_changed_handler(status): - if status['status'] != 'completed': - print json.dumps(status, indent=2, sort_keys=True) + if status['status'] != 'completed': + print json.dumps(status, indent=2, sort_keys=True) def task_completed_handler(status, task_token): - print 'Completed task: %s' % task_token - print json.dumps(status, indent=2, sort_keys=True) + print 'Completed task: %s' % task_token + print json.dumps(status, indent=2, sort_keys=True) def start_encode(): - """ + """ Create client object :param api_key: string. required :param api_url: string. not required :param api_version: int. not required. default 'v1' :return: task object - """ + """ + + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + print 'The client created. Expire date: %s' % client.expire - print 'The client created. Expire date: %s' % client.expire + task = client.create_task() - task = client.create_task() + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + task.custom_start(params) - task.custom_start(params) + if task.error: + raise QencodeTaskException(task.message) - if task.error: - raise QencodeTaskException(task.message) + print 'Start encode. Task: %s' % task.task_token - print 'Start encode. Task: %s' % task.task_token + # using callback methods + task.progress_changed(progress_changed_handler) + task.task_completed(task_completed_handler, task.task_token) - # using callback methods - task.progress_changed(progress_changed_handler) - task.task_completed(task_completed_handler, task.task_token) if __name__ == '__main__': - start_encode() \ No newline at end of file + start_encode() diff --git a/sample-code/start_with_drm.py b/sample-code/start_with_drm.py index ba5f574..c5e27b4 100644 --- a/sample-code/start_with_drm.py +++ b/sample-code/start_with_drm.py @@ -1,15 +1,19 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import sys +import json import os.path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) -import qencode +import sys import time -import json -from qencode import QencodeClientException, QencodeTaskException, fps_drm, cenc_drm -#replace with your API KEY (can be found in your Project settings on Qencode portal) +import qencode +from qencode import QencodeClientException, QencodeTaskException, cenc_drm, fps_drm + +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +) + +# replace with your API KEY (can be found in your Project settings on Qencode portal) API_KEY = 'your-api-qencode-key' DRM_USERNAME = 'my.ezdrm@email.com' DRW_PASSWORD = 'your-ezdrm-password' @@ -40,55 +44,45 @@ def start_encode(): - """ + """ Create client object :param api_key: string. required :param api_url: string. not required :param api_version: int. not required. default 'v1' :return: task object - """ - - client = qencode.client(API_KEY) - if client.error: - raise QencodeClientException(client.message) + """ - print 'The client created. Expire date: %s' % client.expire - - task = client.create_task() - - if task.error: - raise QencodeTaskException(task.message) - - encryption_parameters, payload = cenc_drm(DRM_USERNAME, DRW_PASSWORD) - #encryption_parameters, payload = fps_drm(DRM_USERNAME, DRW_PASSWORD) - - query = QUERY.replace('{cenc_drm}', json.dumps(encryption_parameters)) - - task.custom_start(query) - - if task.error: - raise QencodeTaskException(task.message) - - print 'Start encode. Task: %s' % task.task_token - - while True: - status = task.extend_status() - # print status - print json.dumps(status, indent=2, sort_keys=True) - if status['error'] or status['status'] == 'completed': - break - time.sleep(5) - -if __name__ == '__main__': - start_encode() + client = qencode.client(API_KEY) + if client.error: + raise QencodeClientException(client.message) + print 'The client created. Expire date: %s' % client.expire + task = client.create_task() + if task.error: + raise QencodeTaskException(task.message) + encryption_parameters, payload = cenc_drm(DRM_USERNAME, DRW_PASSWORD) + # encryption_parameters, payload = fps_drm(DRM_USERNAME, DRW_PASSWORD) + query = QUERY.replace('{cenc_drm}', json.dumps(encryption_parameters)) + task.custom_start(query) + if task.error: + raise QencodeTaskException(task.message) + print 'Start encode. Task: %s' % task.task_token + while True: + status = task.extend_status() + # print status + print json.dumps(status, indent=2, sort_keys=True) + if status['error'] or status['status'] == 'completed': + break + time.sleep(5) +if __name__ == '__main__': + start_encode() diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..4ab22d5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,53 @@ +[flake8] +exclude = .*/,.tox,*.egg,qencode/_compat.py,qencode/__*__.py, +select = E,W,F,N +max-line-length = 88 +# Stuff we ignore thanks to black: https://github.com/ambv/black/issues/429 +ignore = E111, + E121, + E122, + E123, + E124, + E125, + E126, + E201, + E202, + E203, + E221, + E222, + E225, + E226, + E227, + E231, + E241, + E251, + E261, + E262, + E265, + E271, + E272, + E302, + E303, + E306, + E502, + E701, + E702, + E703, + E704, + W291, + W292, + W293, + W391, + W503 + +[isort] +combine_as_imports= true +default_section = THIRDPARTY +include_trailing_comma = true +multi_line_output = 3 +known_pytest = pytest,py +known_first_party = qencode +split_before_closing_bracket = true +sections = FUTURE,STDLIB,PYTEST,THIRDPARTY,FIRSTPARTY,LOCALFOLDER +line_length = 88 +not_skip = __init__.py diff --git a/setup.py b/setup.py index 2d00414..fd444a4 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,7 @@ -"""A setuptools based setup module. -""" +"""A setuptools based setup module.""" +from os import path from setuptools import setup -from os import path here = path.abspath(path.dirname(__file__)) @@ -11,12 +10,11 @@ setup( name='qencode', - version='1.0.4', - description="Client library for main features and functionality of Qencode for Python v2.x.", + version='1.1.0', + description="Python bindings for the Qencode API", long_description=long_description, long_description_content_type='text/markdown', url='https://github.com/qencode-dev/qencode-api-python-client', - # url=here, author='Qencode Developer', author_email='team@qencode.com', license='proprietary',