From 33644a2ba98b3de01023db3bacf16ce2f7387e3a Mon Sep 17 00:00:00 2001 From: Stephen Opal Date: Wed, 22 Apr 2020 08:38:18 -0400 Subject: [PATCH 01/47] Update the changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) mode change 100644 => 100755 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100644 new mode 100755 index 8d3d837..5380a1d --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.1 (2020-03-09) + +* Extend Zeep Transport GET and POST HTTP methods to apply the account-specific dynamic domain as the remote host +* Update the NetSuite WSDL compatibility to 2019.2 + ## 0.4.0 (2019-04-29) * Enhancement: Add support for specifying operation/request timeouts From e44ff537bb498c7a19ca8c7b35542b71bb523615 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sat, 2 May 2020 16:47:00 +0200 Subject: [PATCH 02/47] v0.5.0 - Support for NetSuite SuiteTalk Web Services REST API * Feature: Support for SuiteTalk REST Web Services, including standard GET, POST, PATCH, PUT, DELETE requests as well as making SuiteQL queries. For now it's an optional dependency (install with `pip install netsuite[rest_api]`) * Feature: Start a HTTP server via command line to browse REST API OpenAPI spec docs for a given set of records (utilizes Swagger UI) * Breaking change: `--log-level`, `--config-path` and `--config-section` must now be passed directly to the `netsuite` command, and not its sub-commands. --- CHANGELOG.md | 6 + README.md | 119 ++++++++++++- netsuite/__main__.py | 415 +++++++++++++++++++++++++++++++++++++------ netsuite/client.py | 19 +- netsuite/config.py | 6 + netsuite/json.py | 64 +++++++ netsuite/rest_api.py | 198 +++++++++++++++++++++ netsuite/types.py | 3 + setup.py | 42 +++-- tox.ini | 4 +- 10 files changed, 800 insertions(+), 76 deletions(-) create mode 100644 netsuite/json.py create mode 100644 netsuite/rest_api.py create mode 100644 netsuite/types.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 5380a1d..3158acb 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.5.0 (2020-05-02) + +* Feature: Support for SuiteTalk REST Web Services, including standard GET, POST, PATCH, PUT, DELETE requests as well as making SuiteQL queries. For now it's an optional dependency (install with `pip install netsuite[rest_api]`) +* Feature: Start a HTTP server via command line to browse REST API OpenAPI spec docs for a given set of records (utilizes Swagger UI) +* Breaking change: `--log-level`, `--config-path` and `--config-section` must now be passed directly to the `netsuite` command, and not its sub-commands. + ## 0.4.1 (2020-03-09) * Extend Zeep Transport GET and POST HTTP methods to apply the account-specific dynamic domain as the remote host diff --git a/README.md b/README.md index 255a9f3..9544706 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Supported Python versions](https://img.shields.io/pypi/pyversions/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) [![PyPI status (alpha/beta/stable)](https://img.shields.io/pypi/status/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) -Make requests to NetSuite Web Services and Restlets +Make requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets ## Installation @@ -19,6 +19,14 @@ With CLI support: pip install netsuite[cli] +With NetSuite SuiteTalk REST Web Services API support: + + pip install netsuite[rest_api] + +With all features: + + pip install netsuite[all] + ## CLI @@ -42,14 +50,104 @@ The default location that will be read is `~/.config/netsuite.ini`. This can ove Append `--help` to the commands to see full documentation. -### `restlet` - Make requests to restlets +### `rest-api` - Make requests to NetSuite REST API + +See the NetSuite help center for info on how to use the REST API. The `netsuite rest-api openapi-serve` command is also a big help. + +#### `netsuite rest-api get` + +List endpoint examples: ``` -$ echo '{"savedSearchId": 987}' | netsuite restlet 123 - +$ netsuite rest-api get /record/v1/customer +``` + +``` +$ netsuite rest-api get /record/v1/invoice --limit 10 --offset 30 +``` + +``` +$ netsuite rest-api get /record/v1/salesOrder --query 'email IS "john.doe@example.com"' +``` + +Detail endpoint examples: + +``` +$ netsuite rest-api get /record/v1/salesOrder/1337 +``` + +``` +$ netsuite rest-api get /record/v1/invoice/123 --expandSubResources +``` + +#### `netsuite rest-api post` + +Examples: +``` +$ cat ~/customer-no-1-data.json | netsuite rest-api post /record/v1/customer - +``` + +#### `netsuite rest-api put` + +Examples: +``` +$ cat ~/customer-no-1-data.json | netsuite rest-api put /record/v1/customer/123 - +``` + +#### `netsuite rest-api patch` + +Examples: +``` +$ cat ~/changed-customer-data.json | netsuite rest-api patch /record/v1/customer/123 - +``` + +#### `netsuite rest-api delete` + +Examples: +``` +$ netsuite rest-api delete /record/v1/customer/123 +``` + +#### `netsuite rest-api jsonschema` + +Examples: +``` +$ netsuite rest-api jsonschema salesOrder +{"type":"object","properties":... +``` + +#### `netsuite rest-api openapi` + +Examples: +``` +$ netsuite rest-api openapi salesOrder customer invoice +{"openapi":"3.0.1","info":{"title":"NetSuite REST Record API"... +``` + + +#### `netsuite rest-api openapi-serve` + +Start a server that fetches and lists the OpenAPI spec for the given record types, using [Swagger UI](https://swagger.io/tools/swagger-ui/). Defaults to port 8000. + +Examples: + +``` +$ netsuite rest-api openapi-serve customer salesOrder +INFO:netsuite:Fetching OpenAPI spec for record types customer, salesOrder... +INFO:netsuite:NetSuite REST API docs available at http://127.0.0.1:8001 +``` + +It's also possible to fetch the OpenAPI spec for all known record types. This will however take a long time (60+ seconds). +``` +$ netsuite rest-api openapi-serve +WARNING:netsuite:Fetching OpenAPI spec for ALL known record types... This will take a long time! (Consider providing only the record types of interest by passing their names to this command as positional arguments) +INFO:netsuite:NetSuite REST API docs available at http://127.0.0.1:8001 ``` -### `interact` - Interact with web services and/or restlets +### `interact` - Interact with SOAP/REST web services and restlets + +Starts an IPython REPL where you can interact with the client. ``` $ netsuite interact @@ -58,7 +156,16 @@ Available vars: `ns` - NetSuite client Example usage: - results = ns.getList('customer', internalIds=[1337]) + ws_results = ns.getList('customer', internalIds=[1337]) + restlet_results = ns.restlet.request(987) + rest_api_results = await ns.rest_api.get("/record/v1/salesOrder") + +In [1]: rest_api_results = await ns.rest_api.get(" +``` + -In [1]: +### `restlet` - Make requests to restlets + +``` +$ echo '{"savedSearchId": 987}' | netsuite restlet 123 - ``` diff --git a/netsuite/__main__.py b/netsuite/__main__.py index d69fe27..f7562a8 100644 --- a/netsuite/__main__.py +++ b/netsuite/__main__.py @@ -1,41 +1,60 @@ -import json -import sys +import argparse +import asyncio +import functools +import http.server +import inspect import logging import logging.config +import pathlib +import pkg_resources +import sys +import tempfile import IPython -import argh import traitlets import netsuite -from netsuite import config +from netsuite import config, json from netsuite.constants import DEFAULT_INI_PATH, DEFAULT_INI_SECTION +logger = logging.getLogger("netsuite") + + +def main(): + try: + args = parser.parse_args() + except Exception: + parser.print_help() + return + + subparser_name = sys.argv[-1] + + if subparser_name == "rest-api": + rest_api_parser.print_help() + return + + config = _load_config_or_error(args.config_path, args.config_section) + + log_level = getattr(logging, args.log_level) + logging.basicConfig(level=log_level) -def _set_log_level(log_level): - if log_level is not None: - level = getattr(logging, log_level.upper()) - logging.basicConfig() - logging.getLogger('zeep').setLevel(level) - netsuite.logger.setLevel(level) + ret = args.func(config, args) + if inspect.iscoroutinefunction(args.func): + ret = asyncio.run(ret) -@argh.arg('-l', '--log-level', help='The log level to use') -@argh.arg('-p', '--config-path', help='The config file to get settings from') -@argh.arg('-c', '--config-section', help='The config section to get settings from') -def interact( - log_level=None, - config_path=DEFAULT_INI_PATH, - config_section=DEFAULT_INI_SECTION -): - """Starts a REPL to enable live interaction with NetSuite webservices""" - _set_log_level(log_level) + if ret is not None: + print(ret) - conf = config.from_ini(path=config_path, section=config_section) - ns = netsuite.NetSuite(conf) +def version(config, args) -> str: + return pkg_resources.get_distribution("netsuite").version - user_ns = {'ns': ns} + +def interact(config, args): + ns = netsuite.NetSuite(config) + + user_ns = {"ns": ns} banner1 = """Welcome to Netsuite WS client interactive mode Available vars: @@ -44,12 +63,13 @@ def interact( Example usage: ws_results = ns.getList('customer', internalIds=[1337]) restlet_results = ns.restlet.request(987) + rest_api_results = await ns.rest_api.get("/record/v1/salesOrder") """ IPython.embed( user_ns=user_ns, banner1=banner1, - config=traitlets.config.Config(colors='LightBG'), + config=traitlets.config.Config(colors="LightBG"), # To fix no colored input we pass in `using=False` # See: https://github.com/ipython/ipython/issues/11523 # TODO: Remove once this is fixed upstream @@ -57,39 +77,334 @@ def interact( ) -@argh.arg('-l', '--log-level', help='The log level to use') -@argh.arg('-p', '--config-path', help='The config file to get settings from') -@argh.arg('-c', '--config-section', help='The config section to get settings from') -def restlet( - script_id, - payload, - deploy=1, - log_level=None, - config_path=DEFAULT_INI_PATH, - config_section=DEFAULT_INI_SECTION -): - """Make requests to restlets""" - - _set_log_level(log_level) - conf = config.from_ini(path=config_path, section=config_section) - ns = netsuite.NetSuite(conf) - - if not payload: +def restlet(config, args) -> str: + ns = netsuite.NetSuite(config) + + if not args.payload: payload = None - elif payload == '-': - payload = json.load(sys.stdin) + elif args.payload == "-": + with sys.stdin as fh: + payload = json.loads(fh.read()) else: - payload = json.loads(payload) + payload = json.loads(args.payload) resp = ns.restlet.raw_request( - script_id=script_id, - deploy=deploy, + script_id=args.script_id, + deploy=args.deploy, payload=payload, raise_on_bad_status=False, ) return resp.text -command_parser = argh.ArghParser() -command_parser.add_commands([interact, restlet]) -main = command_parser.dispatch +async def rest_api_get(config, args) -> str: + rest_api = _get_rest_api_or_error(config) + params = {} + if args.expandSubResources: + params["expandSubResources"] = "true" + if args.limit: + params["limit"] = args.limit + if args.offset: + params["offset"] = args.offset + if args.query: + params["q"] = args.query + resp = await rest_api.get(args.subpath, params=params) + return json.dumps_str(resp) + + +async def rest_api_post(config, args) -> str: + rest_api = _get_rest_api_or_error(config) + with args.payload_file as fh: + payload_str = fh.read() + + payload = json.loads(payload_str) + + resp = await rest_api.post(args.subpath, json=payload) + return json.dumps_str(resp) + + +async def rest_api_put(config, args) -> str: + rest_api = _get_rest_api_or_error(config) + with args.payload_file as fh: + payload_str = fh.read() + + payload = json.loads(payload_str) + + resp = await rest_api.put(args.subpath, json=payload) + return json.dumps_str(resp) + + +async def rest_api_patch(config, args) -> str: + rest_api = _get_rest_api_or_error(config) + with args.payload_file as fh: + payload_str = fh.read() + + payload = json.loads(payload_str) + + resp = await rest_api.patch(args.subpath, json=payload) + return json.dumps_str(resp) + + +async def rest_api_delete(config, args) -> str: + rest_api = _get_rest_api_or_error(config) + + resp = await rest_api.delete(args.subpath) + return json.dumps_str(resp) + + +async def rest_api_suiteql(config, args) -> str: + rest_api = _get_rest_api_or_error(config) + + with args.q_file as fh: + q = fh.read() + + resp = await rest_api.suiteql(q=q, limit=args.limit, offset=args.offset) + + return json.dumps_str(resp) + + +async def rest_api_jsonschema(config, args) -> str: + rest_api = _get_rest_api_or_error(config) + resp = await rest_api.jsonschema(args.record_type) + return json.dumps_str(resp) + + +async def rest_api_openapi(config, args) -> str: + rest_api = _get_rest_api_or_error(config) + resp = await rest_api.openapi(args.record_types) + return json.dumps_str(resp) + + +async def rest_api_openapi_serve(config, args) -> str: + rest_api = _get_rest_api_or_error(config) + if len(args.record_types) == 0: + logger.warning( + "Fetching OpenAPI spec for ALL known record types... This will take a long " + "time! (Consider providing only the record types of interest by passing " + "their names to this command as positional arguments)" + ) + else: + rt_str = ", ".join(args.record_types) + logger.info(f"Fetching OpenAPI spec for record types {rt_str}...") + spec = await rest_api.openapi(args.record_types) + tempdir = pathlib.Path(tempfile.mkdtemp()) + openapi_file = tempdir / "openapi.json" + html_file = tempdir / "index.html" + openapi_file.write_bytes(json.dumps(spec)) + html = """ + + + + OpenAPI for + + +
+
+ + + + +""" + html_file.write_text(html) + handler_class = functools.partial( + http.server.SimpleHTTPRequestHandler, directory=str(tempdir), + ) + logger.info( + f"NetSuite REST Record API docs available at http://{args.bind}:{args.port}" + ) + try: + http.server.test( + HandlerClass=handler_class, + ServerClass=http.server.ThreadingHTTPServer, + port=args.port, + bind=args.bind, + ) + finally: + html_file.unlink() + openapi_file.unlink() + tempdir.rmdir() + + +def _load_config_or_error(path: str, section: str) -> config.Config: + try: + return config.from_ini(path=path, section=section) + except FileNotFoundError: + parser.error(f"Config file {path} not found") + except KeyError as ex: + if ex.args == (section,): + parser.error(f"No config section `{section}` in file {path}") + else: + raise ex + + +def _get_rest_api_or_error(config: config.Config): + ns = netsuite.NetSuite(config) + + try: + return ns.rest_api # Cached property that initializes NetSuiteRestApi + except RuntimeError as ex: + parser.error(str(ex)) + + +parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument( + "-l", + "--log-level", + help="The log level to use", + default="INFO", + choices=("DEBUG", "INFO", "WARNING", "ERROR"), +) +parser.add_argument( + "-p", + "--config-path", + help="The config file to get settings from", + default=DEFAULT_INI_PATH, +) +parser.add_argument( + "-c", + "--config-section", + help="The config section to get settings from", + default=DEFAULT_INI_SECTION, +) + +subparsers = parser.add_subparsers(help="App CLI", required=True) + +version_parser = subparsers.add_parser("version") +version_parser.set_defaults(func=version) + +interact_parser = subparsers.add_parser( + "interact", + aliases=["i"], + description="Starts a REPL to enable live interaction with NetSuite webservices", +) +interact_parser.set_defaults(func=interact) + +restlet_parser = subparsers.add_parser( + "restlet", description="Make request to a restlet" +) +restlet_parser.set_defaults(func=restlet) +restlet_parser.add_argument("script_id", type=int) +restlet_parser.add_argument("payload") +restlet_parser.add_argument("-d", "--deploy", type=int, default=1) + +rest_api_parser = subparsers.add_parser("rest-api", aliases=["r"]) +rest_api_subparser = rest_api_parser.add_subparsers() + +rest_api_get_parser = rest_api_subparser.add_parser( + "get", description="Make a GET request to NetSuite REST web services" +) +rest_api_get_parser.set_defaults(func=rest_api_get) +rest_api_get_parser.add_argument( + "subpath", help="The subpath to GET, e.g. `/record/v1/salesOrder`", +) +rest_api_get_parser.add_argument( + "-q", + "--query", + help="Search query used to filter results. See NetSuite help center for syntax information. Only works for list endpoints e.g. /record/v1/customer", +) +rest_api_get_parser.add_argument( + "-e", + "--expandSubResources", + action="store_true", + help="Automatically expand all sublists, sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", +) +rest_api_get_parser.add_argument("-l", "--limit", type=int, default=1000) +rest_api_get_parser.add_argument("-o", "--offset", type=int, default=0) + +rest_api_post_parser = rest_api_subparser.add_parser( + "post", description="Make a POST request to NetSuite REST web services" +) +rest_api_post_parser.set_defaults(func=rest_api_post) +rest_api_post_parser.add_argument( + "subpath", help="The subpath to POST to, e.g. `/record/v1/salesOrder`", +) +rest_api_post_parser.add_argument("payload_file", type=argparse.FileType("r")) + +rest_api_put_parser = rest_api_subparser.add_parser( + "put", description="Make a PUT request to NetSuite REST web services" +) +rest_api_put_parser.set_defaults(func=rest_api_put) +rest_api_put_parser.add_argument( + "subpath", help="The subpath to PUT to, e.g. `/record/v1/salesOrder/eid:abc123`", +) +rest_api_put_parser.add_argument("payload_file", type=argparse.FileType("r")) + +rest_api_patch_parser = rest_api_subparser.add_parser( + "patch", description="Make a PATCH request to NetSuite REST web services" +) +rest_api_patch_parser.set_defaults(func=rest_api_patch) +rest_api_patch_parser.add_argument( + "subpath", help="The subpath to PATCH to, e.g. `/record/v1/salesOrder/eid:abc123`", +) +rest_api_patch_parser.add_argument("payload_file", type=argparse.FileType("r")) + +rest_api_delete_parser = rest_api_subparser.add_parser( + "delete", description="Make a delete request to NetSuite REST web services" +) +rest_api_delete_parser.set_defaults(func=rest_api_delete) +rest_api_delete_parser.add_argument( + "subpath", + help="The subpath for the DELETE request, e.g. `/record/v1/salesOrder/eid:abc123`", +) + +rest_api_suiteql_parser = rest_api_subparser.add_parser( + "suiteql", description="Make a SuiteQL request to NetSuite REST web services" +) +rest_api_suiteql_parser.set_defaults(func=rest_api_suiteql) +rest_api_suiteql_parser.add_argument( + "q_file", type=argparse.FileType("r"), help="File containing a SuiteQL query" +) +rest_api_suiteql_parser.add_argument("-l", "--limit", type=int, default=10) +rest_api_suiteql_parser.add_argument("-o", "--offset", type=int, default=0) + +rest_api_jsonschema_parser = rest_api_subparser.add_parser( + "jsonschema", description="Retrieve JSON Schema for the given record type" +) +rest_api_jsonschema_parser.set_defaults(func=rest_api_jsonschema) +rest_api_jsonschema_parser.add_argument( + "record_type", help="The record type to get JSONSchema spec for" +) + +rest_api_openapi_parser = rest_api_subparser.add_parser( + "openapi", + aliases=["oas"], + description="Retrieve OpenAPI spec for the given record types", +) +rest_api_openapi_parser.set_defaults(func=rest_api_openapi) +rest_api_openapi_parser.add_argument( + "record_types", + metavar="record_type", + nargs="+", + help="The record type(s) to get OpenAPI spec for", +) + + +rest_api_openapi_parser = rest_api_subparser.add_parser( + "openapi-serve", + aliases=["oas-serve"], + description="Start a HTTP server on localhost serving the OpenAPI spec via Swagger UI", +) +rest_api_openapi_parser.set_defaults(func=rest_api_openapi_serve) +rest_api_openapi_parser.add_argument( + "record_types", + metavar="record_type", + nargs="*", + help="The record type(s) to get OpenAPI spec for. If not provided the OpenAPI spec for all known record types will be retrieved.", +) +rest_api_openapi_parser.add_argument( + "-p", "--port", default=8000, type=int, help="The port to listen to" +) +rest_api_openapi_parser.add_argument( + "-b", "--bind", default="127.0.0.1", help="The host to bind to" +) diff --git a/netsuite/client.py b/netsuite/client.py index a67be3a..0ccd2a1 100755 --- a/netsuite/client.py +++ b/netsuite/client.py @@ -16,6 +16,7 @@ from . import constants, helpers, passport from .config import Config from .restlet import NetsuiteRestlet +from .rest_api import NetSuiteRestApi from .util import cached_property logger = logging.getLogger(__name__) @@ -162,11 +163,23 @@ def __init__( self.__wsdl_url = wsdl_url self.__cache = cache self.__session = session - self.__restlet = NetsuiteRestlet(self.__config) - @property + @cached_property def restlet(self): - return self.__restlet + return NetsuiteRestlet(self.__config) + + @cached_property + def rest_api(self): + if self.__config.is_token_auth(): + return NetSuiteRestApi( + account=self.config.account, + consumer_key=self.config.consumer_key, + consumer_secret=self.config.consumer_secret, + token_id=self.config.token_id, + token_secret=self.config.token_secret, + ) + else: + raise RuntimeError("Rest API is currently only implemented with token auth") @cached_property def wsdl_url(self) -> str: diff --git a/netsuite/config.py b/netsuite/config.py index 0d0a60f..2bc1c7e 100644 --- a/netsuite/config.py +++ b/netsuite/config.py @@ -97,6 +97,12 @@ def _set_auth_type(self, value: str) -> None: self.auth_type = value assert self.auth_type in (TOKEN, CREDENTIALS) + def is_token_auth(self) -> bool: + return self.auth_type == TOKEN + + def is_credentials_auth(self) -> bool: + return self.auth_type == CREDENTIALS + def _set(self, dct: Dict[str, object]) -> None: # As other setting validations depend on auth_type we set it first auth_type = dct.get('auth_type', self.auth_type) diff --git a/netsuite/json.py b/netsuite/json.py new file mode 100644 index 0000000..91d3ecb --- /dev/null +++ b/netsuite/json.py @@ -0,0 +1,64 @@ +import datetime +from uuid import UUID +from decimal import Decimal +from enum import Enum +from pathlib import Path +from typing import Any, Callable, Dict, Type, Union + +import orjson as _orjson + +__all__ = ("dumps", "loads") + +loads = _orjson.loads + + +def dumps(obj: Any, *args, **kw) -> bytes: + kw["default"] = _orjson_default + return _orjson.dumps(obj, *args, **kw) + + +def dumps_str(obj: Any, *args, **kw) -> str: + return dumps(obj, *args, **kw).decode("utf-8") + + +def _orjson_default(obj: Any) -> Any: + """Handle cases which orjson doesn't know what to do with""" + + # Handle that orjson doesn't support subclasses of str + if isinstance(obj, str): + return str(obj) + else: + encoder = _get_encoder(obj) + return encoder(obj) + + +def _isoformat(o: Union[datetime.date, datetime.time]) -> str: + return o.isoformat() + + +def _get_encoder(obj: Any) -> Any: + for base in obj.__class__.__mro__[:-1]: + try: + encoder = _ENCODERS_BY_TYPE[base] + except KeyError: + continue + return encoder(obj) + else: # We have exited the for loop without finding a suitable encoder + raise TypeError( + f"Object of type '{obj.__class__.__name__}' is not JSON serializable" + ) + + +_ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = { + bytes: lambda o: o.decode(), + datetime.date: _isoformat, + datetime.datetime: _isoformat, + datetime.time: _isoformat, + datetime.timedelta: lambda td: td.total_seconds(), + Decimal: float, + Enum: lambda o: o.value, + frozenset: list, + Path: str, + set: list, + UUID: str, +} diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py new file mode 100644 index 0000000..796d0b9 --- /dev/null +++ b/netsuite/rest_api.py @@ -0,0 +1,198 @@ +import asyncio +import logging +from typing import Iterable, Optional + +try: + import httpx +except ImportError: + httpx = None + +try: + from authlib.integrations.httpx_client import OAuth1Auth +except ImportError: + OAuth1Auth = None + +from . import json +from .types import JsonDict + +logger = logging.getLogger(__name__) + +__all__ = ("NetSuiteRestApi",) + + +class NetsuiteAPIRequestError(Exception): + """Raised when a Netsuite REST API request fails""" + + def __init__(self, status_code: int, response_text: str): + self.status_code = status_code + self.response_text = response_text + + def __str__(self): + return f"HTTP{self.status_code} - {self.response_text}" + + +class NetsuiteAPIResponseParsingError(NetsuiteAPIRequestError): + """Raised when parsing a Netsuite REST API response fails""" + + +class NetSuiteRestApi: + def __init__( + self, + *, + account: str, + consumer_key: str, + consumer_secret: str, + token_id: str, + token_secret: str, + default_timeout: str = 60, + concurrent_requests: int = 10, + ): + if not self.has_required_dependencies(): + raise RuntimeError( + "Missing required dependencies for REST API support. " + "Install with `pip install netsuite[rest_api]`" + ) + self._account = account + self._hostname = self._make_hostname() + self._auth = self._make_auth( + account, consumer_key, consumer_secret, token_id, token_secret + ) + self._default_timeout = default_timeout + self._request_semaphore = asyncio.Semaphore(concurrent_requests) + + @classmethod + def has_required_dependencies(cls) -> bool: + return httpx is not None and OAuth1Auth is not None + + async def get(self, subpath: str, **request_kw) -> JsonDict: + return await self.request("GET", subpath, **request_kw) + + async def post(self, subpath: str, **request_kw): + return await self.request("POST", subpath, **request_kw,) + + async def put(self, subpath: str, **request_kw): + return await self.request("PUT", subpath, **request_kw) + + async def patch(self, subpath: str, **request_kw): + return await self.request("PATCH", subpath, **request_kw) + + async def delete(self, subpath: str, **request_kw): + return await self.request("DELETE", subpath, **request_kw) + + async def suiteql(self, q: str, limit: int = 10, offset: int = 0) -> JsonDict: + return await self.request( + "POST", + "/query/v1/suiteql", + headers={"Prefer": "transient"}, + json={"q": q}, + params={"limit": limit, "offset": offset}, + ) + + async def jsonschema(self, record_type: str, **request_kw) -> JsonDict: + headers = { + **request_kw.pop("headers", {}), + "Accept": "application/schema+json", + } + return await self.request( + "GET", + f"/record/v1/metadata-catalog/{record_type}", + headers=headers, + **request_kw, + ) + + async def openapi(self, record_types: Iterable[str] = (), **request_kw) -> JsonDict: + headers = { + **request_kw.pop("headers", {}), + "Accept": "application/swagger+json", + } + params = request_kw.pop("params", {}) + + if len(record_types) > 0: + params["select"] = ",".join(record_types) + + return await self.request( + "GET", + "/record/v1/metadata-catalog", + headers=headers, + params=params, + **request_kw, + ) + + async def request( + self, method: str, subpath: str, **request_kw + ) -> Optional[JsonDict]: + resp = await self._raw_request(method, subpath, **request_kw) + + if resp.status_code < 200 or resp.status_code > 299: + raise NetsuiteAPIRequestError(resp.status_code, resp.text) + + if resp.status_code == 204: + return None + else: + try: + return json.loads(resp.text) + except Exception: + raise NetsuiteAPIResponseParsingError(resp.status_code, resp.text) + + async def _raw_request( + self, method: str, subpath: str, **request_kw + ) -> httpx.Response: + method = method.upper() + url = self._make_url(subpath) + headers = {**self._make_default_headers(), **request_kw.pop("headers", {})} + + auth = request_kw.pop("auth", self._auth) + timeout = request_kw.pop("timeout", self._default_timeout) + + if "json" in request_kw: + request_kw["data"] = json.dumps_str(request_kw.pop("json")) + + kw = {**request_kw} + logger.debug( + f"Making {method.upper()} request to {url}. Keyword arguments: {kw}" + ) + + async with self._request_semaphore: + async with httpx.AsyncClient() as c: + resp = await c.request( + method=method, + url=url, + headers=headers, + auth=auth, + timeout=timeout, + **kw, + ) + + resp_headers_json = json.dumps_str(dict(resp.headers)) + logger.debug(f"Got response headers from NetSuite REST API: {resp_headers_json}") + + return resp + + def _make_hostname(self): + account_slugified = self._account.lower().replace("_", "-") + return f"{account_slugified}.suitetalk.api.netsuite.com" + + def _make_url(self, subpath: str): + return f"https://{self._hostname}/services/rest{subpath}" + + @staticmethod + def _make_auth( + account: str, + consumer_key: str, + consumer_secret: str, + token_id: str, + token_secret: str, + ): + return OAuth1Auth( + client_id=consumer_key, + client_secret=consumer_secret, + token=token_id, + token_secret=token_secret, + realm=account, + ) + + def _make_default_headers(self): + return { + "Content-Type": "application/json", + "X-NetSuite-PropertyNameValidation": "error", + } diff --git a/netsuite/types.py b/netsuite/types.py new file mode 100644 index 0000000..7552f61 --- /dev/null +++ b/netsuite/types.py @@ -0,0 +1,3 @@ +import typing + +JsonDict = typing.Dict[str, typing.Any] diff --git a/setup.py b/setup.py index c64c46c..dc5eb64 100755 --- a/setup.py +++ b/setup.py @@ -1,9 +1,31 @@ from setuptools import setup +extras_require = { + 'rest_api': [ + 'authlib', + # NOTE: authlib doesn't work with httpx 0.12.1 So we're locking the httpx + # version to one that is compatible. See: + # https://github.com/lepture/authlib/issues/210#issuecomment-612311003 + # TODO: Remove version lock once fixed upstream. + 'httpx==0.12.0', + ], + 'cli': ['ipython'], + 'test': { + 'coverage>=4.2', + 'flake8>=3.0.4', + 'mypy>=0.560', + 'pytest>=3.0.3', + 'responses>=0.5.1', + }, +} +extras_require['all'] = [ + dep for name, lst in extras_require.items() if name != 'test' for dep in lst +] + setup_kwargs = dict( name='netsuite', - version='0.4.1', - description='Wrapper around Netsuite SuiteTalk Web Services and Restlets', + version='0.5.0', + description='Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets', packages=['netsuite'], include_package_data=True, author='Jacob Magnusson', @@ -14,20 +36,9 @@ install_requires=[ 'requests-oauthlib', 'zeep', + 'orjson', ], - extras_require={ - 'cli': [ - 'argh', - 'ipython', - ], - 'test': { - 'coverage>=4.2', - 'flake8>=3.0.4', - 'mypy>=0.560', - 'pytest>=3.0.3', - 'responses>=0.5.1', - }, - }, + extras_require=extras_require, entry_points={ 'console_scripts': [ 'netsuite = netsuite.__main__:main', @@ -42,6 +53,7 @@ 'Programming Language :: Python', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', ], ) diff --git a/tox.ini b/tox.ini index 9dbaa56..1254a27 100644 --- a/tox.ini +++ b/tox.ini @@ -10,13 +10,13 @@ envlist = py36, py37, lint commands = coverage run --source=netsuite -m py.test deps = - .[cli,test] + .[test,all] [testenv:lint] commands = flake8 netsuite tests deps = - .[cli,test] + .[test,all] [flake8] ignore = From 9389dffc19136d56a00d7077bd53f88bb1bd6f63 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sat, 2 May 2020 16:57:25 +0200 Subject: [PATCH 03/47] v0.5.1 - Fix HTML title in OpenAPI Swagger docs --- netsuite/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netsuite/__main__.py b/netsuite/__main__.py index f7562a8..8941220 100644 --- a/netsuite/__main__.py +++ b/netsuite/__main__.py @@ -195,7 +195,7 @@ async def rest_api_openapi_serve(config, args) -> str: - OpenAPI for + NetSuite REST Record API
From 5bef4b8b20d5d1abc21b888a441517c64b0210ad Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sat, 2 May 2020 16:58:57 +0200 Subject: [PATCH 04/47] Bump to 0.5.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index dc5eb64..dde5720 100755 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setup_kwargs = dict( name='netsuite', - version='0.5.0', + version='0.5.1', description='Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets', packages=['netsuite'], include_package_data=True, From b99c939edd010f030da0198f4fe94846738de8ec Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sat, 2 May 2020 17:23:05 +0200 Subject: [PATCH 05/47] v0.5.2 - Improvements and fixes to netsuite rest-api get command * Fix: Only forward explicitly passed in parameters for `netsuite rest-api get` command. Fixes error `Invalid query parameter name: limit. Allowed query parameters are: fields, expand, expandSubResources.` * Feature: Add ability to have `netsuite rest-api get` only return a given list of fields via `--fields` * Feature: Add ability for `netsuite rest-api get` to only expand a given set of sublist and subrecord types via `--expand` --- CHANGELOG.md | 10 ++++++++++ netsuite/__main__.py | 29 +++++++++++++++++++++++------ setup.py | 2 +- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3158acb..66e285d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.5.2 (2020-05-02) + +* Fix: Only forward explicitly passed in parameters for `netsuite rest-api get` command. Fixes error `Invalid query parameter name: limit. Allowed query parameters are: fields, expand, expandSubResources.` +* Feature: Add ability to have `netsuite rest-api get` only return a given list of fields via `--fields` +* Feature: Add ability for `netsuite rest-api get` to only expand a given set of sublist and subrecord types via `--expand` + +## 0.5.1 (2020-05-02) + +* Fix HTML title in OpenAPI Swagger docs + ## 0.5.0 (2020-05-02) * Feature: Support for SuiteTalk REST Web Services, including standard GET, POST, PATCH, PUT, DELETE requests as well as making SuiteQL queries. For now it's an optional dependency (install with `pip install netsuite[rest_api]`) diff --git a/netsuite/__main__.py b/netsuite/__main__.py index 8941220..ad5dd43 100644 --- a/netsuite/__main__.py +++ b/netsuite/__main__.py @@ -100,13 +100,17 @@ def restlet(config, args) -> str: async def rest_api_get(config, args) -> str: rest_api = _get_rest_api_or_error(config) params = {} - if args.expandSubResources: + if args.expandSubResources is True: params["expandSubResources"] = "true" - if args.limit: + if args.limit is not None: params["limit"] = args.limit - if args.offset: + if args.offset is not None: params["offset"] = args.offset - if args.query: + if args.fields is not None: + params["fields"] = ",".join(args.fields) + if args.expand is not None: + params["expand"] = ",".join(args.expand) + if args.query is not None: params["q"] = args.query resp = await rest_api.get(args.subpath, params=params) return json.dumps_str(resp) @@ -319,8 +323,21 @@ def _get_rest_api_or_error(config: config.Config): action="store_true", help="Automatically expand all sublists, sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", ) -rest_api_get_parser.add_argument("-l", "--limit", type=int, default=1000) -rest_api_get_parser.add_argument("-o", "--offset", type=int, default=0) +rest_api_get_parser.add_argument("-l", "--limit", type=int) +rest_api_get_parser.add_argument("-o", "--offset", type=int) +rest_api_get_parser.add_argument( + "-f", + "--fields", + metavar="field", + nargs="*", + help="Only include the given fields in response", +) +rest_api_get_parser.add_argument( + "-E", + "--expand", + nargs="*", + help="Expand the given sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", +) rest_api_post_parser = rest_api_subparser.add_parser( "post", description="Make a POST request to NetSuite REST web services" diff --git a/setup.py b/setup.py index dde5720..f84f29b 100755 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setup_kwargs = dict( name='netsuite', - version='0.5.1', + version='0.5.2', description='Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets', packages=['netsuite'], include_package_data=True, From 60a26069d41704ca369526943638d37142f94edc Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sat, 2 May 2020 17:45:38 +0200 Subject: [PATCH 06/47] Enforce code standard with black and isort Following the code standard that black and isort uses is easily done by running `pip install pre-commit` and then doing a commit. Black and isort will automatically reformat the code then. Also added some instructions on how to develop and do PyPI releases. --- .flake8 | 2 + .isort.cfg | 7 + .pre-commit-config.yaml | 18 +++ README.md | 20 +++ netsuite/__main__.py | 2 +- netsuite/client.py | 322 +++++++++++++++++----------------------- netsuite/config.py | 90 ++++------- netsuite/constants.py | 5 +- netsuite/json.py | 2 +- netsuite/passport.py | 58 +++----- netsuite/rest_api.py | 9 +- netsuite/restlet.py | 36 ++--- netsuite/util.py | 10 +- pyproject.toml | 6 + setup.py | 74 ++++----- tests/test_base.py | 18 ++- tox.ini | 15 +- 17 files changed, 317 insertions(+), 377 deletions(-) create mode 100644 .flake8 create mode 100644 .isort.cfg create mode 100644 .pre-commit-config.yaml create mode 100644 pyproject.toml diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..7b8ac8a --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +ignore = E203,E231,E501,E711,E712,W503,W504 diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..ef27068 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,7 @@ +[settings] +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +use_parentheses = True +line_length = 88 +known_third_party = IPython,orjson,pkg_resources,pytest,requests,requests_oauthlib,setuptools,traitlets,zeep diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..555cba1 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +repos: +- repo: https://gitlab.com/pycqa/flake8 + rev: master + hooks: + - id: flake8 +- repo: https://github.com/asottile/seed-isort-config + rev: v1.9.2 + hooks: + - id: seed-isort-config +- repo: https://github.com/pre-commit/mirrors-isort + rev: master + hooks: + - id: isort +- repo: https://github.com/psf/black + rev: stable + hooks: + - id: black + language_version: python3.7 diff --git a/README.md b/README.md index 9544706..d89c517 100644 --- a/README.md +++ b/README.md @@ -169,3 +169,23 @@ In [1]: rest_api_results = await ns.rest_api.get(" ``` $ echo '{"savedSearchId": 987}' | netsuite restlet 123 - ``` + + +## Developing + +1. Fork repo in GitHub +1. Clone the forked repo: `git clone git@github.com:MYUSERNAME/netsuite.git` +1. Install package in editable mode, with all dependencies: `pip install -e '.[all,test,lint]'` +1. Ensure that pre-commit hooks are installed with: `pre-commit install` +1. Create new branch: `git branch -d my-shiny-pull-request-branch` +1. Code! +1. Run unit tests with: `tox` +1. Commit, push and submit pull request + + +## Creating a new git / PyPI release (maintainers) + +1. Tag the release in git: `git tag -a "X.Y.Z" -m "Version X.Y.Z"` +1. Push the tag upstream: `git push --tags` +1. Build the release: `rm -rf build dist; python3 setup.py sdist bdist_wheel` +1. Push the release: `twine upload dist/*` diff --git a/netsuite/__main__.py b/netsuite/__main__.py index ad5dd43..9201672 100644 --- a/netsuite/__main__.py +++ b/netsuite/__main__.py @@ -6,11 +6,11 @@ import logging import logging.config import pathlib -import pkg_resources import sys import tempfile import IPython +import pkg_resources import traitlets import netsuite diff --git a/netsuite/client.py b/netsuite/client.py index 0ccd2a1..8818360 100755 --- a/netsuite/client.py +++ b/netsuite/client.py @@ -15,8 +15,8 @@ from . import constants, helpers, passport from .config import Config -from .restlet import NetsuiteRestlet from .rest_api import NetSuiteRestApi +from .restlet import NetsuiteRestlet from .util import cached_property logger = logging.getLogger(__name__) @@ -27,10 +27,7 @@ class NetsuiteResponseError(Exception): def WebServiceCall( - path: str = None, - extract: Callable = None, - *, - default: Any = constants.NOT_SET, + path: str = None, extract: Callable = None, *, default: Any = constants.NOT_SET, ) -> Callable: """ Decorator for NetSuite methods returning SOAP responses @@ -47,13 +44,14 @@ def WebServiceCall( Returns: Decorator to use on `NetSuite` web service methods """ + def decorator(fn): @wraps(fn) def wrapper(self, *args, **kw): response = fn(self, *args, **kw) if path is not None: - for part in path.split('.'): + for part in path.split("."): try: response = getattr(response, part) except AttributeError: @@ -63,26 +61,28 @@ def wrapper(self, *args, **kw): return default try: - response_status = response['status'] + response_status = response["status"] except TypeError: response_status = None for record in response: # NOTE: Status is set on each returned record for lists, # really strange... - response_status = record['status'] + response_status = record["status"] break - is_success = response_status['isSuccess'] + is_success = response_status["isSuccess"] if not is_success: - response_detail = response_status['statusDetail'] + response_detail = response_status["statusDetail"] raise NetsuiteResponseError(response_detail) if extract is not None: response = extract(response) return response + return wrapper + return decorator @@ -103,7 +103,7 @@ def __init__(self, wsdl_url, *args, **kwargs): Assign the dynamic host domain component to a class variable """ parsed_wsdl_url = urlparse(wsdl_url) - self._wsdl_url = f'{parsed_wsdl_url.scheme}://{parsed_wsdl_url.netloc}/' + self._wsdl_url = f"{parsed_wsdl_url.scheme}://{parsed_wsdl_url.netloc}/" super().__init__(**kwargs) @@ -112,7 +112,7 @@ def _fix_address(self, address): Munge the address to the dynamic domain, not the default """ - idx = address.index('/', 8) + 1 + idx = address.index("/", 8) + 1 address = self._wsdl_url + address[idx:] return address @@ -130,11 +130,11 @@ def post(self, address, message, headers): class NetSuite: - version = '2019.2.0' - wsdl_url_tmpl = 'https://{account_id}.suitetalk.api.netsuite.com/wsdl/v{underscored_version}/netsuite.wsdl' + version = "2019.2.0" + wsdl_url_tmpl = "https://{account_id}.suitetalk.api.netsuite.com/wsdl/v{underscored_version}/netsuite.wsdl" def __repr__(self) -> str: - return f'' + return f"" def __init__( self, @@ -144,19 +144,19 @@ def __init__( wsdl_url: str = None, cache: zeep.cache.Base = None, session: requests.Session = None, - sandbox: bool = None + sandbox: bool = None, ) -> None: if sandbox is not None: warnings.warn( - 'The `sandbox` flag has been deprecated and no longer has ' - 'any effect. Please locate the correct account ID for your ' - 'sandbox instead (usually `_SB1`)', + "The `sandbox` flag has been deprecated and no longer has " + "any effect. Please locate the correct account ID for your " + "sandbox instead (usually `_SB1`)", DeprecationWarning, ) if version is not None: - assert re.match(r'\d+\.\d+\.\d+', version) + assert re.match(r"\d+\.\d+\.\d+", version) self.version = version self.__config = self._make_config(config) @@ -207,33 +207,30 @@ def config(self) -> Config: @cached_property def hostname(self) -> str: - return self.wsdl_url.replace('https://', '').partition('/')[0] + return self.wsdl_url.replace("https://", "").partition("/")[0] @property def service(self) -> zeep.client.ServiceProxy: return self.client.service - def _make_config( - self, - values_obj: Dict - ) -> Config: + def _make_config(self, values_obj: Dict) -> Config: if isinstance(values_obj, Config): return values_obj return Config(**values_obj) @property def underscored_version(self) -> str: - return self.version.replace('.', '_') + return self.version.replace(".", "_") @property def underscored_version_no_micro(self) -> str: - return self.underscored_version.rpartition('_')[0] + return self.underscored_version.rpartition("_")[0] def _generate_wsdl_url(self) -> str: return self.wsdl_url_tmpl.format( underscored_version=self.underscored_version, # https://followingnetsuite.wordpress.com/2018/10/18/suitetalk-sandbox-urls-addendum/ - account_id=self.config.account.lower().replace('_', '-'), + account_id=self.config.account.lower().replace("_", "-"), ) def _generate_cache(self) -> zeep.cache.Base: @@ -244,9 +241,7 @@ def _generate_session(self) -> requests.Session: def _generate_transport(self) -> zeep.transports.Transport: return NetSuiteTransport( - self._generate_wsdl_url(), - session=self.session, - cache=self.cache, + self._generate_wsdl_url(), session=self.session, cache=self.cache, ) def generate_passport(self) -> Dict: @@ -263,242 +258,223 @@ def with_timeout(self, timeout: int): yield @staticmethod - def _set_default_soapheaders( - client: zeep.Client, - preferences: dict = None - ) -> None: - client.set_default_soapheaders({ - # https://netsuite.custhelp.com/app/answers/detail/a_id/40934 - # (you need to be logged in to SuiteAnswers for this link to work) - # 'preferences': { - # 'warningAsError': True/False, - # 'disableMandatoryCustomFieldValidation': True/False, - # 'disableSystemNotesForCustomFields': True/False, - # 'ignoreReadOnlyFields': True/False, - # 'runServerSuiteScriptAndTriggerWorkflows': True/False, - # }, - }) + def _set_default_soapheaders(client: zeep.Client, preferences: dict = None) -> None: + client.set_default_soapheaders( + { + # https://netsuite.custhelp.com/app/answers/detail/a_id/40934 + # (you need to be logged in to SuiteAnswers for this link to work) + # 'preferences': { + # 'warningAsError': True/False, + # 'disableMandatoryCustomFieldValidation': True/False, + # 'disableSystemNotesForCustomFields': True/False, + # 'ignoreReadOnlyFields': True/False, + # 'runServerSuiteScriptAndTriggerWorkflows': True/False, + # }, + } + ) def _generate_client(self) -> zeep.Client: - client = zeep.Client( - self.wsdl_url, - transport=self.transport, - ) + client = zeep.Client(self.wsdl_url, transport=self.transport,) self._set_default_soapheaders( - client, - preferences=self.config.preferences, + client, preferences=self.config.preferences, ) return client def _get_namespace(self, name: str, sub_namespace: str) -> str: - return ( - 'urn:{name}_{version}.{sub_namespace}.webservices.netsuite.com' - .format( - name=name, - version=self.underscored_version_no_micro, - sub_namespace=sub_namespace, - ) + return "urn:{name}_{version}.{sub_namespace}.webservices.netsuite.com".format( + name=name, + version=self.underscored_version_no_micro, + sub_namespace=sub_namespace, ) - def _type_factory( - self, - name: str, - sub_namespace: str - ) -> zeep.client.Factory: - return self.client.type_factory( - self._get_namespace(name, sub_namespace) - ) + def _type_factory(self, name: str, sub_namespace: str) -> zeep.client.Factory: + return self.client.type_factory(self._get_namespace(name, sub_namespace)) @cached_property def Core(self) -> zeep.client.Factory: - return self._type_factory('core', 'platform') + return self._type_factory("core", "platform") @cached_property def CoreTypes(self) -> zeep.client.Factory: - return self._type_factory('types.core', 'platform') + return self._type_factory("types.core", "platform") @cached_property def FaultsTypes(self) -> zeep.client.Factory: - return self._type_factory('types.faults', 'platform') + return self._type_factory("types.faults", "platform") @cached_property def Faults(self) -> zeep.client.Factory: - return self._type_factory('faults', 'platform') + return self._type_factory("faults", "platform") @cached_property def Messages(self) -> zeep.client.Factory: - return self._type_factory('messages', 'platform') + return self._type_factory("messages", "platform") @cached_property def Common(self) -> zeep.client.Factory: - return self._type_factory('common', 'platform') + return self._type_factory("common", "platform") @cached_property def CommonTypes(self) -> zeep.client.Factory: - return self._type_factory('types.common', 'platform') + return self._type_factory("types.common", "platform") @cached_property def Scheduling(self) -> zeep.client.Factory: - return self._type_factory('scheduling', 'activities') + return self._type_factory("scheduling", "activities") @cached_property def SchedulingTypes(self) -> zeep.client.Factory: - return self._type_factory('types.scheduling', 'activities') + return self._type_factory("types.scheduling", "activities") @cached_property def Communication(self) -> zeep.client.Factory: - return self._type_factory('communication', 'general') + return self._type_factory("communication", "general") @cached_property def CommunicationTypes(self) -> zeep.client.Factory: - return self._type_factory('types.communication', 'general') + return self._type_factory("types.communication", "general") @cached_property def Filecabinet(self) -> zeep.client.Factory: - return self._type_factory('filecabinet', 'documents') + return self._type_factory("filecabinet", "documents") @cached_property def FilecabinetTypes(self) -> zeep.client.Factory: - return self._type_factory('types.filecabinet', 'documents') + return self._type_factory("types.filecabinet", "documents") @cached_property def Relationships(self) -> zeep.client.Factory: - return self._type_factory('relationships', 'lists') + return self._type_factory("relationships", "lists") @cached_property def RelationshipsTypes(self) -> zeep.client.Factory: - return self._type_factory('types.relationships', 'lists') + return self._type_factory("types.relationships", "lists") @cached_property def Support(self) -> zeep.client.Factory: - return self._type_factory('support', 'lists') + return self._type_factory("support", "lists") @cached_property def SupportTypes(self) -> zeep.client.Factory: - return self._type_factory('types.support', 'lists') + return self._type_factory("types.support", "lists") @cached_property def Accounting(self) -> zeep.client.Factory: - return self._type_factory('accounting', 'lists') + return self._type_factory("accounting", "lists") @cached_property def AccountingTypes(self) -> zeep.client.Factory: - return self._type_factory('types.accounting', 'lists') + return self._type_factory("types.accounting", "lists") @cached_property def Sales(self) -> zeep.client.Factory: - return self._type_factory('sales', 'transactions') + return self._type_factory("sales", "transactions") @cached_property def SalesTypes(self) -> zeep.client.Factory: - return self._type_factory('types.sales', 'transactions') + return self._type_factory("types.sales", "transactions") @cached_property def Purchases(self) -> zeep.client.Factory: - return self._type_factory('purchases', 'transactions') + return self._type_factory("purchases", "transactions") @cached_property def PurchasesTypes(self) -> zeep.client.Factory: - return self._type_factory('types.purchases', 'transactions') + return self._type_factory("types.purchases", "transactions") @cached_property def Customers(self) -> zeep.client.Factory: - return self._type_factory('customers', 'transactions') + return self._type_factory("customers", "transactions") @cached_property def CustomersTypes(self) -> zeep.client.Factory: - return self._type_factory('types.customers', 'transactions') + return self._type_factory("types.customers", "transactions") @cached_property def Financial(self) -> zeep.client.Factory: - return self._type_factory('financial', 'transactions') + return self._type_factory("financial", "transactions") @cached_property def FinancialTypes(self) -> zeep.client.Factory: - return self._type_factory('types.financial', 'transactions') + return self._type_factory("types.financial", "transactions") @cached_property def Bank(self) -> zeep.client.Factory: - return self._type_factory('bank', 'transactions') + return self._type_factory("bank", "transactions") @cached_property def BankTypes(self) -> zeep.client.Factory: - return self._type_factory('types.bank', 'transactions') + return self._type_factory("types.bank", "transactions") @cached_property def Inventory(self) -> zeep.client.Factory: - return self._type_factory('inventory', 'transactions') + return self._type_factory("inventory", "transactions") @cached_property def InventoryTypes(self) -> zeep.client.Factory: - return self._type_factory('types.inventory', 'transactions') + return self._type_factory("types.inventory", "transactions") @cached_property def General(self) -> zeep.client.Factory: - return self._type_factory('general', 'transactions') + return self._type_factory("general", "transactions") @cached_property def Customization(self) -> zeep.client.Factory: - return self._type_factory('customization', 'setup') + return self._type_factory("customization", "setup") @cached_property def CustomizationTypes(self) -> zeep.client.Factory: - return self._type_factory('types.customization', 'setup') + return self._type_factory("types.customization", "setup") @cached_property def Employees(self) -> zeep.client.Factory: - return self._type_factory('employees', 'lists') + return self._type_factory("employees", "lists") @cached_property def EmployeesTypes(self) -> zeep.client.Factory: - return self._type_factory('types.employees', 'lists') + return self._type_factory("types.employees", "lists") @cached_property def Website(self) -> zeep.client.Factory: - return self._type_factory('website', 'lists') + return self._type_factory("website", "lists") @cached_property def WebsiteTypes(self) -> zeep.client.Factory: - return self._type_factory('types.website', 'lists') + return self._type_factory("types.website", "lists") @cached_property def EmployeesTransactions(self) -> zeep.client.Factory: - return self._type_factory('employees', 'transactions') + return self._type_factory("employees", "transactions") @cached_property def EmployeesTransactionsTypes(self) -> zeep.client.Factory: - return self._type_factory('types.employees', 'transactions') + return self._type_factory("types.employees", "transactions") @cached_property def Marketing(self) -> zeep.client.Factory: - return self._type_factory('marketing', 'lists') + return self._type_factory("marketing", "lists") @cached_property def MarketingTypes(self) -> zeep.client.Factory: - return self._type_factory('types.marketing', 'lists') + return self._type_factory("types.marketing", "lists") @cached_property def DemandPlanning(self) -> zeep.client.Factory: - return self._type_factory('demandplanning', 'transactions') + return self._type_factory("demandplanning", "transactions") @cached_property def DemandPlanningTypes(self) -> zeep.client.Factory: - return self._type_factory('types.demandplanning', 'transactions') + return self._type_factory("types.demandplanning", "transactions") @cached_property def SupplyChain(self) -> zeep.client.Factory: - return self._type_factory('supplychain', 'lists') + return self._type_factory("supplychain", "lists") @cached_property def SupplyChainTypes(self) -> zeep.client.Factory: - return self._type_factory('types.supplychain', 'lists') + return self._type_factory("types.supplychain", "lists") - def request( - self, - service_name: str, - *args, - **kw - ) -> zeep.xsd.ComplexType: + def request(self, service_name: str, *args, **kw) -> zeep.xsd.ComplexType: """ Make a web service request to NetSuite @@ -512,146 +488,128 @@ def request( return svc(*args, _soapheaders=self.generate_passport(), **kw) @WebServiceCall( - 'body.readResponseList.readResponse', - extract=lambda resp: [r['record'] for r in resp] + "body.readResponseList.readResponse", + extract=lambda resp: [r["record"] for r in resp], ) def getList( self, recordType: str, *, internalIds: Sequence[int] = (), - externalIds: Sequence[str] = () + externalIds: Sequence[str] = (), ) -> List[CompoundValue]: """Get a list of records""" if len(list(internalIds) + list(externalIds)) == 0: - raise ValueError('Please specify `internalId` and/or `externalId`') + raise ValueError("Please specify `internalId` and/or `externalId`") return self.request( - 'getList', + "getList", self.Messages.GetListRequest( baseRef=[ - self.Core.RecordRef( - type=recordType, - internalId=internalId, - ) for internalId in internalIds - ] + [ - self.Core.RecordRef( - type=recordType, - externalId=externalId, - ) for externalId in externalIds + self.Core.RecordRef(type=recordType, internalId=internalId,) + for internalId in internalIds + ] + + [ + self.Core.RecordRef(type=recordType, externalId=externalId,) + for externalId in externalIds ], - ) + ), ) @WebServiceCall( - 'body.readResponse', - extract=lambda resp: resp['record'], + "body.readResponse", extract=lambda resp: resp["record"], ) def get( - self, - recordType: str, - *, - internalId: int = None, - externalId: str = None + self, recordType: str, *, internalId: int = None, externalId: str = None ) -> CompoundValue: """Get a single record""" if len([v for v in (internalId, externalId) if v is not None]) != 1: - raise ValueError('Specify either `internalId` or `externalId`') + raise ValueError("Specify either `internalId` or `externalId`") if internalId: - record_ref = self.Core.RecordRef( - type=recordType, - internalId=internalId, - ) + record_ref = self.Core.RecordRef(type=recordType, internalId=internalId,) else: self.Core.RecordRef( - type=recordType, - externalId=externalId, + type=recordType, externalId=externalId, ) - return self.request('get', baseRef=record_ref) + return self.request("get", baseRef=record_ref) @WebServiceCall( - 'body.getAllResult', - extract=lambda resp: resp['recordList']['record'], + "body.getAllResult", extract=lambda resp: resp["recordList"]["record"], ) def getAll(self, recordType: str) -> List[CompoundValue]: """Get all records of a given type.""" return self.request( - 'getAll', - record=self.Core.GetAllRecord( - recordType=recordType, - ), + "getAll", record=self.Core.GetAllRecord(recordType=recordType,), ) @WebServiceCall( - 'body.writeResponse', - extract=lambda resp: resp['baseRef'], + "body.writeResponse", extract=lambda resp: resp["baseRef"], ) def add(self, record: CompoundValue) -> CompoundValue: """Insert a single record.""" - return self.request('add', record=record) + return self.request("add", record=record) @WebServiceCall( - 'body.writeResponse', - extract=lambda resp: resp['baseRef'], + "body.writeResponse", extract=lambda resp: resp["baseRef"], ) def update(self, record: CompoundValue) -> CompoundValue: """Insert a single record.""" - return self.request('update', record=record) + return self.request("update", record=record) @WebServiceCall( - 'body.writeResponse', - extract=lambda resp: resp['baseRef'], + "body.writeResponse", extract=lambda resp: resp["baseRef"], ) def upsert(self, record: CompoundValue) -> CompoundValue: """Upsert a single record.""" - return self.request('upsert', record=record) + return self.request("upsert", record=record) @WebServiceCall( - 'body.searchResult', - extract=lambda resp: resp['recordList']['record'], + "body.searchResult", extract=lambda resp: resp["recordList"]["record"], ) def search(self, record: CompoundValue) -> List[CompoundValue]: """Search records""" - return self.request('search', searchRecord=record) + return self.request("search", searchRecord=record) @WebServiceCall( - 'body.writeResponseList', - extract=lambda resp: [record['baseRef'] for record in resp], + "body.writeResponseList", + extract=lambda resp: [record["baseRef"] for record in resp], ) def upsertList(self, records: List[CompoundValue]) -> List[CompoundValue]: """Upsert a list of records.""" - return self.request('upsertList', record=records) + return self.request("upsertList", record=records) @WebServiceCall( - 'body.getItemAvailabilityResult', - extract=lambda resp: resp['itemAvailabilityList']['itemAvailability'], - default=[] + "body.getItemAvailabilityResult", + extract=lambda resp: resp["itemAvailabilityList"]["itemAvailability"], + default=[], ) def getItemAvailability( self, *, internalIds: Sequence[int] = (), externalIds: Sequence[str] = (), - lastQtyAvailableChange: datetime = None + lastQtyAvailableChange: datetime = None, ) -> List[Dict]: if len(list(internalIds) + list(externalIds)) == 0: - raise ValueError('Please specify `internalId` and/or `externalId`') + raise ValueError("Please specify `internalId` and/or `externalId`") item_filters = [ - {'type': 'inventoryItem', 'internalId': internalId} + {"type": "inventoryItem", "internalId": internalId} for internalId in internalIds ] + [ - {'type': 'inventoryItem', 'externalId': externalId} + {"type": "inventoryItem", "externalId": externalId} for externalId in externalIds ] return self.request( - 'getItemAvailability', - itemAvailabilityFilter=[{ - 'item': {'recordRef': item_filters}, - 'lastQtyAvailableChange': lastQtyAvailableChange - }], + "getItemAvailability", + itemAvailabilityFilter=[ + { + "item": {"recordRef": item_filters}, + "lastQtyAvailableChange": lastQtyAvailableChange, + } + ], ) diff --git a/netsuite/config.py b/netsuite/config.py index 2bc1c7e..fd1191f 100644 --- a/netsuite/config.py +++ b/netsuite/config.py @@ -3,8 +3,8 @@ from .constants import DEFAULT_INI_PATH, DEFAULT_INI_SECTION, NOT_SET -TOKEN = 'token' -CREDENTIALS = 'credentials' +TOKEN = "token" +CREDENTIALS = "credentials" class Config: @@ -48,42 +48,15 @@ class Config: """Additional preferences""" _settings_mapping = ( - ( - 'account', - {'type': str, 'required': True}, - ), - ( - 'consumer_key', - {'type': str, 'required_for_auth_type': TOKEN}, - ), - ( - 'consumer_secret', - {'type': str, 'required_for_auth_type': TOKEN}, - ), - ( - 'token_id', - {'type': str, 'required_for_auth_type': TOKEN}, - ), - ( - 'token_secret', - {'type': str, 'required_for_auth_type': TOKEN}, - ), - ( - 'application_id', - {'type': str, 'required_for_auth_type': CREDENTIALS}, - ), - ( - 'email', - {'type': str, 'required_for_auth_type': CREDENTIALS}, - ), - ( - 'password', - {'type': str, 'required_for_auth_type': CREDENTIALS}, - ), - ( - 'preferences', - {'type': dict, 'required': False, 'default': lambda: {}}, - ), + ("account", {"type": str, "required": True},), + ("consumer_key", {"type": str, "required_for_auth_type": TOKEN},), + ("consumer_secret", {"type": str, "required_for_auth_type": TOKEN},), + ("token_id", {"type": str, "required_for_auth_type": TOKEN},), + ("token_secret", {"type": str, "required_for_auth_type": TOKEN},), + ("application_id", {"type": str, "required_for_auth_type": CREDENTIALS},), + ("email", {"type": str, "required_for_auth_type": CREDENTIALS},), + ("password", {"type": str, "required_for_auth_type": CREDENTIALS},), + ("preferences", {"type": dict, "required": False, "default": lambda: {}},), ) def __init__(self, **opts) -> None: @@ -93,7 +66,7 @@ def __contains__(self, key: str) -> bool: return hasattr(self, key) def _set_auth_type(self, value: str) -> None: - self._validate_attr('auth_type', value, str, True, {}) + self._validate_attr("auth_type", value, str, True, {}) self.auth_type = value assert self.auth_type in (TOKEN, CREDENTIALS) @@ -105,60 +78,53 @@ def is_credentials_auth(self) -> bool: def _set(self, dct: Dict[str, object]) -> None: # As other setting validations depend on auth_type we set it first - auth_type = dct.get('auth_type', self.auth_type) + auth_type = dct.get("auth_type", self.auth_type) self._set_auth_type(auth_type) for attr, opts in self._settings_mapping: value = dct.get(attr, NOT_SET) - type_ = opts['type'] + type_ = opts["type"] required = opts.get( - 'required', - opts.get('required_for_auth_type') == auth_type + "required", opts.get("required_for_auth_type") == auth_type ) self._validate_attr(attr, value, type_, required, opts) - if value is NOT_SET and 'default' in opts: - value = opts['default']() + if value is NOT_SET and "default" in opts: + value = opts["default"]() setattr(self, attr, (None if value is NOT_SET else value)) def _validate_attr( - self, - attr: str, - value: object, - type_: object, - required: bool, - opts: dict + self, attr: str, value: object, type_: object, required: bool, opts: dict ) -> None: if required and value is NOT_SET: - required_for_auth_type = opts.get('required_for_auth_type') + required_for_auth_type = opts.get("required_for_auth_type") if required_for_auth_type: raise ValueError( - f'Attribute {attr} is required for auth_type=' - f'`{required_for_auth_type}`' + f"Attribute {attr} is required for auth_type=" + f"`{required_for_auth_type}`" ) else: - raise ValueError(f'Attribute {attr} is required') + raise ValueError(f"Attribute {attr} is required") if value is not NOT_SET and not isinstance(value, type_): - raise ValueError(f'Attribute {attr} is not of type `{type_}`') + raise ValueError(f"Attribute {attr} is not of type `{type_}`") def from_ini( - path: str = DEFAULT_INI_PATH, - section: str = DEFAULT_INI_SECTION + path: str = DEFAULT_INI_PATH, section: str = DEFAULT_INI_SECTION ) -> Config: iniconf = configparser.ConfigParser() with open(path) as fp: iniconf.read_file(fp) - config_dict = {'preferences': {}} + config_dict = {"preferences": {}} for key, val in iniconf[section].items(): - if key.startswith('preferences_'): - _, key = key.split('_', 1) - config_dict['preferences'][key] = val + if key.startswith("preferences_"): + _, key = key.split("_", 1) + config_dict["preferences"][key] = val else: config_dict[key] = val diff --git a/netsuite/constants.py b/netsuite/constants.py index b631fc5..c58b2ac 100644 --- a/netsuite/constants.py +++ b/netsuite/constants.py @@ -2,7 +2,6 @@ NOT_SET: object = object() DEFAULT_INI_PATH: str = os.environ.get( - 'NETSUITE_CONFIG', - os.path.expanduser('~/.config/netsuite.ini'), + "NETSUITE_CONFIG", os.path.expanduser("~/.config/netsuite.ini"), ) -DEFAULT_INI_SECTION: str = 'netsuite' +DEFAULT_INI_SECTION: str = "netsuite" diff --git a/netsuite/json.py b/netsuite/json.py index 91d3ecb..25bcf60 100644 --- a/netsuite/json.py +++ b/netsuite/json.py @@ -1,9 +1,9 @@ import datetime -from uuid import UUID from decimal import Decimal from enum import Enum from pathlib import Path from typing import Any, Callable, Dict, Type, Union +from uuid import UUID import orjson as _orjson diff --git a/netsuite/passport.py b/netsuite/passport.py index 529d194..973f281 100644 --- a/netsuite/passport.py +++ b/netsuite/passport.py @@ -8,24 +8,17 @@ from .config import Config -NetSuite = TypeVar('NetSuite') +NetSuite = TypeVar("NetSuite") class Passport: - def get_element(self) -> str: raise NotImplementedError class UserCredentialsPassport(Passport): - def __init__( - self, - ns: NetSuite, - *, - account: str, - email: str, - password: str + self, ns: NetSuite, *, account: str, email: str, password: str ) -> None: self.ns = ns self.account = account @@ -34,14 +27,11 @@ def __init__( def get_element(self) -> CompoundValue: return self.ns.Core.Passport( - account=self.account, - email=self.email, - password=self.password, + account=self.account, email=self.email, password=self.password, ) class TokenPassport(Passport): - def __init__( self, ns: NetSuite, @@ -50,7 +40,7 @@ def __init__( consumer_key: str, consumer_secret: str, token_id: str, - token_secret: str + token_secret: str, ) -> None: self.ns = ns self.account = account @@ -69,34 +59,27 @@ def _generate_timestamp(self) -> str: def _generate_nonce(self, length: int = 20) -> str: """Generate pseudorandom number""" - return ''.join([str(random.randint(0, 9)) for i in range(length)]) + return "".join([str(random.randint(0, 9)) for i in range(length)]) def _get_signature_message(self, nonce: str, timestamp: str) -> str: - return '&'.join(( - self.account, - self.consumer_key, - self.token_id, - nonce, - timestamp, - )) + return "&".join( + (self.account, self.consumer_key, self.token_id, nonce, timestamp,) + ) def _get_signature_key(self) -> str: - return '&'.join((self.consumer_secret, self.token_secret)) + return "&".join((self.consumer_secret, self.token_secret)) def _get_signature_value(self, nonce: str, timestamp: str) -> str: key = self._get_signature_key() message = self._get_signature_message(nonce, timestamp) hashed = hmac.new( - key=key.encode('utf-8'), - msg=message.encode('utf-8'), - digestmod='sha256' + key=key.encode("utf-8"), msg=message.encode("utf-8"), digestmod="sha256" ).digest() return base64.b64encode(hashed).decode() def _get_signature(self, nonce: str, timestamp: str) -> CompoundValue: return self.ns.Core.TokenPassportSignature( - self._get_signature_value(nonce, timestamp), - algorithm='HMAC-SHA256', + self._get_signature_value(nonce, timestamp), algorithm="HMAC-SHA256", ) def get_element(self) -> CompoundValue: @@ -114,7 +97,7 @@ def get_element(self) -> CompoundValue: def make(ns: NetSuite, config: Config) -> Dict: - if config.auth_type == 'token': + if config.auth_type == "token": token_passport = TokenPassport( ns, account=config.account, @@ -123,19 +106,14 @@ def make(ns: NetSuite, config: Config) -> Dict: token_id=config.token_id, token_secret=config.token_secret, ) - return {'tokenPassport': token_passport.get_element()} - elif config.auth_type == 'credentials': + return {"tokenPassport": token_passport.get_element()} + elif config.auth_type == "credentials": passport = UserCredentialsPassport( - ns, - account=config.account, - email=config.email, - password=config.password, + ns, account=config.account, email=config.email, password=config.password, ) return { - 'applicationInfo': { - 'applicationId': config.application_id, - }, - 'passport': passport.get_element(), + "applicationInfo": {"applicationId": config.application_id,}, + "passport": passport.get_element(), } else: - raise NotImplementedError(f'config.auth_type={config.auth_type}') + raise NotImplementedError(f"config.auth_type={config.auth_type}") diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index 796d0b9..b2c9811 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -2,6 +2,9 @@ import logging from typing import Iterable, Optional +from . import json +from .types import JsonDict + try: import httpx except ImportError: @@ -12,8 +15,6 @@ except ImportError: OAuth1Auth = None -from . import json -from .types import JsonDict logger = logging.getLogger(__name__) @@ -164,7 +165,9 @@ async def _raw_request( ) resp_headers_json = json.dumps_str(dict(resp.headers)) - logger.debug(f"Got response headers from NetSuite REST API: {resp_headers_json}") + logger.debug( + f"Got response headers from NetSuite REST API: {resp_headers_json}" + ) return resp diff --git a/netsuite/restlet.py b/netsuite/restlet.py index 46968e1..c83f32f 100644 --- a/netsuite/restlet.py +++ b/netsuite/restlet.py @@ -11,8 +11,9 @@ class NetsuiteRestlet: - _restlet_path_tmpl = \ - '/app/site/hosting/restlet.nl?script={script_id}&deploy={deploy}' + _restlet_path_tmpl = ( + "/app/site/hosting/restlet.nl?script={script_id}&deploy={deploy}" + ) def __init__(self, config, *, hostname=None): self.__config = config @@ -35,7 +36,7 @@ def request( deploy: int = 1, raise_on_bad_status: bool = True, timeout: Union[int, Tuple[int, int]] = None, - **requests_kw + **requests_kw, ): resp = self.raw_request( script_id=script_id, @@ -43,7 +44,7 @@ def request( deploy=deploy, raise_on_bad_status=raise_on_bad_status, timeout=timeout, - **requests_kw + **requests_kw, ) util.raise_for_status_with_body(resp) return resp.json() @@ -56,43 +57,36 @@ def raw_request( deploy: int = 1, raise_on_bad_status: bool = True, timeout: Union[int, Tuple[int, int]] = None, - **requests_kw + **requests_kw, ): url = self._make_url(script_id=script_id, deploy=deploy) headers = self._make_headers() req_headers_json = json.dumps(headers) logger.debug( - f'Making request to restlet at {url}. Payload {payload}. ' - f'Headers: {req_headers_json}' + f"Making request to restlet at {url}. Payload {payload}. " + f"Headers: {req_headers_json}" ) resp = self._request_session.post( - url, - headers=headers, - json=payload, - timeout=timeout, - **requests_kw + url, headers=headers, json=payload, timeout=timeout, **requests_kw ) resp_headers_json = json.dumps(dict(resp.headers)) - logger.debug(f'Got response headers: {resp_headers_json}') + logger.debug(f"Got response headers: {resp_headers_json}") return resp def _make_default_hostname(self): - account_slugified = self.config.account.lower().replace('_', '-') - return f'{account_slugified}.restlets.api.netsuite.com' + account_slugified = self.config.account.lower().replace("_", "-") + return f"{account_slugified}.restlets.api.netsuite.com" def _make_restlet_path(self, script_id: int, deploy: int = 1): - return self._restlet_path_tmpl.format( - script_id=script_id, - deploy=deploy, - ) + return self._restlet_path_tmpl.format(script_id=script_id, deploy=deploy,) def _make_url(self, script_id: int, deploy: int = 1): path = self._make_restlet_path(script_id=script_id, deploy=deploy) - return f'https://{self.hostname}{path}' + return f"https://{self.hostname}{path}" def _make_request_session(self): return requests_oauthlib.OAuth1Session( @@ -105,5 +99,5 @@ def _make_request_session(self): def _make_headers(self): return { - 'Content-Type': 'application/json', + "Content-Type": "application/json", } diff --git a/netsuite/util.py b/netsuite/util.py index 335ddcc..0fab06a 100644 --- a/netsuite/util.py +++ b/netsuite/util.py @@ -1,12 +1,13 @@ import requests -__all__ = ('cached_property', 'raise_for_status_with_body') +__all__ = ("cached_property", "raise_for_status_with_body") class cached_property: """ Decorator that turns an instance method into a cached property From https://speakerdeck.com/u/mitsuhiko/p/didntknow, slide #69 """ + __NOT_SET = object() def __init__(self, func): @@ -25,10 +26,7 @@ def __get__(self, obj, type=None): return value -def raise_for_status_with_body( - response, - on_bad_status=None -): +def raise_for_status_with_body(response, on_bad_status=None): """Raise exception on bad HTTP status and capture response body Also: @@ -42,7 +40,7 @@ def raise_for_status_with_body( except requests.exceptions.RequestException as ex: body = response.text if body and len(ex.args) == 1: - ex.args = (ex.args[0] + f'\nBody: {body}', ) + ex.args = (ex.args[0] + f"\nBody: {body}",) if on_bad_status is not None: on_bad_status() raise ex diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..89f8ef5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = ["setuptools", "wheel"] + +[tool.black] +line-length = 88 +target-version = ['py36', 'py37', 'py38'] diff --git a/setup.py b/setup.py index f84f29b..e6b8283 100755 --- a/setup.py +++ b/setup.py @@ -1,61 +1,51 @@ from setuptools import setup extras_require = { - 'rest_api': [ - 'authlib', + "rest_api": [ + "authlib", # NOTE: authlib doesn't work with httpx 0.12.1 So we're locking the httpx # version to one that is compatible. See: # https://github.com/lepture/authlib/issues/210#issuecomment-612311003 # TODO: Remove version lock once fixed upstream. - 'httpx==0.12.0', + "httpx==0.12.0", ], - 'cli': ['ipython'], - 'test': { - 'coverage>=4.2', - 'flake8>=3.0.4', - 'mypy>=0.560', - 'pytest>=3.0.3', - 'responses>=0.5.1', - }, + "cli": ["ipython"], + "test": ["pytest>=3.0.3", "coverage>=4.2",], + "lint": ["pre-commit>=1.21.0", "flake8>=3.0.4", "black>=19.10b0", "isort>=4.3.21",], } -extras_require['all'] = [ - dep for name, lst in extras_require.items() if name != 'test' for dep in lst +extras_require["all"] = [ + dep + for name, lst in extras_require.items() + if name not in ("test", "lint") + for dep in lst ] setup_kwargs = dict( - name='netsuite', - version='0.5.2', - description='Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets', - packages=['netsuite'], + name="netsuite", + version="0.5.2", + description="Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets", + packages=["netsuite"], include_package_data=True, - author='Jacob Magnusson', - author_email='m@jacobian.se', - url='https://github.com/jmagnusson/netsuite', - license='BSD', - platforms='any', - install_requires=[ - 'requests-oauthlib', - 'zeep', - 'orjson', - ], + author="Jacob Magnusson", + author_email="m@jacobian.se", + url="https://github.com/jmagnusson/netsuite", + license="BSD", + platforms="any", + install_requires=["requests-oauthlib", "zeep", "orjson",], extras_require=extras_require, - entry_points={ - 'console_scripts': [ - 'netsuite = netsuite.__main__:main', - ], - }, + entry_points={"console_scripts": ["netsuite = netsuite.__main__:main",],}, classifiers=[ - 'Development Status :: 3 - Alpha', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', + "Development Status :: 3 - Alpha", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", ], ) -if __name__ == '__main__': +if __name__ == "__main__": setup(**setup_kwargs) diff --git a/tests/test_base.py b/tests/test_base.py index 6b7c0b8..a05c763 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,23 +1,27 @@ import pytest + import netsuite @pytest.fixture def dummy_config(): return { - 'account': '123456', - 'consumer_key': 'abcdefghijklmnopqrstuvwxyz0123456789', - 'consumer_secret': 'abcdefghijklmnopqrstuvwxyz0123456789', - 'token_id': 'abcdefghijklmnopqrstuvwxyz0123456789', - 'token_secret': 'abcdefghijklmnopqrstuvwxyz0123456789', + "account": "123456", + "consumer_key": "abcdefghijklmnopqrstuvwxyz0123456789", + "consumer_secret": "abcdefghijklmnopqrstuvwxyz0123456789", + "token_id": "abcdefghijklmnopqrstuvwxyz0123456789", + "token_secret": "abcdefghijklmnopqrstuvwxyz0123456789", } def test_netsuite_hostname(dummy_config): ns = netsuite.NetSuite(dummy_config) - assert ns.hostname == '123456.suitetalk.api.netsuite.com' + assert ns.hostname == "123456.suitetalk.api.netsuite.com" def test_netsuite_wsdl_url(dummy_config): ns = netsuite.NetSuite(dummy_config) - assert ns.wsdl_url == 'https://123456.suitetalk.api.netsuite.com/wsdl/v2019_2_0/netsuite.wsdl' + assert ( + ns.wsdl_url + == "https://123456.suitetalk.api.netsuite.com/wsdl/v2019_2_0/netsuite.wsdl" + ) diff --git a/tox.ini b/tox.ini index 1254a27..b83a740 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py36, py37, lint +envlist = py36, py37, py38, lint [testenv] commands = @@ -13,13 +13,10 @@ deps = .[test,all] [testenv:lint] +skip_install = true commands = - flake8 netsuite tests + flake8 . + isort --check -rc . + black --check --diff . deps = - .[test,all] - -[flake8] -ignore = - E501, - E711, - E712 + .[lint] From 35a1c4ace0ae00f43a1fe17235d7c17fcd3e9e92 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sat, 2 May 2020 17:47:37 +0200 Subject: [PATCH 07/47] Add python 3.8 to CI --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 96c5cdb..c095a57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,9 @@ matrix: env: TOX_ENV=py36 - python: "3.7" env: TOX_ENV=py37 - - python: "3.6" + - python: "3.8" + env: TOX_ENV=py38 + - python: "3.7" env: TOX_ENV=lint install: From 379de5b7b97e45b56f00943051824b353afcdab8 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 26 May 2020 17:30:02 +0200 Subject: [PATCH 08/47] Fix `netsuite` not being importable unless `httpx` is installed Fixes #18 --- CHANGELOG.md | 4 ++++ netsuite/rest_api.py | 5 ++++- setup.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66e285d..f0e7368 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.5.3 (2020-05-26) + +* Fix: Couldn't import `netsuite` unless `httpx` was installed. Fixes #18 + ## 0.5.2 (2020-05-02) * Fix: Only forward explicitly passed in parameters for `netsuite rest-api get` command. Fixes error `Invalid query parameter name: limit. Allowed query parameters are: fields, expand, expandSubResources.` diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index b2c9811..798fb8e 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -8,7 +8,10 @@ try: import httpx except ImportError: - httpx = None + + class httpx: + Response = None # NOTE: For type hint to work + try: from authlib.integrations.httpx_client import OAuth1Auth diff --git a/setup.py b/setup.py index e6b8283..37045e1 100755 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ setup_kwargs = dict( name="netsuite", - version="0.5.2", + version="0.5.3", description="Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets", packages=["netsuite"], include_package_data=True, From f5489c65fa7e210a2f5e9b665b185f396a56cdf5 Mon Sep 17 00:00:00 2001 From: Micah Mangione Date: Fri, 9 Apr 2021 03:52:56 -0700 Subject: [PATCH 09/47] Fix bug in routine where request body is deleted. Repair a bug in the self._make_auth() routine where the OAuth1 client is generated before a request body is available. This causes all SuiteQL queries to fail, along with any other functions that require a request body. Resolve by storing the connection details in the class and calling that method again when we detect a post body. --- netsuite/rest_api.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index 798fb8e..b0dada7 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -57,10 +57,12 @@ def __init__( "Install with `pip install netsuite[rest_api]`" ) self._account = account + self._consumer_key = consumer_key + self._consumer_secret = consumer_secret + self._token_id = token_id + self._token_secret = token_secret self._hostname = self._make_hostname() - self._auth = self._make_auth( - account, consumer_key, consumer_secret, token_id, token_secret - ) + self._auth = self._make_auth() self._default_timeout = default_timeout self._request_semaphore = asyncio.Semaphore(concurrent_requests) @@ -149,7 +151,7 @@ async def _raw_request( timeout = request_kw.pop("timeout", self._default_timeout) if "json" in request_kw: - request_kw["data"] = json.dumps_str(request_kw.pop("json")) + auth = self._make_auth(force_include_body=True) kw = {**request_kw} logger.debug( @@ -181,20 +183,14 @@ def _make_hostname(self): def _make_url(self, subpath: str): return f"https://{self._hostname}/services/rest{subpath}" - @staticmethod - def _make_auth( - account: str, - consumer_key: str, - consumer_secret: str, - token_id: str, - token_secret: str, - ): + def _make_auth(self, force_include_body: bool = False): return OAuth1Auth( - client_id=consumer_key, - client_secret=consumer_secret, - token=token_id, - token_secret=token_secret, - realm=account, + client_id=self._consumer_key, + client_secret=self._consumer_secret, + token=self._token_id, + token_secret=self._token_secret, + realm=self._account, + force_include_body=force_include_body ) def _make_default_headers(self): From a8e537fef215a9bf7c0dffb099f6d7d88da358c5 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sun, 25 Apr 2021 10:34:17 +0200 Subject: [PATCH 10/47] Add back `json.dumps_str` and always set `force_include_body` to True --- netsuite/rest_api.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index b0dada7..9b65050 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -62,7 +62,6 @@ def __init__( self._token_id = token_id self._token_secret = token_secret self._hostname = self._make_hostname() - self._auth = self._make_auth() self._default_timeout = default_timeout self._request_semaphore = asyncio.Semaphore(concurrent_requests) @@ -147,11 +146,10 @@ async def _raw_request( url = self._make_url(subpath) headers = {**self._make_default_headers(), **request_kw.pop("headers", {})} - auth = request_kw.pop("auth", self._auth) timeout = request_kw.pop("timeout", self._default_timeout) if "json" in request_kw: - auth = self._make_auth(force_include_body=True) + request_kw["data"] = json.dumps_str(request_kw.pop("json")) kw = {**request_kw} logger.debug( @@ -164,7 +162,7 @@ async def _raw_request( method=method, url=url, headers=headers, - auth=auth, + auth=self._make_auth(), timeout=timeout, **kw, ) @@ -183,14 +181,14 @@ def _make_hostname(self): def _make_url(self, subpath: str): return f"https://{self._hostname}/services/rest{subpath}" - def _make_auth(self, force_include_body: bool = False): + def _make_auth(self): return OAuth1Auth( client_id=self._consumer_key, client_secret=self._consumer_secret, token=self._token_id, token_secret=self._token_secret, realm=self._account, - force_include_body=force_include_body + force_include_body=True, ) def _make_default_headers(self): From ae04e19082c3834dd814b4498cac5653cc9ef282 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sun, 25 Apr 2021 12:04:14 +0200 Subject: [PATCH 11/47] Bump to 0.6.0 and move to Github Actions + Poetry --- .flake8 | 3 +- .github/workflows/cd.yml | 29 + .github/workflows/ci.yml | 25 + .github/workflows/codecov.yml | 20 + .github/workflows/docs.yml | 23 + .pre-commit-config.yaml | 30 +- .travis.yml | 25 - CHANGELOG.md | 137 ++- README.md | 178 +--- docs/index.md | 178 ++++ mkdocs.yml | 3 + poetry.lock | 1658 +++++++++++++++++++++++++++++++++ pyproject.toml | 63 +- setup.cfg | 2 - setup.py | 51 - tox.ini | 22 - 16 files changed, 2118 insertions(+), 329 deletions(-) create mode 100644 .github/workflows/cd.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/codecov.yml create mode 100644 .github/workflows/docs.yml delete mode 100644 .travis.yml create mode 100644 docs/index.md create mode 100644 mkdocs.yml create mode 100644 poetry.lock delete mode 100644 setup.cfg delete mode 100755 setup.py delete mode 100644 tox.ini diff --git a/.flake8 b/.flake8 index 7b8ac8a..9776575 100644 --- a/.flake8 +++ b/.flake8 @@ -1,2 +1,3 @@ [flake8] -ignore = E203,E231,E501,E711,E712,W503,W504 +ignore = E501 + diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..794f146 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,29 @@ +name: cd +on: + push: + tags: + - v* +jobs: + cd: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.9 + - uses: abatilo/actions-poetry@v2.1.0 + with: + poetry-version: 1.1.5 + - name: Build + run: poetry build + - uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + prerelease: false + files: | + dist/*.whl + dist/*.tar.gz + - name: Publish to PyPI + env: + POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} + run: poetry publish diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e52642a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: ci + +on: + push: + pull_request: + +jobs: + ci: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9, 3.8, 3.7] + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - uses: abatilo/actions-poetry@v2.1.0 + - run: poetry install --extras all + - run: poetry run pytest -v + - run: poetry run flake8 + - run: poetry run mypy --ignore-missing-imports . + - run: poetry run isort --check --diff . + - run: poetry run black --check --diff . diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 0000000..e347430 --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,20 @@ +name: codecov + +on: + push: + pull_request: + +jobs: + code_coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.9 + - uses: abatilo/actions-poetry@v2.1.0 + - run: poetry install --extras all + - run: poetry run pytest --cov=netsuite --cov-report=xml --cov-report=term + - uses: codecov/codecov-action@v1 + with: + files: ./coverage.xml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..b6be329 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,23 @@ +name: docs +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.9 + - uses: abatilo/actions-poetry@v2.1.0 + with: + poetry-version: 1.1.5 + - run: poetry install --extras all + - run: poetry run mkdocs build + - uses: peaceiris/actions-gh-pages@v3.7.3 + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + publish_dir: ./site diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 555cba1..5230d62 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,22 @@ +exclude: '{{project_slug}}' + +default_language_version: + python: python3 + repos: -- repo: https://gitlab.com/pycqa/flake8 - rev: master + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.9.0 hooks: - - id: flake8 -- repo: https://github.com/asottile/seed-isort-config - rev: v1.9.2 + - id: flake8 + - repo: https://github.com/pycqa/isort + rev: 5.8.0 hooks: - - id: seed-isort-config -- repo: https://github.com/pre-commit/mirrors-isort - rev: master + - id: isort + - repo: https://github.com/psf/black + rev: 20.8b1 hooks: - - id: isort -- repo: https://github.com/psf/black - rev: stable + - id: black + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v0.812 hooks: - - id: black - language_version: python3.7 + - id: mypy diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c095a57..0000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -sudo: required -dist: xenial -language: python -cache: pip - -matrix: - include: - - python: "3.6" - env: TOX_ENV=py36 - - python: "3.7" - env: TOX_ENV=py37 - - python: "3.8" - env: TOX_ENV=py38 - - python: "3.7" - env: TOX_ENV=lint - -install: - - pip install tox - -script: tox -e $TOX_ENV - -after_success: - # Submit data from .coverage to coveralls on success - - pip install coveralls - - coveralls diff --git a/CHANGELOG.md b/CHANGELOG.md index f0e7368..bd902a2 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,74 +1,121 @@ # Changelog +All notable changes to this project will be documented in this file. -## 0.5.3 (2020-05-26) +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -* Fix: Couldn't import `netsuite` unless `httpx` was installed. Fixes #18 +## [[Unreleased] -## 0.5.2 (2020-05-02) +- Nothing -* Fix: Only forward explicitly passed in parameters for `netsuite rest-api get` command. Fixes error `Invalid query parameter name: limit. Allowed query parameters are: fields, expand, expandSubResources.` -* Feature: Add ability to have `netsuite rest-api get` only return a given list of fields via `--fields` -* Feature: Add ability for `netsuite rest-api get` to only expand a given set of sublist and subrecord types via `--expand` +## [[0.6.0](https://github.com/jmagnusson/netsuite/compare/v0.5.3...v0.6.0) - 2021-04-25 -## 0.5.1 (2020-05-02) +### Fixed +- Release 2021.1 wouldn't accept non-GET requests to the REST API without body being part of the signing. Thanks to @mmangione for the PR! (#26) -* Fix HTML title in OpenAPI Swagger docs +### Added +- Documentation site -## 0.5.0 (2020-05-02) +### Changed +- Upgrade to httpx ~0.17 +- Use poetry for package management +- Move to Github Actions -* Feature: Support for SuiteTalk REST Web Services, including standard GET, POST, PATCH, PUT, DELETE requests as well as making SuiteQL queries. For now it's an optional dependency (install with `pip install netsuite[rest_api]`) -* Feature: Start a HTTP server via command line to browse REST API OpenAPI spec docs for a given set of records (utilizes Swagger UI) -* Breaking change: `--log-level`, `--config-path` and `--config-section` must now be passed directly to the `netsuite` command, and not its sub-commands. +## [0.5.3] - 2020-05-26 -## 0.4.1 (2020-03-09) +### Fixed +- Couldn't import `netsuite` unless `httpx` was installed. Fixes #18 -* Extend Zeep Transport GET and POST HTTP methods to apply the account-specific dynamic domain as the remote host -* Update the NetSuite WSDL compatibility to 2019.2 +## [0.5.2] - 2020-05-02 -## 0.4.0 (2019-04-29) +### Fixed +- Only forward explicitly passed in parameters for `netsuite rest-api get` command. Fixes error `Invalid query parameter name: limit. Allowed query parameters are: fields, expand, expandSubResources.` -* Enhancement: Add support for specifying operation/request timeouts -* Enhancement: Throw an exception if Suitetalk returns a response error +### Added +- Ability to have `netsuite rest-api get` only return a given list of fields via `--fields` +- Ability for `netsuite rest-api get` to only expand a given set of sublist and subrecord types via `--expand` -## 0.3.2 (2019-04-11) +## [0.5.1] - 2020-05-02 -* Feature: Add support for `update` and `search` operations +### Changed +- HTML title in OpenAPI Swagger docs -## 0.3.1 (2019-04-11) +## [0.5.0] - 2020-05-02 -* Enhancement: Decrease restlet request time on subsequent requests by half by re-using the OAuth session +### Added +- Support for SuiteTalk REST Web Services, including standard GET, POST, PATCH, PUT, DELETE requests as well as making SuiteQL queries. For now it's an optional dependency (install with `pip install netsuite[rest_api]`) +- Start a HTTP server via command line to browse REST API OpenAPI spec docs for a given set of records (utilizes Swagger UI) -## 0.3.0 (2019-04-10) +### Changed +- `--log-level`, `--config-path` and `--config-section` must now be passed directly to the `netsuite` command, and not its sub-commands. -* Feature: Added support for making requests to restlets -* Feature: New command to utilize the new restlet request capability -* Info: Removed `requests-ntlm` dependency which was never used -* Info: Don't specify `lxml` as a dependency. Implicitly take dependency from `zeep` instead. -* Info: Document usage of CLI utils +## [0.4.1] - 2020-03-09 -## 0.2.2 (2018-12-11) +### Changed +- Extend Zeep Transport GET and POST HTTP methods to apply the account-specific dynamic domain as the remote host +- Update the NetSuite WSDL compatibility to 2019.2 -* Feature: Added `get`, `getAll`, `add`, `upsert` and `upsertList` methods. Big thanks go out to @matmunn for the original PR. (#6) +## [0.4.0] - 2019-04-29 -## 0.2.1 (2018-12-11) +### Added +- Support for specifying operation/request timeouts -* Feature: Helper `NetSuite.to_builtin` to convert zeep objects to python builtins -* Feature: Add `lastQtyAvailableChange` filter +### Changed +- Changed: Throw an exception if Suitetalk returns a response error -## 0.2.0 (2018-12-11) +## [0.3.2] - 2019-04-11 -* Breaking change: Sandbox is now configured through account ID, `sandbox` flag is now a no-op -* Breaking change: New default version is 2018.1.0 -* Breaking change: Account specific domains are now used when `wsdl_url` is left unspecified -* Feature: Support regular credentials Passport -* Info: Listing Python 3.7 as a supported version +### Added +- Support for `update` and `search` operations -## 0.1.1 (2018-04-02) +## [0.3.1] - 2019-04-11 -* Fix: `getItemAvailability` only read first passed in external/internal ID -* Feature: Allow overriding global NS preferences through SOAP headers +### Changed +- Decrease restlet request time on subsequent requests by half by re-using the OAuth session -## 0.1.0 (2018-03-29) +## [0.3.0] - 2019-04-10 -* Initial version. Support for `getList` and `getItemAvailability` -* Please note that there is currently no handling for error responses from the API. TODO! +### Added +- Support for making requests to restlets +- New command to utilize the new restlet request capability +- Added: Document usage of CLI utils +### Removed +- `requests-ntlm` dependency which was never used + +### Changed +- Don't specify `lxml` as a dependency. Implicitly take dependency from `zeep` instead. + +## [0.2.2] - 2018-12-11 + +### Added +- `get`, `getAll`, `add`, `upsert` and `upsertList` methods. Big thanks go out to @matmunn for the original PR. (#6) + +## [0.2.1] - 2018-12-11 + +### Added +- Helper `NetSuite.to_builtin` to convert zeep objects to python builtins +- `lastQtyAvailableChange` filter + +## [0.2.0] - 2018-12-11 + +### Changed +- Sandbox is now configured through account ID, `sandbox` flag is now a no-op +- New default version is 2018.1.0 +- Account specific domains are now used when `wsdl_url` is left unspecified + +### Added +- Support regular credentials Passport +- Listing Python 3.7 as a supported version + +## [0.1.1] - 2018-04-02 + +### Fixed +- `getItemAvailability` only read first passed in external/internal ID + +### Added +- Allow overriding global NS preferences through SOAP headers + +## [0.1.0] - 2018-03-29 + +- Initial version. Support for `getList` and `getItemAvailability` +- Please note that there is currently no handling for error responses from the API diff --git a/README.md b/README.md index d89c517..6502650 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # netsuite -[![Travis CI build status (Linux)](https://travis-ci.org/jmagnusson/netsuite.svg?branch=master)](https://travis-ci.org/jmagnusson/netsuite) +[![Continuous Integration Status](https://github.com/jmagnusson/netsuite/actions/workflows/ci.yml/badge.svg)](https://github.com/jmagnusson/netsuite/actions/workflows/ci.yml) +[![Continuous Delivery Status](https://github.com/jmagnusson/netsuite/actions/workflows/cd.yml/badge.svg)](https://github.com/jmagnusson/netsuite/actions/workflows/cd.yml) +[![Code Coverage](https://img.shields.io/codecov/c/github/jmagnusson/netsuite?color=%2334D058)](https://codecov.io/gh/jmagnusson/netsuite) [![PyPI version](https://img.shields.io/pypi/v/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) [![License](https://img.shields.io/pypi/l/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) -[![Available as wheel](https://img.shields.io/pypi/wheel/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) -[![Supported Python versions](https://img.shields.io/pypi/pyversions/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) +[![Python Versions](https://img.shields.io/pypi/pyversions/netsuite.svg)](https://pypi.org/project/netsuite/) [![PyPI status (alpha/beta/stable)](https://img.shields.io/pypi/status/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) Make requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets @@ -15,177 +16,18 @@ Programmatic use only: pip install netsuite -With CLI support: - - pip install netsuite[cli] - With NetSuite SuiteTalk REST Web Services API support: pip install netsuite[rest_api] -With all features: - - pip install netsuite[all] - - -## CLI - -### Configuration - -To use the command line utilities you must add a config file with a section in this format: - -```ini -[netsuite] -auth_type = token -account = 123456 -consumer_key = 789123 -consumer_secret = 456789 -token_id = 012345 -token_secret = 678901 -``` - -You can add multiple sections like this. The `netsuite` section will be read by default, but can be overridden using the `-c` flag. - -The default location that will be read is `~/.config/netsuite.ini`. This can overriden with the `-p` flag. - -Append `--help` to the commands to see full documentation. - -### `rest-api` - Make requests to NetSuite REST API - -See the NetSuite help center for info on how to use the REST API. The `netsuite rest-api openapi-serve` command is also a big help. - -#### `netsuite rest-api get` - -List endpoint examples: - -``` -$ netsuite rest-api get /record/v1/customer -``` - -``` -$ netsuite rest-api get /record/v1/invoice --limit 10 --offset 30 -``` - -``` -$ netsuite rest-api get /record/v1/salesOrder --query 'email IS "john.doe@example.com"' -``` - -Detail endpoint examples: - -``` -$ netsuite rest-api get /record/v1/salesOrder/1337 -``` - -``` -$ netsuite rest-api get /record/v1/invoice/123 --expandSubResources -``` - -#### `netsuite rest-api post` - -Examples: -``` -$ cat ~/customer-no-1-data.json | netsuite rest-api post /record/v1/customer - -``` - -#### `netsuite rest-api put` - -Examples: -``` -$ cat ~/customer-no-1-data.json | netsuite rest-api put /record/v1/customer/123 - -``` - -#### `netsuite rest-api patch` - -Examples: -``` -$ cat ~/changed-customer-data.json | netsuite rest-api patch /record/v1/customer/123 - -``` - -#### `netsuite rest-api delete` - -Examples: -``` -$ netsuite rest-api delete /record/v1/customer/123 -``` - -#### `netsuite rest-api jsonschema` - -Examples: -``` -$ netsuite rest-api jsonschema salesOrder -{"type":"object","properties":... -``` - -#### `netsuite rest-api openapi` - -Examples: -``` -$ netsuite rest-api openapi salesOrder customer invoice -{"openapi":"3.0.1","info":{"title":"NetSuite REST Record API"... -``` - - -#### `netsuite rest-api openapi-serve` - -Start a server that fetches and lists the OpenAPI spec for the given record types, using [Swagger UI](https://swagger.io/tools/swagger-ui/). Defaults to port 8000. - -Examples: - -``` -$ netsuite rest-api openapi-serve customer salesOrder -INFO:netsuite:Fetching OpenAPI spec for record types customer, salesOrder... -INFO:netsuite:NetSuite REST API docs available at http://127.0.0.1:8001 -``` - -It's also possible to fetch the OpenAPI spec for all known record types. This will however take a long time (60+ seconds). -``` -$ netsuite rest-api openapi-serve -WARNING:netsuite:Fetching OpenAPI spec for ALL known record types... This will take a long time! (Consider providing only the record types of interest by passing their names to this command as positional arguments) -INFO:netsuite:NetSuite REST API docs available at http://127.0.0.1:8001 -``` - - -### `interact` - Interact with SOAP/REST web services and restlets - -Starts an IPython REPL where you can interact with the client. - -``` -$ netsuite interact -Welcome to Netsuite WS client interactive mode -Available vars: - `ns` - NetSuite client - -Example usage: - ws_results = ns.getList('customer', internalIds=[1337]) - restlet_results = ns.restlet.request(987) - rest_api_results = await ns.rest_api.get("/record/v1/salesOrder") - -In [1]: rest_api_results = await ns.rest_api.get(" -``` - - -### `restlet` - Make requests to restlets - -``` -$ echo '{"savedSearchId": 987}' | netsuite restlet 123 - -``` - +With CLI support: -## Developing + pip install netsuite[cli] -1. Fork repo in GitHub -1. Clone the forked repo: `git clone git@github.com:MYUSERNAME/netsuite.git` -1. Install package in editable mode, with all dependencies: `pip install -e '.[all,test,lint]'` -1. Ensure that pre-commit hooks are installed with: `pre-commit install` -1. Create new branch: `git branch -d my-shiny-pull-request-branch` -1. Code! -1. Run unit tests with: `tox` -1. Commit, push and submit pull request +With all features: + pip install netsuite[all] -## Creating a new git / PyPI release (maintainers) +## Documentation -1. Tag the release in git: `git tag -a "X.Y.Z" -m "Version X.Y.Z"` -1. Push the tag upstream: `git push --tags` -1. Build the release: `rm -rf build dist; python3 setup.py sdist bdist_wheel` -1. Push the release: `twine upload dist/*` +Is found here: https://jmagnusson.github.io/netsuite/ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..db97645 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,178 @@ +# netsuite + +Make requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets + +## Installation + +Programmatic use only: + + pip install netsuite + +With NetSuite SuiteTalk REST Web Services API support: + + pip install netsuite[rest_api] + +With CLI support: + + pip install netsuite[cli] + +With all features: + + pip install netsuite[all] + +## CLI + +### Configuration + +To use the command line utilities you must add a config file with a section in this format: + +```ini +[netsuite] +auth_type = token +account = 123456 +consumer_key = 789123 +consumer_secret = 456789 +token_id = 012345 +token_secret = 678901 +``` + +You can add multiple sections like this. The `netsuite` section will be read by default, but can be overridden using the `-c` flag. + +The default location that will be read is `~/.config/netsuite.ini`. This can overriden with the `-p` flag. + +Append `--help` to the commands to see full documentation. + +### `rest-api` - Make requests to NetSuite REST API + +See the NetSuite help center for info on how to use the REST API. The `netsuite rest-api openapi-serve` command is also a big help. + +#### `netsuite rest-api get` + +List endpoint examples: + +``` +$ netsuite rest-api get /record/v1/customer +``` + +``` +$ netsuite rest-api get /record/v1/invoice --limit 10 --offset 30 +``` + +``` +$ netsuite rest-api get /record/v1/salesOrder --query 'email IS "john.doe@example.com"' +``` + +Detail endpoint examples: + +``` +$ netsuite rest-api get /record/v1/salesOrder/1337 +``` + +``` +$ netsuite rest-api get /record/v1/invoice/123 --expandSubResources +``` + +#### `netsuite rest-api post` + +Examples: +``` +$ cat ~/customer-no-1-data.json | netsuite rest-api post /record/v1/customer - +``` + +#### `netsuite rest-api put` + +Examples: +``` +$ cat ~/customer-no-1-data.json | netsuite rest-api put /record/v1/customer/123 - +``` + +#### `netsuite rest-api patch` + +Examples: +``` +$ cat ~/changed-customer-data.json | netsuite rest-api patch /record/v1/customer/123 - +``` + +#### `netsuite rest-api delete` + +Examples: +``` +$ netsuite rest-api delete /record/v1/customer/123 +``` + +#### `netsuite rest-api jsonschema` + +Examples: +``` +$ netsuite rest-api jsonschema salesOrder +{"type":"object","properties":... +``` + +#### `netsuite rest-api openapi` + +Examples: +``` +$ netsuite rest-api openapi salesOrder customer invoice +{"openapi":"3.0.1","info":{"title":"NetSuite REST Record API"... +``` + + +#### `netsuite rest-api openapi-serve` + +Start a server that fetches and lists the OpenAPI spec for the given record types, using [Swagger UI](https://swagger.io/tools/swagger-ui/). Defaults to port 8000. + +Examples: + +``` +$ netsuite rest-api openapi-serve customer salesOrder +INFO:netsuite:Fetching OpenAPI spec for record types customer, salesOrder... +INFO:netsuite:NetSuite REST API docs available at http://127.0.0.1:8001 +``` + +It's also possible to fetch the OpenAPI spec for all known record types. This will however take a long time (60+ seconds). +``` +$ netsuite rest-api openapi-serve +WARNING:netsuite:Fetching OpenAPI spec for ALL known record types... This will take a long time! (Consider providing only the record types of interest by passing their names to this command as positional arguments) +INFO:netsuite:NetSuite REST API docs available at http://127.0.0.1:8001 +``` + + +### `interact` - Interact with SOAP/REST web services and restlets + +Starts an IPython REPL where you can interact with the client. + +``` +$ netsuite interact +Welcome to Netsuite WS client interactive mode +Available vars: + `ns` - NetSuite client + +Example usage: + ws_results = ns.getList('customer', internalIds=[1337]) + restlet_results = ns.restlet.request(987) + rest_api_results = await ns.rest_api.get("/record/v1/salesOrder") + +In [1]: rest_api_results = await ns.rest_api.get(" +``` + + +### `restlet` - Make requests to restlets + +``` +$ echo '{"savedSearchId": 987}' | netsuite restlet 123 - +``` + +## Developers + +To run the tests, do: + +1. Install Poetry (https://python-poetry.org/docs/) +1. Install dependencies `poetry install --extras all` +1. Run tests: `poetry run pytest` + +Before committing and publishing a pull request, do: + +1. Install pre-commit globally: `pip install pre-commit` +1. Run `pre-commit install` to install the Git hook + +[pre-commit](https://pre-commit.com/) will ensure that all code is formatted per our conventions. Failing to run this will probably make the CI tests fail in the PR instead. diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..34f8950 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,3 @@ +site_name: netsuite +theme: + name: material diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..5e0b4f3 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1658 @@ +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "appnope" +version = "0.1.2" +description = "Disable App Nap on macOS >= 10.9" +category = "main" +optional = true +python-versions = "*" + +[[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 = "20.3.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + +[[package]] +name = "authlib" +version = "0.15.3" +description = "The ultimate Python library in building OAuth and OpenID Connect servers." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +cryptography = "*" + +[package.extras] +client = ["requests"] + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "main" +optional = true +python-versions = "*" + +[[package]] +name = "black" +version = "20.8b1" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +appdirs = "*" +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.6,<1" +regex = ">=2020.1.8" +toml = ">=0.10.1" +typed-ast = ">=1.4.0" +typing-extensions = ">=3.7.4" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] + +[[package]] +name = "cached-property" +version = "1.5.2" +description = "A decorator for caching properties in classes." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "certifi" +version = "2020.12.5" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "cffi" +version = "1.14.5" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "chardet" +version = "4.0.0" +description = "Universal encoding detector for Python 2 and 3" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "coverage" +version = "5.5" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +toml = ["toml"] + +[[package]] +name = "cryptography" +version = "3.4.7" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = true +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 = ["doc8", "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.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] + +[[package]] +name = "decorator" +version = "5.0.7" +description = "Decorators for Humans" +category = "main" +optional = true +python-versions = ">=3.5" + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "flake8" +version = "3.9.1" +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 = "future" +version = "0.18.2" +description = "Clean single-source support for Python 3 and 2" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "h11" +version = "0.12.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = true +python-versions = ">=3.6" + +[[package]] +name = "httpcore" +version = "0.12.3" +description = "A minimal low-level HTTP client." +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +h11 = "<1.0.0" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] + +[[package]] +name = "httpx" +version = "0.17.1" +description = "The next generation HTTP client." +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +certifi = "*" +httpcore = ">=0.12.1,<0.13" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotlipy (>=0.7.0,<0.8.0)"] +http2 = ["h2 (>=3.0.0,<4.0.0)"] + +[[package]] +name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "importlib-metadata" +version = "4.0.1" +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)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "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 = "ipython" +version = "7.22.0" +description = "IPython: Productive Interactive Computing" +category = "main" +optional = true +python-versions = ">=3.7" + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" +traitlets = ">=4.2" + +[package.extras] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.16)", "pygments", "qtconsole", "requests", "testpath"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["notebook", "ipywidgets"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.16)"] + +[[package]] +name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "main" +optional = true +python-versions = "*" + +[[package]] +name = "isodate" +version = "0.6.0" +description = "An ISO 8601 date/time/duration parser and formatter" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" + +[[package]] +name = "isort" +version = "5.8.0" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.extras] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] + +[[package]] +name = "jedi" +version = "0.18.0" +description = "An autocompletion tool for Python that can be used for text editors." +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +parso = ">=0.8.0,<0.9.0" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] + +[[package]] +name = "jinja2" +version = "2.11.3" +description = "A very fast and expressive template engine." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +MarkupSafe = ">=0.23" + +[package.extras] +i18n = ["Babel (>=0.8)"] + +[[package]] +name = "joblib" +version = "1.0.1" +description = "Lightweight pipelining with Python functions" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} + +[[package]] +name = "lunr" +version = "0.5.8" +description = "A Python implementation of Lunr.js" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +future = ">=0.16.0" +nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} +six = ">=1.11.0" + +[package.extras] +languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] + +[[package]] +name = "lxml" +version = "4.6.3" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["beautifulsoup4"] +source = ["Cython (>=0.29.7)"] + +[[package]] +name = "markdown" +version = "3.3.4" +description = "Python implementation of Markdown." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "1.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mkdocs" +version = "1.1.2" +description = "Project documentation with Markdown." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +click = ">=3.3" +Jinja2 = ">=2.10.1" +livereload = ">=2.5.1" +lunr = {version = "0.5.8", extras = ["languages"]} +Markdown = ">=3.2.1" +PyYAML = ">=3.10" +tornado = ">=5.0" + +[[package]] +name = "mkdocs-material" +version = "7.1.3" +description = "A Material Design theme for MkDocs" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +markdown = ">=3.2" +mkdocs = ">=1.1" +mkdocs-material-extensions = ">=1.0" +Pygments = ">=2.4" +pymdown-extensions = ">=7.0" + +[[package]] +name = "mkdocs-material-extensions" +version = "1.0.1" +description = "Extension pack for Python Markdown." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +mkdocs-material = ">=5.0.0" + +[[package]] +name = "mypy" +version = "0.812" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +mypy-extensions = ">=0.4.3,<0.5.0" +typed-ast = ">=1.4.0,<1.5.0" +typing-extensions = ">=3.7.4" + +[package.extras] +dmypy = ["psutil (>=4.0)"] + +[[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 = "nltk" +version = "3.6.2" +description = "Natural Language Toolkit" +category = "dev" +optional = false +python-versions = ">=3.5.*" + +[package.dependencies] +click = "*" +joblib = "*" +regex = "*" +tqdm = "*" + +[package.extras] +all = ["matplotlib", "twython", "scipy", "numpy", "gensim (<4.0.0)", "python-crfsuite", "pyparsing", "scikit-learn", "requests"] +corenlp = ["requests"] +machine_learning = ["gensim (<4.0.0)", "numpy", "python-crfsuite", "scikit-learn", "scipy"] +plot = ["matplotlib"] +tgrep = ["pyparsing"] +twitter = ["twython"] + +[[package]] +name = "oauthlib" +version = "3.1.0" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +rsa = ["cryptography"] +signals = ["blinker"] +signedtoken = ["cryptography", "pyjwt (>=1.0.0)"] + +[[package]] +name = "orjson" +version = "3.5.2" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "packaging" +version = "20.9" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pyparsing = ">=2.0.2" + +[[package]] +name = "parso" +version = "0.8.2" +description = "A Python Parser" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pathspec" +version = "0.8.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "main" +optional = true +python-versions = "*" + +[[package]] +name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.18" +description = "Library for building powerful interactive command lines in Python" +category = "main" +optional = true +python-versions = ">=3.6.1" + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +category = "main" +optional = true +python-versions = "*" + +[[package]] +name = "py" +version = "1.10.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.*" + +[[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.20" +description = "C parser in Python" +category = "main" +optional = true +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.8.1" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "pymdown-extensions" +version = "8.1.1" +description = "Extension pack for Python Markdown." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +Markdown = ">=3.2" + +[[package]] +name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "pytest" +version = "6.2.3" +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,<1.0.0a1" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "2.11.1" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +coverage = ">=5.2.1" +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytz" +version = "2021.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyyaml" +version = "5.4.1" +description = "YAML parser and emitter for Python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[[package]] +name = "regex" +version = "2021.4.4" +description = "Alternative regular expression module, to replace re." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "requests" +version = "2.25.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<5" +idna = ">=2.5,<3" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] + +[[package]] +name = "requests-file" +version = "1.5.1" +description = "File transport adapter for Requests" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +requests = ">=1.0.0" +six = "*" + +[[package]] +name = "requests-oauthlib" +version = "1.3.0" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "requests-toolbelt" +version = "0.9.1" +description = "A utility belt for advanced users of python-requests" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rfc3986" +version = "1.4.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "six" +version = "1.15.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "sniffio" +version = "1.2.0" +description = "Sniff out which async library your code is running under" +category = "main" +optional = true +python-versions = ">=3.5" + +[[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 = "tornado" +version = "6.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "dev" +optional = false +python-versions = ">= 3.5" + +[[package]] +name = "tqdm" +version = "4.60.0" +description = "Fast, Extensible Progress Meter" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +notebook = ["ipywidgets (>=6)"] +telegram = ["requests"] + +[[package]] +name = "traitlets" +version = "5.0.5" +description = "Traitlets Python configuration system" +category = "main" +optional = true +python-versions = ">=3.7" + +[package.dependencies] +ipython-genutils = "*" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "typed-ast" +version = "1.4.3" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "typing-extensions" +version = "3.7.4.3" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "urllib3" +version = "1.26.4" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[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)"] +brotli = ["brotlipy (>=0.6.0)"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = true +python-versions = "*" + +[[package]] +name = "zeep" +version = "4.0.0" +description = "A modern/fast Python SOAP client based on lxml / requests" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +appdirs = ">=1.4.0" +attrs = ">=17.2.0" +cached-property = ">=1.3.0" +defusedxml = ">=0.4.1" +isodate = ">=0.5.4" +lxml = ">=3.1.0" +pytz = "*" +requests = ">=2.7.0" +requests-file = ">=1.5.1" +requests-toolbelt = ">=0.7.1" + +[package.extras] +async = ["httpx"] +docs = ["sphinx (>=1.4.0)"] +test = ["coverage[toml] (==5.2.1)", "freezegun (==0.3.15)", "mock (==2.0.0)", "pretend (==1.0.9)", "pytest-cov (==2.8.1)", "pytest-httpx", "pytest-asyncio", "pytest (==6.0.1)", "requests-mock (>=0.7.0)", "isort (==5.3.2)", "flake8 (==3.8.3)", "flake8-blind-except (==0.1.1)", "flake8-debugger (==3.2.1)", "flake8-imports (==0.1.1)"] +xmlsec = ["xmlsec (>=0.6.1)"] + +[[package]] +name = "zipp" +version = "3.4.1" +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 (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[extras] +all = ["authlib", "httpx", "ipython"] +cli = ["ipython"] +rest_api = ["authlib", "httpx"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.7" +content-hash = "f33036b6bf4785592adb3bd4236df55107bbb918074132dd99cf7835dbb34769" + +[metadata.files] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] +appnope = [ + {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, + {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, +] +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-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, +] +authlib = [ + {file = "Authlib-0.15.3-py2.py3-none-any.whl", hash = "sha256:0f6af3a38d37dd77361808dd3f2e258b647668dac6d2cefcefc4c4ebc3c7d2b2"}, + {file = "Authlib-0.15.3.tar.gz", hash = "sha256:7dde11ba45db51e97169c261362fab3193073100b7387e60c159db1eec470bbc"}, +] +backcall = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] +black = [ + {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, +] +cached-property = [ + {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, + {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, +] +certifi = [ + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, +] +cffi = [ + {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"}, + {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"}, + {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"}, + {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"}, + {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"}, + {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"}, + {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"}, + {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"}, + {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"}, + {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"}, + {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"}, + {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"}, + {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"}, + {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"}, + {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"}, + {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"}, + {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"}, + {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"}, + {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"}, + {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"}, + {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"}, + {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"}, + {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"}, + {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"}, + {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, +] +chardet = [ + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, +] +click = [ + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, + {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, +] +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-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, + {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, + {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, + {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, + {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, + {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, + {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, + {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, + {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, + {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, + {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, + {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, + {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, + {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, + {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, + {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, + {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, + {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, + {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, + {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, + {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, + {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, + {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, +] +cryptography = [ + {file = "cryptography-3.4.7-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1"}, + {file = "cryptography-3.4.7-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250"}, + {file = "cryptography-3.4.7-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2"}, + {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6"}, + {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959"}, + {file = "cryptography-3.4.7-cp36-abi3-win32.whl", hash = "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d"}, + {file = "cryptography-3.4.7-cp36-abi3-win_amd64.whl", hash = "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca"}, + {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873"}, + {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d"}, + {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177"}, + {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"}, + {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"}, +] +decorator = [ + {file = "decorator-5.0.7-py3-none-any.whl", hash = "sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"}, + {file = "decorator-5.0.7.tar.gz", hash = "sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060"}, +] +defusedxml = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] +flake8 = [ + {file = "flake8-3.9.1-py2.py3-none-any.whl", hash = "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a"}, + {file = "flake8-3.9.1.tar.gz", hash = "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378"}, +] +future = [ + {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +] +h11 = [ + {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, + {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, +] +httpcore = [ + {file = "httpcore-0.12.3-py3-none-any.whl", hash = "sha256:93e822cd16c32016b414b789aeff4e855d0ccbfc51df563ee34d4dbadbb3bcdc"}, + {file = "httpcore-0.12.3.tar.gz", hash = "sha256:37ae835fb370049b2030c3290e12ed298bf1473c41bb72ca4aa78681eba9b7c9"}, +] +httpx = [ + {file = "httpx-0.17.1-py3-none-any.whl", hash = "sha256:d379653bd457e8257eb0df99cb94557e4aac441b7ba948e333be969298cac272"}, + {file = "httpx-0.17.1.tar.gz", hash = "sha256:cc2a55188e4b25272d2bcd46379d300f632045de4377682aa98a8a6069d55967"}, +] +idna = [ + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, + {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, + {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +ipython = [ + {file = "ipython-7.22.0-py3-none-any.whl", hash = "sha256:c0ce02dfaa5f854809ab7413c601c4543846d9da81010258ecdab299b542d199"}, + {file = "ipython-7.22.0.tar.gz", hash = "sha256:9c900332d4c5a6de534b4befeeb7de44ad0cc42e8327fa41b7685abde58cec74"}, +] +ipython-genutils = [ + {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, + {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, +] +isodate = [ + {file = "isodate-0.6.0-py2.py3-none-any.whl", hash = "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81"}, + {file = "isodate-0.6.0.tar.gz", hash = "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8"}, +] +isort = [ + {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, + {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, +] +jedi = [ + {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, + {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, +] +jinja2 = [ + {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, + {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, +] +joblib = [ + {file = "joblib-1.0.1-py3-none-any.whl", hash = "sha256:feeb1ec69c4d45129954f1b7034954241eedfd6ba39b5e9e4b6883be3332d5e5"}, + {file = "joblib-1.0.1.tar.gz", hash = "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7"}, +] +livereload = [ + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, +] +lunr = [ + {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, + {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, +] +lxml = [ + {file = "lxml-4.6.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2"}, + {file = "lxml-4.6.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f"}, + {file = "lxml-4.6.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d"}, + {file = "lxml-4.6.3-cp27-cp27m-win32.whl", hash = "sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106"}, + {file = "lxml-4.6.3-cp27-cp27m-win_amd64.whl", hash = "sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee"}, + {file = "lxml-4.6.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f"}, + {file = "lxml-4.6.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4"}, + {file = "lxml-4.6.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51"}, + {file = "lxml-4.6.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586"}, + {file = "lxml-4.6.3-cp35-cp35m-win32.whl", hash = "sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2"}, + {file = "lxml-4.6.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4"}, + {file = "lxml-4.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4"}, + {file = "lxml-4.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3"}, + {file = "lxml-4.6.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d"}, + {file = "lxml-4.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec"}, + {file = "lxml-4.6.3-cp36-cp36m-win32.whl", hash = "sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04"}, + {file = "lxml-4.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a"}, + {file = "lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654"}, + {file = "lxml-4.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0"}, + {file = "lxml-4.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3"}, + {file = "lxml-4.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2"}, + {file = "lxml-4.6.3-cp37-cp37m-win32.whl", hash = "sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade"}, + {file = "lxml-4.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b"}, + {file = "lxml-4.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa"}, + {file = "lxml-4.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a"}, + {file = "lxml-4.6.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927"}, + {file = "lxml-4.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791"}, + {file = "lxml-4.6.3-cp38-cp38-win32.whl", hash = "sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28"}, + {file = "lxml-4.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7"}, + {file = "lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0"}, + {file = "lxml-4.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1"}, + {file = "lxml-4.6.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23"}, + {file = "lxml-4.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969"}, + {file = "lxml-4.6.3-cp39-cp39-win32.whl", hash = "sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f"}, + {file = "lxml-4.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83"}, + {file = "lxml-4.6.3.tar.gz", hash = "sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468"}, +] +markdown = [ + {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, + {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, +] +markupsafe = [ + {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, + {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mkdocs = [ + {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, + {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, +] +mkdocs-material = [ + {file = "mkdocs-material-7.1.3.tar.gz", hash = "sha256:e34bba93ad1a0e6f9afc371f4ef55bedabbf13b9a786b013b0ce26ac55ec2932"}, + {file = "mkdocs_material-7.1.3-py2.py3-none-any.whl", hash = "sha256:437638b0de7a9113d7f1c9ddc93c0a29a3b808c71c3606713d8c1fa437697a3e"}, +] +mkdocs-material-extensions = [ + {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, + {file = "mkdocs_material_extensions-1.0.1-py3-none-any.whl", hash = "sha256:d90c807a88348aa6d1805657ec5c0b2d8d609c110e62b9dce4daf7fa981fa338"}, +] +mypy = [ + {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"}, + {file = "mypy-0.812-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c"}, + {file = "mypy-0.812-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521"}, + {file = "mypy-0.812-cp35-cp35m-win_amd64.whl", hash = "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb"}, + {file = "mypy-0.812-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a"}, + {file = "mypy-0.812-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c"}, + {file = "mypy-0.812-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6"}, + {file = "mypy-0.812-cp36-cp36m-win_amd64.whl", hash = "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064"}, + {file = "mypy-0.812-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56"}, + {file = "mypy-0.812-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8"}, + {file = "mypy-0.812-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7"}, + {file = "mypy-0.812-cp37-cp37m-win_amd64.whl", hash = "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564"}, + {file = "mypy-0.812-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506"}, + {file = "mypy-0.812-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5"}, + {file = "mypy-0.812-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66"}, + {file = "mypy-0.812-cp38-cp38-win_amd64.whl", hash = "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e"}, + {file = "mypy-0.812-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a"}, + {file = "mypy-0.812-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a"}, + {file = "mypy-0.812-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97"}, + {file = "mypy-0.812-cp39-cp39-win_amd64.whl", hash = "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df"}, + {file = "mypy-0.812-py3-none-any.whl", hash = "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4"}, + {file = "mypy-0.812.tar.gz", hash = "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119"}, +] +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"}, +] +nltk = [ + {file = "nltk-3.6.2-py3-none-any.whl", hash = "sha256:240e23ab1ab159ef9940777d30c7c72d7e76d91877099218a7585370c11f6b9e"}, + {file = "nltk-3.6.2.zip", hash = "sha256:57d556abed621ab9be225cc6d2df1edce17572efb67a3d754630c9f8381503eb"}, +] +oauthlib = [ + {file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"}, + {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"}, +] +orjson = [ + {file = "orjson-3.5.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:2ba4165883fbef0985bce60bddbf91bc5cea77cc22b1c12fe7a716c6323ab1e7"}, + {file = "orjson-3.5.2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:cee746d186ba9efa47b9d52a649ee0617456a9a4d7a2cbd3ec06330bb9cb372a"}, + {file = "orjson-3.5.2-cp36-cp36m-macosx_10_7_x86_64.whl", hash = "sha256:8591a25a31a89cf2a33e30eb516ab028bad2c72fed04e323917114aaedc07c7d"}, + {file = "orjson-3.5.2-cp36-cp36m-macosx_10_9_universal2.whl", hash = "sha256:38cb8cdbf43eafc6dcbfb10a9e63c80727bb916aee0f75caf5f90e5355b266e1"}, + {file = "orjson-3.5.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:96b403796fc7e44bae843a2a83923925fe048f3a67c10a298fdfc0ff46163c14"}, + {file = "orjson-3.5.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:5b66a62d4c0c44441b23fafcd3d0892296d9793361b14bcc5a5645c88b6a4a71"}, + {file = "orjson-3.5.2-cp36-none-win_amd64.whl", hash = "sha256:609e93919268fadb871aafb7f550c3fe8d3e8c1305cadcc1610b414113b7034e"}, + {file = "orjson-3.5.2-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:200bd4491052d13696456a92d23f086b68b526c2464248733964e8165ac60888"}, + {file = "orjson-3.5.2-cp37-cp37m-macosx_10_9_universal2.whl", hash = "sha256:cc614bf6bfe0181e51dd98a9c53669f08d4d8641efbf1a287113da3059773dea"}, + {file = "orjson-3.5.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:43576bed3be300e9c02629a8d5fb3340fe6474765e6eee9610067def4b3ac19c"}, + {file = "orjson-3.5.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:acd735718b531b78858a7e932c58424c5a3e39e04d61bba3d95ce8a8498ea9e9"}, + {file = "orjson-3.5.2-cp37-none-win_amd64.whl", hash = "sha256:7503145ffd1ae90d487860b97e2867ec61c2c8f001209bb12700ba7833df8ddf"}, + {file = "orjson-3.5.2-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:9c37cf3dbc9c81abed04ba4854454e9f0d8ac7c05fb6c4f36545733e90be6af2"}, + {file = "orjson-3.5.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e6ef00ddc637b7d13926aaccdabac363efdfd348c132410eb054c27e2eae6a7"}, + {file = "orjson-3.5.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:9d0834ca40c6e467fa1f1db3f83a8c3562c03eb2b7067ad09de5019592edb88f"}, + {file = "orjson-3.5.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:d4a2ddc6342a8280dafaa69827b387b95856ef0a6c5812fe91f5bd21ddd2ef36"}, + {file = "orjson-3.5.2-cp38-none-win_amd64.whl", hash = "sha256:f54f8bcf24812a524e8904a80a365f7a287d82fc6ebdee528149616070abe5ab"}, + {file = "orjson-3.5.2-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:8b429471398ea37d848fb53bca6a8c42fb776c278f4fcb6a1d651b8f1fb64947"}, + {file = "orjson-3.5.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13fd458110fbe019c2a67ee539678189444f73bc09b27983c9b42663c63e0445"}, + {file = "orjson-3.5.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8bf1145a06e1245f0c8a8c32df6ffe52d214eb4eb88c3fb32e4ed14e3dc38e0e"}, + {file = "orjson-3.5.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:7e3434010e3f0680e92bb0a6094e4d5c939d0c4258c76397c6bd5263c7d62e86"}, + {file = "orjson-3.5.2-cp39-none-win_amd64.whl", hash = "sha256:df9730cc8cd22b3f54aa55317257f3279e6300157fc0f4ed4424586cd7eb012d"}, + {file = "orjson-3.5.2.tar.gz", hash = "sha256:f385253a6ddac37ea422ec2c0d35772b4f5bf0dc0803ce44543bf7e530423ef8"}, +] +packaging = [ + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, +] +parso = [ + {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, + {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, +] +pathspec = [ + {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, + {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, +] +pexpect = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +pickleshare = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.18-py3-none-any.whl", hash = "sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04"}, + {file = "prompt_toolkit-3.0.18.tar.gz", hash = "sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"}, +] +ptyprocess = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] +py = [ + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, +] +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.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] +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.8.1-py3-none-any.whl", hash = "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"}, + {file = "Pygments-2.8.1.tar.gz", hash = "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94"}, +] +pymdown-extensions = [ + {file = "pymdown-extensions-8.1.1.tar.gz", hash = "sha256:632371fa3bf1b21a0e3f4063010da59b41db049f261f4c0b0872069a9b6d1735"}, + {file = "pymdown_extensions-8.1.1-py3-none-any.whl", hash = "sha256:478b2c04513fbb2db61688d5f6e9030a92fb9be14f1f383535c43f7be9dff95b"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pytest = [ + {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, + {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, +] +pytest-cov = [ + {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, + {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, +] +pytz = [ + {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, + {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, +] +pyyaml = [ + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, +] +regex = [ + {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"}, + {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"}, + {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"}, + {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"}, + {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"}, + {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"}, + {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"}, + {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"}, + {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"}, + {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"}, + {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"}, + {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"}, + {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, +] +requests = [ + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, +] +requests-file = [ + {file = "requests-file-1.5.1.tar.gz", hash = "sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e"}, + {file = "requests_file-1.5.1-py2.py3-none-any.whl", hash = "sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"}, +] +requests-oauthlib = [ + {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"}, + {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"}, + {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"}, +] +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.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"}, + {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"}, +] +six = [ + {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, +] +sniffio = [ + {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, + {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +tornado = [ + {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, + {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, + {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, + {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, + {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, + {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, + {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, + {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, + {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, + {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, + {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, + {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, + {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, + {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, + {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, + {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, + {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, + {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, +] +tqdm = [ + {file = "tqdm-4.60.0-py2.py3-none-any.whl", hash = "sha256:daec693491c52e9498632dfbe9ccfc4882a557f5fa08982db1b4d3adbe0887c3"}, + {file = "tqdm-4.60.0.tar.gz", hash = "sha256:ebdebdb95e3477ceea267decfc0784859aa3df3e27e22d23b83e9b272bf157ae"}, +] +traitlets = [ + {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"}, + {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, +] +typed-ast = [ + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, + {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, + {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, + {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, + {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, + {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, + {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, + {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, + {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, + {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, +] +typing-extensions = [ + {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, + {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, + {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, +] +urllib3 = [ + {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, + {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +zeep = [ + {file = "zeep-4.0.0-py2.py3-none-any.whl", hash = "sha256:12945da854ff10376d384d21a132b66e318784ef05c696ae300a130746f4baa8"}, + {file = "zeep-4.0.0.tar.gz", hash = "sha256:98158e43db33739d41502a1a7e3629dcb62dfd0864ea28c9d43f560a091cfe3f"}, +] +zipp = [ + {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, + {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, +] diff --git a/pyproject.toml b/pyproject.toml index 89f8ef5..9706f69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,65 @@ +[tool.poetry] +name = "netsuite" +version = "0.6.0" +description = "Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets." +authors = ["Jacob Magnusson "] +license = "MIT" +readme = "README.md" +homepage = "https://jmagnusson.github.io/netsuite/" +repository = "https://github.com/jmagnusson/netsuite" +documentation = "https://jmagnusson.github.io/netsuite/" +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", +] + +[tool.poetry.dependencies] +python = "^3.7" +requests-oauthlib = "^1.3" +zeep = "^4" +orjson = "^3" +authlib = {version = "^0.15.3", optional = true} +# As per httpx recommendation we will lock to a fixed minor version until 1.0 is released +httpx = {version = "~0.17", optional = true} +ipython = {version = "^7", optional = true} + +[tool.poetry.extras] +rest_api = ["authlib", "httpx"] +cli = ["ipython"] +all = ["authlib", "httpx", "ipython"] + +[tool.poetry.dev-dependencies] +black = "^20.8b1" +flake8 = "^3.9" +isort = "^5.8" +mkdocs-material = "^7" +mypy = "^0.812" +pytest = "^6" +pytest-cov = "^2.11" + +[tool.poetry.scripts] +netsuite = 'netsuite.__main__:main' + [build-system] -requires = ["setuptools", "wheel"] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" [tool.black] line-length = 88 -target-version = ['py36', 'py37', 'py38'] +target-version = ["py37", "py38"] + +[tool.isort] +profile = "black" +multi_line_output = 3 + +[tool.pytest.ini_options] +markers = [ + "slow: marks tests as slow (deselect with '-m \"not slow\"')", +] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 2a9acf1..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -universal = 1 diff --git a/setup.py b/setup.py deleted file mode 100755 index 37045e1..0000000 --- a/setup.py +++ /dev/null @@ -1,51 +0,0 @@ -from setuptools import setup - -extras_require = { - "rest_api": [ - "authlib", - # NOTE: authlib doesn't work with httpx 0.12.1 So we're locking the httpx - # version to one that is compatible. See: - # https://github.com/lepture/authlib/issues/210#issuecomment-612311003 - # TODO: Remove version lock once fixed upstream. - "httpx==0.12.0", - ], - "cli": ["ipython"], - "test": ["pytest>=3.0.3", "coverage>=4.2",], - "lint": ["pre-commit>=1.21.0", "flake8>=3.0.4", "black>=19.10b0", "isort>=4.3.21",], -} -extras_require["all"] = [ - dep - for name, lst in extras_require.items() - if name not in ("test", "lint") - for dep in lst -] - -setup_kwargs = dict( - name="netsuite", - version="0.5.3", - description="Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets", - packages=["netsuite"], - include_package_data=True, - author="Jacob Magnusson", - author_email="m@jacobian.se", - url="https://github.com/jmagnusson/netsuite", - license="BSD", - platforms="any", - install_requires=["requests-oauthlib", "zeep", "orjson",], - extras_require=extras_require, - entry_points={"console_scripts": ["netsuite = netsuite.__main__:main",],}, - classifiers=[ - "Development Status :: 3 - Alpha", - "Environment :: Web Environment", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - ], -) - -if __name__ == "__main__": - setup(**setup_kwargs) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index b83a740..0000000 --- a/tox.ini +++ /dev/null @@ -1,22 +0,0 @@ -# Tox (http://tox.testrun.org/) is a tool for running tests -# in multiple virtualenvs. This configuration file will run the -# test suite on all supported python versions. To use it, "pip install tox" -# and then run "tox" from this directory. - -[tox] -envlist = py36, py37, py38, lint - -[testenv] -commands = - coverage run --source=netsuite -m py.test -deps = - .[test,all] - -[testenv:lint] -skip_install = true -commands = - flake8 . - isort --check -rc . - black --check --diff . -deps = - .[lint] From 101bbaade137aec11da9fc868d070a91fdd9cd50 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sun, 25 Apr 2021 12:06:28 +0200 Subject: [PATCH 12/47] Lint fixes --- .flake8 | 4 +-- netsuite/__main__.py | 15 +++++++---- netsuite/client.py | 58 ++++++++++++++++++++++++++++++++----------- netsuite/config.py | 45 ++++++++++++++++++++++++++------- netsuite/constants.py | 3 ++- netsuite/passport.py | 24 ++++++++++++++---- netsuite/rest_api.py | 6 ++++- netsuite/restlet.py | 5 +++- netsuite/util.py | 2 +- 9 files changed, 122 insertions(+), 40 deletions(-) diff --git a/.flake8 b/.flake8 index 9776575..9410c79 100644 --- a/.flake8 +++ b/.flake8 @@ -1,3 +1,3 @@ [flake8] -ignore = E501 - +max-line-length = 88 +extend-ignore = E203,E501 diff --git a/netsuite/__main__.py b/netsuite/__main__.py index 9201672..4689eea 100644 --- a/netsuite/__main__.py +++ b/netsuite/__main__.py @@ -222,7 +222,8 @@ async def rest_api_openapi_serve(config, args) -> str: """ html_file.write_text(html) handler_class = functools.partial( - http.server.SimpleHTTPRequestHandler, directory=str(tempdir), + http.server.SimpleHTTPRequestHandler, + directory=str(tempdir), ) logger.info( f"NetSuite REST Record API docs available at http://{args.bind}:{args.port}" @@ -310,7 +311,8 @@ def _get_rest_api_or_error(config: config.Config): ) rest_api_get_parser.set_defaults(func=rest_api_get) rest_api_get_parser.add_argument( - "subpath", help="The subpath to GET, e.g. `/record/v1/salesOrder`", + "subpath", + help="The subpath to GET, e.g. `/record/v1/salesOrder`", ) rest_api_get_parser.add_argument( "-q", @@ -344,7 +346,8 @@ def _get_rest_api_or_error(config: config.Config): ) rest_api_post_parser.set_defaults(func=rest_api_post) rest_api_post_parser.add_argument( - "subpath", help="The subpath to POST to, e.g. `/record/v1/salesOrder`", + "subpath", + help="The subpath to POST to, e.g. `/record/v1/salesOrder`", ) rest_api_post_parser.add_argument("payload_file", type=argparse.FileType("r")) @@ -353,7 +356,8 @@ def _get_rest_api_or_error(config: config.Config): ) rest_api_put_parser.set_defaults(func=rest_api_put) rest_api_put_parser.add_argument( - "subpath", help="The subpath to PUT to, e.g. `/record/v1/salesOrder/eid:abc123`", + "subpath", + help="The subpath to PUT to, e.g. `/record/v1/salesOrder/eid:abc123`", ) rest_api_put_parser.add_argument("payload_file", type=argparse.FileType("r")) @@ -362,7 +366,8 @@ def _get_rest_api_or_error(config: config.Config): ) rest_api_patch_parser.set_defaults(func=rest_api_patch) rest_api_patch_parser.add_argument( - "subpath", help="The subpath to PATCH to, e.g. `/record/v1/salesOrder/eid:abc123`", + "subpath", + help="The subpath to PATCH to, e.g. `/record/v1/salesOrder/eid:abc123`", ) rest_api_patch_parser.add_argument("payload_file", type=argparse.FileType("r")) diff --git a/netsuite/client.py b/netsuite/client.py index 8818360..c9393c1 100755 --- a/netsuite/client.py +++ b/netsuite/client.py @@ -27,7 +27,10 @@ class NetsuiteResponseError(Exception): def WebServiceCall( - path: str = None, extract: Callable = None, *, default: Any = constants.NOT_SET, + path: str = None, + extract: Callable = None, + *, + default: Any = constants.NOT_SET, ) -> Callable: """ Decorator for NetSuite methods returning SOAP responses @@ -241,7 +244,9 @@ def _generate_session(self) -> requests.Session: def _generate_transport(self) -> zeep.transports.Transport: return NetSuiteTransport( - self._generate_wsdl_url(), session=self.session, cache=self.cache, + self._generate_wsdl_url(), + session=self.session, + cache=self.cache, ) def generate_passport(self) -> Dict: @@ -274,9 +279,13 @@ def _set_default_soapheaders(client: zeep.Client, preferences: dict = None) -> N ) def _generate_client(self) -> zeep.Client: - client = zeep.Client(self.wsdl_url, transport=self.transport,) + client = zeep.Client( + self.wsdl_url, + transport=self.transport, + ) self._set_default_soapheaders( - client, preferences=self.config.preferences, + client, + preferences=self.config.preferences, ) return client @@ -507,18 +516,25 @@ def getList( "getList", self.Messages.GetListRequest( baseRef=[ - self.Core.RecordRef(type=recordType, internalId=internalId,) + self.Core.RecordRef( + type=recordType, + internalId=internalId, + ) for internalId in internalIds ] + [ - self.Core.RecordRef(type=recordType, externalId=externalId,) + self.Core.RecordRef( + type=recordType, + externalId=externalId, + ) for externalId in externalIds ], ), ) @WebServiceCall( - "body.readResponse", extract=lambda resp: resp["record"], + "body.readResponse", + extract=lambda resp: resp["record"], ) def get( self, recordType: str, *, internalId: int = None, externalId: str = None @@ -528,46 +544,58 @@ def get( raise ValueError("Specify either `internalId` or `externalId`") if internalId: - record_ref = self.Core.RecordRef(type=recordType, internalId=internalId,) + record_ref = self.Core.RecordRef( + type=recordType, + internalId=internalId, + ) else: self.Core.RecordRef( - type=recordType, externalId=externalId, + type=recordType, + externalId=externalId, ) return self.request("get", baseRef=record_ref) @WebServiceCall( - "body.getAllResult", extract=lambda resp: resp["recordList"]["record"], + "body.getAllResult", + extract=lambda resp: resp["recordList"]["record"], ) def getAll(self, recordType: str) -> List[CompoundValue]: """Get all records of a given type.""" return self.request( - "getAll", record=self.Core.GetAllRecord(recordType=recordType,), + "getAll", + record=self.Core.GetAllRecord( + recordType=recordType, + ), ) @WebServiceCall( - "body.writeResponse", extract=lambda resp: resp["baseRef"], + "body.writeResponse", + extract=lambda resp: resp["baseRef"], ) def add(self, record: CompoundValue) -> CompoundValue: """Insert a single record.""" return self.request("add", record=record) @WebServiceCall( - "body.writeResponse", extract=lambda resp: resp["baseRef"], + "body.writeResponse", + extract=lambda resp: resp["baseRef"], ) def update(self, record: CompoundValue) -> CompoundValue: """Insert a single record.""" return self.request("update", record=record) @WebServiceCall( - "body.writeResponse", extract=lambda resp: resp["baseRef"], + "body.writeResponse", + extract=lambda resp: resp["baseRef"], ) def upsert(self, record: CompoundValue) -> CompoundValue: """Upsert a single record.""" return self.request("upsert", record=record) @WebServiceCall( - "body.searchResult", extract=lambda resp: resp["recordList"]["record"], + "body.searchResult", + extract=lambda resp: resp["recordList"]["record"], ) def search(self, record: CompoundValue) -> List[CompoundValue]: """Search records""" diff --git a/netsuite/config.py b/netsuite/config.py index fd1191f..13d4c2f 100644 --- a/netsuite/config.py +++ b/netsuite/config.py @@ -48,15 +48,42 @@ class Config: """Additional preferences""" _settings_mapping = ( - ("account", {"type": str, "required": True},), - ("consumer_key", {"type": str, "required_for_auth_type": TOKEN},), - ("consumer_secret", {"type": str, "required_for_auth_type": TOKEN},), - ("token_id", {"type": str, "required_for_auth_type": TOKEN},), - ("token_secret", {"type": str, "required_for_auth_type": TOKEN},), - ("application_id", {"type": str, "required_for_auth_type": CREDENTIALS},), - ("email", {"type": str, "required_for_auth_type": CREDENTIALS},), - ("password", {"type": str, "required_for_auth_type": CREDENTIALS},), - ("preferences", {"type": dict, "required": False, "default": lambda: {}},), + ( + "account", + {"type": str, "required": True}, + ), + ( + "consumer_key", + {"type": str, "required_for_auth_type": TOKEN}, + ), + ( + "consumer_secret", + {"type": str, "required_for_auth_type": TOKEN}, + ), + ( + "token_id", + {"type": str, "required_for_auth_type": TOKEN}, + ), + ( + "token_secret", + {"type": str, "required_for_auth_type": TOKEN}, + ), + ( + "application_id", + {"type": str, "required_for_auth_type": CREDENTIALS}, + ), + ( + "email", + {"type": str, "required_for_auth_type": CREDENTIALS}, + ), + ( + "password", + {"type": str, "required_for_auth_type": CREDENTIALS}, + ), + ( + "preferences", + {"type": dict, "required": False, "default": lambda: {}}, + ), ) def __init__(self, **opts) -> None: diff --git a/netsuite/constants.py b/netsuite/constants.py index c58b2ac..99c79f8 100644 --- a/netsuite/constants.py +++ b/netsuite/constants.py @@ -2,6 +2,7 @@ NOT_SET: object = object() DEFAULT_INI_PATH: str = os.environ.get( - "NETSUITE_CONFIG", os.path.expanduser("~/.config/netsuite.ini"), + "NETSUITE_CONFIG", + os.path.expanduser("~/.config/netsuite.ini"), ) DEFAULT_INI_SECTION: str = "netsuite" diff --git a/netsuite/passport.py b/netsuite/passport.py index 973f281..4ae5218 100644 --- a/netsuite/passport.py +++ b/netsuite/passport.py @@ -27,7 +27,9 @@ def __init__( def get_element(self) -> CompoundValue: return self.ns.Core.Passport( - account=self.account, email=self.email, password=self.password, + account=self.account, + email=self.email, + password=self.password, ) @@ -63,7 +65,13 @@ def _generate_nonce(self, length: int = 20) -> str: def _get_signature_message(self, nonce: str, timestamp: str) -> str: return "&".join( - (self.account, self.consumer_key, self.token_id, nonce, timestamp,) + ( + self.account, + self.consumer_key, + self.token_id, + nonce, + timestamp, + ) ) def _get_signature_key(self) -> str: @@ -79,7 +87,8 @@ def _get_signature_value(self, nonce: str, timestamp: str) -> str: def _get_signature(self, nonce: str, timestamp: str) -> CompoundValue: return self.ns.Core.TokenPassportSignature( - self._get_signature_value(nonce, timestamp), algorithm="HMAC-SHA256", + self._get_signature_value(nonce, timestamp), + algorithm="HMAC-SHA256", ) def get_element(self) -> CompoundValue: @@ -109,10 +118,15 @@ def make(ns: NetSuite, config: Config) -> Dict: return {"tokenPassport": token_passport.get_element()} elif config.auth_type == "credentials": passport = UserCredentialsPassport( - ns, account=config.account, email=config.email, password=config.password, + ns, + account=config.account, + email=config.email, + password=config.password, ) return { - "applicationInfo": {"applicationId": config.application_id,}, + "applicationInfo": { + "applicationId": config.application_id, + }, "passport": passport.get_element(), } else: diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index 9b65050..4754f43 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -73,7 +73,11 @@ async def get(self, subpath: str, **request_kw) -> JsonDict: return await self.request("GET", subpath, **request_kw) async def post(self, subpath: str, **request_kw): - return await self.request("POST", subpath, **request_kw,) + return await self.request( + "POST", + subpath, + **request_kw, + ) async def put(self, subpath: str, **request_kw): return await self.request("PUT", subpath, **request_kw) diff --git a/netsuite/restlet.py b/netsuite/restlet.py index c83f32f..63ab8df 100644 --- a/netsuite/restlet.py +++ b/netsuite/restlet.py @@ -82,7 +82,10 @@ def _make_default_hostname(self): return f"{account_slugified}.restlets.api.netsuite.com" def _make_restlet_path(self, script_id: int, deploy: int = 1): - return self._restlet_path_tmpl.format(script_id=script_id, deploy=deploy,) + return self._restlet_path_tmpl.format( + script_id=script_id, + deploy=deploy, + ) def _make_url(self, script_id: int, deploy: int = 1): path = self._make_restlet_path(script_id=script_id, deploy=deploy) diff --git a/netsuite/util.py b/netsuite/util.py index 0fab06a..d15a2d1 100644 --- a/netsuite/util.py +++ b/netsuite/util.py @@ -4,7 +4,7 @@ class cached_property: - """ Decorator that turns an instance method into a cached property + """Decorator that turns an instance method into a cached property From https://speakerdeck.com/u/mitsuhiko/p/didntknow, slide #69 """ From 97e0e33e3148a8b977b580d52ccdf5a9ffe3abcc Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sun, 25 Apr 2021 13:39:11 +0200 Subject: [PATCH 13/47] mypy fixes --- mypy.ini | 3 +++ netsuite/__main__.py | 8 +++++--- netsuite/client.py | 43 ++++++++++++++++++++++++++++++------------- netsuite/config.py | 34 +++++++++++++++++----------------- netsuite/passport.py | 18 ++++++++++++------ netsuite/rest_api.py | 21 +++++++++------------ netsuite/types.py | 3 --- 7 files changed, 76 insertions(+), 54 deletions(-) create mode 100644 mypy.ini delete mode 100644 netsuite/types.py diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..51b77d7 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,3 @@ +[mypy] +show_error_codes = True +ignore_missing_imports = True diff --git a/netsuite/__main__.py b/netsuite/__main__.py index 4689eea..135e932 100644 --- a/netsuite/__main__.py +++ b/netsuite/__main__.py @@ -179,7 +179,7 @@ async def rest_api_openapi(config, args) -> str: return json.dumps_str(resp) -async def rest_api_openapi_serve(config, args) -> str: +async def rest_api_openapi_serve(config, args): rest_api = _get_rest_api_or_error(config) if len(args.record_types) == 0: logger.warning( @@ -241,9 +241,9 @@ async def rest_api_openapi_serve(config, args) -> str: tempdir.rmdir() -def _load_config_or_error(path: str, section: str) -> config.Config: +def _load_config_or_error(path: str, section: str) -> config.Config: # type: ignore[return] try: - return config.from_ini(path=path, section=section) + conf = config.from_ini(path=path, section=section) except FileNotFoundError: parser.error(f"Config file {path} not found") except KeyError as ex: @@ -251,6 +251,8 @@ def _load_config_or_error(path: str, section: str) -> config.Config: parser.error(f"No config section `{section}` in file {path}") else: raise ex + else: + return conf def _get_rest_api_or_error(config: config.Config): diff --git a/netsuite/client.py b/netsuite/client.py index c9393c1..f82205d 100755 --- a/netsuite/client.py +++ b/netsuite/client.py @@ -4,7 +4,7 @@ from contextlib import contextmanager from datetime import datetime from functools import wraps -from typing import Any, Callable, Dict, List, Sequence, Union +from typing import Any, Callable, Dict, List, Optional, Sequence, Union from urllib.parse import urlparse import requests @@ -141,7 +141,7 @@ def __repr__(self) -> str: def __init__( self, - config: Union[Config, Dict], + config: Union[Config, Dict[str, Any]], *, version: str = None, wsdl_url: str = None, @@ -216,7 +216,7 @@ def hostname(self) -> str: def service(self) -> zeep.client.ServiceProxy: return self.client.service - def _make_config(self, values_obj: Dict) -> Config: + def _make_config(self, values_obj: Union[Config, Dict[str, Any]]) -> Config: if isinstance(values_obj, Config): return values_obj return Config(**values_obj) @@ -233,7 +233,7 @@ def _generate_wsdl_url(self) -> str: return self.wsdl_url_tmpl.format( underscored_version=self.underscored_version, # https://followingnetsuite.wordpress.com/2018/10/18/suitetalk-sandbox-urls-addendum/ - account_id=self.config.account.lower().replace("_", "-"), + account_id=(self.config.account or "").lower().replace("_", "-"), ) def _generate_cache(self) -> zeep.cache.Base: @@ -483,7 +483,7 @@ def SupplyChain(self) -> zeep.client.Factory: def SupplyChainTypes(self) -> zeep.client.Factory: return self._type_factory("types.supplychain", "lists") - def request(self, service_name: str, *args, **kw) -> zeep.xsd.ComplexType: + def request(self, service_name: str, *args, **kw): """ Make a web service request to NetSuite @@ -504,13 +504,21 @@ def getList( self, recordType: str, *, - internalIds: Sequence[int] = (), - externalIds: Sequence[str] = (), + internalIds: Optional[Sequence[int]] = None, + externalIds: Optional[Sequence[str]] = None, ) -> List[CompoundValue]: """Get a list of records""" + if internalIds is None: + internalIds = [] + else: + internalIds = list(internalIds) + if externalIds is None: + externalIds = [] + else: + externalIds = list(externalIds) - if len(list(internalIds) + list(externalIds)) == 0: - raise ValueError("Please specify `internalId` and/or `externalId`") + if len(internalIds) + len(externalIds) == 0: + return [] return self.request( "getList", @@ -617,12 +625,21 @@ def upsertList(self, records: List[CompoundValue]) -> List[CompoundValue]: def getItemAvailability( self, *, - internalIds: Sequence[int] = (), - externalIds: Sequence[str] = (), + internalIds: Optional[Sequence[int]] = None, + externalIds: Optional[Sequence[str]] = None, lastQtyAvailableChange: datetime = None, ) -> List[Dict]: - if len(list(internalIds) + list(externalIds)) == 0: - raise ValueError("Please specify `internalId` and/or `externalId`") + if internalIds is None: + internalIds = [] + else: + internalIds = list(internalIds) + if externalIds is None: + externalIds = [] + else: + externalIds = list(externalIds) + + if len(internalIds) + len(externalIds) == 0: + return [] item_filters = [ {"type": "inventoryItem", "internalId": internalId} diff --git a/netsuite/config.py b/netsuite/config.py index 13d4c2f..a0d990e 100644 --- a/netsuite/config.py +++ b/netsuite/config.py @@ -1,5 +1,5 @@ import configparser -from typing import Dict +from typing import Any, Dict, Optional, Tuple from .constants import DEFAULT_INI_PATH, DEFAULT_INI_SECTION, NOT_SET @@ -17,37 +17,37 @@ class Config: Dictionary keys/values that will be set as attribute names/values """ - auth_type = TOKEN + auth_type: str = TOKEN """The authentication type to use, either 'token' or 'credentials'""" - account = None + account: Optional[str] = None """The NetSuite account ID""" - consumer_key = None + consumer_key: Optional[str] = None """The OAuth 1.0 consumer key""" - consumer_secret = None + consumer_secret: Optional[str] = None """The OAuth 1.0 consumer secret""" - token_id = None + token_id: Optional[str] = None """The OAuth 1.0 token ID""" - token_secret = None + token_secret: Optional[str] = None """The OAuth 1.0 token secret""" - application_id = None + application_id: Optional[str] = None """Application ID, used with auth_type=credentials""" - email = None + email: Optional[str] = None """Account e-mail, used with auth_type=credentials""" - password = None + password: Optional[str] = None """Account password, used with auth_type=credentials""" preferences = None """Additional preferences""" - _settings_mapping = ( + _settings_mapping: Tuple[Tuple[str, Dict[str, Any]], ...] = ( ( "account", {"type": str, "required": True}, @@ -86,13 +86,13 @@ class Config: ), ) - def __init__(self, **opts) -> None: + def __init__(self, **opts): self._set(opts) def __contains__(self, key: str) -> bool: return hasattr(self, key) - def _set_auth_type(self, value: str) -> None: + def _set_auth_type(self, value: str): self._validate_attr("auth_type", value, str, True, {}) self.auth_type = value assert self.auth_type in (TOKEN, CREDENTIALS) @@ -103,7 +103,7 @@ def is_token_auth(self) -> bool: def is_credentials_auth(self) -> bool: return self.auth_type == CREDENTIALS - def _set(self, dct: Dict[str, object]) -> None: + def _set(self, dct: Dict[str, Any]): # As other setting validations depend on auth_type we set it first auth_type = dct.get("auth_type", self.auth_type) self._set_auth_type(auth_type) @@ -124,8 +124,8 @@ def _set(self, dct: Dict[str, object]) -> None: setattr(self, attr, (None if value is NOT_SET else value)) def _validate_attr( - self, attr: str, value: object, type_: object, required: bool, opts: dict - ) -> None: + self, attr: str, value: Any, type_: Any, required: bool, opts: Dict[str, Any] + ): if required and value is NOT_SET: required_for_auth_type = opts.get("required_for_auth_type") if required_for_auth_type: @@ -146,7 +146,7 @@ def from_ini( with open(path) as fp: iniconf.read_file(fp) - config_dict = {"preferences": {}} + config_dict: Dict[str, Any] = {"preferences": {}} for key, val in iniconf[section].items(): if key.startswith("preferences_"): diff --git a/netsuite/passport.py b/netsuite/passport.py index 4ae5218..4292269 100644 --- a/netsuite/passport.py +++ b/netsuite/passport.py @@ -4,8 +4,6 @@ from datetime import datetime from typing import Dict, TypeVar -from zeep.xsd.valueobjects import CompoundValue - from .config import Config NetSuite = TypeVar("NetSuite") @@ -25,7 +23,7 @@ def __init__( self.email = email self.password = password - def get_element(self) -> CompoundValue: + def get_element(self): return self.ns.Core.Passport( account=self.account, email=self.email, @@ -85,13 +83,13 @@ def _get_signature_value(self, nonce: str, timestamp: str) -> str: ).digest() return base64.b64encode(hashed).decode() - def _get_signature(self, nonce: str, timestamp: str) -> CompoundValue: - return self.ns.Core.TokenPassportSignature( + def _get_signature(self, nonce: str, timestamp: str): + return self.ns.Core.TokenPassportSignature( # type: ignore[attr-defined] self._get_signature_value(nonce, timestamp), algorithm="HMAC-SHA256", ) - def get_element(self) -> CompoundValue: + def get_element(self): nonce = self._generate_nonce() timestamp = self._generate_timestamp() signature = self._get_signature(nonce, timestamp) @@ -107,6 +105,11 @@ def get_element(self) -> CompoundValue: def make(ns: NetSuite, config: Config) -> Dict: if config.auth_type == "token": + assert isinstance(config.account, str) + assert isinstance(config.consumer_key, str) + assert isinstance(config.consumer_secret, str) + assert isinstance(config.token_id, str) + assert isinstance(config.token_secret, str) token_passport = TokenPassport( ns, account=config.account, @@ -117,6 +120,9 @@ def make(ns: NetSuite, config: Config) -> Dict: ) return {"tokenPassport": token_passport.get_element()} elif config.auth_type == "credentials": + assert isinstance(config.account, str) + assert isinstance(config.email, str) + assert isinstance(config.password, str) passport = UserCredentialsPassport( ns, account=config.account, diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index 4754f43..48e404c 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -1,16 +1,15 @@ import asyncio import logging -from typing import Iterable, Optional +from typing import Sequence from . import json -from .types import JsonDict try: import httpx except ImportError: - class httpx: - Response = None # NOTE: For type hint to work + class httpx: # type: ignore[no-redef] + Response = None try: @@ -48,7 +47,7 @@ def __init__( consumer_secret: str, token_id: str, token_secret: str, - default_timeout: str = 60, + default_timeout: int = 60, concurrent_requests: int = 10, ): if not self.has_required_dependencies(): @@ -69,7 +68,7 @@ def __init__( def has_required_dependencies(cls) -> bool: return httpx is not None and OAuth1Auth is not None - async def get(self, subpath: str, **request_kw) -> JsonDict: + async def get(self, subpath: str, **request_kw): return await self.request("GET", subpath, **request_kw) async def post(self, subpath: str, **request_kw): @@ -88,7 +87,7 @@ async def patch(self, subpath: str, **request_kw): async def delete(self, subpath: str, **request_kw): return await self.request("DELETE", subpath, **request_kw) - async def suiteql(self, q: str, limit: int = 10, offset: int = 0) -> JsonDict: + async def suiteql(self, q: str, limit: int = 10, offset: int = 0): return await self.request( "POST", "/query/v1/suiteql", @@ -97,7 +96,7 @@ async def suiteql(self, q: str, limit: int = 10, offset: int = 0) -> JsonDict: params={"limit": limit, "offset": offset}, ) - async def jsonschema(self, record_type: str, **request_kw) -> JsonDict: + async def jsonschema(self, record_type: str, **request_kw): headers = { **request_kw.pop("headers", {}), "Accept": "application/schema+json", @@ -109,7 +108,7 @@ async def jsonschema(self, record_type: str, **request_kw) -> JsonDict: **request_kw, ) - async def openapi(self, record_types: Iterable[str] = (), **request_kw) -> JsonDict: + async def openapi(self, record_types: Sequence[str] = (), **request_kw): headers = { **request_kw.pop("headers", {}), "Accept": "application/swagger+json", @@ -127,9 +126,7 @@ async def openapi(self, record_types: Iterable[str] = (), **request_kw) -> JsonD **request_kw, ) - async def request( - self, method: str, subpath: str, **request_kw - ) -> Optional[JsonDict]: + async def request(self, method: str, subpath: str, **request_kw): resp = await self._raw_request(method, subpath, **request_kw) if resp.status_code < 200 or resp.status_code > 299: diff --git a/netsuite/types.py b/netsuite/types.py deleted file mode 100644 index 7552f61..0000000 --- a/netsuite/types.py +++ /dev/null @@ -1,3 +0,0 @@ -import typing - -JsonDict = typing.Dict[str, typing.Any] From 2299aae53f4136a76c273054ceec9b348f9098ef Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sun, 25 Apr 2021 14:02:40 +0200 Subject: [PATCH 14/47] 0.6.1 - Fix reference error in `NetSuite.get` --- CHANGELOG.md | 9 +++++++-- netsuite/client.py | 2 +- pyproject.toml | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd902a2..dd9e48d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [[Unreleased] +## [Unreleased] - Nothing -## [[0.6.0](https://github.com/jmagnusson/netsuite/compare/v0.5.3...v0.6.0) - 2021-04-25 +## [0.6.1](https://github.com/jmagnusson/netsuite/compare/v0.6.0...v0.6.1) - 2021-04-25 + +### Fixed +- Fix "local variable 'record_ref' referenced before assignment" error in `NetSuite.get` method - Thanks @VeNoMouS! (#25) + +## [0.6.0] - 2021-04-25 ### Fixed - Release 2021.1 wouldn't accept non-GET requests to the REST API without body being part of the signing. Thanks to @mmangione for the PR! (#26) diff --git a/netsuite/client.py b/netsuite/client.py index f82205d..1e7f72e 100755 --- a/netsuite/client.py +++ b/netsuite/client.py @@ -557,7 +557,7 @@ def get( internalId=internalId, ) else: - self.Core.RecordRef( + record_ref = self.Core.RecordRef( type=recordType, externalId=externalId, ) diff --git a/pyproject.toml b/pyproject.toml index 9706f69..ec12ea1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netsuite" -version = "0.6.0" +version = "0.6.1" description = "Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets." authors = ["Jacob Magnusson "] license = "MIT" From d175c382d412a850e0349876bab23e868d2b8b4c Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sun, 25 Apr 2021 14:13:16 +0200 Subject: [PATCH 15/47] Rebuild GitHub pages From f608db9ed34dc087d5621fed749ef76ff02b75a4 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Sun, 25 Apr 2021 14:43:44 +0200 Subject: [PATCH 16/47] 0.6.2 - Don't require a running asyncio loop to instantiate the REST API class --- CHANGELOG.md | 5 +++++ netsuite/rest_api.py | 11 +++++++++-- pyproject.toml | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd9e48d..646fae6 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nothing +## [0.6.2](https://github.com/jmagnusson/netsuite/compare/v0.6.1...v0.6.2) - 2021-04-25 + +### Fixed +- `NetSuiteRestApi` no longer requires a running asyncio loop to be instantiated + ## [0.6.1](https://github.com/jmagnusson/netsuite/compare/v0.6.0...v0.6.1) - 2021-04-25 ### Fixed diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index 48e404c..b3ac33c 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -3,6 +3,7 @@ from typing import Sequence from . import json +from .util import cached_property try: import httpx @@ -62,7 +63,13 @@ def __init__( self._token_secret = token_secret self._hostname = self._make_hostname() self._default_timeout = default_timeout - self._request_semaphore = asyncio.Semaphore(concurrent_requests) + self._concurrent_requests = concurrent_requests + + @cached_property + def request_semaphore(self) -> asyncio.Semaphore: + # NOTE: Shouldn't be put in __init__ as we might not have a running + # event loop at that time. + return asyncio.Semaphore(self._concurrent_requests) @classmethod def has_required_dependencies(cls) -> bool: @@ -157,7 +164,7 @@ async def _raw_request( f"Making {method.upper()} request to {url}. Keyword arguments: {kw}" ) - async with self._request_semaphore: + async with self.request_semaphore: async with httpx.AsyncClient() as c: resp = await c.request( method=method, diff --git a/pyproject.toml b/pyproject.toml index ec12ea1..8382ae2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netsuite" -version = "0.6.1" +version = "0.6.2" description = "Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets." authors = ["Jacob Magnusson "] license = "MIT" From d40a31f3e34f3efaa3cb329f9e90e7f9932d4f0d Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Mon, 26 Apr 2021 10:05:45 +0200 Subject: [PATCH 17/47] Set black as vscode formatting provider --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..de288e1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} \ No newline at end of file From ec6623cf6f649c92f189c4446c1e4e926afbf10a Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Mon, 26 Apr 2021 10:13:34 +0200 Subject: [PATCH 18/47] 0.6.3 - custom headers to REST API and improve suiteql REST API method --- CHANGELOG.md | 6 + netsuite/__main__.py | 382 +++++++++++++++++++++++++++---------------- netsuite/rest_api.py | 13 +- pyproject.toml | 2 +- 4 files changed, 251 insertions(+), 152 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 646fae6..87aaa88 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nothing +## [0.6.3](https://github.com/jmagnusson/netsuite/compare/v0.6.2...v0.6.3) - 2021-04-26 + +### Added +- Ability to supply custom headers to REST API requests made from CLI via "-H/--header" flag +- Support custom payload, headers and params in suiteql REST API method + ## [0.6.2](https://github.com/jmagnusson/netsuite/compare/v0.6.1...v0.6.2) - 2021-04-25 ### Fixed diff --git a/netsuite/__main__.py b/netsuite/__main__.py index 135e932..e961b62 100644 --- a/netsuite/__main__.py +++ b/netsuite/__main__.py @@ -8,6 +8,7 @@ import pathlib import sys import tempfile +from typing import Dict, List, Optional, Union import IPython import pkg_resources @@ -19,6 +20,8 @@ logger = logging.getLogger("netsuite") +ParsedHeaders = Dict[str, Union[List[str], str]] + def main(): try: @@ -112,7 +115,9 @@ async def rest_api_get(config, args) -> str: params["expand"] = ",".join(args.expand) if args.query is not None: params["q"] = args.query - resp = await rest_api.get(args.subpath, params=params) + resp = await rest_api.get( + args.subpath, params=params, headers=_parse_headers_arg(args.header) + ) return json.dumps_str(resp) @@ -123,7 +128,9 @@ async def rest_api_post(config, args) -> str: payload = json.loads(payload_str) - resp = await rest_api.post(args.subpath, json=payload) + resp = await rest_api.post( + args.subpath, json=payload, headers=_parse_headers_arg(args.header) + ) return json.dumps_str(resp) @@ -134,7 +141,9 @@ async def rest_api_put(config, args) -> str: payload = json.loads(payload_str) - resp = await rest_api.put(args.subpath, json=payload) + resp = await rest_api.put( + args.subpath, json=payload, headers=_parse_headers_arg(args.header) + ) return json.dumps_str(resp) @@ -145,14 +154,15 @@ async def rest_api_patch(config, args) -> str: payload = json.loads(payload_str) - resp = await rest_api.patch(args.subpath, json=payload) + resp = await rest_api.patch( + args.subpath, json=payload, headers=_parse_headers_arg(args.header) + ) return json.dumps_str(resp) async def rest_api_delete(config, args) -> str: rest_api = _get_rest_api_or_error(config) - - resp = await rest_api.delete(args.subpath) + resp = await rest_api.delete(args.subpath, headers=_parse_headers_arg(args.header)) return json.dumps_str(resp) @@ -162,7 +172,12 @@ async def rest_api_suiteql(config, args) -> str: with args.q_file as fh: q = fh.read() - resp = await rest_api.suiteql(q=q, limit=args.limit, offset=args.offset) + resp = await rest_api.suiteql( + q=q, + limit=args.limit, + offset=args.offset, + headers=_parse_headers_arg(args.header), + ) return json.dumps_str(resp) @@ -241,29 +256,6 @@ async def rest_api_openapi_serve(config, args): tempdir.rmdir() -def _load_config_or_error(path: str, section: str) -> config.Config: # type: ignore[return] - try: - conf = config.from_ini(path=path, section=section) - except FileNotFoundError: - parser.error(f"Config file {path} not found") - except KeyError as ex: - if ex.args == (section,): - parser.error(f"No config section `{section}` in file {path}") - else: - raise ex - else: - return conf - - -def _get_rest_api_or_error(config: config.Config): - ns = netsuite.NetSuite(config) - - try: - return ns.rest_api # Cached property that initializes NetSuiteRestApi - except RuntimeError as ex: - parser.error(str(ex)) - - parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( "-l", @@ -308,127 +300,227 @@ def _get_rest_api_or_error(config: config.Config): rest_api_parser = subparsers.add_parser("rest-api", aliases=["r"]) rest_api_subparser = rest_api_parser.add_subparsers() -rest_api_get_parser = rest_api_subparser.add_parser( - "get", description="Make a GET request to NetSuite REST web services" -) -rest_api_get_parser.set_defaults(func=rest_api_get) -rest_api_get_parser.add_argument( - "subpath", - help="The subpath to GET, e.g. `/record/v1/salesOrder`", -) -rest_api_get_parser.add_argument( - "-q", - "--query", - help="Search query used to filter results. See NetSuite help center for syntax information. Only works for list endpoints e.g. /record/v1/customer", -) -rest_api_get_parser.add_argument( - "-e", - "--expandSubResources", - action="store_true", - help="Automatically expand all sublists, sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", -) -rest_api_get_parser.add_argument("-l", "--limit", type=int) -rest_api_get_parser.add_argument("-o", "--offset", type=int) -rest_api_get_parser.add_argument( - "-f", - "--fields", - metavar="field", - nargs="*", - help="Only include the given fields in response", -) -rest_api_get_parser.add_argument( - "-E", - "--expand", - nargs="*", - help="Expand the given sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", -) -rest_api_post_parser = rest_api_subparser.add_parser( - "post", description="Make a POST request to NetSuite REST web services" -) -rest_api_post_parser.set_defaults(func=rest_api_post) -rest_api_post_parser.add_argument( - "subpath", - help="The subpath to POST to, e.g. `/record/v1/salesOrder`", -) -rest_api_post_parser.add_argument("payload_file", type=argparse.FileType("r")) +def _add_rest_api_parsers(): + _add_rest_api_get_parser() + _add_rest_api_post_parser() + _add_rest_api_put_parser() + _add_rest_api_patch_parser() + _add_rest_api_delete_parser() + _add_rest_api_suiteql_parser() + _add_rest_api_jsonschema_parser() + _add_rest_api_openapi_parser() + _add_rest_api_openapi_serve_parser() -rest_api_put_parser = rest_api_subparser.add_parser( - "put", description="Make a PUT request to NetSuite REST web services" -) -rest_api_put_parser.set_defaults(func=rest_api_put) -rest_api_put_parser.add_argument( - "subpath", - help="The subpath to PUT to, e.g. `/record/v1/salesOrder/eid:abc123`", -) -rest_api_put_parser.add_argument("payload_file", type=argparse.FileType("r")) -rest_api_patch_parser = rest_api_subparser.add_parser( - "patch", description="Make a PATCH request to NetSuite REST web services" -) -rest_api_patch_parser.set_defaults(func=rest_api_patch) -rest_api_patch_parser.add_argument( - "subpath", - help="The subpath to PATCH to, e.g. `/record/v1/salesOrder/eid:abc123`", -) -rest_api_patch_parser.add_argument("payload_file", type=argparse.FileType("r")) +def _add_rest_api_get_parser(): + p = rest_api_subparser.add_parser( + "get", description="Make a GET request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_get) + p.add_argument( + "subpath", + help="The subpath to GET, e.g. `/record/v1/salesOrder`", + ) + p.add_argument( + "-q", + "--query", + help="Search query used to filter results. See NetSuite help center for syntax information. Only works for list endpoints e.g. /record/v1/customer", + ) + p.add_argument( + "-e", + "--expandSubResources", + action="store_true", + help="Automatically expand all sublists, sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", + ) + p.add_argument("-l", "--limit", type=int) + p.add_argument("-o", "--offset", type=int) + p.add_argument( + "-f", + "--fields", + metavar="field", + nargs="*", + help="Only include the given fields in response", + ) + p.add_argument( + "-E", + "--expand", + nargs="*", + help="Expand the given sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", + ) + _add_rest_api_headers_arg(p) -rest_api_delete_parser = rest_api_subparser.add_parser( - "delete", description="Make a delete request to NetSuite REST web services" -) -rest_api_delete_parser.set_defaults(func=rest_api_delete) -rest_api_delete_parser.add_argument( - "subpath", - help="The subpath for the DELETE request, e.g. `/record/v1/salesOrder/eid:abc123`", -) -rest_api_suiteql_parser = rest_api_subparser.add_parser( - "suiteql", description="Make a SuiteQL request to NetSuite REST web services" -) -rest_api_suiteql_parser.set_defaults(func=rest_api_suiteql) -rest_api_suiteql_parser.add_argument( - "q_file", type=argparse.FileType("r"), help="File containing a SuiteQL query" -) -rest_api_suiteql_parser.add_argument("-l", "--limit", type=int, default=10) -rest_api_suiteql_parser.add_argument("-o", "--offset", type=int, default=0) +def _add_rest_api_post_parser(): + p = rest_api_subparser.add_parser( + "post", description="Make a POST request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_post) + p.add_argument( + "subpath", + help="The subpath to POST to, e.g. `/record/v1/salesOrder`", + ) + p.add_argument("payload_file", type=argparse.FileType("r")) + _add_rest_api_headers_arg(p) -rest_api_jsonschema_parser = rest_api_subparser.add_parser( - "jsonschema", description="Retrieve JSON Schema for the given record type" -) -rest_api_jsonschema_parser.set_defaults(func=rest_api_jsonschema) -rest_api_jsonschema_parser.add_argument( - "record_type", help="The record type to get JSONSchema spec for" -) -rest_api_openapi_parser = rest_api_subparser.add_parser( - "openapi", - aliases=["oas"], - description="Retrieve OpenAPI spec for the given record types", -) -rest_api_openapi_parser.set_defaults(func=rest_api_openapi) -rest_api_openapi_parser.add_argument( - "record_types", - metavar="record_type", - nargs="+", - help="The record type(s) to get OpenAPI spec for", -) +def _add_rest_api_put_parser(): + p = rest_api_subparser.add_parser( + "put", description="Make a PUT request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_put) + p.add_argument( + "subpath", + help="The subpath to PUT to, e.g. `/record/v1/salesOrder/eid:abc123`", + ) + p.add_argument("payload_file", type=argparse.FileType("r")) + _add_rest_api_headers_arg(p) -rest_api_openapi_parser = rest_api_subparser.add_parser( - "openapi-serve", - aliases=["oas-serve"], - description="Start a HTTP server on localhost serving the OpenAPI spec via Swagger UI", -) -rest_api_openapi_parser.set_defaults(func=rest_api_openapi_serve) -rest_api_openapi_parser.add_argument( - "record_types", - metavar="record_type", - nargs="*", - help="The record type(s) to get OpenAPI spec for. If not provided the OpenAPI spec for all known record types will be retrieved.", -) -rest_api_openapi_parser.add_argument( - "-p", "--port", default=8000, type=int, help="The port to listen to" -) -rest_api_openapi_parser.add_argument( - "-b", "--bind", default="127.0.0.1", help="The host to bind to" -) +def _add_rest_api_patch_parser(): + p = rest_api_subparser.add_parser( + "patch", description="Make a PATCH request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_patch) + p.add_argument( + "subpath", + help="The subpath to PATCH to, e.g. `/record/v1/salesOrder/eid:abc123`", + ) + p.add_argument("payload_file", type=argparse.FileType("r")) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_delete_parser(): + p = rest_api_subparser.add_parser( + "delete", description="Make a delete request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_delete) + p.add_argument( + "subpath", + help="The subpath for the DELETE request, e.g. `/record/v1/salesOrder/eid:abc123`", + ) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_suiteql_parser(): + p = rest_api_subparser.add_parser( + "suiteql", description="Make a SuiteQL request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_suiteql) + p.add_argument( + "q_file", type=argparse.FileType("r"), help="File containing a SuiteQL query" + ) + p.add_argument("-l", "--limit", type=int, default=10) + p.add_argument("-o", "--offset", type=int, default=0) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_jsonschema_parser(): + p = rest_api_subparser.add_parser( + "jsonschema", description="Retrieve JSON Schema for the given record type" + ) + p.set_defaults(func=rest_api_jsonschema) + p.add_argument("record_type", help="The record type to get JSONSchema spec for") + _add_rest_api_headers_arg(p) + + +def _add_rest_api_openapi_parser(): + p = rest_api_subparser.add_parser( + "openapi", + aliases=["oas"], + description="Retrieve OpenAPI spec for the given record types", + ) + p.set_defaults(func=rest_api_openapi) + p.add_argument( + "record_types", + metavar="record_type", + nargs="+", + help="The record type(s) to get OpenAPI spec for", + ) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_openapi_serve_parser(): + p = rest_api_subparser.add_parser( + "openapi-serve", + aliases=["oas-serve"], + description="Start a HTTP server on localhost serving the OpenAPI spec via Swagger UI", + ) + p.set_defaults(func=rest_api_openapi_serve) + p.add_argument( + "record_types", + metavar="record_type", + nargs="*", + help="The record type(s) to get OpenAPI spec for. If not provided the OpenAPI spec for all known record types will be retrieved.", + ) + p.add_argument("-p", "--port", default=8000, type=int, help="The port to listen to") + p.add_argument("-b", "--bind", default="127.0.0.1", help="The host to bind to") + + +def _load_config_or_error(path: str, section: str) -> config.Config: # type: ignore[return] + try: + conf = config.from_ini(path=path, section=section) + except FileNotFoundError: + parser.error(f"Config file {path} not found") + except KeyError as ex: + if ex.args == (section,): + parser.error(f"No config section `{section}` in file {path}") + else: + raise ex + else: + return conf + + +def _get_rest_api_or_error(config: config.Config): + ns = netsuite.NetSuite(config) + + try: + return ns.rest_api # Cached property that initializes NetSuiteRestApi + except RuntimeError as ex: + parser.error(str(ex)) + + +def _parse_headers_arg( + headers: Optional[List[str]], +) -> ParsedHeaders: + out: ParsedHeaders = {} + + if headers is None: + headers = [] + + for raw_header in headers: + err = False + try: + k, v = raw_header.split(":", maxsplit=1) + except ValueError: + err = True + else: + k, v = (k.strip(), v.strip()) + if not k or not v: + err = True + if err: + parser.error( + f"Invalid header: `{raw_header}``. Should have format: `NAME: VALUE`" + ) + else: + existing = out.get(k) + if existing: + if isinstance(existing, list): + existing.append(v) + else: + out[k] = [existing, v] + else: + out[k] = v + return out + + +def _add_rest_api_headers_arg(parser_: argparse.ArgumentParser): + parser_.add_argument( + "-H", + "--header", + action="append", + help="Headers to append. Can be specified multiple time and the format for each is `KEY: VALUE`", + ) + + +_add_rest_api_parsers() diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index b3ac33c..fd11c00 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -94,19 +94,20 @@ async def patch(self, subpath: str, **request_kw): async def delete(self, subpath: str, **request_kw): return await self.request("DELETE", subpath, **request_kw) - async def suiteql(self, q: str, limit: int = 10, offset: int = 0): + async def suiteql(self, q: str, limit: int = 10, offset: int = 0, **request_kw): return await self.request( "POST", "/query/v1/suiteql", - headers={"Prefer": "transient"}, - json={"q": q}, - params={"limit": limit, "offset": offset}, + headers={"Prefer": "transient", **request_kw.pop("headers", {})}, + json={"q": q, **request_kw.pop("json", {})}, + params={"limit": limit, "offset": offset, **request_kw.pop("params", {})}, + **request_kw, ) async def jsonschema(self, record_type: str, **request_kw): headers = { - **request_kw.pop("headers", {}), "Accept": "application/schema+json", + **request_kw.pop("headers", {}), } return await self.request( "GET", @@ -117,8 +118,8 @@ async def jsonschema(self, record_type: str, **request_kw): async def openapi(self, record_types: Sequence[str] = (), **request_kw): headers = { - **request_kw.pop("headers", {}), "Accept": "application/swagger+json", + **request_kw.pop("headers", {}), } params = request_kw.pop("params", {}) diff --git a/pyproject.toml b/pyproject.toml index 8382ae2..f298213 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netsuite" -version = "0.6.2" +version = "0.6.3" description = "Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets." authors = ["Jacob Magnusson "] license = "MIT" From 292a61becb72f5025d40972d8169ece3dbaafa03 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Mon, 26 Apr 2021 10:52:00 +0200 Subject: [PATCH 19/47] Mark as Beta version and add disclaimer about breaking APIs --- README.md | 4 ++++ pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6502650..61c8b74 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ Make requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets +## Beta quality disclaimer + +The project's API is still very much in fluctuation. Please consider pinning your dependency to this package to a minor version (e.g. `poetry add netsuite~0.6` or `pipenv install netsuite~=0.6.0`), which is guaranteed to have no breaking changes. From 1.0 and forward we will keep a stable API. + ## Installation Programmatic use only: diff --git a/pyproject.toml b/pyproject.toml index f298213..b6f3e73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ homepage = "https://jmagnusson.github.io/netsuite/" repository = "https://github.com/jmagnusson/netsuite" documentation = "https://jmagnusson.github.io/netsuite/" classifiers = [ - "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", From e6815cc43952a8f3d3976607e7367d6414cd85e4 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Mon, 26 Apr 2021 10:52:06 +0200 Subject: [PATCH 20/47] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87aaa88..97dadc5 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Documentation site +### Removed +- Python 3.6 support + ### Changed - Upgrade to httpx ~0.17 - Use poetry for package management From b35f9610e9f8c14165b74aca6e2dd81e5668276a Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 27 Apr 2021 16:26:39 +0200 Subject: [PATCH 21/47] v0.7.0 - This release breaks a lot of things. Please read carefully. --- .github/workflows/ci.yml | 19 +- .isort.cfg | 7 - .vscode/settings.json | 7 +- CHANGELOG.md | 23 + README.md | 14 +- docs/index.md | 45 +- netsuite/__init__.py | 10 +- netsuite/__main__.py | 526 ---------------------- netsuite/cli/__init__.py | 1 + netsuite/cli/helpers.py | 19 + netsuite/cli/interact.py | 41 ++ netsuite/cli/main.py | 76 ++++ netsuite/cli/misc.py | 13 + netsuite/cli/rest_api.py | 378 ++++++++++++++++ netsuite/cli/restlet.py | 108 +++++ netsuite/cli/soap_api.py | 78 ++++ netsuite/client.py | 662 +--------------------------- netsuite/config.py | 176 ++------ netsuite/exceptions.py | 13 + netsuite/json.py | 23 +- netsuite/rest_api.py | 141 +----- netsuite/rest_api_base.py | 99 +++++ netsuite/restlet.py | 119 ++--- netsuite/soap_api/__init__.py | 2 + netsuite/soap_api/client.py | 538 ++++++++++++++++++++++ netsuite/soap_api/decorators.py | 73 +++ netsuite/soap_api/exceptions.py | 5 + netsuite/{ => soap_api}/helpers.py | 2 +- netsuite/{ => soap_api}/passport.py | 53 +-- netsuite/soap_api/transports.py | 38 ++ netsuite/soap_api/zeep.py | 57 +++ netsuite/util.py | 74 ++-- poetry.lock | 98 ++-- pyproject.toml | 21 +- tests/test_base.py | 28 +- 35 files changed, 1864 insertions(+), 1723 deletions(-) delete mode 100644 .isort.cfg delete mode 100644 netsuite/__main__.py create mode 100644 netsuite/cli/__init__.py create mode 100644 netsuite/cli/helpers.py create mode 100644 netsuite/cli/interact.py create mode 100644 netsuite/cli/main.py create mode 100644 netsuite/cli/misc.py create mode 100644 netsuite/cli/rest_api.py create mode 100644 netsuite/cli/restlet.py create mode 100644 netsuite/cli/soap_api.py mode change 100755 => 100644 netsuite/client.py create mode 100644 netsuite/exceptions.py create mode 100644 netsuite/rest_api_base.py create mode 100644 netsuite/soap_api/__init__.py create mode 100644 netsuite/soap_api/client.py create mode 100644 netsuite/soap_api/decorators.py create mode 100644 netsuite/soap_api/exceptions.py rename netsuite/{ => soap_api}/helpers.py (96%) rename netsuite/{ => soap_api}/passport.py (63%) create mode 100644 netsuite/soap_api/transports.py create mode 100644 netsuite/soap_api/zeep.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e52642a..3557d2d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,11 +5,12 @@ on: pull_request: jobs: - ci: + unittests: runs-on: ubuntu-latest strategy: matrix: python-version: [3.9, 3.8, 3.7] + extras: [all, soap_api, orjson, cli] os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v2 @@ -17,8 +18,22 @@ jobs: with: python-version: ${{ matrix.python-version }} - uses: abatilo/actions-poetry@v2.1.0 - - run: poetry install --extras all + - run: poetry install --extras ${{ matrix.extras }} - run: poetry run pytest -v + linting: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9] + extras: [all] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - uses: abatilo/actions-poetry@v2.1.0 + - run: poetry install --extras ${{ matrix.extras }} - run: poetry run flake8 - run: poetry run mypy --ignore-missing-imports . - run: poetry run isort --check --diff . diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index ef27068..0000000 --- a/.isort.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[settings] -multi_line_output = 3 -include_trailing_comma = True -force_grid_wrap = 0 -use_parentheses = True -line_length = 88 -known_third_party = IPython,orjson,pkg_resources,pytest,requests,requests_oauthlib,setuptools,traitlets,zeep diff --git a/.vscode/settings.json b/.vscode/settings.json index de288e1..83d19f1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "python.formatting.provider": "black" -} \ No newline at end of file + "python.formatting.provider": "black", + "files.exclude": { + "poetry.lock": true + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 97dadc5..59b4c71 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nothing +## [0.7.0](https://github.com/jmagnusson/netsuite/compare/v0.6.3...v0.7.0) - 2021-04-26 + +This release breaks a lot of things. Please read carefully. + +### Changed +- The SOAP API has been changed to use async +- Move NetSuite version to 2021.1 +- The `netsuite.restlet.NetsuiteRestlet` interface is now asynchronous +- SOAP Web Services support is no longer included in the default install, please use `netsuite[soap_api]` (unfortunately `zeep` pulls in a lot of other dependencies) +- REST API and Restlet are now supported with the default install +- The `netsuite.client.NetSuite` class is now just a thin layer around each of the different API types (SOAP, REST, Restlets) +- `netsuite.restlet.NetsuiteRestlet` has been renamed to `netsuite.restlet.NetSuiteRestlet` + +### Added +- `netsuite.restlet.NetSuiteRestlet` now support all four HTTP verbs GET, POST, PUT & DELETE via dedicated functions `.get`, `.post`, `.put` & `.delete` +- Dependency [pydantic](https://pydantic-docs.helpmanual.io/) has been added to help with config validation + +### Removed +- Authentication via User credentials (will no longer work from NetSuite 2021.2 release) +- `netsuite.restlet.NetSuiteRestApi.request` and `netsuite.restlet.NetSuiteRestlet.request` no longer exists - use each dedicated "verb method" instead +- Dead code for setting SOAP preferences +- CLI sub-command aliases `i` (interact) and `r` (rest-api) have been removed to avoid confusion + ## [0.6.3](https://github.com/jmagnusson/netsuite/compare/v0.6.2...v0.6.3) - 2021-04-26 ### Added diff --git a/README.md b/README.md index 61c8b74..2d79643 100644 --- a/README.md +++ b/README.md @@ -8,26 +8,30 @@ [![Python Versions](https://img.shields.io/pypi/pyversions/netsuite.svg)](https://pypi.org/project/netsuite/) [![PyPI status (alpha/beta/stable)](https://img.shields.io/pypi/status/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) -Make requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets +Make async requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets ## Beta quality disclaimer -The project's API is still very much in fluctuation. Please consider pinning your dependency to this package to a minor version (e.g. `poetry add netsuite~0.6` or `pipenv install netsuite~=0.6.0`), which is guaranteed to have no breaking changes. From 1.0 and forward we will keep a stable API. +The project's API is still very much in fluctuation. Please consider pinning your dependency to this package to a minor version (e.g. `poetry add netsuite~0.7` or `pipenv install netsuite~=0.7.0`), which is guaranteed to have no breaking changes. From 1.0 and forward we will keep a stable API. ## Installation -Programmatic use only: +With default features (REST API + Restlet support): pip install netsuite -With NetSuite SuiteTalk REST Web Services API support: +With Web Services SOAP API support: - pip install netsuite[rest_api] + pip install netsuite[soap_api] With CLI support: pip install netsuite[cli] +With `orjson` package (faster JSON handling): + + pip install netsuite[orjson] + With all features: pip install netsuite[all] diff --git a/docs/index.md b/docs/index.md index db97645..56c2d9e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,25 +1,58 @@ # netsuite -Make requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets +Make async requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets ## Installation -Programmatic use only: +With default features (REST API + Restlet support): pip install netsuite -With NetSuite SuiteTalk REST Web Services API support: +With Web Services SOAP API support: - pip install netsuite[rest_api] + pip install netsuite[soap_api] With CLI support: pip install netsuite[cli] +With `orjson` package (faster JSON handling): + + pip install netsuite[orjson] + With all features: pip install netsuite[all] + +## Programmatic use + +```python +import asyncio + +from netsuite import NetSuite, Config, TokenAuth + +config = Config( + account="12345", + auth=TokenAuth(consumer_key="abc", consumer_secret="123", token_id="xyz", token_secret="456"), +) + +ns = NetSuite(config) + + +async def async_main(): + # NOTE: SOAP needs `pip install netsuite[soap_api]` + soap_api_results = await ns.soap_api.getList('customer', internalIds=[1337]) + + rest_api_results = await ns.rest_api.get("/record/v1/salesOrder") + + restlet_results = await ns.restlet.get(987, deploy=2) + +if __name__ == "__main__": + asyncio.run(async_main()) + +``` + ## CLI ### Configuration @@ -148,9 +181,9 @@ Available vars: `ns` - NetSuite client Example usage: - ws_results = ns.getList('customer', internalIds=[1337]) - restlet_results = ns.restlet.request(987) + soap_api_results = ns.soap_api.getList('customer', internalIds=[1337]) rest_api_results = await ns.rest_api.get("/record/v1/salesOrder") + restlet_results = await ns.restlet.get(987, deploy=2) In [1]: rest_api_results = await ns.rest_api.get(" ``` diff --git a/netsuite/__init__.py b/netsuite/__init__.py index d183cf7..8c4c9e0 100644 --- a/netsuite/__init__.py +++ b/netsuite/__init__.py @@ -1,6 +1,6 @@ -import logging - from . import constants # noqa -from .client import NetSuite # noqa - -logger = logging.getLogger(__name__) +from .client import * # noqa +from .config import * # noqa +from .rest_api import * # noqa +from .restlet import * # noqa +from .soap_api import * # noqa diff --git a/netsuite/__main__.py b/netsuite/__main__.py deleted file mode 100644 index e961b62..0000000 --- a/netsuite/__main__.py +++ /dev/null @@ -1,526 +0,0 @@ -import argparse -import asyncio -import functools -import http.server -import inspect -import logging -import logging.config -import pathlib -import sys -import tempfile -from typing import Dict, List, Optional, Union - -import IPython -import pkg_resources -import traitlets - -import netsuite -from netsuite import config, json -from netsuite.constants import DEFAULT_INI_PATH, DEFAULT_INI_SECTION - -logger = logging.getLogger("netsuite") - -ParsedHeaders = Dict[str, Union[List[str], str]] - - -def main(): - try: - args = parser.parse_args() - except Exception: - parser.print_help() - return - - subparser_name = sys.argv[-1] - - if subparser_name == "rest-api": - rest_api_parser.print_help() - return - - config = _load_config_or_error(args.config_path, args.config_section) - - log_level = getattr(logging, args.log_level) - logging.basicConfig(level=log_level) - - ret = args.func(config, args) - - if inspect.iscoroutinefunction(args.func): - ret = asyncio.run(ret) - - if ret is not None: - print(ret) - - -def version(config, args) -> str: - return pkg_resources.get_distribution("netsuite").version - - -def interact(config, args): - ns = netsuite.NetSuite(config) - - user_ns = {"ns": ns} - - banner1 = """Welcome to Netsuite WS client interactive mode -Available vars: - `ns` - NetSuite client - -Example usage: - ws_results = ns.getList('customer', internalIds=[1337]) - restlet_results = ns.restlet.request(987) - rest_api_results = await ns.rest_api.get("/record/v1/salesOrder") -""" - - IPython.embed( - user_ns=user_ns, - banner1=banner1, - config=traitlets.config.Config(colors="LightBG"), - # To fix no colored input we pass in `using=False` - # See: https://github.com/ipython/ipython/issues/11523 - # TODO: Remove once this is fixed upstream - using=False, - ) - - -def restlet(config, args) -> str: - ns = netsuite.NetSuite(config) - - if not args.payload: - payload = None - elif args.payload == "-": - with sys.stdin as fh: - payload = json.loads(fh.read()) - else: - payload = json.loads(args.payload) - - resp = ns.restlet.raw_request( - script_id=args.script_id, - deploy=args.deploy, - payload=payload, - raise_on_bad_status=False, - ) - return resp.text - - -async def rest_api_get(config, args) -> str: - rest_api = _get_rest_api_or_error(config) - params = {} - if args.expandSubResources is True: - params["expandSubResources"] = "true" - if args.limit is not None: - params["limit"] = args.limit - if args.offset is not None: - params["offset"] = args.offset - if args.fields is not None: - params["fields"] = ",".join(args.fields) - if args.expand is not None: - params["expand"] = ",".join(args.expand) - if args.query is not None: - params["q"] = args.query - resp = await rest_api.get( - args.subpath, params=params, headers=_parse_headers_arg(args.header) - ) - return json.dumps_str(resp) - - -async def rest_api_post(config, args) -> str: - rest_api = _get_rest_api_or_error(config) - with args.payload_file as fh: - payload_str = fh.read() - - payload = json.loads(payload_str) - - resp = await rest_api.post( - args.subpath, json=payload, headers=_parse_headers_arg(args.header) - ) - return json.dumps_str(resp) - - -async def rest_api_put(config, args) -> str: - rest_api = _get_rest_api_or_error(config) - with args.payload_file as fh: - payload_str = fh.read() - - payload = json.loads(payload_str) - - resp = await rest_api.put( - args.subpath, json=payload, headers=_parse_headers_arg(args.header) - ) - return json.dumps_str(resp) - - -async def rest_api_patch(config, args) -> str: - rest_api = _get_rest_api_or_error(config) - with args.payload_file as fh: - payload_str = fh.read() - - payload = json.loads(payload_str) - - resp = await rest_api.patch( - args.subpath, json=payload, headers=_parse_headers_arg(args.header) - ) - return json.dumps_str(resp) - - -async def rest_api_delete(config, args) -> str: - rest_api = _get_rest_api_or_error(config) - resp = await rest_api.delete(args.subpath, headers=_parse_headers_arg(args.header)) - return json.dumps_str(resp) - - -async def rest_api_suiteql(config, args) -> str: - rest_api = _get_rest_api_or_error(config) - - with args.q_file as fh: - q = fh.read() - - resp = await rest_api.suiteql( - q=q, - limit=args.limit, - offset=args.offset, - headers=_parse_headers_arg(args.header), - ) - - return json.dumps_str(resp) - - -async def rest_api_jsonschema(config, args) -> str: - rest_api = _get_rest_api_or_error(config) - resp = await rest_api.jsonschema(args.record_type) - return json.dumps_str(resp) - - -async def rest_api_openapi(config, args) -> str: - rest_api = _get_rest_api_or_error(config) - resp = await rest_api.openapi(args.record_types) - return json.dumps_str(resp) - - -async def rest_api_openapi_serve(config, args): - rest_api = _get_rest_api_or_error(config) - if len(args.record_types) == 0: - logger.warning( - "Fetching OpenAPI spec for ALL known record types... This will take a long " - "time! (Consider providing only the record types of interest by passing " - "their names to this command as positional arguments)" - ) - else: - rt_str = ", ".join(args.record_types) - logger.info(f"Fetching OpenAPI spec for record types {rt_str}...") - spec = await rest_api.openapi(args.record_types) - tempdir = pathlib.Path(tempfile.mkdtemp()) - openapi_file = tempdir / "openapi.json" - html_file = tempdir / "index.html" - openapi_file.write_bytes(json.dumps(spec)) - html = """ - - - - NetSuite REST Record API - - -
-
- - - - -""" - html_file.write_text(html) - handler_class = functools.partial( - http.server.SimpleHTTPRequestHandler, - directory=str(tempdir), - ) - logger.info( - f"NetSuite REST Record API docs available at http://{args.bind}:{args.port}" - ) - try: - http.server.test( - HandlerClass=handler_class, - ServerClass=http.server.ThreadingHTTPServer, - port=args.port, - bind=args.bind, - ) - finally: - html_file.unlink() - openapi_file.unlink() - tempdir.rmdir() - - -parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) -parser.add_argument( - "-l", - "--log-level", - help="The log level to use", - default="INFO", - choices=("DEBUG", "INFO", "WARNING", "ERROR"), -) -parser.add_argument( - "-p", - "--config-path", - help="The config file to get settings from", - default=DEFAULT_INI_PATH, -) -parser.add_argument( - "-c", - "--config-section", - help="The config section to get settings from", - default=DEFAULT_INI_SECTION, -) - -subparsers = parser.add_subparsers(help="App CLI", required=True) - -version_parser = subparsers.add_parser("version") -version_parser.set_defaults(func=version) - -interact_parser = subparsers.add_parser( - "interact", - aliases=["i"], - description="Starts a REPL to enable live interaction with NetSuite webservices", -) -interact_parser.set_defaults(func=interact) - -restlet_parser = subparsers.add_parser( - "restlet", description="Make request to a restlet" -) -restlet_parser.set_defaults(func=restlet) -restlet_parser.add_argument("script_id", type=int) -restlet_parser.add_argument("payload") -restlet_parser.add_argument("-d", "--deploy", type=int, default=1) - -rest_api_parser = subparsers.add_parser("rest-api", aliases=["r"]) -rest_api_subparser = rest_api_parser.add_subparsers() - - -def _add_rest_api_parsers(): - _add_rest_api_get_parser() - _add_rest_api_post_parser() - _add_rest_api_put_parser() - _add_rest_api_patch_parser() - _add_rest_api_delete_parser() - _add_rest_api_suiteql_parser() - _add_rest_api_jsonschema_parser() - _add_rest_api_openapi_parser() - _add_rest_api_openapi_serve_parser() - - -def _add_rest_api_get_parser(): - p = rest_api_subparser.add_parser( - "get", description="Make a GET request to NetSuite REST web services" - ) - p.set_defaults(func=rest_api_get) - p.add_argument( - "subpath", - help="The subpath to GET, e.g. `/record/v1/salesOrder`", - ) - p.add_argument( - "-q", - "--query", - help="Search query used to filter results. See NetSuite help center for syntax information. Only works for list endpoints e.g. /record/v1/customer", - ) - p.add_argument( - "-e", - "--expandSubResources", - action="store_true", - help="Automatically expand all sublists, sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", - ) - p.add_argument("-l", "--limit", type=int) - p.add_argument("-o", "--offset", type=int) - p.add_argument( - "-f", - "--fields", - metavar="field", - nargs="*", - help="Only include the given fields in response", - ) - p.add_argument( - "-E", - "--expand", - nargs="*", - help="Expand the given sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", - ) - _add_rest_api_headers_arg(p) - - -def _add_rest_api_post_parser(): - p = rest_api_subparser.add_parser( - "post", description="Make a POST request to NetSuite REST web services" - ) - p.set_defaults(func=rest_api_post) - p.add_argument( - "subpath", - help="The subpath to POST to, e.g. `/record/v1/salesOrder`", - ) - p.add_argument("payload_file", type=argparse.FileType("r")) - _add_rest_api_headers_arg(p) - - -def _add_rest_api_put_parser(): - p = rest_api_subparser.add_parser( - "put", description="Make a PUT request to NetSuite REST web services" - ) - p.set_defaults(func=rest_api_put) - p.add_argument( - "subpath", - help="The subpath to PUT to, e.g. `/record/v1/salesOrder/eid:abc123`", - ) - p.add_argument("payload_file", type=argparse.FileType("r")) - _add_rest_api_headers_arg(p) - - -def _add_rest_api_patch_parser(): - p = rest_api_subparser.add_parser( - "patch", description="Make a PATCH request to NetSuite REST web services" - ) - p.set_defaults(func=rest_api_patch) - p.add_argument( - "subpath", - help="The subpath to PATCH to, e.g. `/record/v1/salesOrder/eid:abc123`", - ) - p.add_argument("payload_file", type=argparse.FileType("r")) - _add_rest_api_headers_arg(p) - - -def _add_rest_api_delete_parser(): - p = rest_api_subparser.add_parser( - "delete", description="Make a delete request to NetSuite REST web services" - ) - p.set_defaults(func=rest_api_delete) - p.add_argument( - "subpath", - help="The subpath for the DELETE request, e.g. `/record/v1/salesOrder/eid:abc123`", - ) - _add_rest_api_headers_arg(p) - - -def _add_rest_api_suiteql_parser(): - p = rest_api_subparser.add_parser( - "suiteql", description="Make a SuiteQL request to NetSuite REST web services" - ) - p.set_defaults(func=rest_api_suiteql) - p.add_argument( - "q_file", type=argparse.FileType("r"), help="File containing a SuiteQL query" - ) - p.add_argument("-l", "--limit", type=int, default=10) - p.add_argument("-o", "--offset", type=int, default=0) - _add_rest_api_headers_arg(p) - - -def _add_rest_api_jsonschema_parser(): - p = rest_api_subparser.add_parser( - "jsonschema", description="Retrieve JSON Schema for the given record type" - ) - p.set_defaults(func=rest_api_jsonschema) - p.add_argument("record_type", help="The record type to get JSONSchema spec for") - _add_rest_api_headers_arg(p) - - -def _add_rest_api_openapi_parser(): - p = rest_api_subparser.add_parser( - "openapi", - aliases=["oas"], - description="Retrieve OpenAPI spec for the given record types", - ) - p.set_defaults(func=rest_api_openapi) - p.add_argument( - "record_types", - metavar="record_type", - nargs="+", - help="The record type(s) to get OpenAPI spec for", - ) - _add_rest_api_headers_arg(p) - - -def _add_rest_api_openapi_serve_parser(): - p = rest_api_subparser.add_parser( - "openapi-serve", - aliases=["oas-serve"], - description="Start a HTTP server on localhost serving the OpenAPI spec via Swagger UI", - ) - p.set_defaults(func=rest_api_openapi_serve) - p.add_argument( - "record_types", - metavar="record_type", - nargs="*", - help="The record type(s) to get OpenAPI spec for. If not provided the OpenAPI spec for all known record types will be retrieved.", - ) - p.add_argument("-p", "--port", default=8000, type=int, help="The port to listen to") - p.add_argument("-b", "--bind", default="127.0.0.1", help="The host to bind to") - - -def _load_config_or_error(path: str, section: str) -> config.Config: # type: ignore[return] - try: - conf = config.from_ini(path=path, section=section) - except FileNotFoundError: - parser.error(f"Config file {path} not found") - except KeyError as ex: - if ex.args == (section,): - parser.error(f"No config section `{section}` in file {path}") - else: - raise ex - else: - return conf - - -def _get_rest_api_or_error(config: config.Config): - ns = netsuite.NetSuite(config) - - try: - return ns.rest_api # Cached property that initializes NetSuiteRestApi - except RuntimeError as ex: - parser.error(str(ex)) - - -def _parse_headers_arg( - headers: Optional[List[str]], -) -> ParsedHeaders: - out: ParsedHeaders = {} - - if headers is None: - headers = [] - - for raw_header in headers: - err = False - try: - k, v = raw_header.split(":", maxsplit=1) - except ValueError: - err = True - else: - k, v = (k.strip(), v.strip()) - if not k or not v: - err = True - if err: - parser.error( - f"Invalid header: `{raw_header}``. Should have format: `NAME: VALUE`" - ) - else: - existing = out.get(k) - if existing: - if isinstance(existing, list): - existing.append(v) - else: - out[k] = [existing, v] - else: - out[k] = v - return out - - -def _add_rest_api_headers_arg(parser_: argparse.ArgumentParser): - parser_.add_argument( - "-H", - "--header", - action="append", - help="Headers to append. Can be specified multiple time and the format for each is `KEY: VALUE`", - ) - - -_add_rest_api_parsers() diff --git a/netsuite/cli/__init__.py b/netsuite/cli/__init__.py new file mode 100644 index 0000000..38f3549 --- /dev/null +++ b/netsuite/cli/__init__.py @@ -0,0 +1 @@ +from .main import * # noqa diff --git a/netsuite/cli/helpers.py b/netsuite/cli/helpers.py new file mode 100644 index 0000000..ff153dd --- /dev/null +++ b/netsuite/cli/helpers.py @@ -0,0 +1,19 @@ +import argparse + +from ..config import Config + +__all__ = () + + +def load_config_or_error(parser: argparse.ArgumentParser, path: str, section: str) -> Config: # type: ignore[return] + try: + conf = Config.from_ini(path=path, section=section) + except FileNotFoundError: + parser.error(f"Config file {path} not found") + except KeyError as ex: + if ex.args == (section,): + parser.error(f"No config section `{section}` in file {path}") + else: + raise ex + else: + return conf diff --git a/netsuite/cli/interact.py b/netsuite/cli/interact.py new file mode 100644 index 0000000..a384941 --- /dev/null +++ b/netsuite/cli/interact.py @@ -0,0 +1,41 @@ +import IPython +import traitlets + +from ..client import NetSuite + +__all__ = () + + +def add_parser(parser, subparser): + interact_parser = subparser.add_parser( + "interact", + description="Starts a REPL to enable live interaction with NetSuite webservices", + ) + interact_parser.set_defaults(func=interact) + return (interact_parser, None) + + +def interact(config, args): + ns = NetSuite(config) + + user_ns = {"ns": ns} + + banner1 = """Welcome to Netsuite WS client interactive mode +Available vars: + `ns` - NetSuite client + +Example usage: + soap_results = await ns.soap_api.getList('customer', internalIds=[1337]) + restlet_results = await ns.restlet.get(987, deploy=2) + rest_api_results = await ns.rest_api.get("/record/v1/salesOrder") +""" + + IPython.embed( + user_ns=user_ns, + banner1=banner1, + config=traitlets.config.Config(colors="LightBG"), + # To fix no colored input we pass in `using=False` + # See: https://github.com/ipython/ipython/issues/11523 + # TODO: Remove once this is fixed upstream + using=False, + ) diff --git a/netsuite/cli/main.py b/netsuite/cli/main.py new file mode 100644 index 0000000..b13954d --- /dev/null +++ b/netsuite/cli/main.py @@ -0,0 +1,76 @@ +import argparse +import asyncio +import inspect +import logging +import sys + +from ..constants import DEFAULT_INI_PATH, DEFAULT_INI_SECTION +from . import helpers, interact, misc, rest_api, restlet, soap_api + +__all__ = ("main",) + + +def main(): + + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument( + "-l", + "--log-level", + help="The log level to use", + default="INFO", + choices=("DEBUG", "INFO", "WARNING", "ERROR"), + ) + parser.add_argument( + "-p", + "--config-path", + help="The config file to get settings from", + default=DEFAULT_INI_PATH, + ) + parser.add_argument( + "-c", + "--config-section", + help="The config section to get settings from", + default=DEFAULT_INI_SECTION, + ) + + subparser = parser.add_subparsers(help="App CLI", required=True) + + misc.add_parser(parser, subparser) + interact.add_parser(parser, subparser) + restlet_parser, _ = restlet.add_parser(parser, subparser) + rest_api_parser, _ = rest_api.add_parser(parser, subparser) + soap_api_parser, _ = soap_api.add_parser(parser, subparser) + + try: + args = parser.parse_args() + except Exception: + parser.print_help() + return + + subparser_name = sys.argv[-1] + + # Show help section instead of an error when no arguments were passed... + if subparser_name == "rest-api": + rest_api_parser.print_help() + return + elif subparser_name == "soap-api": + rest_api_parser.print_help() + return + elif subparser_name == "restlet": + restlet_parser.print_help() + return + + config = helpers.load_config_or_error(parser, args.config_path, args.config_section) + + log_level = getattr(logging, args.log_level) + logging.basicConfig(level=log_level) + + ret = args.func(config, args) + + if inspect.iscoroutinefunction(args.func): + ret = asyncio.run(ret) + + if ret is not None: + print(ret) diff --git a/netsuite/cli/misc.py b/netsuite/cli/misc.py new file mode 100644 index 0000000..51e2181 --- /dev/null +++ b/netsuite/cli/misc.py @@ -0,0 +1,13 @@ +import pkg_resources + +__all__ = () + + +def add_parser(parser, subparser): + version_parser = subparser.add_parser("version") + version_parser.set_defaults(func=version) + return (version_parser, None) + + +def version(config, args) -> str: + return pkg_resources.get_distribution("netsuite").version diff --git a/netsuite/cli/rest_api.py b/netsuite/cli/rest_api.py new file mode 100644 index 0000000..31da524 --- /dev/null +++ b/netsuite/cli/rest_api.py @@ -0,0 +1,378 @@ +import argparse +import functools +import http.server +import logging +import logging.config +import pathlib +import tempfile +from typing import Dict, List, Optional, Union + +from .. import json +from ..client import NetSuite +from ..config import Config + +logger = logging.getLogger("netsuite") + +ParsedHeaders = Dict[str, Union[List[str], str]] + +__all__ = () + + +def add_parser(parser, subparser): + rest_api_parser = subparser.add_parser("rest-api") + rest_api_subparser = rest_api_parser.add_subparsers() + _add_rest_api_get_parser(rest_api_parser, rest_api_subparser) + _add_rest_api_post_parser(rest_api_parser, rest_api_subparser) + _add_rest_api_put_parser(rest_api_parser, rest_api_subparser) + _add_rest_api_patch_parser(rest_api_parser, rest_api_subparser) + _add_rest_api_delete_parser(rest_api_parser, rest_api_subparser) + _add_rest_api_suiteql_parser(rest_api_parser, rest_api_subparser) + _add_rest_api_jsonschema_parser(rest_api_parser, rest_api_subparser) + _add_rest_api_openapi_parser(rest_api_parser, rest_api_subparser) + _add_rest_api_openapi_serve_parser(rest_api_parser, rest_api_subparser) + + return (rest_api_parser, rest_api_subparser) + + +def _add_rest_api_get_parser(parser, subparser): + async def rest_api_get(config, args) -> str: + rest_api = _get_rest_api_or_error(parser, config) + params = {} + if args.expandSubResources is True: + params["expandSubResources"] = "true" + if args.limit is not None: + params["limit"] = args.limit + if args.offset is not None: + params["offset"] = args.offset + if args.fields is not None: + params["fields"] = ",".join(args.fields) + if args.expand is not None: + params["expand"] = ",".join(args.expand) + if args.query is not None: + params["q"] = args.query + resp = await rest_api.get( + args.subpath, params=params, headers=_parse_headers_arg(parser, args.header) + ) + return json.dumps(resp) + + p = subparser.add_parser( + "get", description="Make a GET request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_get) + p.add_argument( + "subpath", + help="The subpath to GET, e.g. `/record/v1/salesOrder`", + ) + p.add_argument( + "-q", + "--query", + help="Search query used to filter results. See NetSuite help center for syntax information. Only works for list endpoints e.g. /record/v1/customer", + ) + p.add_argument( + "-e", + "--expandSubResources", + action="store_true", + help="Automatically expand all sublists, sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", + ) + p.add_argument("-l", "--limit", type=int) + p.add_argument("-o", "--offset", type=int) + p.add_argument( + "-f", + "--fields", + metavar="field", + nargs="*", + help="Only include the given fields in response", + ) + p.add_argument( + "-E", + "--expand", + nargs="*", + help="Expand the given sublist lines and subrecords on this record. Only works for detail endpoints e.g. /record/v1/invoice/123.", + ) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_post_parser(parser, subparser): + async def rest_api_post(config, args) -> str: + rest_api = _get_rest_api_or_error(parser, config) + with args.payload_file as fh: + payload_str = fh.read() + + payload = json.loads(payload_str) + + resp = await rest_api.post( + args.subpath, json=payload, headers=_parse_headers_arg(parser, args.header) + ) + return json.dumps(resp) + + p = subparser.add_parser( + "post", description="Make a POST request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_post) + p.add_argument( + "subpath", + help="The subpath to POST to, e.g. `/record/v1/salesOrder`", + ) + p.add_argument("payload_file", type=argparse.FileType("r")) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_put_parser(parser, subparser): + async def rest_api_put(config, args) -> str: + rest_api = _get_rest_api_or_error(parser, config) + with args.payload_file as fh: + payload_str = fh.read() + + payload = json.loads(payload_str) + + resp = await rest_api.put( + args.subpath, json=payload, headers=_parse_headers_arg(parser, args.header) + ) + return json.dumps(resp) + + p = subparser.add_parser( + "put", description="Make a PUT request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_put) + p.add_argument( + "subpath", + help="The subpath to PUT to, e.g. `/record/v1/salesOrder/eid:abc123`", + ) + p.add_argument("payload_file", type=argparse.FileType("r")) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_patch_parser(parser, subparser): + async def rest_api_patch(config, args) -> str: + rest_api = _get_rest_api_or_error(parser, config) + with args.payload_file as fh: + payload_str = fh.read() + + payload = json.loads(payload_str) + + resp = await rest_api.patch( + args.subpath, json=payload, headers=_parse_headers_arg(parser, args.header) + ) + return json.dumps(resp) + + p = subparser.add_parser( + "patch", description="Make a PATCH request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_patch) + p.add_argument( + "subpath", + help="The subpath to PATCH to, e.g. `/record/v1/salesOrder/eid:abc123`", + ) + p.add_argument("payload_file", type=argparse.FileType("r")) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_delete_parser(parser, subparser): + async def rest_api_delete(config, args) -> str: + rest_api = _get_rest_api_or_error(parser, config) + resp = await rest_api.delete( + args.subpath, headers=_parse_headers_arg(parser, args.header) + ) + return json.dumps(resp) + + p = subparser.add_parser( + "delete", description="Make a DELETE request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_delete) + p.add_argument( + "subpath", + help="The subpath for the DELETE request, e.g. `/record/v1/salesOrder/eid:abc123`", + ) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_suiteql_parser(parser, subparser): + async def rest_api_suiteql(config, args) -> str: + rest_api = _get_rest_api_or_error(parser, config) + + with args.q_file as fh: + q = fh.read() + + resp = await rest_api.suiteql( + q=q, + limit=args.limit, + offset=args.offset, + headers=_parse_headers_arg(parser, args.header), + ) + + return json.dumps(resp) + + p = subparser.add_parser( + "suiteql", description="Make a SuiteQL request to NetSuite REST web services" + ) + p.set_defaults(func=rest_api_suiteql) + p.add_argument( + "q_file", type=argparse.FileType("r"), help="File containing a SuiteQL query" + ) + p.add_argument("-l", "--limit", type=int, default=10) + p.add_argument("-o", "--offset", type=int, default=0) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_jsonschema_parser(parser, subparser): + async def rest_api_jsonschema(config, args) -> str: + rest_api = _get_rest_api_or_error(parser, config) + resp = await rest_api.jsonschema(args.record_type) + return json.dumps(resp) + + p = subparser.add_parser( + "jsonschema", description="Retrieve JSON Schema for the given record type" + ) + p.set_defaults(func=rest_api_jsonschema) + p.add_argument("record_type", help="The record type to get JSONSchema spec for") + _add_rest_api_headers_arg(p) + + +def _add_rest_api_openapi_parser(parser, subparser): + async def rest_api_openapi(config, args) -> str: + rest_api = _get_rest_api_or_error(parser, config) + resp = await rest_api.openapi(args.record_types) + return json.dumps(resp) + + p = subparser.add_parser( + "openapi", + description="Retrieve OpenAPI spec for the given record types", + ) + p.set_defaults(func=rest_api_openapi) + p.add_argument( + "record_types", + metavar="record_type", + nargs="+", + help="The record type(s) to get OpenAPI spec for", + ) + _add_rest_api_headers_arg(p) + + +def _add_rest_api_openapi_serve_parser(parser, subparser): + async def rest_api_openapi_serve(config, args): + rest_api = _get_rest_api_or_error(parser, config) + if len(args.record_types) == 0: + logger.warning( + "Fetching OpenAPI spec for ALL known record types... This will take a long " + "time! (Consider providing only the record types of interest by passing " + "their names to this command as positional arguments)" + ) + else: + rt_str = ", ".join(args.record_types) + logger.info(f"Fetching OpenAPI spec for record types {rt_str}...") + spec = await rest_api.openapi(args.record_types) + tempdir = pathlib.Path(tempfile.mkdtemp()) + openapi_file = tempdir / "openapi.json" + html_file = tempdir / "index.html" + openapi_file.write_bytes(json.dumps(spec)) + html = """ + + + + NetSuite REST Record API + + +
+
+ + + + + """ + html_file.write_text(html) + handler_class = functools.partial( + http.server.SimpleHTTPRequestHandler, + directory=str(tempdir), + ) + logger.info( + f"NetSuite REST Record API docs available at http://{args.bind}:{args.port}" + ) + try: + http.server.test( + HandlerClass=handler_class, + ServerClass=http.server.ThreadingHTTPServer, + port=args.port, + bind=args.bind, + ) + finally: + html_file.unlink() + openapi_file.unlink() + tempdir.rmdir() + + p = subparser.add_parser( + "openapi-serve", + description="Start a HTTP server on localhost serving the OpenAPI spec via Swagger UI", + ) + p.set_defaults(func=rest_api_openapi_serve) + p.add_argument( + "record_types", + metavar="record_type", + nargs="*", + help="The record type(s) to get OpenAPI spec for. If not provided the OpenAPI spec for all known record types will be retrieved.", + ) + p.add_argument("-p", "--port", default=8000, type=int, help="The port to listen to") + p.add_argument("-b", "--bind", default="127.0.0.1", help="The host to bind to") + + +def _get_rest_api_or_error(parser, config: Config): + ns = NetSuite(config) + + try: + return ns.rest_api # Cached property that initializes NetSuiteRestApi + except RuntimeError as ex: + parser.error(str(ex)) + + +def _parse_headers_arg( + parser, + headers: Optional[List[str]], +) -> ParsedHeaders: + out: ParsedHeaders = {} + + if headers is None: + headers = [] + + for raw_header in headers: + err = False + try: + k, v = raw_header.split(":", maxsplit=1) + except ValueError: + err = True + else: + k, v = (k.strip(), v.strip()) + if not k or not v: + err = True + if err: + parser.error( + f"Invalid header: `{raw_header}``. Should have format: `NAME: VALUE`" + ) + else: + existing = out.get(k) + if existing: + if isinstance(existing, list): + existing.append(v) + else: + out[k] = [existing, v] + else: + out[k] = v + return out + + +def _add_rest_api_headers_arg(parser): + parser.add_argument( + "-H", + "--header", + action="append", + help="Headers to append. Can be specified multiple time and the format for each is `KEY: VALUE`", + ) diff --git a/netsuite/cli/restlet.py b/netsuite/cli/restlet.py new file mode 100644 index 0000000..86a55c5 --- /dev/null +++ b/netsuite/cli/restlet.py @@ -0,0 +1,108 @@ +import argparse + +from .. import json +from ..client import NetSuite +from ..config import Config + +__all__ = () + + +def add_parser(parser, subparser): + restlet_parser = subparser.add_parser( + "restlet", description="Make NetSuite Restlet requests" + ) + restlet_subparser = restlet_parser.add_subparsers() + _add_restlet_get_parser(restlet_parser, restlet_subparser) + _add_restlet_post_parser(restlet_parser, restlet_subparser) + _add_restlet_put_parser(restlet_parser, restlet_subparser) + _add_restlet_delete_parser(restlet_parser, restlet_subparser) + + return (restlet_parser, restlet_subparser) + + +def _add_restlet_get_parser(parser, subparser): + async def restlet_get(config, args) -> str: + restlet = _get_restlet_or_error(parser, config) + + resp = await restlet.get(script_id=args.script_id, deploy=args.deploy) + return json.dumps(resp) + + p = subparser.add_parser( + "get", description="Make a GET request to NetSuite Restlet" + ) + _add_default_restlet_args(p) + p.set_defaults(func=restlet_get) + + +def _add_restlet_post_parser(parser, subparser): + async def restlet_post(config, args) -> str: + restlet = _get_restlet_or_error(parser, config) + + with args.payload_file as fh: + payload_str = fh.read() + + payload = json.loads(payload_str) + + resp = await restlet.post( + script_id=args.script_id, deploy=args.deploy, json=payload + ) + return json.dumps(resp) + + p = subparser.add_parser( + "post", description="Make a POST request to NetSuite Restlet" + ) + p.set_defaults(func=restlet_post) + _add_default_restlet_args(p) + p.add_argument("payload_file", type=argparse.FileType("r")) + + +def _add_restlet_put_parser(parser, subparser): + async def restlet_put(config, args) -> str: + restlet = _get_restlet_or_error(parser, config) + + with args.payload_file as fh: + payload_str = fh.read() + + payload = json.loads(payload_str) + + resp = await restlet.put( + script_id=args.script_id, deploy=args.deploy, json=payload + ) + return json.dumps(resp) + + p = subparser.add_parser( + "put", description="Make a PUT request to NetSuite Restlet" + ) + p.set_defaults(func=restlet_put) + _add_default_restlet_args(p) + p.add_argument("payload_file", type=argparse.FileType("r")) + + +def _add_restlet_delete_parser(parser, subparser): + async def restlet_delete(config, args) -> str: + restlet = _get_restlet_or_error(parser, config) + + resp = await restlet.put(script_id=args.script_id, deploy=args.deploy) + return json.dumps(resp) + + p = subparser.add_parser( + "delete", description="Make a DELETE request to a NetSuite Restlet" + ) + p.set_defaults(func=restlet_delete) + _add_default_restlet_args(p) + + +def _get_restlet_or_error(parser, config: Config): + ns = NetSuite(config) + + try: + return ns.restlet # Cached property that initializes NetSuiteRestlet + except RuntimeError as ex: + parser.error(str(ex)) + + +def _add_default_restlet_args(parser_: argparse.ArgumentParser): + parser_.add_argument("script_id", type=int, help="The script to run") + parser_.add_argument( + "-d", "--deploy", type=int, default=1, help="The deployment version" + ) diff --git a/netsuite/cli/soap_api.py b/netsuite/cli/soap_api.py new file mode 100644 index 0000000..2390a63 --- /dev/null +++ b/netsuite/cli/soap_api.py @@ -0,0 +1,78 @@ +from .. import json +from ..client import NetSuite +from ..config import Config +from ..soap_api import helpers + +__all__ = () + + +def add_parser(parser, subparser): + soap_api_parser = subparser.add_parser( + "soap-api", description="Make NetSuite SuiteTalk Web Services SOAP requests" + ) + soap_api_subparser = soap_api_parser.add_subparsers() + _add_get_parser(soap_api_parser, soap_api_subparser) + _add_get_list_parser(soap_api_parser, soap_api_subparser) + + return (soap_api_parser, soap_api_subparser) + + +def _add_get_list_parser(parser, subparser): + async def getList(config, args) -> str: + soap_api = _get_soap_api_or_error(parser, config) + resp = await soap_api.getList( + args.record_type, externalIds=args.externalId, internalIds=args.internalId + ) + return _dump_response(resp) + + p = subparser.add_parser("getList", description="Call the getList method") + p.add_argument("record_type", help="The record type to get") + p.add_argument( + "-e", + "--externalId", + action="append", + help="External IDs to get", + ) + p.add_argument( + "-i", + "--internalId", + action="append", + help="Internal IDs to get", + ) + p.set_defaults(func=getList) + + +def _add_get_parser(parser, subparser): + async def get(config, args) -> str: + soap_api = _get_soap_api_or_error(parser, config) + resp = await soap_api.get( + args.record_type, externalId=args.externalId, internalId=args.internalId + ) + return _dump_response(resp) + + p = subparser.add_parser("get", description="Call the `get` method") + p.add_argument("record_type", help="The record type to get") + p.add_argument( + "-e", + "--externalId", + help="External ID to get", + ) + p.add_argument( + "-i", + "--internalId", + help="Internal ID to get", + ) + p.set_defaults(func=get) + + +def _get_soap_api_or_error(parser, config: Config): + ns = NetSuite(config) + + try: + return ns.soap_api # Cached property that initializes NetSuiteRestApi + except RuntimeError as ex: + parser.error(str(ex)) + + +def _dump_response(resp) -> str: + return json.dumps(helpers.to_builtin(resp)) diff --git a/netsuite/client.py b/netsuite/client.py old mode 100755 new mode 100644 index 1e7f72e..598e87f --- a/netsuite/client.py +++ b/netsuite/client.py @@ -1,660 +1,36 @@ -import logging -import re -import warnings -from contextlib import contextmanager -from datetime import datetime -from functools import wraps -from typing import Any, Callable, Dict, List, Optional, Sequence, Union -from urllib.parse import urlparse +from typing import Any, Dict, Optional -import requests -import zeep -from zeep.cache import SqliteCache -from zeep.transports import Transport -from zeep.xsd.valueobjects import CompoundValue - -from . import constants, helpers, passport from .config import Config from .rest_api import NetSuiteRestApi -from .restlet import NetsuiteRestlet +from .restlet import NetSuiteRestlet +from .soap_api import NetSuiteSoapApi from .util import cached_property -logger = logging.getLogger(__name__) - - -class NetsuiteResponseError(Exception): - """Raised when a Netsuite result was marked as unsuccessful""" - - -def WebServiceCall( - path: str = None, - extract: Callable = None, - *, - default: Any = constants.NOT_SET, -) -> Callable: - """ - Decorator for NetSuite methods returning SOAP responses - - Args: - path: - A dot-separated path for specifying where relevant data resides (where the `status` attribute is set) - extract: - A function to extract data from response before returning it. - default: - If the existing path does not exist in response, return this - instead. - - Returns: - Decorator to use on `NetSuite` web service methods - """ - - def decorator(fn): - @wraps(fn) - def wrapper(self, *args, **kw): - response = fn(self, *args, **kw) - - if path is not None: - for part in path.split("."): - try: - response = getattr(response, part) - except AttributeError: - if default is constants.NOT_SET: - raise - else: - return default - - try: - response_status = response["status"] - except TypeError: - response_status = None - for record in response: - # NOTE: Status is set on each returned record for lists, - # really strange... - response_status = record["status"] - break - - is_success = response_status["isSuccess"] - - if not is_success: - response_detail = response_status["statusDetail"] - raise NetsuiteResponseError(response_detail) - - if extract is not None: - response = extract(response) - - return response - - return wrapper - - return decorator - - -class NetSuiteTransport(Transport): - """ - NetSuite dynamic domain wrapper for zeep.transports.transport - - Latest NetSuite WSDL now uses relative definition addresses - - zeep maps reflective remote calls to the base WSDL address, - rather than the dynamic subscriber domain - - Wrap the zeep transports service with our address modifications - """ - - def __init__(self, wsdl_url, *args, **kwargs): - """ - Assign the dynamic host domain component to a class variable - """ - parsed_wsdl_url = urlparse(wsdl_url) - self._wsdl_url = f"{parsed_wsdl_url.scheme}://{parsed_wsdl_url.netloc}/" - - super().__init__(**kwargs) - - def _fix_address(self, address): - """ - Munge the address to the dynamic domain, not the default - """ - - idx = address.index("/", 8) + 1 - address = self._wsdl_url + address[idx:] - return address - - def get(self, address, params, headers): - """ - Update the GET address before providing it to zeep.transports.transport - """ - return super().get(self._fix_address(address), params, headers) - - def post(self, address, message, headers): - """ - Update the POST address before providing it to zeep.transports.transport - """ - return super().post(self._fix_address(address), message, headers) +__all__ = ("NetSuite",) class NetSuite: - version = "2019.2.0" - wsdl_url_tmpl = "https://{account_id}.suitetalk.api.netsuite.com/wsdl/v{underscored_version}/netsuite.wsdl" - - def __repr__(self) -> str: - return f"" - def __init__( self, - config: Union[Config, Dict[str, Any]], + config: Config, *, - version: str = None, - wsdl_url: str = None, - cache: zeep.cache.Base = None, - session: requests.Session = None, - sandbox: bool = None, - ) -> None: - - if sandbox is not None: - warnings.warn( - "The `sandbox` flag has been deprecated and no longer has " - "any effect. Please locate the correct account ID for your " - "sandbox instead (usually `_SB1`)", - DeprecationWarning, - ) - - if version is not None: - assert re.match(r"\d+\.\d+\.\d+", version) - self.version = version - - self.__config = self._make_config(config) - self.__wsdl_url = wsdl_url - self.__cache = cache - self.__session = session - - @cached_property - def restlet(self): - return NetsuiteRestlet(self.__config) - - @cached_property - def rest_api(self): - if self.__config.is_token_auth(): - return NetSuiteRestApi( - account=self.config.account, - consumer_key=self.config.consumer_key, - consumer_secret=self.config.consumer_secret, - token_id=self.config.token_id, - token_secret=self.config.token_secret, - ) - else: - raise RuntimeError("Rest API is currently only implemented with token auth") - - @cached_property - def wsdl_url(self) -> str: - return self.__wsdl_url or self._generate_wsdl_url() - - @cached_property - def cache(self) -> zeep.cache.Base: - return self.__cache or self._generate_cache() - - @cached_property - def session(self) -> requests.Session: - return self.__session or self._generate_session() - - @cached_property - def client(self) -> zeep.Client: - return self._generate_client() - - @cached_property - def transport(self): - return self._generate_transport() - - @property - def config(self) -> Config: - return self.__config - - @cached_property - def hostname(self) -> str: - return self.wsdl_url.replace("https://", "").partition("/")[0] - - @property - def service(self) -> zeep.client.ServiceProxy: - return self.client.service - - def _make_config(self, values_obj: Union[Config, Dict[str, Any]]) -> Config: - if isinstance(values_obj, Config): - return values_obj - return Config(**values_obj) - - @property - def underscored_version(self) -> str: - return self.version.replace(".", "_") - - @property - def underscored_version_no_micro(self) -> str: - return self.underscored_version.rpartition("_")[0] - - def _generate_wsdl_url(self) -> str: - return self.wsdl_url_tmpl.format( - underscored_version=self.underscored_version, - # https://followingnetsuite.wordpress.com/2018/10/18/suitetalk-sandbox-urls-addendum/ - account_id=(self.config.account or "").lower().replace("_", "-"), - ) - - def _generate_cache(self) -> zeep.cache.Base: - return SqliteCache(timeout=60 * 60 * 24 * 365) - - def _generate_session(self) -> requests.Session: - return requests.Session() - - def _generate_transport(self) -> zeep.transports.Transport: - return NetSuiteTransport( - self._generate_wsdl_url(), - session=self.session, - cache=self.cache, - ) - - def generate_passport(self) -> Dict: - return passport.make(self, self.config) - - def to_builtin(self, obj, *args, **kw): - """Turn zeep XML object into python built-in data structures""" - return helpers.to_builtin(obj, *args, **kw) - - @contextmanager - def with_timeout(self, timeout: int): - """Run SuiteTalk operation with the specified timeout""" - with self.transport.settings(timeout=timeout): - yield - - @staticmethod - def _set_default_soapheaders(client: zeep.Client, preferences: dict = None) -> None: - client.set_default_soapheaders( - { - # https://netsuite.custhelp.com/app/answers/detail/a_id/40934 - # (you need to be logged in to SuiteAnswers for this link to work) - # 'preferences': { - # 'warningAsError': True/False, - # 'disableMandatoryCustomFieldValidation': True/False, - # 'disableSystemNotesForCustomFields': True/False, - # 'ignoreReadOnlyFields': True/False, - # 'runServerSuiteScriptAndTriggerWorkflows': True/False, - # }, - } - ) - - def _generate_client(self) -> zeep.Client: - client = zeep.Client( - self.wsdl_url, - transport=self.transport, - ) - self._set_default_soapheaders( - client, - preferences=self.config.preferences, - ) - return client - - def _get_namespace(self, name: str, sub_namespace: str) -> str: - return "urn:{name}_{version}.{sub_namespace}.webservices.netsuite.com".format( - name=name, - version=self.underscored_version_no_micro, - sub_namespace=sub_namespace, - ) - - def _type_factory(self, name: str, sub_namespace: str) -> zeep.client.Factory: - return self.client.type_factory(self._get_namespace(name, sub_namespace)) - - @cached_property - def Core(self) -> zeep.client.Factory: - return self._type_factory("core", "platform") - - @cached_property - def CoreTypes(self) -> zeep.client.Factory: - return self._type_factory("types.core", "platform") - - @cached_property - def FaultsTypes(self) -> zeep.client.Factory: - return self._type_factory("types.faults", "platform") - - @cached_property - def Faults(self) -> zeep.client.Factory: - return self._type_factory("faults", "platform") - - @cached_property - def Messages(self) -> zeep.client.Factory: - return self._type_factory("messages", "platform") - - @cached_property - def Common(self) -> zeep.client.Factory: - return self._type_factory("common", "platform") - - @cached_property - def CommonTypes(self) -> zeep.client.Factory: - return self._type_factory("types.common", "platform") - - @cached_property - def Scheduling(self) -> zeep.client.Factory: - return self._type_factory("scheduling", "activities") - - @cached_property - def SchedulingTypes(self) -> zeep.client.Factory: - return self._type_factory("types.scheduling", "activities") - - @cached_property - def Communication(self) -> zeep.client.Factory: - return self._type_factory("communication", "general") - - @cached_property - def CommunicationTypes(self) -> zeep.client.Factory: - return self._type_factory("types.communication", "general") + soap_api_options: Optional[Dict[str, Any]] = None, + rest_api_options: Optional[Dict[str, Any]] = None, + restlet_options: Optional[Dict[str, Any]] = None, + ): + self._config = config + self._soap_api_options = soap_api_options or {} + self._rest_api_options = rest_api_options or {} + self._restlet_options = restlet_options or {} @cached_property - def Filecabinet(self) -> zeep.client.Factory: - return self._type_factory("filecabinet", "documents") + def rest_api(self) -> NetSuiteRestApi: + return NetSuiteRestApi(self._config, **self._rest_api_options) @cached_property - def FilecabinetTypes(self) -> zeep.client.Factory: - return self._type_factory("types.filecabinet", "documents") + def soap_api(self) -> NetSuiteSoapApi: + return NetSuiteSoapApi(self._config, **self._soap_api_options) @cached_property - def Relationships(self) -> zeep.client.Factory: - return self._type_factory("relationships", "lists") - - @cached_property - def RelationshipsTypes(self) -> zeep.client.Factory: - return self._type_factory("types.relationships", "lists") - - @cached_property - def Support(self) -> zeep.client.Factory: - return self._type_factory("support", "lists") - - @cached_property - def SupportTypes(self) -> zeep.client.Factory: - return self._type_factory("types.support", "lists") - - @cached_property - def Accounting(self) -> zeep.client.Factory: - return self._type_factory("accounting", "lists") - - @cached_property - def AccountingTypes(self) -> zeep.client.Factory: - return self._type_factory("types.accounting", "lists") - - @cached_property - def Sales(self) -> zeep.client.Factory: - return self._type_factory("sales", "transactions") - - @cached_property - def SalesTypes(self) -> zeep.client.Factory: - return self._type_factory("types.sales", "transactions") - - @cached_property - def Purchases(self) -> zeep.client.Factory: - return self._type_factory("purchases", "transactions") - - @cached_property - def PurchasesTypes(self) -> zeep.client.Factory: - return self._type_factory("types.purchases", "transactions") - - @cached_property - def Customers(self) -> zeep.client.Factory: - return self._type_factory("customers", "transactions") - - @cached_property - def CustomersTypes(self) -> zeep.client.Factory: - return self._type_factory("types.customers", "transactions") - - @cached_property - def Financial(self) -> zeep.client.Factory: - return self._type_factory("financial", "transactions") - - @cached_property - def FinancialTypes(self) -> zeep.client.Factory: - return self._type_factory("types.financial", "transactions") - - @cached_property - def Bank(self) -> zeep.client.Factory: - return self._type_factory("bank", "transactions") - - @cached_property - def BankTypes(self) -> zeep.client.Factory: - return self._type_factory("types.bank", "transactions") - - @cached_property - def Inventory(self) -> zeep.client.Factory: - return self._type_factory("inventory", "transactions") - - @cached_property - def InventoryTypes(self) -> zeep.client.Factory: - return self._type_factory("types.inventory", "transactions") - - @cached_property - def General(self) -> zeep.client.Factory: - return self._type_factory("general", "transactions") - - @cached_property - def Customization(self) -> zeep.client.Factory: - return self._type_factory("customization", "setup") - - @cached_property - def CustomizationTypes(self) -> zeep.client.Factory: - return self._type_factory("types.customization", "setup") - - @cached_property - def Employees(self) -> zeep.client.Factory: - return self._type_factory("employees", "lists") - - @cached_property - def EmployeesTypes(self) -> zeep.client.Factory: - return self._type_factory("types.employees", "lists") - - @cached_property - def Website(self) -> zeep.client.Factory: - return self._type_factory("website", "lists") - - @cached_property - def WebsiteTypes(self) -> zeep.client.Factory: - return self._type_factory("types.website", "lists") - - @cached_property - def EmployeesTransactions(self) -> zeep.client.Factory: - return self._type_factory("employees", "transactions") - - @cached_property - def EmployeesTransactionsTypes(self) -> zeep.client.Factory: - return self._type_factory("types.employees", "transactions") - - @cached_property - def Marketing(self) -> zeep.client.Factory: - return self._type_factory("marketing", "lists") - - @cached_property - def MarketingTypes(self) -> zeep.client.Factory: - return self._type_factory("types.marketing", "lists") - - @cached_property - def DemandPlanning(self) -> zeep.client.Factory: - return self._type_factory("demandplanning", "transactions") - - @cached_property - def DemandPlanningTypes(self) -> zeep.client.Factory: - return self._type_factory("types.demandplanning", "transactions") - - @cached_property - def SupplyChain(self) -> zeep.client.Factory: - return self._type_factory("supplychain", "lists") - - @cached_property - def SupplyChainTypes(self) -> zeep.client.Factory: - return self._type_factory("types.supplychain", "lists") - - def request(self, service_name: str, *args, **kw): - """ - Make a web service request to NetSuite - - Args: - service_name: - The NetSuite service to call - Returns: - The response from NetSuite - """ - svc = getattr(self.service, service_name) - return svc(*args, _soapheaders=self.generate_passport(), **kw) - - @WebServiceCall( - "body.readResponseList.readResponse", - extract=lambda resp: [r["record"] for r in resp], - ) - def getList( - self, - recordType: str, - *, - internalIds: Optional[Sequence[int]] = None, - externalIds: Optional[Sequence[str]] = None, - ) -> List[CompoundValue]: - """Get a list of records""" - if internalIds is None: - internalIds = [] - else: - internalIds = list(internalIds) - if externalIds is None: - externalIds = [] - else: - externalIds = list(externalIds) - - if len(internalIds) + len(externalIds) == 0: - return [] - - return self.request( - "getList", - self.Messages.GetListRequest( - baseRef=[ - self.Core.RecordRef( - type=recordType, - internalId=internalId, - ) - for internalId in internalIds - ] - + [ - self.Core.RecordRef( - type=recordType, - externalId=externalId, - ) - for externalId in externalIds - ], - ), - ) - - @WebServiceCall( - "body.readResponse", - extract=lambda resp: resp["record"], - ) - def get( - self, recordType: str, *, internalId: int = None, externalId: str = None - ) -> CompoundValue: - """Get a single record""" - if len([v for v in (internalId, externalId) if v is not None]) != 1: - raise ValueError("Specify either `internalId` or `externalId`") - - if internalId: - record_ref = self.Core.RecordRef( - type=recordType, - internalId=internalId, - ) - else: - record_ref = self.Core.RecordRef( - type=recordType, - externalId=externalId, - ) - - return self.request("get", baseRef=record_ref) - - @WebServiceCall( - "body.getAllResult", - extract=lambda resp: resp["recordList"]["record"], - ) - def getAll(self, recordType: str) -> List[CompoundValue]: - """Get all records of a given type.""" - return self.request( - "getAll", - record=self.Core.GetAllRecord( - recordType=recordType, - ), - ) - - @WebServiceCall( - "body.writeResponse", - extract=lambda resp: resp["baseRef"], - ) - def add(self, record: CompoundValue) -> CompoundValue: - """Insert a single record.""" - return self.request("add", record=record) - - @WebServiceCall( - "body.writeResponse", - extract=lambda resp: resp["baseRef"], - ) - def update(self, record: CompoundValue) -> CompoundValue: - """Insert a single record.""" - return self.request("update", record=record) - - @WebServiceCall( - "body.writeResponse", - extract=lambda resp: resp["baseRef"], - ) - def upsert(self, record: CompoundValue) -> CompoundValue: - """Upsert a single record.""" - return self.request("upsert", record=record) - - @WebServiceCall( - "body.searchResult", - extract=lambda resp: resp["recordList"]["record"], - ) - def search(self, record: CompoundValue) -> List[CompoundValue]: - """Search records""" - return self.request("search", searchRecord=record) - - @WebServiceCall( - "body.writeResponseList", - extract=lambda resp: [record["baseRef"] for record in resp], - ) - def upsertList(self, records: List[CompoundValue]) -> List[CompoundValue]: - """Upsert a list of records.""" - return self.request("upsertList", record=records) - - @WebServiceCall( - "body.getItemAvailabilityResult", - extract=lambda resp: resp["itemAvailabilityList"]["itemAvailability"], - default=[], - ) - def getItemAvailability( - self, - *, - internalIds: Optional[Sequence[int]] = None, - externalIds: Optional[Sequence[str]] = None, - lastQtyAvailableChange: datetime = None, - ) -> List[Dict]: - if internalIds is None: - internalIds = [] - else: - internalIds = list(internalIds) - if externalIds is None: - externalIds = [] - else: - externalIds = list(externalIds) - - if len(internalIds) + len(externalIds) == 0: - return [] - - item_filters = [ - {"type": "inventoryItem", "internalId": internalId} - for internalId in internalIds - ] + [ - {"type": "inventoryItem", "externalId": externalId} - for externalId in externalIds - ] - - return self.request( - "getItemAvailability", - itemAvailabilityFilter=[ - { - "item": {"recordRef": item_filters}, - "lastQtyAvailableChange": lastQtyAvailableChange, - } - ], - ) + def restlet(self) -> NetSuiteRestlet: + return NetSuiteRestlet(self._config, **self._restlet_options) diff --git a/netsuite/config.py b/netsuite/config.py index a0d990e..d386b86 100644 --- a/netsuite/config.py +++ b/netsuite/config.py @@ -1,158 +1,50 @@ import configparser -from typing import Any, Dict, Optional, Tuple +from typing import Dict, Union -from .constants import DEFAULT_INI_PATH, DEFAULT_INI_SECTION, NOT_SET +from pydantic import BaseModel -TOKEN = "token" -CREDENTIALS = "credentials" +from .constants import DEFAULT_INI_PATH, DEFAULT_INI_SECTION +__all__ = ("Config", "TokenAuth") -class Config: - """ - Takes dictionary keys/values that will be set as attribute names/values - on the config object if they exist as attributes - Args: - **opts: - Dictionary keys/values that will be set as attribute names/values - """ +class TokenAuth(BaseModel): + consumer_key: str + consumer_secret: str + token_id: str + token_secret: str - auth_type: str = TOKEN - """The authentication type to use, either 'token' or 'credentials'""" - account: Optional[str] = None - """The NetSuite account ID""" +class Config(BaseModel): + account: str + auth: TokenAuth + # TODO: Support OAuth2 + # auth: Union[OAuth2, TokenAuth] - consumer_key: Optional[str] = None - """The OAuth 1.0 consumer key""" + @property + def account_slugified(self) -> str: + # https://followingnetsuite.wordpress.com/2018/10/18/suitetalk-sandbox-urls-addendum/ + return self.account.lower().replace("_", "-") - consumer_secret: Optional[str] = None - """The OAuth 1.0 consumer secret""" + @classmethod + def from_ini( + cls, path: str = DEFAULT_INI_PATH, section: str = DEFAULT_INI_SECTION + ) -> "Config": + iniconf = configparser.ConfigParser() + with open(path) as fp: + iniconf.read_file(fp) - token_id: Optional[str] = None - """The OAuth 1.0 token ID""" + d: Dict[str, Union[str, Dict[str, str]]] = {"auth": {}} - token_secret: Optional[str] = None - """The OAuth 1.0 token secret""" + auth_type = iniconf[section].get("auth_type", "token") - application_id: Optional[str] = None - """Application ID, used with auth_type=credentials""" + if auth_type != "token": + raise RuntimeError(f"Only token auth is supported, not `{auth_type}`") - email: Optional[str] = None - """Account e-mail, used with auth_type=credentials""" - - password: Optional[str] = None - """Account password, used with auth_type=credentials""" - - preferences = None - """Additional preferences""" - - _settings_mapping: Tuple[Tuple[str, Dict[str, Any]], ...] = ( - ( - "account", - {"type": str, "required": True}, - ), - ( - "consumer_key", - {"type": str, "required_for_auth_type": TOKEN}, - ), - ( - "consumer_secret", - {"type": str, "required_for_auth_type": TOKEN}, - ), - ( - "token_id", - {"type": str, "required_for_auth_type": TOKEN}, - ), - ( - "token_secret", - {"type": str, "required_for_auth_type": TOKEN}, - ), - ( - "application_id", - {"type": str, "required_for_auth_type": CREDENTIALS}, - ), - ( - "email", - {"type": str, "required_for_auth_type": CREDENTIALS}, - ), - ( - "password", - {"type": str, "required_for_auth_type": CREDENTIALS}, - ), - ( - "preferences", - {"type": dict, "required": False, "default": lambda: {}}, - ), - ) - - def __init__(self, **opts): - self._set(opts) - - def __contains__(self, key: str) -> bool: - return hasattr(self, key) - - def _set_auth_type(self, value: str): - self._validate_attr("auth_type", value, str, True, {}) - self.auth_type = value - assert self.auth_type in (TOKEN, CREDENTIALS) - - def is_token_auth(self) -> bool: - return self.auth_type == TOKEN - - def is_credentials_auth(self) -> bool: - return self.auth_type == CREDENTIALS - - def _set(self, dct: Dict[str, Any]): - # As other setting validations depend on auth_type we set it first - auth_type = dct.get("auth_type", self.auth_type) - self._set_auth_type(auth_type) - - for attr, opts in self._settings_mapping: - value = dct.get(attr, NOT_SET) - type_ = opts["type"] - - required = opts.get( - "required", opts.get("required_for_auth_type") == auth_type - ) - - self._validate_attr(attr, value, type_, required, opts) - - if value is NOT_SET and "default" in opts: - value = opts["default"]() - - setattr(self, attr, (None if value is NOT_SET else value)) - - def _validate_attr( - self, attr: str, value: Any, type_: Any, required: bool, opts: Dict[str, Any] - ): - if required and value is NOT_SET: - required_for_auth_type = opts.get("required_for_auth_type") - if required_for_auth_type: - raise ValueError( - f"Attribute {attr} is required for auth_type=" - f"`{required_for_auth_type}`" - ) + for key, val in iniconf[section].items(): + if auth_type == "token" and key in TokenAuth.__fields__: + d["auth"][key] = val # type: ignore[index] else: - raise ValueError(f"Attribute {attr} is required") - if value is not NOT_SET and not isinstance(value, type_): - raise ValueError(f"Attribute {attr} is not of type `{type_}`") - - -def from_ini( - path: str = DEFAULT_INI_PATH, section: str = DEFAULT_INI_SECTION -) -> Config: - iniconf = configparser.ConfigParser() - with open(path) as fp: - iniconf.read_file(fp) - - config_dict: Dict[str, Any] = {"preferences": {}} - - for key, val in iniconf[section].items(): - if key.startswith("preferences_"): - _, key = key.split("_", 1) - config_dict["preferences"][key] = val - else: - config_dict[key] = val + d[key] = val - return Config(**config_dict) + return cls(**d) diff --git a/netsuite/exceptions.py b/netsuite/exceptions.py new file mode 100644 index 0000000..10ecbf0 --- /dev/null +++ b/netsuite/exceptions.py @@ -0,0 +1,13 @@ +class NetsuiteAPIRequestError(Exception): + """Raised when a Netsuite REST API request fails""" + + def __init__(self, status_code: int, response_text: str): + self.status_code = status_code + self.response_text = response_text + + def __str__(self): + return f"HTTP{self.status_code} - {self.response_text}" + + +class NetsuiteAPIResponseParsingError(NetsuiteAPIRequestError): + """Raised when parsing a Netsuite REST API response fails""" diff --git a/netsuite/json.py b/netsuite/json.py index 25bcf60..06ef74e 100644 --- a/netsuite/json.py +++ b/netsuite/json.py @@ -5,20 +5,27 @@ from typing import Any, Callable, Dict, Type, Union from uuid import UUID -import orjson as _orjson - __all__ = ("dumps", "loads") -loads = _orjson.loads +try: + import orjson as _json +except ImportError: + import json as _json # type: ignore[no-redef] + + HAS_ORJSON = False +else: + HAS_ORJSON = True -def dumps(obj: Any, *args, **kw) -> bytes: - kw["default"] = _orjson_default - return _orjson.dumps(obj, *args, **kw) +loads = _json.loads -def dumps_str(obj: Any, *args, **kw) -> str: - return dumps(obj, *args, **kw).decode("utf-8") +def dumps(obj: Any, *args, **kw) -> str: + if HAS_ORJSON: + kw["default"] = _orjson_default + return _json.dumps(obj, *args, **kw).decode("utf-8") + else: + return _json.dumps(obj, *args, **kw) # type: ignore[return-value] def _orjson_default(obj: Any) -> Any: diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index fd11c00..c509dda 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -1,101 +1,48 @@ -import asyncio import logging from typing import Sequence -from . import json -from .util import cached_property - -try: - import httpx -except ImportError: - - class httpx: # type: ignore[no-redef] - Response = None - - -try: - from authlib.integrations.httpx_client import OAuth1Auth -except ImportError: - OAuth1Auth = None - +from .config import Config +from .rest_api_base import RestApiBase logger = logging.getLogger(__name__) __all__ = ("NetSuiteRestApi",) -class NetsuiteAPIRequestError(Exception): - """Raised when a Netsuite REST API request fails""" - - def __init__(self, status_code: int, response_text: str): - self.status_code = status_code - self.response_text = response_text - - def __str__(self): - return f"HTTP{self.status_code} - {self.response_text}" - - -class NetsuiteAPIResponseParsingError(NetsuiteAPIRequestError): - """Raised when parsing a Netsuite REST API response fails""" - - -class NetSuiteRestApi: +class NetSuiteRestApi(RestApiBase): def __init__( self, + config: Config, *, - account: str, - consumer_key: str, - consumer_secret: str, - token_id: str, - token_secret: str, default_timeout: int = 60, concurrent_requests: int = 10, ): - if not self.has_required_dependencies(): - raise RuntimeError( - "Missing required dependencies for REST API support. " - "Install with `pip install netsuite[rest_api]`" - ) - self._account = account - self._consumer_key = consumer_key - self._consumer_secret = consumer_secret - self._token_id = token_id - self._token_secret = token_secret + self._config = config self._hostname = self._make_hostname() self._default_timeout = default_timeout self._concurrent_requests = concurrent_requests - @cached_property - def request_semaphore(self) -> asyncio.Semaphore: - # NOTE: Shouldn't be put in __init__ as we might not have a running - # event loop at that time. - return asyncio.Semaphore(self._concurrent_requests) - - @classmethod - def has_required_dependencies(cls) -> bool: - return httpx is not None and OAuth1Auth is not None - async def get(self, subpath: str, **request_kw): - return await self.request("GET", subpath, **request_kw) + return await self._request("GET", subpath, **request_kw) async def post(self, subpath: str, **request_kw): - return await self.request( + return await self._request( "POST", subpath, **request_kw, ) async def put(self, subpath: str, **request_kw): - return await self.request("PUT", subpath, **request_kw) + return await self._request("PUT", subpath, **request_kw) async def patch(self, subpath: str, **request_kw): - return await self.request("PATCH", subpath, **request_kw) + return await self._request("PATCH", subpath, **request_kw) async def delete(self, subpath: str, **request_kw): - return await self.request("DELETE", subpath, **request_kw) + return await self._request("DELETE", subpath, **request_kw) async def suiteql(self, q: str, limit: int = 10, offset: int = 0, **request_kw): - return await self.request( + return await self._request( "POST", "/query/v1/suiteql", headers={"Prefer": "transient", **request_kw.pop("headers", {})}, @@ -109,7 +56,7 @@ async def jsonschema(self, record_type: str, **request_kw): "Accept": "application/schema+json", **request_kw.pop("headers", {}), } - return await self.request( + return await self._request( "GET", f"/record/v1/metadata-catalog/{record_type}", headers=headers, @@ -126,7 +73,7 @@ async def openapi(self, record_types: Sequence[str] = (), **request_kw): if len(record_types) > 0: params["select"] = ",".join(record_types) - return await self.request( + return await self._request( "GET", "/record/v1/metadata-catalog", headers=headers, @@ -134,72 +81,12 @@ async def openapi(self, record_types: Sequence[str] = (), **request_kw): **request_kw, ) - async def request(self, method: str, subpath: str, **request_kw): - resp = await self._raw_request(method, subpath, **request_kw) - - if resp.status_code < 200 or resp.status_code > 299: - raise NetsuiteAPIRequestError(resp.status_code, resp.text) - - if resp.status_code == 204: - return None - else: - try: - return json.loads(resp.text) - except Exception: - raise NetsuiteAPIResponseParsingError(resp.status_code, resp.text) - - async def _raw_request( - self, method: str, subpath: str, **request_kw - ) -> httpx.Response: - method = method.upper() - url = self._make_url(subpath) - headers = {**self._make_default_headers(), **request_kw.pop("headers", {})} - - timeout = request_kw.pop("timeout", self._default_timeout) - - if "json" in request_kw: - request_kw["data"] = json.dumps_str(request_kw.pop("json")) - - kw = {**request_kw} - logger.debug( - f"Making {method.upper()} request to {url}. Keyword arguments: {kw}" - ) - - async with self.request_semaphore: - async with httpx.AsyncClient() as c: - resp = await c.request( - method=method, - url=url, - headers=headers, - auth=self._make_auth(), - timeout=timeout, - **kw, - ) - - resp_headers_json = json.dumps_str(dict(resp.headers)) - logger.debug( - f"Got response headers from NetSuite REST API: {resp_headers_json}" - ) - - return resp - def _make_hostname(self): - account_slugified = self._account.lower().replace("_", "-") - return f"{account_slugified}.suitetalk.api.netsuite.com" + return f"{self._config.account_slugified}.suitetalk.api.netsuite.com" def _make_url(self, subpath: str): return f"https://{self._hostname}/services/rest{subpath}" - def _make_auth(self): - return OAuth1Auth( - client_id=self._consumer_key, - client_secret=self._consumer_secret, - token=self._token_id, - token_secret=self._token_secret, - realm=self._account, - force_include_body=True, - ) - def _make_default_headers(self): return { "Content-Type": "application/json", diff --git a/netsuite/rest_api_base.py b/netsuite/rest_api_base.py new file mode 100644 index 0000000..27f5c79 --- /dev/null +++ b/netsuite/rest_api_base.py @@ -0,0 +1,99 @@ +import asyncio +import logging + +from . import json +from .exceptions import NetsuiteAPIRequestError, NetsuiteAPIResponseParsingError +from .util import cached_property + +try: + import httpx +except ImportError: + + class httpx: # type: ignore[no-redef] + Response = None + + +try: + from authlib.integrations.httpx_client import OAuth1Auth +except ImportError: + OAuth1Auth = None + +__all__ = ("RestApiBase",) + +logger = logging.getLogger(__name__) + + +class RestApiBase: + _concurrent_requests: int = 10 + _default_timeout: int = 10 + + @cached_property + def _request_semaphore(self) -> asyncio.Semaphore: + # NOTE: Shouldn't be put in __init__ as we might not have a running + # event loop at that time. + return asyncio.Semaphore(self._concurrent_requests) + + async def _request(self, method: str, subpath: str, **request_kw): + resp = await self._request_impl(method, subpath, **request_kw) + + if resp.status_code < 200 or resp.status_code > 299: + raise NetsuiteAPIRequestError(resp.status_code, resp.text) + + if resp.status_code == 204: + return None + else: + try: + return json.loads(resp.text) + except Exception: + raise NetsuiteAPIResponseParsingError(resp.status_code, resp.text) + + async def _request_impl( + self, method: str, subpath: str, **request_kw + ) -> httpx.Response: + method = method.upper() + url = self._make_url(subpath) + + headers = {**self._make_default_headers(), **request_kw.pop("headers", {})} + + timeout = request_kw.pop("timeout", self._default_timeout) + + if "json" in request_kw: + request_kw["data"] = json.dumps(request_kw.pop("json")) + + kw = {**request_kw} + logger.debug( + f"Making {method.upper()} request to {url}. Keyword arguments: {kw}" + ) + + async with self._request_semaphore: + async with httpx.AsyncClient() as c: + resp = await c.request( + method=method, + url=url, + headers=headers, + auth=self._make_auth(), + timeout=timeout, + **kw, + ) + + resp_headers_json = json.dumps(dict(resp.headers)) + logger.debug(f"Got response headers from NetSuite: {resp_headers_json}") + + return resp + + def _make_url(self, subpath: str): + raise NotImplementedError + + def _make_auth(self): + auth = self._config.auth + return OAuth1Auth( + client_id=auth.consumer_key, + client_secret=auth.consumer_secret, + token=auth.token_id, + token_secret=auth.token_secret, + realm=self._config.account, + force_include_body=True, + ) + + def _make_default_headers(self): + return {"Content-Type": "application/json"} diff --git a/netsuite/restlet.py b/netsuite/restlet.py index 63ab8df..97455e4 100644 --- a/netsuite/restlet.py +++ b/netsuite/restlet.py @@ -1,106 +1,47 @@ -import json import logging -from typing import Any, Tuple, Union -import requests_oauthlib - -from . import util +from .config import Config +from .rest_api_base import RestApiBase logger = logging.getLogger(__name__) +__all__ = ("NetSuiteRestlet",) -class NetsuiteRestlet: - - _restlet_path_tmpl = ( - "/app/site/hosting/restlet.nl?script={script_id}&deploy={deploy}" - ) - - def __init__(self, config, *, hostname=None): - self.__config = config - self.__hostname = hostname or self._make_default_hostname() - self._request_session = self._make_request_session() - @property - def config(self): - return self.__config - - @property - def hostname(self): - return self.__hostname - - def request( +class NetSuiteRestlet(RestApiBase): + def __init__( self, - script_id: int, - payload: Any = None, + config: Config, *, - deploy: int = 1, - raise_on_bad_status: bool = True, - timeout: Union[int, Tuple[int, int]] = None, - **requests_kw, + default_timeout: int = 60, + concurrent_requests: int = 10, ): - resp = self.raw_request( - script_id=script_id, - payload=payload, - deploy=deploy, - raise_on_bad_status=raise_on_bad_status, - timeout=timeout, - **requests_kw, - ) - util.raise_for_status_with_body(resp) - return resp.json() - - def raw_request( - self, - script_id: int, - payload: Any = None, - *, - deploy: int = 1, - raise_on_bad_status: bool = True, - timeout: Union[int, Tuple[int, int]] = None, - **requests_kw, - ): - url = self._make_url(script_id=script_id, deploy=deploy) - headers = self._make_headers() - - req_headers_json = json.dumps(headers) - logger.debug( - f"Making request to restlet at {url}. Payload {payload}. " - f"Headers: {req_headers_json}" - ) - - resp = self._request_session.post( - url, headers=headers, json=payload, timeout=timeout, **requests_kw - ) + self._config = config + self._hostname = self._make_hostname() + self._default_timeout = default_timeout + self._concurrent_requests = concurrent_requests - resp_headers_json = json.dumps(dict(resp.headers)) - logger.debug(f"Got response headers: {resp_headers_json}") + async def get(self, script_id: int, *, deploy: int = 1, **request_kw): + subpath = self._make_restlet_params(script_id, deploy) + return await self._request("GET", subpath, **request_kw) - return resp + async def post(self, script_id: int, *, deploy: int = 1, **request_kw): + subpath = self._make_restlet_params(script_id, deploy) + return await self._request("POST", subpath, **request_kw) - def _make_default_hostname(self): - account_slugified = self.config.account.lower().replace("_", "-") - return f"{account_slugified}.restlets.api.netsuite.com" + async def put(self, script_id: int, *, deploy: int = 1, **request_kw): + subpath = self._make_restlet_params(script_id, deploy) + return await self._request("PUT", subpath, **request_kw) - def _make_restlet_path(self, script_id: int, deploy: int = 1): - return self._restlet_path_tmpl.format( - script_id=script_id, - deploy=deploy, - ) + async def delete(self, script_id: int, *, deploy: int = 1, **request_kw): + subpath = self._make_restlet_params(script_id, deploy) + return await self._request("DELETE", subpath, **request_kw) - def _make_url(self, script_id: int, deploy: int = 1): - path = self._make_restlet_path(script_id=script_id, deploy=deploy) - return f"https://{self.hostname}{path}" + def _make_restlet_params(self, script_id: int, deploy: int = 1) -> str: + return f"?script={script_id}&deploy={deploy}" - def _make_request_session(self): - return requests_oauthlib.OAuth1Session( - client_key=self.config.consumer_key, - client_secret=self.config.consumer_secret, - resource_owner_key=self.config.token_id, - resource_owner_secret=self.config.token_secret, - realm=self.config.account, - ) + def _make_hostname(self): + return f"{self._config.account_slugified}.restlets.api.netsuite.com" - def _make_headers(self): - return { - "Content-Type": "application/json", - } + def _make_url(self, subpath: str) -> str: + return f"https://{self._hostname}/app/site/hosting/restlet.nl{subpath}" diff --git a/netsuite/soap_api/__init__.py b/netsuite/soap_api/__init__.py new file mode 100644 index 0000000..38ba87b --- /dev/null +++ b/netsuite/soap_api/__init__.py @@ -0,0 +1,2 @@ +from .client import * # noqa +from .exceptions import * # noqa diff --git a/netsuite/soap_api/client.py b/netsuite/soap_api/client.py new file mode 100644 index 0000000..f54ff65 --- /dev/null +++ b/netsuite/soap_api/client.py @@ -0,0 +1,538 @@ +import logging +import re +from contextlib import contextmanager +from datetime import datetime +from typing import Dict, List, Optional, Sequence + +import requests + +from ..config import Config +from ..util import cached_property +from . import helpers, passport, zeep +from .decorators import WebServiceCall +from .transports import AsyncNetSuiteTransport + +logger = logging.getLogger(__name__) + +__all__ = ("NetSuiteSoapApi",) + + +# TODO: Submit PR for the following changes (1170 uses a different method) +# This avoids the following warning on asyncio loop shutdown: +# +# UserWarning: Unclosed . +# See https://www.python-httpx.org/async/#opening-and-closing-clients +# for details. +# +class _AsyncClient(zeep.client.AsyncClient): + async def __aenter__(self): + await self.transport.client.__aenter__() + return self + + async def __aexit__(self, exc_type=None, exc_value=None, traceback=None) -> None: + await self.transport.client.__aexit__( + exc_type=exc_type, exc_value=exc_value, traceback=traceback + ) + + +class NetSuiteSoapApi: + version = "2021.1.0" + wsdl_url_tmpl = "https://{account_slug}.suitetalk.api.netsuite.com/wsdl/v{underscored_version}/netsuite.wsdl" + + def __init__( + self, + config: Config, + *, + version: str = None, + wsdl_url: str = None, + cache: zeep.cache.Base = None, + session: requests.Session = None, + ) -> None: + self._ensure_required_dependencies() + if version is not None: + assert re.match(r"\d+\.\d+\.\d+", version) + self.version = version + self.config = config + self.__wsdl_url = wsdl_url + self.__cache = cache + self.__session = session + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.hostname}({self.version})>" + + async def __aenter__(self): + await self.client.__aenter__() + return self + + async def __aexit__(self, exc_type=None, exc_value=None, traceback=None) -> None: + await self.client.__aexit__( + exc_type=exc_type, exc_value=exc_value, traceback=traceback + ) + + @cached_property + def wsdl_url(self) -> str: + return self.__wsdl_url or self._generate_wsdl_url() + + @cached_property + def cache(self) -> zeep.cache.Base: + return self.__cache or self._generate_cache() + + @cached_property + def session(self) -> requests.Session: + return self.__session or self._generate_session() + + @cached_property + def client(self) -> _AsyncClient: + return self._generate_client() + + @cached_property + def transport(self): + return self._generate_transport() + + @cached_property + def hostname(self) -> str: + return self.wsdl_url.replace("https://", "").partition("/")[0] + + @property + def service(self) -> zeep.client.ServiceProxy: + return self.client.service + + @property + def underscored_version(self) -> str: + return self.version.replace(".", "_") + + @property + def underscored_version_no_micro(self) -> str: + return self.underscored_version.rpartition("_")[0] + + def _generate_wsdl_url(self) -> str: + return self.wsdl_url_tmpl.format( + underscored_version=self.underscored_version, + account_slug=self.config.account_slugified, + ) + + def _generate_cache(self) -> zeep.cache.Base: + return zeep.cache.SqliteCache(timeout=60 * 60 * 24 * 365) + + def _generate_session(self) -> requests.Session: + return requests.Session() + + def _generate_transport(self) -> zeep.transports.AsyncTransport: + return AsyncNetSuiteTransport( + self._generate_wsdl_url(), + session=self.session, + cache=self.cache, + ) + + def generate_passport(self) -> Dict: + return passport.make(self, self.config) + + def to_builtin(self, obj, *args, **kw): + """Turn zeep XML object into python built-in data structures""" + return helpers.to_builtin(obj, *args, **kw) + + @contextmanager + def with_timeout(self, timeout: int): + """Run SuiteTalk operation with the specified timeout""" + with self.transport.settings(timeout=timeout): + yield + + def _generate_client(self) -> _AsyncClient: + return _AsyncClient( + self.wsdl_url, + transport=self.transport, + ) + + def _get_namespace(self, name: str, sub_namespace: str) -> str: + return "urn:{name}_{version}.{sub_namespace}.webservices.netsuite.com".format( + name=name, + version=self.underscored_version_no_micro, + sub_namespace=sub_namespace, + ) + + def _type_factory(self, name: str, sub_namespace: str) -> zeep.client.Factory: + return self.client.type_factory(self._get_namespace(name, sub_namespace)) + + @classmethod + def _ensure_required_dependencies(cls): + if not cls._has_required_dependencies(): + raise RuntimeError( + "Missing required dependencies for SOAP Web Services API support. " + "Install with `pip install netsuite[soap_api]`" + ) + + @classmethod + def _has_required_dependencies(cls) -> bool: + return zeep.ZEEP_INSTALLED + + @cached_property + def Core(self) -> zeep.client.Factory: + return self._type_factory("core", "platform") + + @cached_property + def CoreTypes(self) -> zeep.client.Factory: + return self._type_factory("types.core", "platform") + + @cached_property + def FaultsTypes(self) -> zeep.client.Factory: + return self._type_factory("types.faults", "platform") + + @cached_property + def Faults(self) -> zeep.client.Factory: + return self._type_factory("faults", "platform") + + @cached_property + def Messages(self) -> zeep.client.Factory: + return self._type_factory("messages", "platform") + + @cached_property + def Common(self) -> zeep.client.Factory: + return self._type_factory("common", "platform") + + @cached_property + def CommonTypes(self) -> zeep.client.Factory: + return self._type_factory("types.common", "platform") + + @cached_property + def Scheduling(self) -> zeep.client.Factory: + return self._type_factory("scheduling", "activities") + + @cached_property + def SchedulingTypes(self) -> zeep.client.Factory: + return self._type_factory("types.scheduling", "activities") + + @cached_property + def Communication(self) -> zeep.client.Factory: + return self._type_factory("communication", "general") + + @cached_property + def CommunicationTypes(self) -> zeep.client.Factory: + return self._type_factory("types.communication", "general") + + @cached_property + def Filecabinet(self) -> zeep.client.Factory: + return self._type_factory("filecabinet", "documents") + + @cached_property + def FilecabinetTypes(self) -> zeep.client.Factory: + return self._type_factory("types.filecabinet", "documents") + + @cached_property + def Relationships(self) -> zeep.client.Factory: + return self._type_factory("relationships", "lists") + + @cached_property + def RelationshipsTypes(self) -> zeep.client.Factory: + return self._type_factory("types.relationships", "lists") + + @cached_property + def Support(self) -> zeep.client.Factory: + return self._type_factory("support", "lists") + + @cached_property + def SupportTypes(self) -> zeep.client.Factory: + return self._type_factory("types.support", "lists") + + @cached_property + def Accounting(self) -> zeep.client.Factory: + return self._type_factory("accounting", "lists") + + @cached_property + def AccountingTypes(self) -> zeep.client.Factory: + return self._type_factory("types.accounting", "lists") + + @cached_property + def Sales(self) -> zeep.client.Factory: + return self._type_factory("sales", "transactions") + + @cached_property + def SalesTypes(self) -> zeep.client.Factory: + return self._type_factory("types.sales", "transactions") + + @cached_property + def Purchases(self) -> zeep.client.Factory: + return self._type_factory("purchases", "transactions") + + @cached_property + def PurchasesTypes(self) -> zeep.client.Factory: + return self._type_factory("types.purchases", "transactions") + + @cached_property + def Customers(self) -> zeep.client.Factory: + return self._type_factory("customers", "transactions") + + @cached_property + def CustomersTypes(self) -> zeep.client.Factory: + return self._type_factory("types.customers", "transactions") + + @cached_property + def Financial(self) -> zeep.client.Factory: + return self._type_factory("financial", "transactions") + + @cached_property + def FinancialTypes(self) -> zeep.client.Factory: + return self._type_factory("types.financial", "transactions") + + @cached_property + def Bank(self) -> zeep.client.Factory: + return self._type_factory("bank", "transactions") + + @cached_property + def BankTypes(self) -> zeep.client.Factory: + return self._type_factory("types.bank", "transactions") + + @cached_property + def Inventory(self) -> zeep.client.Factory: + return self._type_factory("inventory", "transactions") + + @cached_property + def InventoryTypes(self) -> zeep.client.Factory: + return self._type_factory("types.inventory", "transactions") + + @cached_property + def General(self) -> zeep.client.Factory: + return self._type_factory("general", "transactions") + + @cached_property + def Customization(self) -> zeep.client.Factory: + return self._type_factory("customization", "setup") + + @cached_property + def CustomizationTypes(self) -> zeep.client.Factory: + return self._type_factory("types.customization", "setup") + + @cached_property + def Employees(self) -> zeep.client.Factory: + return self._type_factory("employees", "lists") + + @cached_property + def EmployeesTypes(self) -> zeep.client.Factory: + return self._type_factory("types.employees", "lists") + + @cached_property + def Website(self) -> zeep.client.Factory: + return self._type_factory("website", "lists") + + @cached_property + def WebsiteTypes(self) -> zeep.client.Factory: + return self._type_factory("types.website", "lists") + + @cached_property + def EmployeesTransactions(self) -> zeep.client.Factory: + return self._type_factory("employees", "transactions") + + @cached_property + def EmployeesTransactionsTypes(self) -> zeep.client.Factory: + return self._type_factory("types.employees", "transactions") + + @cached_property + def Marketing(self) -> zeep.client.Factory: + return self._type_factory("marketing", "lists") + + @cached_property + def MarketingTypes(self) -> zeep.client.Factory: + return self._type_factory("types.marketing", "lists") + + @cached_property + def DemandPlanning(self) -> zeep.client.Factory: + return self._type_factory("demandplanning", "transactions") + + @cached_property + def DemandPlanningTypes(self) -> zeep.client.Factory: + return self._type_factory("types.demandplanning", "transactions") + + @cached_property + def SupplyChain(self) -> zeep.client.Factory: + return self._type_factory("supplychain", "lists") + + @cached_property + def SupplyChainTypes(self) -> zeep.client.Factory: + return self._type_factory("types.supplychain", "lists") + + async def request(self, service_name: str, *args, **kw): + """ + Make a web service request to NetSuite + + Args: + service_name: + The NetSuite service to call + Returns: + The response from NetSuite + """ + svc = getattr(self.service, service_name) + # NOTE: Using httpx context manager here + # This avoids the following error on asyncio close: + # + # UserWarning: Unclosed . + # See https://www.python-httpx.org/async/#opening-and-closing-clients + # for details. + # + async with self: + return await svc(*args, _soapheaders=self.generate_passport(), **kw) + + @WebServiceCall( + "body.readResponseList.readResponse", + extract=lambda resp: [r["record"] for r in resp], + ) + async def getList( + self, + recordType: str, + *, + internalIds: Optional[Sequence[int]] = None, + externalIds: Optional[Sequence[str]] = None, + ) -> List[zeep.xsd.CompoundValue]: + """Get a list of records""" + if internalIds is None: + internalIds = [] + else: + internalIds = list(internalIds) + if externalIds is None: + externalIds = [] + else: + externalIds = list(externalIds) + + if len(internalIds) + len(externalIds) == 0: + return [] + + return await self.request( + "getList", + self.Messages.GetListRequest( + baseRef=[ + self.Core.RecordRef( + type=recordType, + internalId=internalId, + ) + for internalId in internalIds + ] + + [ + self.Core.RecordRef( + type=recordType, + externalId=externalId, + ) + for externalId in externalIds + ], + ), + ) + + @WebServiceCall( + "body.readResponse", + extract=lambda resp: resp["record"], + ) + async def get( + self, recordType: str, *, internalId: int = None, externalId: str = None + ) -> zeep.xsd.CompoundValue: + """Get a single record""" + if len([v for v in (internalId, externalId) if v is not None]) != 1: + raise ValueError("Specify either `internalId` or `externalId`") + + if internalId: + record_ref = self.Core.RecordRef( + type=recordType, + internalId=internalId, + ) + else: + record_ref = self.Core.RecordRef( + type=recordType, + externalId=externalId, + ) + + return await self.request("get", baseRef=record_ref) + + @WebServiceCall( + "body.getAllResult", + extract=lambda resp: resp["recordList"]["record"], + ) + async def getAll(self, recordType: str) -> List[zeep.xsd.CompoundValue]: + """Get all records of a given type.""" + return await self.request( + "getAll", + record=self.Core.GetAllRecord( + recordType=recordType, + ), + ) + + @WebServiceCall( + "body.writeResponse", + extract=lambda resp: resp["baseRef"], + ) + async def add(self, record: zeep.xsd.CompoundValue) -> zeep.xsd.CompoundValue: + """Insert a single record.""" + return await self.request("add", record=record) + + @WebServiceCall( + "body.writeResponse", + extract=lambda resp: resp["baseRef"], + ) + async def update(self, record: zeep.xsd.CompoundValue) -> zeep.xsd.CompoundValue: + """Insert a single record.""" + return await self.request("update", record=record) + + @WebServiceCall( + "body.writeResponse", + extract=lambda resp: resp["baseRef"], + ) + async def upsert(self, record: zeep.xsd.CompoundValue) -> zeep.xsd.CompoundValue: + """Upsert a single record.""" + return await self.request("upsert", record=record) + + @WebServiceCall( + "body.searchResult", + extract=lambda resp: resp["recordList"]["record"], + ) + async def search( + self, record: zeep.xsd.CompoundValue + ) -> List[zeep.xsd.CompoundValue]: + """Search records""" + return await self.request("search", searchRecord=record) + + @WebServiceCall( + "body.writeResponseList", + extract=lambda resp: [record["baseRef"] for record in resp], + ) + async def upsertList( + self, records: List[zeep.xsd.CompoundValue] + ) -> List[zeep.xsd.CompoundValue]: + """Upsert a list of records.""" + return await self.request("upsertList", record=records) + + @WebServiceCall( + "body.getItemAvailabilityResult", + extract=lambda resp: resp["itemAvailabilityList"]["itemAvailability"], + default=[], + ) + async def getItemAvailability( + self, + *, + internalIds: Optional[Sequence[int]] = None, + externalIds: Optional[Sequence[str]] = None, + lastQtyAvailableChange: datetime = None, + ) -> List[Dict]: + if internalIds is None: + internalIds = [] + else: + internalIds = list(internalIds) + if externalIds is None: + externalIds = [] + else: + externalIds = list(externalIds) + + if len(internalIds) + len(externalIds) == 0: + return [] + + item_filters = [ + {"type": "inventoryItem", "internalId": internalId} + for internalId in internalIds + ] + [ + {"type": "inventoryItem", "externalId": externalId} + for externalId in externalIds + ] + + return await self.request( + "getItemAvailability", + itemAvailabilityFilter=[ + { + "item": {"recordRef": item_filters}, + "lastQtyAvailableChange": lastQtyAvailableChange, + } + ], + ) diff --git a/netsuite/soap_api/decorators.py b/netsuite/soap_api/decorators.py new file mode 100644 index 0000000..b00c375 --- /dev/null +++ b/netsuite/soap_api/decorators.py @@ -0,0 +1,73 @@ +from functools import wraps +from typing import Any, Callable + +from .. import constants +from . import zeep +from .exceptions import NetsuiteResponseError + +__all__ = ("WebServiceCall",) + + +def WebServiceCall( + path: str = None, + extract: Callable = None, + *, + default: Any = constants.NOT_SET, +) -> Callable: + """ + Decorator for NetSuite methods returning SOAP responses + + Args: + path: + A dot-separated path for specifying where relevant data resides (where the `status` attribute is set) + extract: + A function to extract data from response before returning it. + default: + If the existing path does not exist in response, return this + instead. + + Returns: + Decorator to use on `NetSuite` web service methods + """ + + def decorator(fn): + @wraps(fn) + def wrapper(self, *args, **kw): + response = fn(self, *args, **kw) + if not isinstance(response, zeep.xsd.ComplexType): + return response + + if path is not None: + for part in path.split("."): + try: + response = getattr(response, part) + except AttributeError: + if default is constants.NOT_SET: + raise + else: + return default + + try: + response_status = response["status"] + except TypeError: + response_status = None + for record in response: + # NOTE: Status is set on each returned record for lists, + # really strange... + response_status = record["status"] + break + + is_success = response_status["isSuccess"] + + if not is_success: + response_detail = response_status["statusDetail"] + raise NetsuiteResponseError(response_detail) + + if extract is not None: + response = extract(response) + + return response + + return wrapper + + return decorator diff --git a/netsuite/soap_api/exceptions.py b/netsuite/soap_api/exceptions.py new file mode 100644 index 0000000..1124c73 --- /dev/null +++ b/netsuite/soap_api/exceptions.py @@ -0,0 +1,5 @@ +__all__ = ("NetsuiteResponseError",) + + +class NetsuiteResponseError(Exception): + """Raised when a Netsuite result was marked as unsuccessful""" diff --git a/netsuite/helpers.py b/netsuite/soap_api/helpers.py similarity index 96% rename from netsuite/helpers.py rename to netsuite/soap_api/helpers.py index 9f0ff1f..a2949dc 100644 --- a/netsuite/helpers.py +++ b/netsuite/soap_api/helpers.py @@ -1,4 +1,4 @@ -import zeep.helpers +from . import zeep def to_builtin(obj, *, target_cls=dict): diff --git a/netsuite/passport.py b/netsuite/soap_api/passport.py similarity index 63% rename from netsuite/passport.py rename to netsuite/soap_api/passport.py index 4292269..f35aa9c 100644 --- a/netsuite/passport.py +++ b/netsuite/soap_api/passport.py @@ -4,7 +4,7 @@ from datetime import datetime from typing import Dict, TypeVar -from .config import Config +from ..config import Config, TokenAuth NetSuite = TypeVar("NetSuite") @@ -14,23 +14,6 @@ def get_element(self) -> str: raise NotImplementedError -class UserCredentialsPassport(Passport): - def __init__( - self, ns: NetSuite, *, account: str, email: str, password: str - ) -> None: - self.ns = ns - self.account = account - self.email = email - self.password = password - - def get_element(self): - return self.ns.Core.Passport( - account=self.account, - email=self.email, - password=self.password, - ) - - class TokenPassport(Passport): def __init__( self, @@ -104,36 +87,16 @@ def get_element(self): def make(ns: NetSuite, config: Config) -> Dict: - if config.auth_type == "token": - assert isinstance(config.account, str) - assert isinstance(config.consumer_key, str) - assert isinstance(config.consumer_secret, str) - assert isinstance(config.token_id, str) - assert isinstance(config.token_secret, str) + auth = config.auth + if isinstance(auth, TokenAuth): token_passport = TokenPassport( ns, account=config.account, - consumer_key=config.consumer_key, - consumer_secret=config.consumer_secret, - token_id=config.token_id, - token_secret=config.token_secret, + consumer_key=auth.consumer_key, + consumer_secret=auth.consumer_secret, + token_id=auth.token_id, + token_secret=auth.token_secret, ) return {"tokenPassport": token_passport.get_element()} - elif config.auth_type == "credentials": - assert isinstance(config.account, str) - assert isinstance(config.email, str) - assert isinstance(config.password, str) - passport = UserCredentialsPassport( - ns, - account=config.account, - email=config.email, - password=config.password, - ) - return { - "applicationInfo": { - "applicationId": config.application_id, - }, - "passport": passport.get_element(), - } else: - raise NotImplementedError(f"config.auth_type={config.auth_type}") + raise NotImplementedError(auth.__class__) diff --git a/netsuite/soap_api/transports.py b/netsuite/soap_api/transports.py new file mode 100644 index 0000000..b40e555 --- /dev/null +++ b/netsuite/soap_api/transports.py @@ -0,0 +1,38 @@ +import urllib.parse + +from . import zeep + +__all__ = ("AsyncNetSuiteTransport",) + + +# TODO: ASYNC! Maybe remove this custom transport?!?! + + +class AsyncNetSuiteTransport(zeep.transports.AsyncTransport): + """ + NetSuite company-specific domain wrapper for zeep.transports.transport + + Latest NetSuite WSDL now uses relative definition addresses + + zeep maps reflective remote calls to the base WSDL address, + rather than the dynamic subscriber domain + + Wrap the zeep transports service with our address modifications + """ + + def __init__(self, wsdl_url, *args, **kwargs): + parsed = urllib.parse.urlparse(wsdl_url) + self._netsuite_base_url = f"{parsed.scheme}://{parsed.netloc}" + super().__init__(*args, **kwargs) + + def _fix_address(self, address): + """Munge the address to the company-specific domain, not the default""" + idx = address.index("/", 8) + path = address[idx:] + return f"{self._netsuite_base_url}{path}" + + async def get(self, address, params, headers): + return await super().get(self._fix_address(address), params, headers) + + async def post(self, address, message, headers): + return await super().post(self._fix_address(address), message, headers) diff --git a/netsuite/soap_api/zeep.py b/netsuite/soap_api/zeep.py new file mode 100644 index 0000000..7912ecd --- /dev/null +++ b/netsuite/soap_api/zeep.py @@ -0,0 +1,57 @@ +# Compatibility module - in case SOAP isn't enabled in library +try: + import zeep as __zeep # noqa +except ImportError: + ZEEP_INSTALLED = False +else: + ZEEP_INSTALLED = True + +if ZEEP_INSTALLED: + from zeep import * # noqa + from zeep import cache, client, helpers, transports, xsd +else: + + class _Transport: + ... + + class _BaseCache: + ... + + class _SqliteCache: + ... + + class _CompoundValue: + ... + + class _Client: + ... + + class _ServiceProxy: + ... + + class _Factory: + ... + + class _valueobjects: + CompoundValue = _CompoundValue + + class cache: # type: ignore[no-redef] + Base = _BaseCache + SqliteCache = _SqliteCache + + class client: # type: ignore[no-redef] + Client = _Client + AsyncClient = _Client + ServiceProxy = _ServiceProxy + Factory = _Factory + + class transports: # type: ignore[no-redef] + Transport = _Transport + AsyncTransport = _Transport + + class xsd: # type: ignore[no-redef] + CompoundValue = _CompoundValue + valueobjects = _valueobjects + + class helpers: # type: ignore[no-redef] + serialize_object = None diff --git a/netsuite/util.py b/netsuite/util.py index d15a2d1..437592a 100644 --- a/netsuite/util.py +++ b/netsuite/util.py @@ -1,46 +1,28 @@ -import requests - -__all__ = ("cached_property", "raise_for_status_with_body") - - -class cached_property: - """Decorator that turns an instance method into a cached property - From https://speakerdeck.com/u/mitsuhiko/p/didntknow, slide #69 - """ - - __NOT_SET = object() - - def __init__(self, func): - self.func = func - self.__name__ = func.__name__ - self.__doc__ = func.__doc__ - self.__module__ = func.__module__ - - def __get__(self, obj, type=None): - if obj is None: - return self - value = obj.__dict__.get(self.__name__, self.__NOT_SET) - if value is self.__NOT_SET: - value = self.func(obj) - obj.__dict__[self.__name__] = value - return value - - -def raise_for_status_with_body(response, on_bad_status=None): - """Raise exception on bad HTTP status and capture response body - - Also: - * If an exception occurs the response body will be added to the - exception string. - * If `on_bad_status` is provided this function will run on a request - exception. - """ - try: - response.raise_for_status() - except requests.exceptions.RequestException as ex: - body = response.text - if body and len(ex.args) == 1: - ex.args = (ex.args[0] + f"\nBody: {body}",) - if on_bad_status is not None: - on_bad_status() - raise ex +__all__ = ("cached_property",) + + +try: + from functools import cached_property # Python 3.8+ +except ImportError: + + class cached_property: # type: ignore[no-redef] + """Decorator that turns an instance method into a cached property + From https://speakerdeck.com/u/mitsuhiko/p/didntknow, slide #69 + """ + + __NOT_SET = object() + + def __init__(self, func): + self.func = func + self.__name__ = func.__name__ + self.__doc__ = func.__doc__ + self.__module__ = func.__module__ + + def __get__(self, obj, type=None): + if obj is None: + return self + value = obj.__dict__.get(self.__name__, self.__NOT_SET) + if value is self.__NOT_SET: + value = self.func(obj) + obj.__dict__[self.__name__] = value + return value diff --git a/poetry.lock b/poetry.lock index 5e0b4f3..6f14e79 100644 --- a/poetry.lock +++ b/poetry.lock @@ -204,7 +204,7 @@ name = "h11" version = "0.12.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" category = "main" -optional = true +optional = false python-versions = ">=3.6" [[package]] @@ -212,7 +212,7 @@ name = "httpcore" version = "0.12.3" description = "A minimal low-level HTTP client." category = "main" -optional = true +optional = false python-versions = ">=3.6" [package.dependencies] @@ -227,7 +227,7 @@ name = "httpx" version = "0.17.1" description = "The next generation HTTP client." category = "main" -optional = true +optional = false python-versions = ">=3.6" [package.dependencies] @@ -533,25 +533,12 @@ plot = ["matplotlib"] tgrep = ["pyparsing"] twitter = ["twython"] -[[package]] -name = "oauthlib" -version = "3.1.0" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.extras] -rsa = ["cryptography"] -signals = ["blinker"] -signedtoken = ["cryptography", "pyjwt (>=1.0.0)"] - [[package]] name = "orjson" version = "3.5.2" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" category = "main" -optional = false +optional = true python-versions = ">=3.6" [[package]] @@ -661,6 +648,21 @@ category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pydantic" +version = "1.8.1" +description = "Data validation and settings management using python 3.6 type hinting" +category = "main" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +typing-extensions = ">=3.7.4.3" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + [[package]] name = "pyflakes" version = "2.3.1" @@ -787,21 +789,6 @@ python-versions = "*" requests = ">=1.0.0" six = "*" -[[package]] -name = "requests-oauthlib" -version = "1.3.0" -description = "OAuthlib authentication support for Requests." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -oauthlib = ">=3.0.0" -requests = ">=2.0.0" - -[package.extras] -rsa = ["oauthlib[signedtoken] (>=3.0.0)"] - [[package]] name = "requests-toolbelt" version = "0.9.1" @@ -818,7 +805,7 @@ name = "rfc3986" version = "1.4.0" description = "Validating URI References per RFC 3986" category = "main" -optional = true +optional = false python-versions = "*" [package.dependencies] @@ -840,7 +827,7 @@ name = "sniffio" version = "1.2.0" description = "Sniff out which async library your code is running under" category = "main" -optional = true +optional = false python-versions = ">=3.5" [[package]] @@ -898,7 +885,7 @@ python-versions = "*" name = "typing-extensions" version = "3.7.4.3" description = "Backported and Experimental Type Hints for Python 3.5+" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -936,6 +923,7 @@ appdirs = ">=1.4.0" attrs = ">=17.2.0" cached-property = ">=1.3.0" defusedxml = ">=0.4.1" +httpx = {version = "*", optional = true, markers = "extra == \"async\""} isodate = ">=0.5.4" lxml = ">=3.1.0" pytz = "*" @@ -962,14 +950,17 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [extras] -all = ["authlib", "httpx", "ipython"] +all = ["zeep", "authlib", "httpx", "ipython", "orjson"] cli = ["ipython"] +orjson = ["orjson"] rest_api = ["authlib", "httpx"] +restlet = ["authlib", "httpx"] +soap_api = ["zeep"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "f33036b6bf4785592adb3bd4236df55107bbb918074132dd99cf7835dbb34769" +content-hash = "9517a6a6ce83b916cc6902dadebcf1f5e6a9474beb67ed636eed67692a94204a" [metadata.files] appdirs = [ @@ -1344,10 +1335,6 @@ nltk = [ {file = "nltk-3.6.2-py3-none-any.whl", hash = "sha256:240e23ab1ab159ef9940777d30c7c72d7e76d91877099218a7585370c11f6b9e"}, {file = "nltk-3.6.2.zip", hash = "sha256:57d556abed621ab9be225cc6d2df1edce17572efb67a3d754630c9f8381503eb"}, ] -oauthlib = [ - {file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"}, - {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"}, -] orjson = [ {file = "orjson-3.5.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:2ba4165883fbef0985bce60bddbf91bc5cea77cc22b1c12fe7a716c6323ab1e7"}, {file = "orjson-3.5.2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:cee746d186ba9efa47b9d52a649ee0617456a9a4d7a2cbd3ec06330bb9cb372a"}, @@ -1417,6 +1404,30 @@ pycparser = [ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, ] +pydantic = [ + {file = "pydantic-1.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0c40162796fc8d0aa744875b60e4dc36834db9f2a25dbf9ba9664b1915a23850"}, + {file = "pydantic-1.8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fff29fe54ec419338c522b908154a2efabeee4f483e48990f87e189661f31ce3"}, + {file = "pydantic-1.8.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:fbfb608febde1afd4743c6822c19060a8dbdd3eb30f98e36061ba4973308059e"}, + {file = "pydantic-1.8.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:eb8ccf12295113ce0de38f80b25f736d62f0a8d87c6b88aca645f168f9c78771"}, + {file = "pydantic-1.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:20d42f1be7c7acc352b3d09b0cf505a9fab9deb93125061b376fbe1f06a5459f"}, + {file = "pydantic-1.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dde4ca368e82791de97c2ec019681ffb437728090c0ff0c3852708cf923e0c7d"}, + {file = "pydantic-1.8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3bbd023c981cbe26e6e21c8d2ce78485f85c2e77f7bab5ec15b7d2a1f491918f"}, + {file = "pydantic-1.8.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:830ef1a148012b640186bf4d9789a206c56071ff38f2460a32ae67ca21880eb8"}, + {file = "pydantic-1.8.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:fb77f7a7e111db1832ae3f8f44203691e15b1fa7e5a1cb9691d4e2659aee41c4"}, + {file = "pydantic-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:3bcb9d7e1f9849a6bdbd027aabb3a06414abd6068cb3b21c49427956cce5038a"}, + {file = "pydantic-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2287ebff0018eec3cc69b1d09d4b7cebf277726fa1bd96b45806283c1d808683"}, + {file = "pydantic-1.8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:4bbc47cf7925c86a345d03b07086696ed916c7663cb76aa409edaa54546e53e2"}, + {file = "pydantic-1.8.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:6388ef4ef1435364c8cc9a8192238aed030595e873d8462447ccef2e17387125"}, + {file = "pydantic-1.8.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:dd4888b300769ecec194ca8f2699415f5f7760365ddbe243d4fd6581485fa5f0"}, + {file = "pydantic-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:8fbb677e4e89c8ab3d450df7b1d9caed23f254072e8597c33279460eeae59b99"}, + {file = "pydantic-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2f2736d9a996b976cfdfe52455ad27462308c9d3d0ae21a2aa8b4cd1a78f47b9"}, + {file = "pydantic-1.8.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3114d74329873af0a0e8004627f5389f3bb27f956b965ddd3e355fe984a1789c"}, + {file = "pydantic-1.8.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:258576f2d997ee4573469633592e8b99aa13bda182fcc28e875f866016c8e07e"}, + {file = "pydantic-1.8.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c17a0b35c854049e67c68b48d55e026c84f35593c66d69b278b8b49e2484346f"}, + {file = "pydantic-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:e8bc082afef97c5fd3903d05c6f7bb3a6af9fc18631b4cc9fedeb4720efb0c58"}, + {file = "pydantic-1.8.1-py3-none-any.whl", hash = "sha256:e3f8790c47ac42549dc8b045a67b0ca371c7f66e73040d0197ce6172b385e520"}, + {file = "pydantic-1.8.1.tar.gz", hash = "sha256:26cf3cb2e68ec6c0cfcb6293e69fb3450c5fd1ace87f46b64f678b0d29eac4c3"}, +] pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, @@ -1527,11 +1538,6 @@ requests-file = [ {file = "requests-file-1.5.1.tar.gz", hash = "sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e"}, {file = "requests_file-1.5.1-py2.py3-none-any.whl", hash = "sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"}, ] -requests-oauthlib = [ - {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"}, - {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"}, - {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"}, -] 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"}, diff --git a/pyproject.toml b/pyproject.toml index b6f3e73..553b2f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "netsuite" -version = "0.6.3" -description = "Wrapper around Netsuite SuiteTalk SOAP/REST Web Services and Restlets." +version = "0.7.0" +description = "Make async requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets" authors = ["Jacob Magnusson "] license = "MIT" readme = "README.md" @@ -22,18 +22,19 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.7" -requests-oauthlib = "^1.3" -zeep = "^4" -orjson = "^3" -authlib = {version = "^0.15.3", optional = true} +authlib = "^0.15.3" # As per httpx recommendation we will lock to a fixed minor version until 1.0 is released -httpx = {version = "~0.17", optional = true} +httpx = "~0.17" +pydantic = "^1.8" +orjson = {version = "^3", optional = true} ipython = {version = "^7", optional = true} +zeep = {version = "^4.0.0", extras = ["async"]} [tool.poetry.extras] -rest_api = ["authlib", "httpx"] +soap_api = ["zeep"] cli = ["ipython"] -all = ["authlib", "httpx", "ipython"] +orjson = ["orjson"] +all = ["zeep", "ipython", "orjson"] [tool.poetry.dev-dependencies] black = "^20.8b1" @@ -45,7 +46,7 @@ pytest = "^6" pytest-cov = "^2.11" [tool.poetry.scripts] -netsuite = 'netsuite.__main__:main' +netsuite = 'netsuite.cli:main' [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/test_base.py b/tests/test_base.py index a05c763..1926a84 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,27 +1,29 @@ import pytest -import netsuite +from netsuite import Config, NetSuiteSoapApi @pytest.fixture def dummy_config(): - return { - "account": "123456", - "consumer_key": "abcdefghijklmnopqrstuvwxyz0123456789", - "consumer_secret": "abcdefghijklmnopqrstuvwxyz0123456789", - "token_id": "abcdefghijklmnopqrstuvwxyz0123456789", - "token_secret": "abcdefghijklmnopqrstuvwxyz0123456789", - } + return Config( + account="123456", + auth={ + "consumer_key": "abcdefghijklmnopqrstuvwxyz0123456789", + "consumer_secret": "abcdefghijklmnopqrstuvwxyz0123456789", + "token_id": "abcdefghijklmnopqrstuvwxyz0123456789", + "token_secret": "abcdefghijklmnopqrstuvwxyz0123456789", + }, + ) def test_netsuite_hostname(dummy_config): - ns = netsuite.NetSuite(dummy_config) - assert ns.hostname == "123456.suitetalk.api.netsuite.com" + soap_api = NetSuiteSoapApi(dummy_config) + assert soap_api.hostname == "123456.suitetalk.api.netsuite.com" def test_netsuite_wsdl_url(dummy_config): - ns = netsuite.NetSuite(dummy_config) + soap_api = NetSuiteSoapApi(dummy_config) assert ( - ns.wsdl_url - == "https://123456.suitetalk.api.netsuite.com/wsdl/v2019_2_0/netsuite.wsdl" + soap_api.wsdl_url + == "https://123456.suitetalk.api.netsuite.com/wsdl/v2021_1_0/netsuite.wsdl" ) From f16f4359568570d74d734f79fab132892cb426b9 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 27 Apr 2021 16:28:49 +0200 Subject: [PATCH 22/47] zeep wasn't marked as optional in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 553b2f3..fd3b9cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ httpx = "~0.17" pydantic = "^1.8" orjson = {version = "^3", optional = true} ipython = {version = "^7", optional = true} -zeep = {version = "^4.0.0", extras = ["async"]} +zeep = {version = "^4.0.0", optional = true, extras = ["async"]} [tool.poetry.extras] soap_api = ["zeep"] From 96b902b12d01392723448a1c28f47d6051d52cb4 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 27 Apr 2021 16:31:54 +0200 Subject: [PATCH 23/47] CI/CD fixes --- .github/workflows/cd.yml | 4 ++-- .github/workflows/ci.yml | 8 ++++++-- .github/workflows/codecov.yml | 4 +++- .github/workflows/docs.yml | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 794f146..e8cd15a 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -11,9 +11,9 @@ jobs: - uses: actions/setup-python@v2 with: python-version: 3.9 - - uses: abatilo/actions-poetry@v2.1.0 + - uses: abatilo/actions-poetry@v2.1.2 with: - poetry-version: 1.1.5 + poetry-version: 1.1.6 - name: Build run: poetry build - uses: marvinpinto/action-automatic-releases@latest diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3557d2d..eb7d818 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,9 @@ jobs: - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - - uses: abatilo/actions-poetry@v2.1.0 + - uses: abatilo/actions-poetry@v2.1.2 + with: + poetry-version: 1.1.6 - run: poetry install --extras ${{ matrix.extras }} - run: poetry run pytest -v linting: @@ -32,7 +34,9 @@ jobs: - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - - uses: abatilo/actions-poetry@v2.1.0 + - uses: abatilo/actions-poetry@v2.1.2 + with: + poetry-version: 1.1.6 - run: poetry install --extras ${{ matrix.extras }} - run: poetry run flake8 - run: poetry run mypy --ignore-missing-imports . diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index e347430..40104e1 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -12,7 +12,9 @@ jobs: - uses: actions/setup-python@v2 with: python-version: 3.9 - - uses: abatilo/actions-poetry@v2.1.0 + - uses: abatilo/actions-poetry@v2.1.2 + with: + poetry-version: 1.1.6 - run: poetry install --extras all - run: poetry run pytest --cov=netsuite --cov-report=xml --cov-report=term - uses: codecov/codecov-action@v1 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b6be329..aa4d871 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,9 +12,9 @@ jobs: - uses: actions/setup-python@v2 with: python-version: 3.9 - - uses: abatilo/actions-poetry@v2.1.0 + - uses: abatilo/actions-poetry@v2.1.2 with: - poetry-version: 1.1.5 + poetry-version: 1.1.6 - run: poetry install --extras all - run: poetry run mkdocs build - uses: peaceiris/actions-gh-pages@v3.7.3 From 9f943d6bf70ad81dc120e77062d14142ad11412f Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 27 Apr 2021 16:43:59 +0200 Subject: [PATCH 24/47] Fix requests not being available --- netsuite/soap_api/client.py | 10 ++++------ netsuite/soap_api/zeep.py | 4 ++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/netsuite/soap_api/client.py b/netsuite/soap_api/client.py index f54ff65..b4f564b 100644 --- a/netsuite/soap_api/client.py +++ b/netsuite/soap_api/client.py @@ -4,8 +4,6 @@ from datetime import datetime from typing import Dict, List, Optional, Sequence -import requests - from ..config import Config from ..util import cached_property from . import helpers, passport, zeep @@ -46,7 +44,7 @@ def __init__( version: str = None, wsdl_url: str = None, cache: zeep.cache.Base = None, - session: requests.Session = None, + session: zeep.requests.Session = None, ) -> None: self._ensure_required_dependencies() if version is not None: @@ -78,7 +76,7 @@ def cache(self) -> zeep.cache.Base: return self.__cache or self._generate_cache() @cached_property - def session(self) -> requests.Session: + def session(self) -> zeep.requests.Session: return self.__session or self._generate_session() @cached_property @@ -114,8 +112,8 @@ def _generate_wsdl_url(self) -> str: def _generate_cache(self) -> zeep.cache.Base: return zeep.cache.SqliteCache(timeout=60 * 60 * 24 * 365) - def _generate_session(self) -> requests.Session: - return requests.Session() + def _generate_session(self) -> zeep.requests.Session: + return zeep.requests.Session() def _generate_transport(self) -> zeep.transports.AsyncTransport: return AsyncNetSuiteTransport( diff --git a/netsuite/soap_api/zeep.py b/netsuite/soap_api/zeep.py index 7912ecd..6eb8e0f 100644 --- a/netsuite/soap_api/zeep.py +++ b/netsuite/soap_api/zeep.py @@ -7,6 +7,7 @@ ZEEP_INSTALLED = True if ZEEP_INSTALLED: + import requests from zeep import * # noqa from zeep import cache, client, helpers, transports, xsd else: @@ -55,3 +56,6 @@ class xsd: # type: ignore[no-redef] class helpers: # type: ignore[no-redef] serialize_object = None + + class requests: # type: ignore[no-redef] + Session = None From 4f94345492a1ba8b6a487b734599ad942d13e079 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 27 Apr 2021 16:56:08 +0200 Subject: [PATCH 25/47] httpx ~0.18 --- CHANGELOG.md | 1 + poetry.lock | 50 ++++++++++++++++++++++++-------------------------- pyproject.toml | 2 +- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59b4c71..f0dcae6 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This release breaks a lot of things. Please read carefully. - REST API and Restlet are now supported with the default install - The `netsuite.client.NetSuite` class is now just a thin layer around each of the different API types (SOAP, REST, Restlets) - `netsuite.restlet.NetsuiteRestlet` has been renamed to `netsuite.restlet.NetSuiteRestlet` +- Upgrade to httpx ~0.18 ### Added - `netsuite.restlet.NetSuiteRestlet` now support all four HTTP verbs GET, POST, PUT & DELETE via dedicated functions `.get`, `.post`, `.put` & `.delete` diff --git a/poetry.lock b/poetry.lock index 6f14e79..19247b3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -41,7 +41,7 @@ name = "authlib" version = "0.15.3" description = "The ultimate Python library in building OAuth and OpenID Connect servers." category = "main" -optional = true +optional = false python-versions = "*" [package.dependencies] @@ -85,7 +85,7 @@ name = "cached-property" version = "1.5.2" description = "A decorator for caching properties in classes." category = "main" -optional = false +optional = true python-versions = "*" [[package]] @@ -101,7 +101,7 @@ name = "cffi" version = "1.14.5" description = "Foreign Function Interface for Python calling C code." category = "main" -optional = true +optional = false python-versions = "*" [package.dependencies] @@ -112,7 +112,7 @@ name = "chardet" version = "4.0.0" description = "Universal encoding detector for Python 2 and 3" category = "main" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] @@ -147,7 +147,7 @@ name = "cryptography" version = "3.4.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" -optional = true +optional = false python-versions = ">=3.6" [package.dependencies] @@ -174,7 +174,7 @@ name = "defusedxml" version = "0.7.1" description = "XML bomb protection for Python stdlib modules" category = "main" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] @@ -209,7 +209,7 @@ python-versions = ">=3.6" [[package]] name = "httpcore" -version = "0.12.3" +version = "0.13.0" description = "A minimal low-level HTTP client." category = "main" optional = false @@ -224,7 +224,7 @@ http2 = ["h2 (>=3,<5)"] [[package]] name = "httpx" -version = "0.17.1" +version = "0.18.0" description = "The next generation HTTP client." category = "main" optional = false @@ -232,7 +232,7 @@ python-versions = ">=3.6" [package.dependencies] certifi = "*" -httpcore = ">=0.12.1,<0.13" +httpcore = ">=0.13.0,<0.14.0" rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} sniffio = "*" @@ -316,7 +316,7 @@ name = "isodate" version = "0.6.0" description = "An ISO 8601 date/time/duration parser and formatter" category = "main" -optional = false +optional = true python-versions = "*" [package.dependencies] @@ -405,7 +405,7 @@ name = "lxml" version = "4.6.3" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." category = "main" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" [package.extras] @@ -645,7 +645,7 @@ name = "pycparser" version = "2.20" description = "C parser in Python" category = "main" -optional = true +optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] @@ -740,7 +740,7 @@ name = "pytz" version = "2021.1" description = "World timezone definitions, modern and historical" category = "main" -optional = false +optional = true python-versions = "*" [[package]] @@ -764,7 +764,7 @@ name = "requests" version = "2.25.1" description = "Python HTTP for Humans." category = "main" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] @@ -782,7 +782,7 @@ name = "requests-file" version = "1.5.1" description = "File transport adapter for Requests" category = "main" -optional = false +optional = true python-versions = "*" [package.dependencies] @@ -794,7 +794,7 @@ name = "requests-toolbelt" version = "0.9.1" description = "A utility belt for advanced users of python-requests" category = "main" -optional = false +optional = true python-versions = "*" [package.dependencies] @@ -894,7 +894,7 @@ name = "urllib3" version = "1.26.4" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] @@ -915,7 +915,7 @@ name = "zeep" version = "4.0.0" description = "A modern/fast Python SOAP client based on lxml / requests" category = "main" -optional = false +optional = true python-versions = ">=3.6" [package.dependencies] @@ -950,17 +950,15 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [extras] -all = ["zeep", "authlib", "httpx", "ipython", "orjson"] +all = ["zeep", "ipython", "orjson"] cli = ["ipython"] orjson = ["orjson"] -rest_api = ["authlib", "httpx"] -restlet = ["authlib", "httpx"] soap_api = ["zeep"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "9517a6a6ce83b916cc6902dadebcf1f5e6a9474beb67ed636eed67692a94204a" +content-hash = "2a6403a21b74d0c78e97bc92027e378f10c6de842cb58e2108b487ffbd9795f6" [metadata.files] appdirs = [ @@ -1137,12 +1135,12 @@ h11 = [ {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, ] httpcore = [ - {file = "httpcore-0.12.3-py3-none-any.whl", hash = "sha256:93e822cd16c32016b414b789aeff4e855d0ccbfc51df563ee34d4dbadbb3bcdc"}, - {file = "httpcore-0.12.3.tar.gz", hash = "sha256:37ae835fb370049b2030c3290e12ed298bf1473c41bb72ca4aa78681eba9b7c9"}, + {file = "httpcore-0.13.0-py3-none-any.whl", hash = "sha256:1aab7711fa42a5df6043fdb62a3fb0f7a5899bccb53d0b0e500aa038acb0976c"}, + {file = "httpcore-0.13.0.tar.gz", hash = "sha256:d5602a0a9a5aa29937be6060acb2ac31461217d33e3e5e993038e51c841e0dec"}, ] httpx = [ - {file = "httpx-0.17.1-py3-none-any.whl", hash = "sha256:d379653bd457e8257eb0df99cb94557e4aac441b7ba948e333be969298cac272"}, - {file = "httpx-0.17.1.tar.gz", hash = "sha256:cc2a55188e4b25272d2bcd46379d300f632045de4377682aa98a8a6069d55967"}, + {file = "httpx-0.18.0-py3-none-any.whl", hash = "sha256:72a87c5f9f88443bf6875d14dad453f27092abcc27c86fa78ae2575ee9411885"}, + {file = "httpx-0.18.0.tar.gz", hash = "sha256:46cfb242ae6b85c3f21f7774cb42a465acdf73741e49f6746ab481f16d0e0590"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, diff --git a/pyproject.toml b/pyproject.toml index fd3b9cc..e2ded52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ python = "^3.7" authlib = "^0.15.3" # As per httpx recommendation we will lock to a fixed minor version until 1.0 is released -httpx = "~0.17" +httpx = "~0.18" pydantic = "^1.8" orjson = {version = "^3", optional = true} ipython = {version = "^7", optional = true} From af780cf33f07ca1797449ed6bff5b0aaee3991e8 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 27 Apr 2021 16:56:32 +0200 Subject: [PATCH 26/47] Fix failing tests and add basic REST API and Restlet tests --- netsuite/rest_api.py | 8 ++++++-- netsuite/restlet.py | 8 ++++++-- tests/conftest.py | 16 ++++++++++++++++ tests/test_base.py | 29 ----------------------------- tests/test_rest_api.py | 6 ++++++ tests/test_restlet.py | 6 ++++++ tests/test_soap_api.py | 19 +++++++++++++++++++ 7 files changed, 59 insertions(+), 33 deletions(-) create mode 100644 tests/conftest.py delete mode 100644 tests/test_base.py create mode 100644 tests/test_rest_api.py create mode 100644 tests/test_restlet.py create mode 100644 tests/test_soap_api.py diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index c509dda..b4adbff 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -3,6 +3,7 @@ from .config import Config from .rest_api_base import RestApiBase +from .util import cached_property logger = logging.getLogger(__name__) @@ -18,10 +19,13 @@ def __init__( concurrent_requests: int = 10, ): self._config = config - self._hostname = self._make_hostname() self._default_timeout = default_timeout self._concurrent_requests = concurrent_requests + @cached_property + def hostname(self) -> str: + return self._make_hostname() + async def get(self, subpath: str, **request_kw): return await self._request("GET", subpath, **request_kw) @@ -85,7 +89,7 @@ def _make_hostname(self): return f"{self._config.account_slugified}.suitetalk.api.netsuite.com" def _make_url(self, subpath: str): - return f"https://{self._hostname}/services/rest{subpath}" + return f"https://{self.hostname}/services/rest{subpath}" def _make_default_headers(self): return { diff --git a/netsuite/restlet.py b/netsuite/restlet.py index 97455e4..c55ed1d 100644 --- a/netsuite/restlet.py +++ b/netsuite/restlet.py @@ -2,6 +2,7 @@ from .config import Config from .rest_api_base import RestApiBase +from .util import cached_property logger = logging.getLogger(__name__) @@ -17,10 +18,13 @@ def __init__( concurrent_requests: int = 10, ): self._config = config - self._hostname = self._make_hostname() self._default_timeout = default_timeout self._concurrent_requests = concurrent_requests + @cached_property + def hostname(self) -> str: + return self._make_hostname() + async def get(self, script_id: int, *, deploy: int = 1, **request_kw): subpath = self._make_restlet_params(script_id, deploy) return await self._request("GET", subpath, **request_kw) @@ -44,4 +48,4 @@ def _make_hostname(self): return f"{self._config.account_slugified}.restlets.api.netsuite.com" def _make_url(self, subpath: str) -> str: - return f"https://{self._hostname}/app/site/hosting/restlet.nl{subpath}" + return f"https://{self.hostname}/app/site/hosting/restlet.nl{subpath}" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..959e312 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,16 @@ +import pytest + +from netsuite import Config + + +@pytest.fixture +def dummy_config(): + return Config( + account="123456_SB1", + auth={ + "consumer_key": "abcdefghijklmnopqrstuvwxyz0123456789", + "consumer_secret": "abcdefghijklmnopqrstuvwxyz0123456789", + "token_id": "abcdefghijklmnopqrstuvwxyz0123456789", + "token_secret": "abcdefghijklmnopqrstuvwxyz0123456789", + }, + ) diff --git a/tests/test_base.py b/tests/test_base.py deleted file mode 100644 index 1926a84..0000000 --- a/tests/test_base.py +++ /dev/null @@ -1,29 +0,0 @@ -import pytest - -from netsuite import Config, NetSuiteSoapApi - - -@pytest.fixture -def dummy_config(): - return Config( - account="123456", - auth={ - "consumer_key": "abcdefghijklmnopqrstuvwxyz0123456789", - "consumer_secret": "abcdefghijklmnopqrstuvwxyz0123456789", - "token_id": "abcdefghijklmnopqrstuvwxyz0123456789", - "token_secret": "abcdefghijklmnopqrstuvwxyz0123456789", - }, - ) - - -def test_netsuite_hostname(dummy_config): - soap_api = NetSuiteSoapApi(dummy_config) - assert soap_api.hostname == "123456.suitetalk.api.netsuite.com" - - -def test_netsuite_wsdl_url(dummy_config): - soap_api = NetSuiteSoapApi(dummy_config) - assert ( - soap_api.wsdl_url - == "https://123456.suitetalk.api.netsuite.com/wsdl/v2021_1_0/netsuite.wsdl" - ) diff --git a/tests/test_rest_api.py b/tests/test_rest_api.py new file mode 100644 index 0000000..39f1bd6 --- /dev/null +++ b/tests/test_rest_api.py @@ -0,0 +1,6 @@ +from netsuite import NetSuiteRestApi + + +def test_expected_hostname(dummy_config): + rest_api = NetSuiteRestApi(dummy_config) + assert rest_api.hostname == "123456-sb1.suitetalk.api.netsuite.com" diff --git a/tests/test_restlet.py b/tests/test_restlet.py new file mode 100644 index 0000000..402823f --- /dev/null +++ b/tests/test_restlet.py @@ -0,0 +1,6 @@ +from netsuite import NetSuiteRestlet + + +def test_expected_hostname(dummy_config): + restlet = NetSuiteRestlet(dummy_config) + assert restlet.hostname == "123456-sb1.restlets.api.netsuite.com" diff --git a/tests/test_soap_api.py b/tests/test_soap_api.py new file mode 100644 index 0000000..710aed1 --- /dev/null +++ b/tests/test_soap_api.py @@ -0,0 +1,19 @@ +import pytest + +from netsuite import NetSuiteSoapApi +from netsuite.soap_api.zeep import ZEEP_INSTALLED + +pytestmark = pytest.mark.skipif(not ZEEP_INSTALLED, reason="Requires zeep") + + +def test_netsuite_hostname(dummy_config): + soap_api = NetSuiteSoapApi(dummy_config) + assert soap_api.hostname == "123456-sb1.suitetalk.api.netsuite.com" + + +def test_netsuite_wsdl_url(dummy_config): + soap_api = NetSuiteSoapApi(dummy_config) + assert ( + soap_api.wsdl_url + == "https://123456-sb1.suitetalk.api.netsuite.com/wsdl/v2021_1_0/netsuite.wsdl" + ) From c80515cf1ab2c8c2d8a0636cec6482897f81ba24 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 27 Apr 2021 17:05:05 +0200 Subject: [PATCH 27/47] Ignore pushed version tags from triggering CI --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb7d818..bc18d55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,8 @@ name: ci on: push: + tags-ignore: + - ^v*.* # Ignore version tags from triggering CI run pull_request: jobs: From 86b4bd4ad42e6d0818df2b6aa67c0a5cbb3a97dc Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 27 Apr 2021 17:09:52 +0200 Subject: [PATCH 28/47] Changelog updates --- CHANGELOG.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0dcae6..e966a43 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,23 +13,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 This release breaks a lot of things. Please read carefully. ### Changed -- The SOAP API has been changed to use async -- Move NetSuite version to 2021.1 -- The `netsuite.restlet.NetsuiteRestlet` interface is now asynchronous -- SOAP Web Services support is no longer included in the default install, please use `netsuite[soap_api]` (unfortunately `zeep` pulls in a lot of other dependencies) -- REST API and Restlet are now supported with the default install +- SOAP and Restlet APIs are now async (i.e. this library is no longer useable in a non-async environment) - The `netsuite.client.NetSuite` class is now just a thin layer around each of the different API types (SOAP, REST, Restlets) +- SOAP Web Services support is no longer included in the default install, please use `netsuite[soap_api]` (unfortunately `zeep` pulls in a lot of other dependencies, so I decided to remove it by default) - `netsuite.restlet.NetsuiteRestlet` has been renamed to `netsuite.restlet.NetSuiteRestlet` +- Move NetSuite version to 2021.1 - Upgrade to httpx ~0.18 ### Added - `netsuite.restlet.NetSuiteRestlet` now support all four HTTP verbs GET, POST, PUT & DELETE via dedicated functions `.get`, `.post`, `.put` & `.delete` +- REST API and Restlet are now supported with the default install +- CLI now has a new sub-command `soap-api`, which currently only support `get` and `getList` - Dependency [pydantic](https://pydantic-docs.helpmanual.io/) has been added to help with config validation ### Removed -- Authentication via User credentials (will no longer work from NetSuite 2021.2 release) +- Authentication via User credentials has been removed (will no longer work from NetSuite 2021.2 release anyway) - `netsuite.restlet.NetSuiteRestApi.request` and `netsuite.restlet.NetSuiteRestlet.request` no longer exists - use each dedicated "verb method" instead -- Dead code for setting SOAP preferences +- Removed dead code for setting SOAP preferences - CLI sub-command aliases `i` (interact) and `r` (rest-api) have been removed to avoid confusion ## [0.6.3](https://github.com/jmagnusson/netsuite/compare/v0.6.2...v0.6.3) - 2021-04-26 From f4e9f5290a3a50ac1b922f30d8e981aa6472f6db Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Tue, 27 Apr 2021 17:14:53 +0200 Subject: [PATCH 29/47] Hopefully fixes CI not running --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc18d55..1fe25a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,8 +2,8 @@ name: ci on: push: - tags-ignore: - - ^v*.* # Ignore version tags from triggering CI run + branches-ignore: [] + tags-ignore: ['v*.*'] # Ignore version tags from triggering CI run pull_request: jobs: From 49e06426b2065bd7be977f0b26fe003b7487f735 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Wed, 28 Apr 2021 10:41:15 +0200 Subject: [PATCH 30/47] v0.8.0 - Default signing method is now HMAC-SHA256 for REST API and Restlet Fixes #27 --- CHANGELOG.md | 11 ++++++++++- netsuite/rest_api.py | 6 ++++-- netsuite/rest_api_base.py | 28 +++++++++++++++++----------- netsuite/restlet.py | 6 ++++-- poetry.lock | 19 ++++++++++++++++++- pyproject.toml | 3 ++- 6 files changed, 55 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e966a43..b37dbe7 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nothing -## [0.7.0](https://github.com/jmagnusson/netsuite/compare/v0.6.3...v0.7.0) - 2021-04-26 +## [0.8.0](https://github.com/jmagnusson/netsuite/compare/v0.7.0...v0.8.0) - 2021-04-28 + +### Changed +- Default signing method is now HMAC-SHA256 for REST API and Restlet. The old default of HMAC-SHA1 can be set via the `signature_method` keyword argument. (Thanks to @zerodarkzone, issue #27) + +### Added +- HMAC-SHA256 signing method when making requests to REST API and Restlets +- Dependency `oauthlib` + +## [0.7.0](https://github.com/jmagnusson/netsuite/compare/v0.6.3...v0.7.0) - 2021-04-27 This release breaks a lot of things. Please read carefully. diff --git a/netsuite/rest_api.py b/netsuite/rest_api.py index b4adbff..680647d 100644 --- a/netsuite/rest_api.py +++ b/netsuite/rest_api.py @@ -1,8 +1,8 @@ import logging from typing import Sequence +from . import rest_api_base from .config import Config -from .rest_api_base import RestApiBase from .util import cached_property logger = logging.getLogger(__name__) @@ -10,17 +10,19 @@ __all__ = ("NetSuiteRestApi",) -class NetSuiteRestApi(RestApiBase): +class NetSuiteRestApi(rest_api_base.RestApiBase): def __init__( self, config: Config, *, default_timeout: int = 60, concurrent_requests: int = 10, + signature_method: str = rest_api_base.DEFAULT_SIGNATURE_METHOD, ): self._config = config self._default_timeout = default_timeout self._concurrent_requests = concurrent_requests + self._signature_method = signature_method @cached_property def hostname(self) -> str: diff --git a/netsuite/rest_api_base.py b/netsuite/rest_api_base.py index 27f5c79..a54f7bc 100644 --- a/netsuite/rest_api_base.py +++ b/netsuite/rest_api_base.py @@ -1,31 +1,36 @@ import asyncio import logging +import httpx +from authlib.integrations.httpx_client import OAuth1Auth +from authlib.oauth1.rfc5849.client_auth import ClientAuth +from authlib.oauth1.rfc5849.signature import generate_signature_base_string +from oauthlib.oauth1.rfc5849.signature import sign_hmac_sha256 + from . import json from .exceptions import NetsuiteAPIRequestError, NetsuiteAPIResponseParsingError from .util import cached_property -try: - import httpx -except ImportError: +__all__ = ("RestApiBase",) + +DEFAULT_SIGNATURE_METHOD = "HMAC-SHA256" - class httpx: # type: ignore[no-redef] - Response = None +logger = logging.getLogger(__name__) -try: - from authlib.integrations.httpx_client import OAuth1Auth -except ImportError: - OAuth1Auth = None +def authlib_hmac_sha256_sign_method(client, request): + """Sign a HMAC-SHA256 signature.""" + base_string = generate_signature_base_string(request) + return sign_hmac_sha256(base_string, client.client_secret, client.token_secret) -__all__ = ("RestApiBase",) -logger = logging.getLogger(__name__) +ClientAuth.register_signature_method("HMAC-SHA256", authlib_hmac_sha256_sign_method) class RestApiBase: _concurrent_requests: int = 10 _default_timeout: int = 10 + _signature_method: str = DEFAULT_SIGNATURE_METHOD @cached_property def _request_semaphore(self) -> asyncio.Semaphore: @@ -93,6 +98,7 @@ def _make_auth(self): token_secret=auth.token_secret, realm=self._config.account, force_include_body=True, + signature_method=self._signature_method, ) def _make_default_headers(self): diff --git a/netsuite/restlet.py b/netsuite/restlet.py index c55ed1d..0533ea2 100644 --- a/netsuite/restlet.py +++ b/netsuite/restlet.py @@ -1,7 +1,7 @@ import logging +from . import rest_api_base from .config import Config -from .rest_api_base import RestApiBase from .util import cached_property logger = logging.getLogger(__name__) @@ -9,17 +9,19 @@ __all__ = ("NetSuiteRestlet",) -class NetSuiteRestlet(RestApiBase): +class NetSuiteRestlet(rest_api_base.RestApiBase): def __init__( self, config: Config, *, default_timeout: int = 60, concurrent_requests: int = 10, + signature_method: str = rest_api_base.DEFAULT_SIGNATURE_METHOD, ): self._config = config self._default_timeout = default_timeout self._concurrent_requests = concurrent_requests + self._signature_method = signature_method @cached_property def hostname(self) -> str: diff --git a/poetry.lock b/poetry.lock index 19247b3..ad9c030 100644 --- a/poetry.lock +++ b/poetry.lock @@ -533,6 +533,19 @@ plot = ["matplotlib"] tgrep = ["pyparsing"] twitter = ["twython"] +[[package]] +name = "oauthlib" +version = "3.1.0" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +rsa = ["cryptography"] +signals = ["blinker"] +signedtoken = ["cryptography", "pyjwt (>=1.0.0)"] + [[package]] name = "orjson" version = "3.5.2" @@ -958,7 +971,7 @@ soap_api = ["zeep"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "2a6403a21b74d0c78e97bc92027e378f10c6de842cb58e2108b487ffbd9795f6" +content-hash = "676885e746bce362c87d710492c8fefd1e7852e67a87c31bdd3bb710e962bce5" [metadata.files] appdirs = [ @@ -1333,6 +1346,10 @@ nltk = [ {file = "nltk-3.6.2-py3-none-any.whl", hash = "sha256:240e23ab1ab159ef9940777d30c7c72d7e76d91877099218a7585370c11f6b9e"}, {file = "nltk-3.6.2.zip", hash = "sha256:57d556abed621ab9be225cc6d2df1edce17572efb67a3d754630c9f8381503eb"}, ] +oauthlib = [ + {file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"}, + {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"}, +] orjson = [ {file = "orjson-3.5.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:2ba4165883fbef0985bce60bddbf91bc5cea77cc22b1c12fe7a716c6323ab1e7"}, {file = "orjson-3.5.2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:cee746d186ba9efa47b9d52a649ee0617456a9a4d7a2cbd3ec06330bb9cb372a"}, diff --git a/pyproject.toml b/pyproject.toml index e2ded52..d09f077 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netsuite" -version = "0.7.0" +version = "0.8.0" description = "Make async requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets" authors = ["Jacob Magnusson "] license = "MIT" @@ -29,6 +29,7 @@ pydantic = "^1.8" orjson = {version = "^3", optional = true} ipython = {version = "^7", optional = true} zeep = {version = "^4.0.0", optional = true, extras = ["async"]} +oauthlib = "^3.1.0" [tool.poetry.extras] soap_api = ["zeep"] From b6db6e56e2bc8c432215f1e2c955667fb2c3504a Mon Sep 17 00:00:00 2001 From: Fabrice Bernhard Date: Tue, 16 Nov 2021 19:12:54 +0100 Subject: [PATCH 31/47] jmagnusson renamed to jacobsvante --- CHANGELOG.md | 10 +++++----- README.md | 8 ++++---- pyproject.toml | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b37dbe7..34cb046 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nothing -## [0.8.0](https://github.com/jmagnusson/netsuite/compare/v0.7.0...v0.8.0) - 2021-04-28 +## [0.8.0](https://github.com/jacobsvante/netsuite/compare/v0.7.0...v0.8.0) - 2021-04-28 ### Changed - Default signing method is now HMAC-SHA256 for REST API and Restlet. The old default of HMAC-SHA1 can be set via the `signature_method` keyword argument. (Thanks to @zerodarkzone, issue #27) @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - HMAC-SHA256 signing method when making requests to REST API and Restlets - Dependency `oauthlib` -## [0.7.0](https://github.com/jmagnusson/netsuite/compare/v0.6.3...v0.7.0) - 2021-04-27 +## [0.7.0](https://github.com/jacobsvante/netsuite/compare/v0.6.3...v0.7.0) - 2021-04-27 This release breaks a lot of things. Please read carefully. @@ -41,18 +41,18 @@ This release breaks a lot of things. Please read carefully. - Removed dead code for setting SOAP preferences - CLI sub-command aliases `i` (interact) and `r` (rest-api) have been removed to avoid confusion -## [0.6.3](https://github.com/jmagnusson/netsuite/compare/v0.6.2...v0.6.3) - 2021-04-26 +## [0.6.3](https://github.com/jacobsvante/netsuite/compare/v0.6.2...v0.6.3) - 2021-04-26 ### Added - Ability to supply custom headers to REST API requests made from CLI via "-H/--header" flag - Support custom payload, headers and params in suiteql REST API method -## [0.6.2](https://github.com/jmagnusson/netsuite/compare/v0.6.1...v0.6.2) - 2021-04-25 +## [0.6.2](https://github.com/jacobsvante/netsuite/compare/v0.6.1...v0.6.2) - 2021-04-25 ### Fixed - `NetSuiteRestApi` no longer requires a running asyncio loop to be instantiated -## [0.6.1](https://github.com/jmagnusson/netsuite/compare/v0.6.0...v0.6.1) - 2021-04-25 +## [0.6.1](https://github.com/jacobsvante/netsuite/compare/v0.6.0...v0.6.1) - 2021-04-25 ### Fixed - Fix "local variable 'record_ref' referenced before assignment" error in `NetSuite.get` method - Thanks @VeNoMouS! (#25) diff --git a/README.md b/README.md index 2d79643..5cc27df 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # netsuite -[![Continuous Integration Status](https://github.com/jmagnusson/netsuite/actions/workflows/ci.yml/badge.svg)](https://github.com/jmagnusson/netsuite/actions/workflows/ci.yml) -[![Continuous Delivery Status](https://github.com/jmagnusson/netsuite/actions/workflows/cd.yml/badge.svg)](https://github.com/jmagnusson/netsuite/actions/workflows/cd.yml) -[![Code Coverage](https://img.shields.io/codecov/c/github/jmagnusson/netsuite?color=%2334D058)](https://codecov.io/gh/jmagnusson/netsuite) +[![Continuous Integration Status](https://github.com/jacobsvante/netsuite/actions/workflows/ci.yml/badge.svg)](https://github.com/jacobsvante/netsuite/actions/workflows/ci.yml) +[![Continuous Delivery Status](https://github.com/jacobsvante/netsuite/actions/workflows/cd.yml/badge.svg)](https://github.com/jacobsvante/netsuite/actions/workflows/cd.yml) +[![Code Coverage](https://img.shields.io/codecov/c/github/jacobsvante/netsuite?color=%2334D058)](https://codecov.io/gh/jacobsvante/netsuite) [![PyPI version](https://img.shields.io/pypi/v/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) [![License](https://img.shields.io/pypi/l/netsuite.svg)](https://pypi.python.org/pypi/netsuite/) [![Python Versions](https://img.shields.io/pypi/pyversions/netsuite.svg)](https://pypi.org/project/netsuite/) @@ -38,4 +38,4 @@ With all features: ## Documentation -Is found here: https://jmagnusson.github.io/netsuite/ +Is found here: https://jacobsvante.github.io/netsuite/ diff --git a/pyproject.toml b/pyproject.toml index d09f077..ed537ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,9 +5,9 @@ description = "Make async requests to NetSuite SuiteTalk SOAP/REST Web Services authors = ["Jacob Magnusson "] license = "MIT" readme = "README.md" -homepage = "https://jmagnusson.github.io/netsuite/" -repository = "https://github.com/jmagnusson/netsuite" -documentation = "https://jmagnusson.github.io/netsuite/" +homepage = "https://jacobsvante.github.io/netsuite/" +repository = "https://github.com/jacobsvante/netsuite" +documentation = "https://jacobsvante.github.io/netsuite/" classifiers = [ "Development Status :: 4 - Beta", "Environment :: Web Environment", From 544cb598abc85ae5e65921e3733c39e4d47f0c04 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Wed, 17 Nov 2021 14:49:30 +0100 Subject: [PATCH 32/47] chore: Upgrade black - issue with typed-ast and py3.9 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ed537ad..acd85e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ orjson = ["orjson"] all = ["zeep", "ipython", "orjson"] [tool.poetry.dev-dependencies] -black = "^20.8b1" +black = "^21.11b0" flake8 = "^3.9" isort = "^5.8" mkdocs-material = "^7" From 04249758d02f0d1d7b27a8f49353bc02f84a47b2 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Wed, 17 Nov 2021 14:50:11 +0100 Subject: [PATCH 33/47] chore: Don't include poetry.lock in repo --- .gitignore | 1 + poetry.lock | 1424 +++++++++++++++++++++++++++------------------------ 2 files changed, 742 insertions(+), 683 deletions(-) diff --git a/.gitignore b/.gitignore index 743eb82..ef74cd2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ /Pipfile.lock /.mypy* /.pytest_cache +/poetry.lock diff --git a/poetry.lock b/poetry.lock index ad9c030..4f9285b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,20 @@ [[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "anyio" +version = "3.3.4" +description = "High level compatibility layer for multiple asynchronous event loop implementations" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6.2" + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=6.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16)"] [[package]] name = "appnope" @@ -24,21 +34,21 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "20.3.0" +version = "21.2.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +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", "zope.interface", "furo", "sphinx", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +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 = "authlib" -version = "0.15.3" +version = "0.15.5" description = "The ultimate Python library in building OAuth and OpenID Connect servers." category = "main" optional = false @@ -60,25 +70,31 @@ python-versions = "*" [[package]] name = "black" -version = "20.8b1" +version = "21.11b0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.2" [package.dependencies] -appdirs = "*" click = ">=7.1.2" mypy-extensions = ">=0.4.3" -pathspec = ">=0.6,<1" +pathspec = ">=0.9.0,<1" +platformdirs = ">=2" regex = ">=2020.1.8" -toml = ">=0.10.1" -typed-ast = ">=1.4.0" -typing-extensions = ">=3.7.4" +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.3.2)", "aiohttp-cors"] +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 = "cached-property" @@ -90,7 +106,7 @@ python-versions = "*" [[package]] name = "certifi" -version = "2020.12.5" +version = "2021.10.8" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -98,7 +114,7 @@ python-versions = "*" [[package]] name = "cffi" -version = "1.14.5" +version = "1.15.0" description = "Foreign Function Interface for Python calling C code." category = "main" optional = false @@ -108,20 +124,27 @@ python-versions = "*" pycparser = "*" [[package]] -name = "chardet" -version = "4.0.0" -description = "Universal encoding detector for Python 2 and 3" +name = "charset-normalizer" +version = "2.0.7" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "7.1.2" +version = "8.0.3" description = "Composable command line interface toolkit" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -133,18 +156,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" -version = "5.5" +version = "6.1.2" description = "Code coverage measurement for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=3.6" [package.extras] -toml = ["toml"] +toml = ["tomli"] [[package]] name = "cryptography" -version = "3.4.7" +version = "35.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false @@ -157,29 +180,21 @@ cffi = ">=1.12" docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] docstest = ["doc8", "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)"] +sdist = ["setuptools_rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] [[package]] name = "decorator" -version = "5.0.7" +version = "5.1.0" description = "Decorators for Humans" category = "main" optional = true python-versions = ">=3.5" -[[package]] -name = "defusedxml" -version = "0.7.1" -description = "XML bomb protection for Python stdlib modules" -category = "main" -optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - [[package]] name = "flake8" -version = "3.9.1" +version = "3.9.2" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false @@ -192,12 +207,18 @@ pycodestyle = ">=2.7.0,<2.8.0" pyflakes = ">=2.3.0,<2.4.0" [[package]] -name = "future" -version = "0.18.2" -description = "Clean single-source support for Python 3 and 2" +name = "ghp-import" +version = "2.0.2" +description = "Copy your docs directly to the gh-pages branch." category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "*" + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["twine", "markdown", "flake8", "wheel"] [[package]] name = "h11" @@ -209,14 +230,15 @@ python-versions = ">=3.6" [[package]] name = "httpcore" -version = "0.13.0" +version = "0.13.7" description = "A minimal low-level HTTP client." category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -h11 = "<1.0.0" +anyio = ">=3.0.0,<4.0.0" +h11 = ">=0.11,<0.13" sniffio = ">=1.0.0,<2.0.0" [package.extras] @@ -224,7 +246,7 @@ http2 = ["h2 (>=3,<5)"] [[package]] name = "httpx" -version = "0.18.0" +version = "0.18.2" description = "The next generation HTTP client." category = "main" optional = false @@ -232,25 +254,25 @@ python-versions = ">=3.6" [package.dependencies] certifi = "*" -httpcore = ">=0.13.0,<0.14.0" +httpcore = ">=0.13.3,<0.14.0" rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} sniffio = "*" [package.extras] -brotli = ["brotlipy (>=0.7.0,<0.8.0)"] +brotli = ["brotlicffi (>=1.0.0,<2.0.0)"] http2 = ["h2 (>=3.0.0,<4.0.0)"] [[package]] name = "idna" -version = "2.10" +version = "3.3" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.0.1" +version = "4.8.2" description = "Read metadata from Python packages" category = "dev" optional = false @@ -262,7 +284,8 @@ zipp = ">=0.5" [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)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +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" @@ -274,7 +297,7 @@ python-versions = "*" [[package]] name = "ipython" -version = "7.22.0" +version = "7.29.0" description = "IPython: Productive Interactive Computing" category = "main" optional = true @@ -286,6 +309,7 @@ backcall = "*" colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" jedi = ">=0.16" +matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} pickleshare = "*" prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" @@ -293,7 +317,7 @@ pygments = "*" traitlets = ">=4.2" [package.extras] -all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.16)", "pygments", "qtconsole", "requests", "testpath"] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] doc = ["Sphinx (>=1.3)"] kernel = ["ipykernel"] nbconvert = ["nbconvert"] @@ -301,15 +325,7 @@ nbformat = ["nbformat"] notebook = ["notebook", "ipywidgets"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.16)"] - -[[package]] -name = "ipython-genutils" -version = "0.2.0" -description = "Vestigial utilities from IPython" -category = "main" -optional = true -python-versions = "*" +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"] [[package]] name = "isodate" @@ -324,20 +340,21 @@ six = "*" [[package]] name = "isort" -version = "5.8.0" +version = "5.10.1" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.6.1,<4.0" [package.extras] pipfile_deprecated_finder = ["pipreqs", "requirementslib"] requirements_deprecated_finder = ["pipreqs", "pip-api"] colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] [[package]] name = "jedi" -version = "0.18.0" +version = "0.18.1" description = "An autocompletion tool for Python that can be used for text editors." category = "main" optional = true @@ -348,61 +365,25 @@ parso = ">=0.8.0,<0.9.0" [package.extras] qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] name = "jinja2" -version = "2.11.3" +version = "3.0.3" description = "A very fast and expressive template engine." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -MarkupSafe = ">=0.23" - -[package.extras] -i18n = ["Babel (>=0.8)"] - -[[package]] -name = "joblib" -version = "1.0.1" -description = "Lightweight pipelining with Python functions" -category = "dev" -optional = false python-versions = ">=3.6" -[[package]] -name = "livereload" -version = "2.6.3" -description = "Python LiveReload is an awesome tool for web developers" -category = "dev" -optional = false -python-versions = "*" - [package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} - -[[package]] -name = "lunr" -version = "0.5.8" -description = "A Python implementation of Lunr.js" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -future = ">=0.16.0" -nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} -six = ">=1.11.0" +MarkupSafe = ">=2.0" [package.extras] -languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] +i18n = ["Babel (>=2.7)"] [[package]] name = "lxml" -version = "4.6.3" +version = "4.6.4" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." category = "main" optional = true @@ -416,25 +397,33 @@ source = ["Cython (>=0.29.7)"] [[package]] name = "markdown" -version = "3.3.4" +version = "3.3.5" description = "Python implementation of Markdown." category = "dev" optional = false python-versions = ">=3.6" -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - [package.extras] testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" -version = "1.1.1" +version = "2.0.1" description = "Safely add untrusted strings to HTML/XML markup." category = "dev" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = ">=3.6" + +[[package]] +name = "matplotlib-inline" +version = "0.1.3" +description = "Inline Matplotlib backend for Jupyter" +category = "main" +optional = true +python-versions = ">=3.5" + +[package.dependencies] +traitlets = "*" [[package]] name = "mccabe" @@ -444,48 +433,60 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "mkdocs" -version = "1.1.2" +version = "1.2.3" description = "Project documentation with Markdown." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] click = ">=3.3" +ghp-import = ">=1.0" +importlib-metadata = ">=3.10" Jinja2 = ">=2.10.1" -livereload = ">=2.5.1" -lunr = {version = "0.5.8", extras = ["languages"]} Markdown = ">=3.2.1" +mergedeep = ">=1.3.4" +packaging = ">=20.5" PyYAML = ">=3.10" -tornado = ">=5.0" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] [[package]] name = "mkdocs-material" -version = "7.1.3" +version = "7.3.6" description = "A Material Design theme for MkDocs" category = "dev" optional = false python-versions = "*" [package.dependencies] +jinja2 = ">=2.11.1" markdown = ">=3.2" -mkdocs = ">=1.1" +mkdocs = ">=1.2.3" mkdocs-material-extensions = ">=1.0" -Pygments = ">=2.4" -pymdown-extensions = ">=7.0" +pygments = ">=2.10" +pymdown-extensions = ">=9.0" [[package]] name = "mkdocs-material-extensions" -version = "1.0.1" +version = "1.0.3" description = "Extension pack for Python Markdown." category = "dev" optional = false -python-versions = ">=3.5" - -[package.dependencies] -mkdocs-material = ">=5.0.0" +python-versions = ">=3.6" [[package]] name = "mypy" @@ -511,59 +512,37 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "nltk" -version = "3.6.2" -description = "Natural Language Toolkit" -category = "dev" -optional = false -python-versions = ">=3.5.*" - -[package.dependencies] -click = "*" -joblib = "*" -regex = "*" -tqdm = "*" - -[package.extras] -all = ["matplotlib", "twython", "scipy", "numpy", "gensim (<4.0.0)", "python-crfsuite", "pyparsing", "scikit-learn", "requests"] -corenlp = ["requests"] -machine_learning = ["gensim (<4.0.0)", "numpy", "python-crfsuite", "scikit-learn", "scipy"] -plot = ["matplotlib"] -tgrep = ["pyparsing"] -twitter = ["twython"] - [[package]] name = "oauthlib" -version = "3.1.0" +version = "3.1.1" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.extras] -rsa = ["cryptography"] -signals = ["blinker"] -signedtoken = ["cryptography", "pyjwt (>=1.0.0)"] +rsa = ["cryptography (>=3.0.0,<4)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "orjson" -version = "3.5.2" +version = "3.6.4" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" category = "main" optional = true -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "packaging" -version = "20.9" +version = "21.2" description = "Core utilities for Python packages" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] -pyparsing = ">=2.0.2" +pyparsing = ">=2.0.2,<3" [[package]] name = "parso" @@ -579,11 +558,11 @@ testing = ["docopt", "pytest (<6.0.0)"] [[package]] name = "pathspec" -version = "0.8.1" +version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "pexpect" @@ -604,27 +583,40 @@ category = "main" optional = true python-versions = "*" +[[package]] +name = "platformdirs" +version = "2.4.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" +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 = "0.13.1" +version = "1.0.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "prompt-toolkit" -version = "3.0.18" +version = "3.0.22" description = "Library for building powerful interactive command lines in Python" category = "main" optional = true -python-versions = ">=3.6.1" +python-versions = ">=3.6.2" [package.dependencies] wcwidth = "*" @@ -639,11 +631,11 @@ python-versions = "*" [[package]] name = "py" -version = "1.10.0" +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.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pycodestyle" @@ -655,7 +647,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pycparser" -version = "2.20" +version = "2.21" description = "C parser in Python" category = "main" optional = false @@ -663,7 +655,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pydantic" -version = "1.8.1" +version = "1.8.2" description = "Data validation and settings management using python 3.6 type hinting" category = "main" optional = false @@ -686,7 +678,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.8.1" +version = "2.10.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = false @@ -694,7 +686,7 @@ python-versions = ">=3.5" [[package]] name = "pymdown-extensions" -version = "8.1.1" +version = "9.1" description = "Extension pack for Python Markdown." category = "dev" optional = false @@ -713,7 +705,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pytest" -version = "6.2.3" +version = "6.2.5" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -726,7 +718,7 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<1.0.0a1" +pluggy = ">=0.12,<2.0" py = ">=1.8.2" toml = "*" @@ -735,7 +727,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm [[package]] name = "pytest-cov" -version = "2.11.1" +version = "2.12.1" description = "Pytest plugin for measuring coverage." category = "dev" optional = false @@ -744,13 +736,25 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] coverage = ">=5.2.1" pytest = ">=4.6" +toml = "*" [package.extras] -testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" [[package]] name = "pytz" -version = "2021.1" +version = "2021.3" description = "World timezone definitions, modern and historical" category = "main" optional = true @@ -758,15 +762,26 @@ python-versions = "*" [[package]] name = "pyyaml" -version = "5.4.1" +version = "6.0" description = "YAML parser and emitter for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.6" + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyyaml = "*" [[package]] name = "regex" -version = "2021.4.4" +version = "2021.11.10" description = "Alternative regular expression module, to replace re." category = "dev" optional = false @@ -774,21 +789,21 @@ python-versions = "*" [[package]] name = "requests" -version = "2.25.1" +version = "2.26.0" description = "Python HTTP for Humans." category = "main" optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<5" -idna = ">=2.5,<3" +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] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] name = "requests-file" @@ -815,7 +830,7 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "rfc3986" -version = "1.4.0" +version = "1.5.0" description = "Validating URI References per RFC 3986" category = "main" optional = false @@ -829,7 +844,7 @@ idna2008 = ["idna"] [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" optional = false @@ -852,37 +867,21 @@ optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] -name = "tornado" -version = "6.1" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "dev" -optional = false -python-versions = ">= 3.5" - -[[package]] -name = "tqdm" -version = "4.60.0" -description = "Fast, Extensible Progress Meter" +name = "tomli" +version = "1.2.2" +description = "A lil' TOML parser" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" - -[package.extras] -dev = ["py-make (>=0.1.0)", "twine", "wheel"] -notebook = ["ipywidgets (>=6)"] -telegram = ["requests"] +python-versions = ">=3.6" [[package]] name = "traitlets" -version = "5.0.5" +version = "5.1.1" description = "Traitlets Python configuration system" category = "main" optional = true python-versions = ">=3.7" -[package.dependencies] -ipython-genutils = "*" - [package.extras] test = ["pytest"] @@ -896,24 +895,35 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "3.7.4.3" -description = "Backported and Experimental Type Hints for Python 3.5+" +version = "4.0.0" +description = "Backported and Experimental Type Hints for Python 3.6+" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "urllib3" -version = "1.26.4" +version = "1.26.7" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] +brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -brotli = ["brotlipy (>=0.6.0)"] + +[[package]] +name = "watchdog" +version = "2.1.6" +description = "Filesystem events monitoring" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "wcwidth" @@ -925,20 +935,19 @@ python-versions = "*" [[package]] name = "zeep" -version = "4.0.0" +version = "4.1.0" description = "A modern/fast Python SOAP client based on lxml / requests" category = "main" optional = true python-versions = ">=3.6" [package.dependencies] -appdirs = ">=1.4.0" attrs = ">=17.2.0" cached-property = ">=1.3.0" -defusedxml = ">=0.4.1" httpx = {version = "*", optional = true, markers = "extra == \"async\""} isodate = ">=0.5.4" -lxml = ">=3.1.0" +lxml = ">=4.6.0" +platformdirs = ">=1.4.0" pytz = "*" requests = ">=2.7.0" requests-file = ">=1.5.1" @@ -952,7 +961,7 @@ xmlsec = ["xmlsec (>=0.6.1)"] [[package]] name = "zipp" -version = "3.4.1" +version = "3.6.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false @@ -960,7 +969,7 @@ python-versions = ">=3.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +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"] [extras] all = ["zeep", "ipython", "orjson"] @@ -971,12 +980,12 @@ soap_api = ["zeep"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "676885e746bce362c87d710492c8fefd1e7852e67a87c31bdd3bb710e962bce5" +content-hash = "ab34c13feff970232114042008a9fe490d7b63d4ab1e00d817f885e738c47b84" [metadata.files] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +anyio = [ + {file = "anyio-3.3.4-py3-none-any.whl", hash = "sha256:4fd09a25ab7fa01d34512b7249e366cd10358cdafc95022c7ff8c8f8a5026d66"}, + {file = "anyio-3.3.4.tar.gz", hash = "sha256:67da67b5b21f96b9d3d65daa6ea99f5d5282cb09f50eb4456f8fb51dffefc3ff"}, ] appnope = [ {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, @@ -987,332 +996,380 @@ atomicwrites = [ {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, - {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] authlib = [ - {file = "Authlib-0.15.3-py2.py3-none-any.whl", hash = "sha256:0f6af3a38d37dd77361808dd3f2e258b647668dac6d2cefcefc4c4ebc3c7d2b2"}, - {file = "Authlib-0.15.3.tar.gz", hash = "sha256:7dde11ba45db51e97169c261362fab3193073100b7387e60c159db1eec470bbc"}, + {file = "Authlib-0.15.5-py2.py3-none-any.whl", hash = "sha256:ecf4a7a9f2508c0bb07e93a752dd3c495cfaffc20e864ef0ffc95e3f40d2abaf"}, + {file = "Authlib-0.15.5.tar.gz", hash = "sha256:b83cf6360c8e92b0e9df0d1f32d675790bcc4e3c03977499b1eed24dcdef4252"}, ] backcall = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] black = [ - {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, + {file = "black-21.11b0-py3-none-any.whl", hash = "sha256:0b1f66cbfadcd332ceeaeecf6373d9991d451868d2e2219ad0ac1213fb701117"}, + {file = "black-21.11b0.tar.gz", hash = "sha256:83f3852301c8dcb229e9c444dd79f573c8d31c7c2dad9bbaaa94c808630e32aa"}, ] cached-property = [ {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, ] certifi = [ - {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, - {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, + {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.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"}, - {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"}, - {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"}, - {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"}, - {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"}, - {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"}, - {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"}, - {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"}, - {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"}, - {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"}, - {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"}, - {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"}, - {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"}, - {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"}, - {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"}, - {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"}, - {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"}, - {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"}, - {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"}, - {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"}, - {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"}, - {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"}, - {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"}, - {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"}, - {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"}, - {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"}, - {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"}, - {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"}, - {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"}, - {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"}, - {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"}, - {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"}, - {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"}, - {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"}, - {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"}, - {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"}, - {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, -] -chardet = [ - {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, - {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, + {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.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"}, + {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"}, ] click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, ] 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-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, - {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, - {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, - {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, - {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, - {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, - {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, - {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, - {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, - {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, - {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, - {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, - {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, - {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, - {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, - {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, - {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, - {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, - {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, - {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, - {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, - {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, - {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, - {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, + {file = "coverage-6.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:675adb3b3380967806b3cbb9c5b00ceb29b1c472692100a338730c1d3e59c8b9"}, + {file = "coverage-6.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95a58336aa111af54baa451c33266a8774780242cab3704b7698d5e514840758"}, + {file = "coverage-6.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0a595a781f8e186580ff8e3352dd4953b1944289bec7705377c80c7e36c4d6c"}, + {file = "coverage-6.1.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d3c5f49ce6af61154060640ad3b3281dbc46e2e0ef2fe78414d7f8a324f0b649"}, + {file = "coverage-6.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:310c40bed6b626fd1f463e5a83dba19a61c4eb74e1ac0d07d454ebbdf9047e9d"}, + {file = "coverage-6.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a4d48e42e17d3de212f9af44f81ab73b9378a4b2b8413fd708d0d9023f2bbde4"}, + {file = "coverage-6.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ffa545230ca2ad921ad066bf8fd627e7be43716b6e0fcf8e32af1b8188ccb0ab"}, + {file = "coverage-6.1.2-cp310-cp310-win32.whl", hash = "sha256:cd2d11a59afa5001ff28073ceca24ae4c506da4355aba30d1e7dd2bd0d2206dc"}, + {file = "coverage-6.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:96129e41405887a53a9cc564f960d7f853cc63d178f3a182fdd302e4cab2745b"}, + {file = "coverage-6.1.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1de9c6f5039ee2b1860b7bad2c7bc3651fbeb9368e4c4d93e98a76358cdcb052"}, + {file = "coverage-6.1.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:80cb70264e9a1d04b519cdba3cd0dc42847bf8e982a4d55c769b9b0ee7cdce1e"}, + {file = "coverage-6.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:ba6125d4e55c0b8e913dad27b22722eac7abdcb1f3eab1bd090eee9105660266"}, + {file = "coverage-6.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8492d37acdc07a6eac6489f6c1954026f2260a85a4c2bb1e343fe3d35f5ee21a"}, + {file = "coverage-6.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66af99c7f7b64d050d37e795baadf515b4561124f25aae6e1baa482438ecc388"}, + {file = "coverage-6.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ebcc03e1acef4ff44f37f3c61df478d6e469a573aa688e5a162f85d7e4c3860d"}, + {file = "coverage-6.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d44a8136eebbf544ad91fef5bd2b20ef0c9b459c65a833c923d9aa4546b204"}, + {file = "coverage-6.1.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c18725f3cffe96732ef96f3de1939d81215fd6d7d64900dcc4acfe514ea4fcbf"}, + {file = "coverage-6.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c8e9c4bcaaaa932be581b3d8b88b677489975f845f7714efc8cce77568b6711c"}, + {file = "coverage-6.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:06d009e8a29483cbc0520665bc46035ffe9ae0e7484a49f9782c2a716e37d0a0"}, + {file = "coverage-6.1.2-cp36-cp36m-win32.whl", hash = "sha256:e5432d9c329b11c27be45ee5f62cf20a33065d482c8dec1941d6670622a6fb8f"}, + {file = "coverage-6.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:82fdcb64bf08aa5db881db061d96db102c77397a570fbc112e21c48a4d9cb31b"}, + {file = "coverage-6.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:94f558f8555e79c48c422045f252ef41eb43becdd945e9c775b45ebfc0cbd78f"}, + {file = "coverage-6.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:046647b96969fda1ae0605f61288635209dd69dcd27ba3ec0bf5148bc157f954"}, + {file = "coverage-6.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cc799916b618ec9fd00135e576424165691fec4f70d7dc12cfaef09268a2478c"}, + {file = "coverage-6.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62646d98cf0381ffda301a816d6ac6c35fc97aa81b09c4c52d66a15c4bef9d7c"}, + {file = "coverage-6.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:27a3df08a855522dfef8b8635f58bab81341b2fb5f447819bc252da3aa4cf44c"}, + {file = "coverage-6.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:610c0ba11da8de3a753dc4b1f71894f9f9debfdde6559599f303286e70aeb0c2"}, + {file = "coverage-6.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:35b246ae3a2c042dc8f410c94bcb9754b18179cdb81ff9477a9089dbc9ecc186"}, + {file = "coverage-6.1.2-cp37-cp37m-win32.whl", hash = "sha256:0cde7d9fe2fb55ff68ebe7fb319ef188e9b88e0a3d1c9c5db7dd829cd93d2193"}, + {file = "coverage-6.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:958ac66272ff20e63d818627216e3d7412fdf68a2d25787b89a5c6f1eb7fdd93"}, + {file = "coverage-6.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a300b39c3d5905686c75a369d2a66e68fd01472ea42e16b38c948bd02b29e5bd"}, + {file = "coverage-6.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d3855d5d26292539861f5ced2ed042fc2aa33a12f80e487053aed3bcb6ced13"}, + {file = "coverage-6.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:586d38dfc7da4a87f5816b203ff06dd7c1bb5b16211ccaa0e9788a8da2b93696"}, + {file = "coverage-6.1.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a34fccb45f7b2d890183a263578d60a392a1a218fdc12f5bce1477a6a68d4373"}, + {file = "coverage-6.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bc1ee1318f703bc6c971da700d74466e9b86e0c443eb85983fb2a1bd20447263"}, + {file = "coverage-6.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3f546f48d5d80a90a266769aa613bc0719cb3e9c2ef3529d53f463996dd15a9d"}, + {file = "coverage-6.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd92ece726055e80d4e3f01fff3b91f54b18c9c357c48fcf6119e87e2461a091"}, + {file = "coverage-6.1.2-cp38-cp38-win32.whl", hash = "sha256:24ed38ec86754c4d5a706fbd5b52b057c3df87901a8610d7e5642a08ec07087e"}, + {file = "coverage-6.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:97ef6e9119bd39d60ef7b9cd5deea2b34869c9f0b9777450a7e3759c1ab09b9b"}, + {file = "coverage-6.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e5a8c947a2a89c56655ecbb789458a3a8e3b0cbf4c04250331df8f647b3de59"}, + {file = "coverage-6.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a39590d1e6acf6a3c435c5d233f72f5d43b585f5be834cff1f21fec4afda225"}, + {file = "coverage-6.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9d2c2e3ce7b8cc932a2f918186964bd44de8c84e2f9ef72dc616f5bb8be22e71"}, + {file = "coverage-6.1.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3348865798c077c695cae00da0924136bb5cc501f236cfd6b6d9f7a3c94e0ec4"}, + {file = "coverage-6.1.2-cp39-cp39-win32.whl", hash = "sha256:fae3fe111670e51f1ebbc475823899524e3459ea2db2cb88279bbfb2a0b8a3de"}, + {file = "coverage-6.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:af45eea024c0e3a25462fade161afab4f0d9d9e0d5a5d53e86149f74f0a35ecc"}, + {file = "coverage-6.1.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:eab14fdd410500dae50fd14ccc332e65543e7b39f6fc076fe90603a0e5d2f929"}, + {file = "coverage-6.1.2.tar.gz", hash = "sha256:d9a635114b88c0ab462e0355472d00a180a5fbfd8511e7f18e4ac32652e7d972"}, ] cryptography = [ - {file = "cryptography-3.4.7-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1"}, - {file = "cryptography-3.4.7-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250"}, - {file = "cryptography-3.4.7-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2"}, - {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6"}, - {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959"}, - {file = "cryptography-3.4.7-cp36-abi3-win32.whl", hash = "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d"}, - {file = "cryptography-3.4.7-cp36-abi3-win_amd64.whl", hash = "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca"}, - {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873"}, - {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d"}, - {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177"}, - {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"}, - {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"}, + {file = "cryptography-35.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9"}, + {file = "cryptography-35.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992"}, + {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6"}, + {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d"}, + {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6"}, + {file = "cryptography-35.0.0-cp36-abi3-win32.whl", hash = "sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8"}, + {file = "cryptography-35.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588"}, + {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953"}, + {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6"}, + {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2"}, + {file = "cryptography-35.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c"}, + {file = "cryptography-35.0.0.tar.gz", hash = "sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d"}, ] decorator = [ - {file = "decorator-5.0.7-py3-none-any.whl", hash = "sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"}, - {file = "decorator-5.0.7.tar.gz", hash = "sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060"}, -] -defusedxml = [ - {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, - {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, + {file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"}, + {file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"}, ] flake8 = [ - {file = "flake8-3.9.1-py2.py3-none-any.whl", hash = "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a"}, - {file = "flake8-3.9.1.tar.gz", hash = "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378"}, + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] -future = [ - {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +ghp-import = [ + {file = "ghp-import-2.0.2.tar.gz", hash = "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071"}, + {file = "ghp_import-2.0.2-py3-none-any.whl", hash = "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46"}, ] h11 = [ {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, ] httpcore = [ - {file = "httpcore-0.13.0-py3-none-any.whl", hash = "sha256:1aab7711fa42a5df6043fdb62a3fb0f7a5899bccb53d0b0e500aa038acb0976c"}, - {file = "httpcore-0.13.0.tar.gz", hash = "sha256:d5602a0a9a5aa29937be6060acb2ac31461217d33e3e5e993038e51c841e0dec"}, + {file = "httpcore-0.13.7-py3-none-any.whl", hash = "sha256:369aa481b014cf046f7067fddd67d00560f2f00426e79569d99cb11245134af0"}, + {file = "httpcore-0.13.7.tar.gz", hash = "sha256:036f960468759e633574d7c121afba48af6419615d36ab8ede979f1ad6276fa3"}, ] httpx = [ - {file = "httpx-0.18.0-py3-none-any.whl", hash = "sha256:72a87c5f9f88443bf6875d14dad453f27092abcc27c86fa78ae2575ee9411885"}, - {file = "httpx-0.18.0.tar.gz", hash = "sha256:46cfb242ae6b85c3f21f7774cb42a465acdf73741e49f6746ab481f16d0e0590"}, + {file = "httpx-0.18.2-py3-none-any.whl", hash = "sha256:979afafecb7d22a1d10340bafb403cf2cb75aff214426ff206521fc79d26408c"}, + {file = "httpx-0.18.2.tar.gz", hash = "sha256:9f99c15d33642d38bce8405df088c1c4cfd940284b4290cacbfb02e64f4877c6"}, ] idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, + {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.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, - {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, + {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"}, ] ipython = [ - {file = "ipython-7.22.0-py3-none-any.whl", hash = "sha256:c0ce02dfaa5f854809ab7413c601c4543846d9da81010258ecdab299b542d199"}, - {file = "ipython-7.22.0.tar.gz", hash = "sha256:9c900332d4c5a6de534b4befeeb7de44ad0cc42e8327fa41b7685abde58cec74"}, -] -ipython-genutils = [ - {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, - {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, + {file = "ipython-7.29.0-py3-none-any.whl", hash = "sha256:a658beaf856ce46bc453366d5dc6b2ddc6c481efd3540cb28aa3943819caac9f"}, + {file = "ipython-7.29.0.tar.gz", hash = "sha256:4f69d7423a5a1972f6347ff233e38bbf4df6a150ef20fbb00c635442ac3060aa"}, ] isodate = [ {file = "isodate-0.6.0-py2.py3-none-any.whl", hash = "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81"}, {file = "isodate-0.6.0.tar.gz", hash = "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8"}, ] isort = [ - {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, - {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, ] jedi = [ - {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, - {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, + {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, + {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, ] jinja2 = [ - {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, - {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, -] -joblib = [ - {file = "joblib-1.0.1-py3-none-any.whl", hash = "sha256:feeb1ec69c4d45129954f1b7034954241eedfd6ba39b5e9e4b6883be3332d5e5"}, - {file = "joblib-1.0.1.tar.gz", hash = "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7"}, -] -livereload = [ - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, -] -lunr = [ - {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, - {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, + {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, + {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, ] lxml = [ - {file = "lxml-4.6.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2"}, - {file = "lxml-4.6.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f"}, - {file = "lxml-4.6.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d"}, - {file = "lxml-4.6.3-cp27-cp27m-win32.whl", hash = "sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106"}, - {file = "lxml-4.6.3-cp27-cp27m-win_amd64.whl", hash = "sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee"}, - {file = "lxml-4.6.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f"}, - {file = "lxml-4.6.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4"}, - {file = "lxml-4.6.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51"}, - {file = "lxml-4.6.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586"}, - {file = "lxml-4.6.3-cp35-cp35m-win32.whl", hash = "sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2"}, - {file = "lxml-4.6.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4"}, - {file = "lxml-4.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4"}, - {file = "lxml-4.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3"}, - {file = "lxml-4.6.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d"}, - {file = "lxml-4.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec"}, - {file = "lxml-4.6.3-cp36-cp36m-win32.whl", hash = "sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04"}, - {file = "lxml-4.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a"}, - {file = "lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654"}, - {file = "lxml-4.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0"}, - {file = "lxml-4.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3"}, - {file = "lxml-4.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2"}, - {file = "lxml-4.6.3-cp37-cp37m-win32.whl", hash = "sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade"}, - {file = "lxml-4.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b"}, - {file = "lxml-4.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa"}, - {file = "lxml-4.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a"}, - {file = "lxml-4.6.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927"}, - {file = "lxml-4.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791"}, - {file = "lxml-4.6.3-cp38-cp38-win32.whl", hash = "sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28"}, - {file = "lxml-4.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7"}, - {file = "lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0"}, - {file = "lxml-4.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1"}, - {file = "lxml-4.6.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23"}, - {file = "lxml-4.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969"}, - {file = "lxml-4.6.3-cp39-cp39-win32.whl", hash = "sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f"}, - {file = "lxml-4.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83"}, - {file = "lxml-4.6.3.tar.gz", hash = "sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468"}, + {file = "lxml-4.6.4-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bbf2dc330bd44bfc0254ab37677ec60f7c7ecea55ad8ba1b8b2ea7bf20c265f5"}, + {file = "lxml-4.6.4-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b667c51682fe9b9788c69465956baa8b6999531876ccedcafc895c74ad716cd8"}, + {file = "lxml-4.6.4-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:72e730d33fe2e302fd07285f14624fca5e5e2fb2bb4fb2c3941e318c41c443d1"}, + {file = "lxml-4.6.4-cp27-cp27m-win32.whl", hash = "sha256:433df8c7dde0f9e41cbf4f36b0829d50a378116ef5e962ba3881f2f5f025c7be"}, + {file = "lxml-4.6.4-cp27-cp27m-win_amd64.whl", hash = "sha256:35752ee40f7bbf6adc9ff4e1f4b84794a3593736dcce80db32e3c2aa85e294ac"}, + {file = "lxml-4.6.4-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ff5bb2a198ea67403bb6818705e9a4f90e0313f2215428ec51001ce56d939fb"}, + {file = "lxml-4.6.4-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9b87727561c1150c0cc91c5d9d389448b37a7d15f0ba939ed3d1acb2f11bf6c5"}, + {file = "lxml-4.6.4-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:45fdb2899c755138722797161547a40b3e2a06feda620cc41195ee7e97806d81"}, + {file = "lxml-4.6.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:38b9de0de3aa689fe9fb9877ae1be1e83b8cf9621f7e62049d0436b9ecf4ad64"}, + {file = "lxml-4.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:662523cd2a0246740225c7e32531f2e766544122e58bee70e700a024cfc0cf81"}, + {file = "lxml-4.6.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:4aa349c5567651f34d4eaae7de6ed5b523f6d70a288f9c6fbac22d13a0784e04"}, + {file = "lxml-4.6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:08eb9200d88b376a8ed5e50f1dc1d1a45b49305169674002a3b5929943390591"}, + {file = "lxml-4.6.4-cp310-cp310-win32.whl", hash = "sha256:bdc224f216ead849e902151112efef6e96c41ee1322e15d4e5f7c8a826929aee"}, + {file = "lxml-4.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ab6db93a2b6b66cbf62b4e4a7135f476e708e8c5c990d186584142c77d7f975a"}, + {file = "lxml-4.6.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50790313df028aa05cf22be9a8da033b86c42fa32523e4fd944827b482b17bf0"}, + {file = "lxml-4.6.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6764998345552b1dfc9326a932d2bad6367c6b37a176bb73ada6b9486bf602f7"}, + {file = "lxml-4.6.4-cp35-cp35m-win32.whl", hash = "sha256:543b239b191bb3b6d9bef5f09f1fb2be5b7eb09ab4d386aa655e4d53fbe9ff47"}, + {file = "lxml-4.6.4-cp35-cp35m-win_amd64.whl", hash = "sha256:a75c1ad05eedb1a3ff2a34a52a4f0836cfaa892e12796ba39a7732c82701eff4"}, + {file = "lxml-4.6.4-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:47e955112ce64241fdb357acf0216081f9f3255b3ac9c502ca4b3323ec1ca558"}, + {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:20d7c8d90d449c6a353b15ee0459abae8395dbe59ad01e406ccbf30cd81c6f98"}, + {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:240db6f3228d26e3c6f4fad914b9ddaaf8707254e8b3efd564dc680c8ec3c264"}, + {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:351482da8dd028834028537f08724b1de22d40dcf3bb723b469446564f409074"}, + {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e678a643177c0e5ec947b645fa7bc84260dfb9b6bf8fb1fdd83008dfc2ca5928"}, + {file = "lxml-4.6.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:15d0381feb56f08f78c5cc4fc385ddfe0bde1456e37f54a9322833371aec4060"}, + {file = "lxml-4.6.4-cp36-cp36m-win32.whl", hash = "sha256:4ba74afe5ee5cb5e28d83b513a6e8f0875fda1dc1a9aea42cc0065f029160d2a"}, + {file = "lxml-4.6.4-cp36-cp36m-win_amd64.whl", hash = "sha256:9c91a73971a922c13070fd8fa5a114c858251791ba2122a941e6aa781c713e44"}, + {file = "lxml-4.6.4-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:6020c70ff695106bf80651953a23e37718ef1fee9abd060dcad8e32ab2dc13f3"}, + {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f5dd358536b8a964bf6bd48de038754c1609e72e5f17f5d21efe2dda17594dbf"}, + {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7ae7089d81fc502df4b217ad77f03c54039fe90dac0acbe70448d7e53bfbc57e"}, + {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:80d10d53d3184837445ff8562021bdd37f57c4cadacbf9d8726cc16220a00d54"}, + {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e95da348d57eb448d226a44b868ff2ca5786fbcbe417ac99ff62d0a7d724b9c7"}, + {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ffd65cfa33fed01735c82aca640fde4cc63f0414775cba11e06f84fae2085a6e"}, + {file = "lxml-4.6.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:877666418598f6cb289546c77ff87590cfd212f903b522b0afa0b9fb73b3ccfb"}, + {file = "lxml-4.6.4-cp37-cp37m-win32.whl", hash = "sha256:e91d24623e747eeb2d8121f4a94c6a7ad27dc48e747e2dc95bfe88632bd028a2"}, + {file = "lxml-4.6.4-cp37-cp37m-win_amd64.whl", hash = "sha256:4ec9a80dd5704ecfde54319b6964368daf02848c8954d3bacb9b64d1c7659159"}, + {file = "lxml-4.6.4-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:2901625f4a878a055d275beedc20ba9cb359cefc4386a967222fee29eb236038"}, + {file = "lxml-4.6.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b567178a74a2261345890eac66fbf394692a6e002709d329f28a673ca6042473"}, + {file = "lxml-4.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4717123f7c11c81e0da69989e5a64079c3f402b0efeb4c6241db6c369d657bd8"}, + {file = "lxml-4.6.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:cf201bf5594d1aab139fe53e3fca457e4f8204a5bbd65d48ab3b82a16f517868"}, + {file = "lxml-4.6.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a77a3470ba37e11872c75ca95baf9b3312133a3d5a5dc720803b23098c653976"}, + {file = "lxml-4.6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:619c6d2b552bba00491e96c0518aad94002651c108a0f7364ff2d7798812c00e"}, + {file = "lxml-4.6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:601f0ab75538b280aaf1e720eb9d68d4fa104ac274e1e9e6971df488f4dcdb0f"}, + {file = "lxml-4.6.4-cp38-cp38-win32.whl", hash = "sha256:75d3c5bbc0ddbad03bb68b9be638599f67e4b98ed3dcd0fec9f6f39e41ee96cb"}, + {file = "lxml-4.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:4341d135f5660db10184963d9c3418c3e28d7f868aaf8b11a323ebf85813f7f4"}, + {file = "lxml-4.6.4-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:9db24803fa71e3305fe4a7812782b708da21a0b774b130dd1860cf40a6d7a3ee"}, + {file = "lxml-4.6.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:afd60230ad9d8bcba005945ec3a343722f09e0b7f8ae804246e5d2cfc6bd71a6"}, + {file = "lxml-4.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:0c15e1cd55055956e77b0732270f1c6005850696bc3ef3e03d01e78af84eaa42"}, + {file = "lxml-4.6.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6d422b3c729737d8a39279a25fa156c983a56458f8b2f97661ee6fb22b80b1d6"}, + {file = "lxml-4.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2eb90f6ec3c236ef2f1bb38aee7c0d23e77d423d395af6326e7cca637519a4cb"}, + {file = "lxml-4.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:51a0e5d243687596f46e24e464121d4b232ad772e2d1785b2a2c0eb413c285d4"}, + {file = "lxml-4.6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d43bd68714049c84e297c005456a15ecdec818f7b5aa5868c8b0a865cfb78a44"}, + {file = "lxml-4.6.4-cp39-cp39-win32.whl", hash = "sha256:ee9e4b07b0eba4b6a521509e9e1877476729c1243246b6959de697ebea739643"}, + {file = "lxml-4.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:48eaac2991b3036175b42ee8d3c23f4cca13f2be8426bf29401a690ab58c88f4"}, + {file = "lxml-4.6.4-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:2b06a91cf7b8acea7793006e4ae50646cef0fe35ce5acd4f5cb1c77eb228e4a1"}, + {file = "lxml-4.6.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:523f195948a1ba4f9f5b7294d83c6cd876547dc741820750a7e5e893a24bbe38"}, + {file = "lxml-4.6.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:b0ca0ada9d3bc18bd6f611bd001a28abdd49ab9698bd6d717f7f5394c8e94628"}, + {file = "lxml-4.6.4-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:197b7cb7a753cf553a45115739afd8458464a28913da00f5c525063f94cd3f48"}, + {file = "lxml-4.6.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:6298f5b42a26581206ef63fffa97c754245d329414108707c525512a5197f2ba"}, + {file = "lxml-4.6.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0b12c95542f04d10cba46b3ff28ea52ea56995b78cf918f0b11b05e75812bb79"}, + {file = "lxml-4.6.4.tar.gz", hash = "sha256:daf9bd1fee31f1c7a5928b3e1059e09a8d683ea58fb3ffc773b6c88cb8d1399c"}, ] markdown = [ - {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, - {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, + {file = "Markdown-3.3.5-py3-none-any.whl", hash = "sha256:0d2d09f75cb8d1ffc6770c65c61770b23a61708101f47bda416a002a0edbc480"}, + {file = "Markdown-3.3.5.tar.gz", hash = "sha256:26e9546bfbcde5fcd072bd8f612c9c1b6e2677cb8aadbdf65206674f46dde069"}, ] markupsafe = [ - {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, - {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, + {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, +] +matplotlib-inline = [ + {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, + {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] +mergedeep = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] mkdocs = [ - {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, - {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, + {file = "mkdocs-1.2.3-py3-none-any.whl", hash = "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072"}, + {file = "mkdocs-1.2.3.tar.gz", hash = "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1"}, ] mkdocs-material = [ - {file = "mkdocs-material-7.1.3.tar.gz", hash = "sha256:e34bba93ad1a0e6f9afc371f4ef55bedabbf13b9a786b013b0ce26ac55ec2932"}, - {file = "mkdocs_material-7.1.3-py2.py3-none-any.whl", hash = "sha256:437638b0de7a9113d7f1c9ddc93c0a29a3b808c71c3606713d8c1fa437697a3e"}, + {file = "mkdocs-material-7.3.6.tar.gz", hash = "sha256:1b1dbd8ef2508b358d93af55a5c5db3f141c95667fad802301ec621c40c7c217"}, + {file = "mkdocs_material-7.3.6-py2.py3-none-any.whl", hash = "sha256:1b6b3e9e09f922c2d7f1160fe15c8f43d4adc0d6fb81aa6ff0cbc7ef5b78ec75"}, ] mkdocs-material-extensions = [ - {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, - {file = "mkdocs_material_extensions-1.0.1-py3-none-any.whl", hash = "sha256:d90c807a88348aa6d1805657ec5c0b2d8d609c110e62b9dce4daf7fa981fa338"}, + {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, + {file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"}, ] mypy = [ {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"}, @@ -1342,50 +1399,46 @@ 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"}, ] -nltk = [ - {file = "nltk-3.6.2-py3-none-any.whl", hash = "sha256:240e23ab1ab159ef9940777d30c7c72d7e76d91877099218a7585370c11f6b9e"}, - {file = "nltk-3.6.2.zip", hash = "sha256:57d556abed621ab9be225cc6d2df1edce17572efb67a3d754630c9f8381503eb"}, -] oauthlib = [ - {file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"}, - {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"}, + {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"}, + {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"}, ] orjson = [ - {file = "orjson-3.5.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:2ba4165883fbef0985bce60bddbf91bc5cea77cc22b1c12fe7a716c6323ab1e7"}, - {file = "orjson-3.5.2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:cee746d186ba9efa47b9d52a649ee0617456a9a4d7a2cbd3ec06330bb9cb372a"}, - {file = "orjson-3.5.2-cp36-cp36m-macosx_10_7_x86_64.whl", hash = "sha256:8591a25a31a89cf2a33e30eb516ab028bad2c72fed04e323917114aaedc07c7d"}, - {file = "orjson-3.5.2-cp36-cp36m-macosx_10_9_universal2.whl", hash = "sha256:38cb8cdbf43eafc6dcbfb10a9e63c80727bb916aee0f75caf5f90e5355b266e1"}, - {file = "orjson-3.5.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:96b403796fc7e44bae843a2a83923925fe048f3a67c10a298fdfc0ff46163c14"}, - {file = "orjson-3.5.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:5b66a62d4c0c44441b23fafcd3d0892296d9793361b14bcc5a5645c88b6a4a71"}, - {file = "orjson-3.5.2-cp36-none-win_amd64.whl", hash = "sha256:609e93919268fadb871aafb7f550c3fe8d3e8c1305cadcc1610b414113b7034e"}, - {file = "orjson-3.5.2-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:200bd4491052d13696456a92d23f086b68b526c2464248733964e8165ac60888"}, - {file = "orjson-3.5.2-cp37-cp37m-macosx_10_9_universal2.whl", hash = "sha256:cc614bf6bfe0181e51dd98a9c53669f08d4d8641efbf1a287113da3059773dea"}, - {file = "orjson-3.5.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:43576bed3be300e9c02629a8d5fb3340fe6474765e6eee9610067def4b3ac19c"}, - {file = "orjson-3.5.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:acd735718b531b78858a7e932c58424c5a3e39e04d61bba3d95ce8a8498ea9e9"}, - {file = "orjson-3.5.2-cp37-none-win_amd64.whl", hash = "sha256:7503145ffd1ae90d487860b97e2867ec61c2c8f001209bb12700ba7833df8ddf"}, - {file = "orjson-3.5.2-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:9c37cf3dbc9c81abed04ba4854454e9f0d8ac7c05fb6c4f36545733e90be6af2"}, - {file = "orjson-3.5.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e6ef00ddc637b7d13926aaccdabac363efdfd348c132410eb054c27e2eae6a7"}, - {file = "orjson-3.5.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:9d0834ca40c6e467fa1f1db3f83a8c3562c03eb2b7067ad09de5019592edb88f"}, - {file = "orjson-3.5.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:d4a2ddc6342a8280dafaa69827b387b95856ef0a6c5812fe91f5bd21ddd2ef36"}, - {file = "orjson-3.5.2-cp38-none-win_amd64.whl", hash = "sha256:f54f8bcf24812a524e8904a80a365f7a287d82fc6ebdee528149616070abe5ab"}, - {file = "orjson-3.5.2-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:8b429471398ea37d848fb53bca6a8c42fb776c278f4fcb6a1d651b8f1fb64947"}, - {file = "orjson-3.5.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13fd458110fbe019c2a67ee539678189444f73bc09b27983c9b42663c63e0445"}, - {file = "orjson-3.5.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8bf1145a06e1245f0c8a8c32df6ffe52d214eb4eb88c3fb32e4ed14e3dc38e0e"}, - {file = "orjson-3.5.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:7e3434010e3f0680e92bb0a6094e4d5c939d0c4258c76397c6bd5263c7d62e86"}, - {file = "orjson-3.5.2-cp39-none-win_amd64.whl", hash = "sha256:df9730cc8cd22b3f54aa55317257f3279e6300157fc0f4ed4424586cd7eb012d"}, - {file = "orjson-3.5.2.tar.gz", hash = "sha256:f385253a6ddac37ea422ec2c0d35772b4f5bf0dc0803ce44543bf7e530423ef8"}, + {file = "orjson-3.6.4-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:fc01a15f3101628fd619158daec79b30d7461149735e73542ca8c13be6b835be"}, + {file = "orjson-3.6.4-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:48a69fed90f551bf9e9bb7a63e363fed4f67fc7c6e6bfb057054dc78f6721e9e"}, + {file = "orjson-3.6.4-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:3722f02f50861d5e2a6be9d50bfe8da27a5155bb60043118a4e1ceb8c7040cf7"}, + {file = "orjson-3.6.4-cp310-none-win_amd64.whl", hash = "sha256:231a99a728322d0271e970b149c57deb67315e6837e6cd4166cf51d30161700c"}, + {file = "orjson-3.6.4-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:6cd300421b41f7e84e388b1792a18c3fc4c440ae3039434b9320956be05f0102"}, + {file = "orjson-3.6.4-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e55ef66ee1d35b1c43db275aff3a1ba7e0408b31e624912a612bd799df14e73e"}, + {file = "orjson-3.6.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef8d332af8e6f7d6d2c1f3b5384c8d239800c1405b136da5f1710e802918d57"}, + {file = "orjson-3.6.4-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:8896e242a92733e454378e22711bd43a55fda4e80604fcefcc064ca977623673"}, + {file = "orjson-3.6.4-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:bdfa6f29f7b6aad70ce14591b99fba651008afa6bc3759f158887bcdc568b452"}, + {file = "orjson-3.6.4-cp37-none-win_amd64.whl", hash = "sha256:7c16c44872d33da0b97050a9ea8f7bc04e930c56e8185657bc200e1875a671da"}, + {file = "orjson-3.6.4-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:b467551f3be1dd08aff70c261cc883b63483eb0e31861ffe2cd8dac4fec7cfa9"}, + {file = "orjson-3.6.4-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7bf61afef12f6416db3ea377f3491ca8ac677d3cac6db1ebffb7a5fe92cce3ca"}, + {file = "orjson-3.6.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:014ea74d4a5dd6a7e98540768072d5bd8c2fedbcbbedcbbaecbb614e66080e81"}, + {file = "orjson-3.6.4-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:705cb90c536b4b9336c06b4a62c3c62e50354ddf20a2e48eb62bf34fb93d5b1f"}, + {file = "orjson-3.6.4-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:159e2240fc36720a5cb51a1cbc9905dcb8758aad50b3e7f14f6178ce2e842004"}, + {file = "orjson-3.6.4-cp38-none-win_amd64.whl", hash = "sha256:d2ae087866a1050de83c2a28490850badb41aeeb8a4605c84dd6004d4e58b5a4"}, + {file = "orjson-3.6.4-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:b4a7efe039b1154b23e5df8787ac01e4621213aed303b6304a5f8ad89c01455d"}, + {file = "orjson-3.6.4-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7b24f97ed76005f447e152b0e493abce8c60f010131998295175446312a71caf"}, + {file = "orjson-3.6.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1121187e2a721864b52e5dbb3cf8dd4a4546519a5fef1e13fa777347fb8884a2"}, + {file = "orjson-3.6.4-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:4edffd9e2298ff4f4f939aa67248eba043dc65c9e7d940c28a62c5502c6f2aa8"}, + {file = "orjson-3.6.4-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:e236fe94d8a77532f0065870fe265bd53e229012f39af99f79f5f1d4a8b0067c"}, + {file = "orjson-3.6.4-cp39-none-win_amd64.whl", hash = "sha256:5448cc1edd4c4bafc968404f92f0e9a582b4326ca442346bd1d1179a6faf52d9"}, + {file = "orjson-3.6.4.tar.gz", hash = "sha256:f8dbc428fc6d7420f231a7133d8dff4c882e64acb585dcf2fda74bdcfe1a6d9d"}, ] packaging = [ - {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, - {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, + {file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"}, + {file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"}, ] parso = [ {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, ] pathspec = [ - {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, - {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, @@ -1395,159 +1448,183 @@ pickleshare = [ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] +platformdirs = [ + {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, + {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, +] pluggy = [ - {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, - {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.18-py3-none-any.whl", hash = "sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04"}, - {file = "prompt_toolkit-3.0.18.tar.gz", hash = "sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"}, + {file = "prompt_toolkit-3.0.22-py3-none-any.whl", hash = "sha256:48d85cdca8b6c4f16480c7ce03fd193666b62b0a21667ca56b4bb5ad679d1170"}, + {file = "prompt_toolkit-3.0.22.tar.gz", hash = "sha256:449f333dd120bd01f5d296a8ce1452114ba3a71fae7288d2f0ae2c918764fa72"}, ] ptyprocess = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] py = [ - {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, - {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, + {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.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, - {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] pydantic = [ - {file = "pydantic-1.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0c40162796fc8d0aa744875b60e4dc36834db9f2a25dbf9ba9664b1915a23850"}, - {file = "pydantic-1.8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fff29fe54ec419338c522b908154a2efabeee4f483e48990f87e189661f31ce3"}, - {file = "pydantic-1.8.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:fbfb608febde1afd4743c6822c19060a8dbdd3eb30f98e36061ba4973308059e"}, - {file = "pydantic-1.8.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:eb8ccf12295113ce0de38f80b25f736d62f0a8d87c6b88aca645f168f9c78771"}, - {file = "pydantic-1.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:20d42f1be7c7acc352b3d09b0cf505a9fab9deb93125061b376fbe1f06a5459f"}, - {file = "pydantic-1.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dde4ca368e82791de97c2ec019681ffb437728090c0ff0c3852708cf923e0c7d"}, - {file = "pydantic-1.8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3bbd023c981cbe26e6e21c8d2ce78485f85c2e77f7bab5ec15b7d2a1f491918f"}, - {file = "pydantic-1.8.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:830ef1a148012b640186bf4d9789a206c56071ff38f2460a32ae67ca21880eb8"}, - {file = "pydantic-1.8.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:fb77f7a7e111db1832ae3f8f44203691e15b1fa7e5a1cb9691d4e2659aee41c4"}, - {file = "pydantic-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:3bcb9d7e1f9849a6bdbd027aabb3a06414abd6068cb3b21c49427956cce5038a"}, - {file = "pydantic-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2287ebff0018eec3cc69b1d09d4b7cebf277726fa1bd96b45806283c1d808683"}, - {file = "pydantic-1.8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:4bbc47cf7925c86a345d03b07086696ed916c7663cb76aa409edaa54546e53e2"}, - {file = "pydantic-1.8.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:6388ef4ef1435364c8cc9a8192238aed030595e873d8462447ccef2e17387125"}, - {file = "pydantic-1.8.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:dd4888b300769ecec194ca8f2699415f5f7760365ddbe243d4fd6581485fa5f0"}, - {file = "pydantic-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:8fbb677e4e89c8ab3d450df7b1d9caed23f254072e8597c33279460eeae59b99"}, - {file = "pydantic-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2f2736d9a996b976cfdfe52455ad27462308c9d3d0ae21a2aa8b4cd1a78f47b9"}, - {file = "pydantic-1.8.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3114d74329873af0a0e8004627f5389f3bb27f956b965ddd3e355fe984a1789c"}, - {file = "pydantic-1.8.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:258576f2d997ee4573469633592e8b99aa13bda182fcc28e875f866016c8e07e"}, - {file = "pydantic-1.8.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c17a0b35c854049e67c68b48d55e026c84f35593c66d69b278b8b49e2484346f"}, - {file = "pydantic-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:e8bc082afef97c5fd3903d05c6f7bb3a6af9fc18631b4cc9fedeb4720efb0c58"}, - {file = "pydantic-1.8.1-py3-none-any.whl", hash = "sha256:e3f8790c47ac42549dc8b045a67b0ca371c7f66e73040d0197ce6172b385e520"}, - {file = "pydantic-1.8.1.tar.gz", hash = "sha256:26cf3cb2e68ec6c0cfcb6293e69fb3450c5fd1ace87f46b64f678b0d29eac4c3"}, + {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, + {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, + {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, + {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, + {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, + {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, + {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, + {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, + {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, + {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, ] 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.8.1-py3-none-any.whl", hash = "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"}, - {file = "Pygments-2.8.1.tar.gz", hash = "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94"}, + {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, + {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, ] pymdown-extensions = [ - {file = "pymdown-extensions-8.1.1.tar.gz", hash = "sha256:632371fa3bf1b21a0e3f4063010da59b41db049f261f4c0b0872069a9b6d1735"}, - {file = "pymdown_extensions-8.1.1-py3-none-any.whl", hash = "sha256:478b2c04513fbb2db61688d5f6e9030a92fb9be14f1f383535c43f7be9dff95b"}, + {file = "pymdown-extensions-9.1.tar.gz", hash = "sha256:74247f2c80f1d9e3c7242abe1c16317da36c6f26c7ad4b8a7f457f0ec20f0365"}, + {file = "pymdown_extensions-9.1-py3-none-any.whl", hash = "sha256:b03e66f91f33af4a6e7a0e20c740313522995f69a03d86316b1449766c473d0e"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ - {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, - {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, + {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-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, - {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, + {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, + {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] pytz = [ - {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, - {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, + {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, + {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, ] pyyaml = [ - {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, - {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, - {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, - {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, - {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, - {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, - {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, - {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, - {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] +pyyaml-env-tag = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] regex = [ - {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"}, - {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"}, - {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"}, - {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"}, - {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"}, - {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"}, - {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"}, - {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"}, - {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"}, - {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"}, - {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"}, - {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"}, - {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, + {file = "regex-2021.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf"}, + {file = "regex-2021.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4"}, + {file = "regex-2021.11.10-cp310-cp310-win32.whl", hash = "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a"}, + {file = "regex-2021.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12"}, + {file = "regex-2021.11.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e"}, + {file = "regex-2021.11.10-cp36-cp36m-win32.whl", hash = "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4"}, + {file = "regex-2021.11.10-cp36-cp36m-win_amd64.whl", hash = "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e"}, + {file = "regex-2021.11.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f"}, + {file = "regex-2021.11.10-cp37-cp37m-win32.whl", hash = "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec"}, + {file = "regex-2021.11.10-cp37-cp37m-win_amd64.whl", hash = "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4"}, + {file = "regex-2021.11.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83"}, + {file = "regex-2021.11.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94"}, + {file = "regex-2021.11.10-cp38-cp38-win32.whl", hash = "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc"}, + {file = "regex-2021.11.10-cp38-cp38-win_amd64.whl", hash = "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d"}, + {file = "regex-2021.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b"}, + {file = "regex-2021.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef"}, + {file = "regex-2021.11.10-cp39-cp39-win32.whl", hash = "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a"}, + {file = "regex-2021.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29"}, + {file = "regex-2021.11.10.tar.gz", hash = "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6"}, ] requests = [ - {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, - {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, + {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, + {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] requests-file = [ {file = "requests-file-1.5.1.tar.gz", hash = "sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e"}, @@ -1558,12 +1635,12 @@ requests-toolbelt = [ {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, ] rfc3986 = [ - {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"}, - {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"}, + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, ] six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] sniffio = [ {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, @@ -1573,56 +1650,13 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -tornado = [ - {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, - {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, - {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, - {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, - {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, - {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, - {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, - {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, - {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, - {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, - {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, - {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, - {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, - {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, - {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, - {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, - {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, - {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, -] -tqdm = [ - {file = "tqdm-4.60.0-py2.py3-none-any.whl", hash = "sha256:daec693491c52e9498632dfbe9ccfc4882a557f5fa08982db1b4d3adbe0887c3"}, - {file = "tqdm-4.60.0.tar.gz", hash = "sha256:ebdebdb95e3477ceea267decfc0784859aa3df3e27e22d23b83e9b272bf157ae"}, +tomli = [ + {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, + {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, ] traitlets = [ - {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"}, - {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, + {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"}, + {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"}, ] typed-ast = [ {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, @@ -1657,23 +1691,47 @@ typed-ast = [ {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, + {file = "typing_extensions-4.0.0-py3-none-any.whl", hash = "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"}, + {file = "typing_extensions-4.0.0.tar.gz", hash = "sha256:2cdf80e4e04866a9b3689a51869016d36db0814d84b8d8a568d22781d45d27ed"}, ] urllib3 = [ - {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, - {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, + {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, + {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, +] +watchdog = [ + {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"}, + {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b"}, + {file = "watchdog-2.1.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542"}, + {file = "watchdog-2.1.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669"}, + {file = "watchdog-2.1.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660"}, + {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3"}, + {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04"}, + {file = "watchdog-2.1.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b"}, + {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604"}, + {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6"}, + {file = "watchdog-2.1.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9"}, + {file = "watchdog-2.1.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_armv7l.whl", hash = "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_i686.whl", hash = "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_s390x.whl", hash = "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15"}, + {file = "watchdog-2.1.6-py3-none-win32.whl", hash = "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d"}, + {file = "watchdog-2.1.6-py3-none-win_amd64.whl", hash = "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5"}, + {file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"}, + {file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] zeep = [ - {file = "zeep-4.0.0-py2.py3-none-any.whl", hash = "sha256:12945da854ff10376d384d21a132b66e318784ef05c696ae300a130746f4baa8"}, - {file = "zeep-4.0.0.tar.gz", hash = "sha256:98158e43db33739d41502a1a7e3629dcb62dfd0864ea28c9d43f560a091cfe3f"}, + {file = "zeep-4.1.0-py2.py3-none-any.whl", hash = "sha256:81c491092b71f5b276de8c63dfd452be3f322622c48a54f3a497cf913bdfb2f4"}, + {file = "zeep-4.1.0.tar.gz", hash = "sha256:5867f2eadd6b028d9751f4155af590d3aaf9280e3a0ed5e15a53343921c956e5"}, ] zipp = [ - {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, - {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, + {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, + {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, ] From ea360bd2a70a5a5545c2d921ad60163a340f7e26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Dec 2021 21:00:12 +0000 Subject: [PATCH 34/47] Bump lxml from 4.6.4 to 4.6.5 Bumps [lxml](https://github.com/lxml/lxml) from 4.6.4 to 4.6.5. - [Release notes](https://github.com/lxml/lxml/releases) - [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt) - [Commits](https://github.com/lxml/lxml/compare/lxml-4.6.4...lxml-4.6.5) --- updated-dependencies: - dependency-name: lxml dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 150 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 62 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4f9285b..4eae6ec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -383,10 +383,10 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "lxml" -version = "4.6.4" +version = "4.6.5" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." category = "main" -optional = true +optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" [package.extras] @@ -1211,66 +1211,66 @@ jinja2 = [ {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, ] lxml = [ - {file = "lxml-4.6.4-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bbf2dc330bd44bfc0254ab37677ec60f7c7ecea55ad8ba1b8b2ea7bf20c265f5"}, - {file = "lxml-4.6.4-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b667c51682fe9b9788c69465956baa8b6999531876ccedcafc895c74ad716cd8"}, - {file = "lxml-4.6.4-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:72e730d33fe2e302fd07285f14624fca5e5e2fb2bb4fb2c3941e318c41c443d1"}, - {file = "lxml-4.6.4-cp27-cp27m-win32.whl", hash = "sha256:433df8c7dde0f9e41cbf4f36b0829d50a378116ef5e962ba3881f2f5f025c7be"}, - {file = "lxml-4.6.4-cp27-cp27m-win_amd64.whl", hash = "sha256:35752ee40f7bbf6adc9ff4e1f4b84794a3593736dcce80db32e3c2aa85e294ac"}, - {file = "lxml-4.6.4-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ff5bb2a198ea67403bb6818705e9a4f90e0313f2215428ec51001ce56d939fb"}, - {file = "lxml-4.6.4-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9b87727561c1150c0cc91c5d9d389448b37a7d15f0ba939ed3d1acb2f11bf6c5"}, - {file = "lxml-4.6.4-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:45fdb2899c755138722797161547a40b3e2a06feda620cc41195ee7e97806d81"}, - {file = "lxml-4.6.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:38b9de0de3aa689fe9fb9877ae1be1e83b8cf9621f7e62049d0436b9ecf4ad64"}, - {file = "lxml-4.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:662523cd2a0246740225c7e32531f2e766544122e58bee70e700a024cfc0cf81"}, - {file = "lxml-4.6.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:4aa349c5567651f34d4eaae7de6ed5b523f6d70a288f9c6fbac22d13a0784e04"}, - {file = "lxml-4.6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:08eb9200d88b376a8ed5e50f1dc1d1a45b49305169674002a3b5929943390591"}, - {file = "lxml-4.6.4-cp310-cp310-win32.whl", hash = "sha256:bdc224f216ead849e902151112efef6e96c41ee1322e15d4e5f7c8a826929aee"}, - {file = "lxml-4.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ab6db93a2b6b66cbf62b4e4a7135f476e708e8c5c990d186584142c77d7f975a"}, - {file = "lxml-4.6.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50790313df028aa05cf22be9a8da033b86c42fa32523e4fd944827b482b17bf0"}, - {file = "lxml-4.6.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6764998345552b1dfc9326a932d2bad6367c6b37a176bb73ada6b9486bf602f7"}, - {file = "lxml-4.6.4-cp35-cp35m-win32.whl", hash = "sha256:543b239b191bb3b6d9bef5f09f1fb2be5b7eb09ab4d386aa655e4d53fbe9ff47"}, - {file = "lxml-4.6.4-cp35-cp35m-win_amd64.whl", hash = "sha256:a75c1ad05eedb1a3ff2a34a52a4f0836cfaa892e12796ba39a7732c82701eff4"}, - {file = "lxml-4.6.4-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:47e955112ce64241fdb357acf0216081f9f3255b3ac9c502ca4b3323ec1ca558"}, - {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:20d7c8d90d449c6a353b15ee0459abae8395dbe59ad01e406ccbf30cd81c6f98"}, - {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:240db6f3228d26e3c6f4fad914b9ddaaf8707254e8b3efd564dc680c8ec3c264"}, - {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:351482da8dd028834028537f08724b1de22d40dcf3bb723b469446564f409074"}, - {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e678a643177c0e5ec947b645fa7bc84260dfb9b6bf8fb1fdd83008dfc2ca5928"}, - {file = "lxml-4.6.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:15d0381feb56f08f78c5cc4fc385ddfe0bde1456e37f54a9322833371aec4060"}, - {file = "lxml-4.6.4-cp36-cp36m-win32.whl", hash = "sha256:4ba74afe5ee5cb5e28d83b513a6e8f0875fda1dc1a9aea42cc0065f029160d2a"}, - {file = "lxml-4.6.4-cp36-cp36m-win_amd64.whl", hash = "sha256:9c91a73971a922c13070fd8fa5a114c858251791ba2122a941e6aa781c713e44"}, - {file = "lxml-4.6.4-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:6020c70ff695106bf80651953a23e37718ef1fee9abd060dcad8e32ab2dc13f3"}, - {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f5dd358536b8a964bf6bd48de038754c1609e72e5f17f5d21efe2dda17594dbf"}, - {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7ae7089d81fc502df4b217ad77f03c54039fe90dac0acbe70448d7e53bfbc57e"}, - {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:80d10d53d3184837445ff8562021bdd37f57c4cadacbf9d8726cc16220a00d54"}, - {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e95da348d57eb448d226a44b868ff2ca5786fbcbe417ac99ff62d0a7d724b9c7"}, - {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ffd65cfa33fed01735c82aca640fde4cc63f0414775cba11e06f84fae2085a6e"}, - {file = "lxml-4.6.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:877666418598f6cb289546c77ff87590cfd212f903b522b0afa0b9fb73b3ccfb"}, - {file = "lxml-4.6.4-cp37-cp37m-win32.whl", hash = "sha256:e91d24623e747eeb2d8121f4a94c6a7ad27dc48e747e2dc95bfe88632bd028a2"}, - {file = "lxml-4.6.4-cp37-cp37m-win_amd64.whl", hash = "sha256:4ec9a80dd5704ecfde54319b6964368daf02848c8954d3bacb9b64d1c7659159"}, - {file = "lxml-4.6.4-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:2901625f4a878a055d275beedc20ba9cb359cefc4386a967222fee29eb236038"}, - {file = "lxml-4.6.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b567178a74a2261345890eac66fbf394692a6e002709d329f28a673ca6042473"}, - {file = "lxml-4.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4717123f7c11c81e0da69989e5a64079c3f402b0efeb4c6241db6c369d657bd8"}, - {file = "lxml-4.6.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:cf201bf5594d1aab139fe53e3fca457e4f8204a5bbd65d48ab3b82a16f517868"}, - {file = "lxml-4.6.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a77a3470ba37e11872c75ca95baf9b3312133a3d5a5dc720803b23098c653976"}, - {file = "lxml-4.6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:619c6d2b552bba00491e96c0518aad94002651c108a0f7364ff2d7798812c00e"}, - {file = "lxml-4.6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:601f0ab75538b280aaf1e720eb9d68d4fa104ac274e1e9e6971df488f4dcdb0f"}, - {file = "lxml-4.6.4-cp38-cp38-win32.whl", hash = "sha256:75d3c5bbc0ddbad03bb68b9be638599f67e4b98ed3dcd0fec9f6f39e41ee96cb"}, - {file = "lxml-4.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:4341d135f5660db10184963d9c3418c3e28d7f868aaf8b11a323ebf85813f7f4"}, - {file = "lxml-4.6.4-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:9db24803fa71e3305fe4a7812782b708da21a0b774b130dd1860cf40a6d7a3ee"}, - {file = "lxml-4.6.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:afd60230ad9d8bcba005945ec3a343722f09e0b7f8ae804246e5d2cfc6bd71a6"}, - {file = "lxml-4.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:0c15e1cd55055956e77b0732270f1c6005850696bc3ef3e03d01e78af84eaa42"}, - {file = "lxml-4.6.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6d422b3c729737d8a39279a25fa156c983a56458f8b2f97661ee6fb22b80b1d6"}, - {file = "lxml-4.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2eb90f6ec3c236ef2f1bb38aee7c0d23e77d423d395af6326e7cca637519a4cb"}, - {file = "lxml-4.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:51a0e5d243687596f46e24e464121d4b232ad772e2d1785b2a2c0eb413c285d4"}, - {file = "lxml-4.6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d43bd68714049c84e297c005456a15ecdec818f7b5aa5868c8b0a865cfb78a44"}, - {file = "lxml-4.6.4-cp39-cp39-win32.whl", hash = "sha256:ee9e4b07b0eba4b6a521509e9e1877476729c1243246b6959de697ebea739643"}, - {file = "lxml-4.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:48eaac2991b3036175b42ee8d3c23f4cca13f2be8426bf29401a690ab58c88f4"}, - {file = "lxml-4.6.4-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:2b06a91cf7b8acea7793006e4ae50646cef0fe35ce5acd4f5cb1c77eb228e4a1"}, - {file = "lxml-4.6.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:523f195948a1ba4f9f5b7294d83c6cd876547dc741820750a7e5e893a24bbe38"}, - {file = "lxml-4.6.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:b0ca0ada9d3bc18bd6f611bd001a28abdd49ab9698bd6d717f7f5394c8e94628"}, - {file = "lxml-4.6.4-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:197b7cb7a753cf553a45115739afd8458464a28913da00f5c525063f94cd3f48"}, - {file = "lxml-4.6.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:6298f5b42a26581206ef63fffa97c754245d329414108707c525512a5197f2ba"}, - {file = "lxml-4.6.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0b12c95542f04d10cba46b3ff28ea52ea56995b78cf918f0b11b05e75812bb79"}, - {file = "lxml-4.6.4.tar.gz", hash = "sha256:daf9bd1fee31f1c7a5928b3e1059e09a8d683ea58fb3ffc773b6c88cb8d1399c"}, + {file = "lxml-4.6.5-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:abcf7daa5ebcc89328326254f6dd6d566adb483d4d00178892afd386ab389de2"}, + {file = "lxml-4.6.5-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3884476a90d415be79adfa4e0e393048630d0d5bcd5757c4c07d8b4b00a1096b"}, + {file = "lxml-4.6.5-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:add017c5bd6b9ec3a5f09248396b6ee2ce61c5621f087eb2269c813cd8813808"}, + {file = "lxml-4.6.5-cp27-cp27m-win32.whl", hash = "sha256:a702005e447d712375433ed0499cb6e1503fadd6c96a47f51d707b4d37b76d3c"}, + {file = "lxml-4.6.5-cp27-cp27m-win_amd64.whl", hash = "sha256:da07c7e7fc9a3f40446b78c54dbba8bfd5c9100dfecb21b65bfe3f57844f5e71"}, + {file = "lxml-4.6.5-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a708c291900c40a7ecf23f1d2384ed0bc0604e24094dd13417c7e7f8f7a50d93"}, + {file = "lxml-4.6.5-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f33d8efb42e4fc2b31b3b4527940b25cdebb3026fb56a80c1c1c11a4271d2352"}, + {file = "lxml-4.6.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:f6befb83bca720b71d6bd6326a3b26e9496ae6649e26585de024890fe50f49b8"}, + {file = "lxml-4.6.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:59d77bfa3bea13caee95bc0d3f1c518b15049b97dd61ea8b3d71ce677a67f808"}, + {file = "lxml-4.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:68a851176c931e2b3de6214347b767451243eeed3bea34c172127bbb5bf6c210"}, + {file = "lxml-4.6.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7790a273225b0c46e5f859c1327f0f659896cc72eaa537d23aa3ad9ff2a1cc1"}, + {file = "lxml-4.6.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6548fc551de15f310dd0564751d9dc3d405278d45ea9b2b369ed1eccf142e1f5"}, + {file = "lxml-4.6.5-cp310-cp310-win32.whl", hash = "sha256:dc8a0dbb2a10ae8bb609584f5c504789f0f3d0d81840da4849102ec84289f952"}, + {file = "lxml-4.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:1ccbfe5d17835db906f2bab6f15b34194db1a5b07929cba3cf45a96dbfbfefc0"}, + {file = "lxml-4.6.5-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca9a40497f7e97a2a961c04fa8a6f23d790b0521350a8b455759d786b0bcb203"}, + {file = "lxml-4.6.5-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e5b4b0d9440046ead3bd425eb2b852499241ee0cef1ae151038e4f87ede888c4"}, + {file = "lxml-4.6.5-cp35-cp35m-win32.whl", hash = "sha256:87f8f7df70b90fbe7b49969f07b347e3f978f8bd1046bb8ecae659921869202b"}, + {file = "lxml-4.6.5-cp35-cp35m-win_amd64.whl", hash = "sha256:ce52aad32ec6e46d1a91ff8b8014a91538800dd533914bfc4a82f5018d971408"}, + {file = "lxml-4.6.5-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:8021eeff7fabde21b9858ed058a8250ad230cede91764d598c2466b0ba70db8b"}, + {file = "lxml-4.6.5-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:cab343b265e38d4e00649cbbad9278b734c5715f9bcbb72c85a1f99b1a58e19a"}, + {file = "lxml-4.6.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:3534d7c468c044f6aef3c0aff541db2826986a29ea73f2ca831f5d5284d9b570"}, + {file = "lxml-4.6.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdb98f4c9e8a1735efddfaa995b0c96559792da15d56b76428bdfc29f77c4cdb"}, + {file = "lxml-4.6.5-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5ea121cb66d7e5cb396b4c3ca90471252b94e01809805cfe3e4e44be2db3a99c"}, + {file = "lxml-4.6.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:121fc6f71c692b49af6c963b84ab7084402624ffbe605287da362f8af0668ea3"}, + {file = "lxml-4.6.5-cp36-cp36m-win32.whl", hash = "sha256:1a2a7659b8eb93c6daee350a0d844994d49245a0f6c05c747f619386fb90ba04"}, + {file = "lxml-4.6.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2f77556266a8fe5428b8759fbfc4bd70be1d1d9c9b25d2a414f6a0c0b0f09120"}, + {file = "lxml-4.6.5-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:558485218ee06458643b929765ac1eb04519ca3d1e2dcc288517de864c747c33"}, + {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ba0006799f21d83c3717fe20e2707a10bbc296475155aadf4f5850f6659b96b9"}, + {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:916d457ad84e05b7db52700bad0a15c56e0c3000dcaf1263b2fb7a56fe148996"}, + {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c580c2a61d8297a6e47f4d01f066517dbb019be98032880d19ece7f337a9401d"}, + {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a21b78af7e2e13bec6bea12fc33bc05730197674f3e5402ce214d07026ccfebd"}, + {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:46515773570a33eae13e451c8fcf440222ef24bd3b26f40774dd0bd8b6db15b2"}, + {file = "lxml-4.6.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:124f09614f999551ac65e5b9875981ce4b66ac4b8e2ba9284572f741935df3d9"}, + {file = "lxml-4.6.5-cp37-cp37m-win32.whl", hash = "sha256:b4015baed99d046c760f09a4c59d234d8f398a454380c3cf0b859aba97136090"}, + {file = "lxml-4.6.5-cp37-cp37m-win_amd64.whl", hash = "sha256:12ae2339d32a2b15010972e1e2467345b7bf962e155671239fba74c229564b7f"}, + {file = "lxml-4.6.5-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:76b6c296e4f7a1a8a128aec42d128646897f9ae9a700ef6839cdc9b3900db9b5"}, + {file = "lxml-4.6.5-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:534032a5ceb34bba1da193b7d386ac575127cc39338379f39a164b10d97ade89"}, + {file = "lxml-4.6.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:60aeb14ff9022d2687ef98ce55f6342944c40d00916452bb90899a191802137a"}, + {file = "lxml-4.6.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9801bcd52ac9c795a7d81ea67471a42cffe532e46cfb750cd5713befc5c019c0"}, + {file = "lxml-4.6.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3b95fb7e6f9c2f53db88f4642231fc2b8907d854e614710996a96f1f32018d5c"}, + {file = "lxml-4.6.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:642eb4cabd997c9b949a994f9643cd8ae00cf4ca8c5cd9c273962296fadf1c44"}, + {file = "lxml-4.6.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:af4139172ff0263d269abdcc641e944c9de4b5d660894a3ec7e9f9db63b56ac9"}, + {file = "lxml-4.6.5-cp38-cp38-win32.whl", hash = "sha256:57cf05466917e08f90e323f025b96f493f92c0344694f5702579ab4b7e2eb10d"}, + {file = "lxml-4.6.5-cp38-cp38-win_amd64.whl", hash = "sha256:4f415624cf8b065796649a5e4621773dc5c9ea574a944c76a7f8a6d3d2906b41"}, + {file = "lxml-4.6.5-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7679bb6e4d9a3978a46ab19a3560e8d2b7265ef3c88152e7fdc130d649789887"}, + {file = "lxml-4.6.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c34234a1bc9e466c104372af74d11a9f98338a3f72fae22b80485171a64e0144"}, + {file = "lxml-4.6.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4b9390bf973e3907d967b75be199cf1978ca8443183cf1e78ad80ad8be9cf242"}, + {file = "lxml-4.6.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fcc849b28f584ed1dbf277291ded5c32bb3476a37032df4a1d523b55faa5f944"}, + {file = "lxml-4.6.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:46f21f2600d001af10e847df9eb3b832e8a439f696c04891bcb8a8cedd859af9"}, + {file = "lxml-4.6.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:99cf827f5a783038eb313beee6533dddb8bdb086d7269c5c144c1c952d142ace"}, + {file = "lxml-4.6.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:925174cafb0f1179a7fd38da90302555d7445e34c9ece68019e53c946be7f542"}, + {file = "lxml-4.6.5-cp39-cp39-win32.whl", hash = "sha256:12d8d6fe3ddef629ac1349fa89a638b296a34b6529573f5055d1cb4e5245f73b"}, + {file = "lxml-4.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:a52e8f317336a44836475e9c802f51c2dc38d612eaa76532cb1d17690338b63b"}, + {file = "lxml-4.6.5-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:11ae552a78612620afd15625be9f1b82e3cc2e634f90d6b11709b10a100cba59"}, + {file = "lxml-4.6.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:473701599665d874919d05bb33b56180447b3a9da8d52d6d9799f381ce23f95c"}, + {file = "lxml-4.6.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7f00cc64b49d2ef19ddae898a3def9dd8fda9c3d27c8a174c2889ee757918e71"}, + {file = "lxml-4.6.5-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:73e8614258404b2689a26cb5d002512b8bc4dfa18aca86382f68f959aee9b0c8"}, + {file = "lxml-4.6.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ff44de36772b05c2eb74f2b4b6d1ae29b8f41ed5506310ce1258d44826ee38c1"}, + {file = "lxml-4.6.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:5d5254c815c186744c8f922e2ce861a2bdeabc06520b4b30b2f7d9767791ce6e"}, + {file = "lxml-4.6.5.tar.gz", hash = "sha256:6e84edecc3a82f90d44ddee2ee2a2630d4994b8471816e226d2b771cda7ac4ca"}, ] markdown = [ {file = "Markdown-3.3.5-py3-none-any.whl", hash = "sha256:0d2d09f75cb8d1ffc6770c65c61770b23a61708101f47bda416a002a0edbc480"}, @@ -1405,6 +1405,7 @@ oauthlib = [ ] orjson = [ {file = "orjson-3.6.4-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:fc01a15f3101628fd619158daec79b30d7461149735e73542ca8c13be6b835be"}, + {file = "orjson-3.6.4-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:c840e6ca222f76e7f13e9ee2f0650c9ee449e5e4aae38c73ab6ecaf3077ea21c"}, {file = "orjson-3.6.4-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:48a69fed90f551bf9e9bb7a63e363fed4f67fc7c6e6bfb057054dc78f6721e9e"}, {file = "orjson-3.6.4-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:3722f02f50861d5e2a6be9d50bfe8da27a5155bb60043118a4e1ceb8c7040cf7"}, {file = "orjson-3.6.4-cp310-none-win_amd64.whl", hash = "sha256:231a99a728322d0271e970b149c57deb67315e6837e6cd4166cf51d30161700c"}, @@ -1580,6 +1581,11 @@ regex = [ {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f"}, {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0"}, {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4"}, + {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9ed0b1e5e0759d6b7f8e2f143894b2a7f3edd313f38cf44e1e15d360e11749b"}, + {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:473e67837f786404570eae33c3b64a4b9635ae9f00145250851a1292f484c063"}, + {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2fee3ed82a011184807d2127f1733b4f6b2ff6ec7151d83ef3477f3b96a13d03"}, + {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d5fd67df77bab0d3f4ea1d7afca9ef15c2ee35dfb348c7b57ffb9782a6e4db6e"}, + {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5d408a642a5484b9b4d11dea15a489ea0928c7e410c7525cd892f4d04f2f617b"}, {file = "regex-2021.11.10-cp310-cp310-win32.whl", hash = "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a"}, {file = "regex-2021.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12"}, {file = "regex-2021.11.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc"}, @@ -1589,6 +1595,11 @@ regex = [ {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733"}, {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23"}, {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e"}, + {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:74cbeac0451f27d4f50e6e8a8f3a52ca074b5e2da9f7b505c4201a57a8ed6286"}, + {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:3598893bde43091ee5ca0a6ad20f08a0435e93a69255eeb5f81b85e81e329264"}, + {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:50a7ddf3d131dc5633dccdb51417e2d1910d25cbcf842115a3a5893509140a3a"}, + {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:61600a7ca4bcf78a96a68a27c2ae9389763b5b94b63943d5158f2a377e09d29a"}, + {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:563d5f9354e15e048465061509403f68424fef37d5add3064038c2511c8f5e00"}, {file = "regex-2021.11.10-cp36-cp36m-win32.whl", hash = "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4"}, {file = "regex-2021.11.10-cp36-cp36m-win_amd64.whl", hash = "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e"}, {file = "regex-2021.11.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"}, @@ -1598,6 +1609,11 @@ regex = [ {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a"}, {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e"}, {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f"}, + {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:42b50fa6666b0d50c30a990527127334d6b96dd969011e843e726a64011485da"}, + {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6e1d2cc79e8dae442b3fa4a26c5794428b98f81389af90623ffcc650ce9f6732"}, + {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:0416f7399e918c4b0e074a0f66e5191077ee2ca32a0f99d4c187a62beb47aa05"}, + {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:ce298e3d0c65bd03fa65ffcc6db0e2b578e8f626d468db64fdf8457731052942"}, + {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dc07f021ee80510f3cd3af2cad5b6a3b3a10b057521d9e6aaeb621730d320c5a"}, {file = "regex-2021.11.10-cp37-cp37m-win32.whl", hash = "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec"}, {file = "regex-2021.11.10-cp37-cp37m-win_amd64.whl", hash = "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4"}, {file = "regex-2021.11.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83"}, @@ -1608,6 +1624,11 @@ regex = [ {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec"}, {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe"}, {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94"}, + {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f5be7805e53dafe94d295399cfbe5227f39995a997f4fd8539bf3cbdc8f47ca8"}, + {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a955b747d620a50408b7fdf948e04359d6e762ff8a85f5775d907ceced715129"}, + {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:139a23d1f5d30db2cc6c7fd9c6d6497872a672db22c4ae1910be22d4f4b2068a"}, + {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ca49e1ab99593438b204e00f3970e7a5f70d045267051dfa6b5f4304fcfa1dbf"}, + {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:96fc32c16ea6d60d3ca7f63397bff5c75c5a562f7db6dec7d412f7c4d2e78ec0"}, {file = "regex-2021.11.10-cp38-cp38-win32.whl", hash = "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc"}, {file = "regex-2021.11.10-cp38-cp38-win_amd64.whl", hash = "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d"}, {file = "regex-2021.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b"}, @@ -1618,6 +1639,11 @@ regex = [ {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449"}, {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b"}, {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef"}, + {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cd410a1cbb2d297c67d8521759ab2ee3f1d66206d2e4328502a487589a2cb21b"}, + {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e6096b0688e6e14af6a1b10eaad86b4ff17935c49aa774eac7c95a57a4e8c296"}, + {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:529801a0d58809b60b3531ee804d3e3be4b412c94b5d267daa3de7fadef00f49"}, + {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f594b96fe2e0821d026365f72ac7b4f0b487487fb3d4aaf10dd9d97d88a9737"}, + {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2409b5c9cef7054dde93a9803156b411b677affc84fca69e908b1cb2c540025d"}, {file = "regex-2021.11.10-cp39-cp39-win32.whl", hash = "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a"}, {file = "regex-2021.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29"}, {file = "regex-2021.11.10.tar.gz", hash = "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6"}, From b05c059af9c53bd932672b52dbc15d54d3c553f6 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 08:38:15 +0200 Subject: [PATCH 35/47] chore: Ignore .DS_Store --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ef74cd2..674fd32 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ /.mypy* /.pytest_cache /poetry.lock +.DS_Store From 1b1d27187dd32da3083ac5b7c28bef1a3464c1b2 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 08:40:27 +0200 Subject: [PATCH 36/47] chore: Remove poetry.lock --- poetry.lock | 1763 --------------------------------------------------- 1 file changed, 1763 deletions(-) delete mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 4eae6ec..0000000 --- a/poetry.lock +++ /dev/null @@ -1,1763 +0,0 @@ -[[package]] -name = "anyio" -version = "3.3.4" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] -test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=6.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16)"] - -[[package]] -name = "appnope" -version = "0.1.2" -description = "Disable App Nap on macOS >= 10.9" -category = "main" -optional = true -python-versions = "*" - -[[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 = "main" -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 = "authlib" -version = "0.15.5" -description = "The ultimate Python library in building OAuth and OpenID Connect servers." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -cryptography = "*" - -[package.extras] -client = ["requests"] - -[[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -category = "main" -optional = true -python-versions = "*" - -[[package]] -name = "black" -version = "21.11b0" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -click = ">=7.1.2" -mypy-extensions = ">=0.4.3" -pathspec = ">=0.9.0,<1" -platformdirs = ">=2" -regex = ">=2020.1.8" -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 = "cached-property" -version = "1.5.2" -description = "A decorator for caching properties in classes." -category = "main" -optional = true -python-versions = "*" - -[[package]] -name = "certifi" -version = "2021.10.8" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "cffi" -version = "1.15.0" -description = "Foreign Function Interface for Python calling C code." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "2.0.7" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = true -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 = "colorama" -version = "0.4.4" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "coverage" -version = "6.1.2" -description = "Code coverage measurement for Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "cryptography" -version = "35.0.0" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = ">=1.12" - -[package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] -docstest = ["doc8", "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 = "decorator" -version = "5.1.0" -description = "Decorators for Humans" -category = "main" -optional = true -python-versions = ">=3.5" - -[[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 = "ghp-import" -version = "2.0.2" -description = "Copy your docs directly to the gh-pages branch." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -python-dateutil = ">=2.8.1" - -[package.extras] -dev = ["twine", "markdown", "flake8", "wheel"] - -[[package]] -name = "h11" -version = "0.12.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "httpcore" -version = "0.13.7" -description = "A minimal low-level HTTP client." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -anyio = ">=3.0.0,<4.0.0" -h11 = ">=0.11,<0.13" -sniffio = ">=1.0.0,<2.0.0" - -[package.extras] -http2 = ["h2 (>=3,<5)"] - -[[package]] -name = "httpx" -version = "0.18.2" -description = "The next generation HTTP client." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -certifi = "*" -httpcore = ">=0.13.3,<0.14.0" -rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} -sniffio = "*" - -[package.extras] -brotli = ["brotlicffi (>=1.0.0,<2.0.0)"] -http2 = ["h2 (>=3.0.0,<4.0.0)"] - -[[package]] -name = "idna" -version = "3.3" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -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 = "ipython" -version = "7.29.0" -description = "IPython: Productive Interactive Computing" -category = "main" -optional = true -python-versions = ">=3.7" - -[package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" -pygments = "*" -traitlets = ">=4.2" - -[package.extras] -all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] -doc = ["Sphinx (>=1.3)"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["notebook", "ipywidgets"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"] - -[[package]] -name = "isodate" -version = "0.6.0" -description = "An ISO 8601 date/time/duration parser and formatter" -category = "main" -optional = true -python-versions = "*" - -[package.dependencies] -six = "*" - -[[package]] -name = "isort" -version = "5.10.1" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.6.1,<4.0" - -[package.extras] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] -requirements_deprecated_finder = ["pipreqs", "pip-api"] -colors = ["colorama (>=0.4.3,<0.5.0)"] -plugins = ["setuptools"] - -[[package]] -name = "jedi" -version = "0.18.1" -description = "An autocompletion tool for Python that can be used for text editors." -category = "main" -optional = true -python-versions = ">=3.6" - -[package.dependencies] -parso = ">=0.8.0,<0.9.0" - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] - -[[package]] -name = "jinja2" -version = "3.0.3" -description = "A very fast and expressive template engine." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "lxml" -version = "4.6.5" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["beautifulsoup4"] -source = ["Cython (>=0.29.7)"] - -[[package]] -name = "markdown" -version = "3.3.5" -description = "Python implementation of Markdown." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "markupsafe" -version = "2.0.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "matplotlib-inline" -version = "0.1.3" -description = "Inline Matplotlib backend for Jupyter" -category = "main" -optional = true -python-versions = ">=3.5" - -[package.dependencies] -traitlets = "*" - -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "mergedeep" -version = "1.3.4" -description = "A deep merge function for 🐍." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "mkdocs" -version = "1.2.3" -description = "Project documentation with Markdown." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -click = ">=3.3" -ghp-import = ">=1.0" -importlib-metadata = ">=3.10" -Jinja2 = ">=2.10.1" -Markdown = ">=3.2.1" -mergedeep = ">=1.3.4" -packaging = ">=20.5" -PyYAML = ">=3.10" -pyyaml-env-tag = ">=0.1" -watchdog = ">=2.0" - -[package.extras] -i18n = ["babel (>=2.9.0)"] - -[[package]] -name = "mkdocs-material" -version = "7.3.6" -description = "A Material Design theme for MkDocs" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -jinja2 = ">=2.11.1" -markdown = ">=3.2" -mkdocs = ">=1.2.3" -mkdocs-material-extensions = ">=1.0" -pygments = ">=2.10" -pymdown-extensions = ">=9.0" - -[[package]] -name = "mkdocs-material-extensions" -version = "1.0.3" -description = "Extension pack for Python Markdown." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "mypy" -version = "0.812" -description = "Optional static typing for Python" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -mypy-extensions = ">=0.4.3,<0.5.0" -typed-ast = ">=1.4.0,<1.5.0" -typing-extensions = ">=3.7.4" - -[package.extras] -dmypy = ["psutil (>=4.0)"] - -[[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 = "oauthlib" -version = "3.1.1" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -rsa = ["cryptography (>=3.0.0,<4)"] -signals = ["blinker (>=1.4.0)"] -signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"] - -[[package]] -name = "orjson" -version = "3.6.4" -description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -category = "main" -optional = true -python-versions = ">=3.7" - -[[package]] -name = "packaging" -version = "21.2" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2,<3" - -[[package]] -name = "parso" -version = "0.8.2" -description = "A Python Parser" -category = "main" -optional = true -python-versions = ">=3.6" - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] - -[[package]] -name = "pathspec" -version = "0.9.0" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[[package]] -name = "pexpect" -version = "4.8.0" -description = "Pexpect allows easy control of interactive console applications." -category = "main" -optional = true -python-versions = "*" - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -category = "main" -optional = true -python-versions = "*" - -[[package]] -name = "platformdirs" -version = "2.4.0" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "prompt-toolkit" -version = "3.0.22" -description = "Library for building powerful interactive command lines in Python" -category = "main" -optional = true -python-versions = ">=3.6.2" - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -category = "main" -optional = true -python-versions = "*" - -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "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 = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pydantic" -version = "1.8.2" -description = "Data validation and settings management using python 3.6 type hinting" -category = "main" -optional = false -python-versions = ">=3.6.1" - -[package.dependencies] -typing-extensions = ">=3.7.4.3" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.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 = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "pymdown-extensions" -version = "9.1" -description = "Extension pack for Python Markdown." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -Markdown = ">=3.2" - -[[package]] -name = "pyparsing" -version = "2.4.7" -description = "Python parsing module" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[[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 = "2.12.1" -description = "Pytest plugin for measuring coverage." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -coverage = ">=5.2.1" -pytest = ">=4.6" -toml = "*" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytz" -version = "2021.3" -description = "World timezone definitions, modern and historical" -category = "main" -optional = true -python-versions = "*" - -[[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pyyaml-env-tag" -version = "0.1" -description = "A custom YAML tag for referencing environment variables in YAML files. " -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyyaml = "*" - -[[package]] -name = "regex" -version = "2021.11.10" -description = "Alternative regular expression module, to replace re." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "requests" -version = "2.26.0" -description = "Python HTTP for Humans." -category = "main" -optional = true -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-file" -version = "1.5.1" -description = "File transport adapter for Requests" -category = "main" -optional = true -python-versions = "*" - -[package.dependencies] -requests = ">=1.0.0" -six = "*" - -[[package]] -name = "requests-toolbelt" -version = "0.9.1" -description = "A utility belt for advanced users of python-requests" -category = "main" -optional = true -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 = "main" -optional = false -python-versions = "*" - -[package.dependencies] -idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} - -[package.extras] -idna2008 = ["idna"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "sniffio" -version = "1.2.0" -description = "Sniff out which async library your code is running under" -category = "main" -optional = false -python-versions = ">=3.5" - -[[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 = "traitlets" -version = "5.1.1" -description = "Traitlets Python configuration system" -category = "main" -optional = true -python-versions = ">=3.7" - -[package.extras] -test = ["pytest"] - -[[package]] -name = "typed-ast" -version = "1.4.3" -description = "a fork of Python 2 and 3 ast modules with type comment support" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "typing-extensions" -version = "4.0.0" -description = "Backported and Experimental Type Hints for Python 3.6+" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "urllib3" -version = "1.26.7" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.extras] -brotli = ["brotlipy (>=0.6.0)"] -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 = "watchdog" -version = "2.1.6" -description = "Filesystem events monitoring" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "wcwidth" -version = "0.2.5" -description = "Measures the displayed width of unicode strings in a terminal" -category = "main" -optional = true -python-versions = "*" - -[[package]] -name = "zeep" -version = "4.1.0" -description = "A modern/fast Python SOAP client based on lxml / requests" -category = "main" -optional = true -python-versions = ">=3.6" - -[package.dependencies] -attrs = ">=17.2.0" -cached-property = ">=1.3.0" -httpx = {version = "*", optional = true, markers = "extra == \"async\""} -isodate = ">=0.5.4" -lxml = ">=4.6.0" -platformdirs = ">=1.4.0" -pytz = "*" -requests = ">=2.7.0" -requests-file = ">=1.5.1" -requests-toolbelt = ">=0.7.1" - -[package.extras] -async = ["httpx"] -docs = ["sphinx (>=1.4.0)"] -test = ["coverage[toml] (==5.2.1)", "freezegun (==0.3.15)", "mock (==2.0.0)", "pretend (==1.0.9)", "pytest-cov (==2.8.1)", "pytest-httpx", "pytest-asyncio", "pytest (==6.0.1)", "requests-mock (>=0.7.0)", "isort (==5.3.2)", "flake8 (==3.8.3)", "flake8-blind-except (==0.1.1)", "flake8-debugger (==3.2.1)", "flake8-imports (==0.1.1)"] -xmlsec = ["xmlsec (>=0.6.1)"] - -[[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"] - -[extras] -all = ["zeep", "ipython", "orjson"] -cli = ["ipython"] -orjson = ["orjson"] -soap_api = ["zeep"] - -[metadata] -lock-version = "1.1" -python-versions = "^3.7" -content-hash = "ab34c13feff970232114042008a9fe490d7b63d4ab1e00d817f885e738c47b84" - -[metadata.files] -anyio = [ - {file = "anyio-3.3.4-py3-none-any.whl", hash = "sha256:4fd09a25ab7fa01d34512b7249e366cd10358cdafc95022c7ff8c8f8a5026d66"}, - {file = "anyio-3.3.4.tar.gz", hash = "sha256:67da67b5b21f96b9d3d65daa6ea99f5d5282cb09f50eb4456f8fb51dffefc3ff"}, -] -appnope = [ - {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, - {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, -] -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"}, -] -authlib = [ - {file = "Authlib-0.15.5-py2.py3-none-any.whl", hash = "sha256:ecf4a7a9f2508c0bb07e93a752dd3c495cfaffc20e864ef0ffc95e3f40d2abaf"}, - {file = "Authlib-0.15.5.tar.gz", hash = "sha256:b83cf6360c8e92b0e9df0d1f32d675790bcc4e3c03977499b1eed24dcdef4252"}, -] -backcall = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, -] -black = [ - {file = "black-21.11b0-py3-none-any.whl", hash = "sha256:0b1f66cbfadcd332ceeaeecf6373d9991d451868d2e2219ad0ac1213fb701117"}, - {file = "black-21.11b0.tar.gz", hash = "sha256:83f3852301c8dcb229e9c444dd79f573c8d31c7c2dad9bbaaa94c808630e32aa"}, -] -cached-property = [ - {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, - {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, -] -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.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"}, - {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"}, -] -click = [ - {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, - {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, -] -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.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:675adb3b3380967806b3cbb9c5b00ceb29b1c472692100a338730c1d3e59c8b9"}, - {file = "coverage-6.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95a58336aa111af54baa451c33266a8774780242cab3704b7698d5e514840758"}, - {file = "coverage-6.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0a595a781f8e186580ff8e3352dd4953b1944289bec7705377c80c7e36c4d6c"}, - {file = "coverage-6.1.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d3c5f49ce6af61154060640ad3b3281dbc46e2e0ef2fe78414d7f8a324f0b649"}, - {file = "coverage-6.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:310c40bed6b626fd1f463e5a83dba19a61c4eb74e1ac0d07d454ebbdf9047e9d"}, - {file = "coverage-6.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a4d48e42e17d3de212f9af44f81ab73b9378a4b2b8413fd708d0d9023f2bbde4"}, - {file = "coverage-6.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ffa545230ca2ad921ad066bf8fd627e7be43716b6e0fcf8e32af1b8188ccb0ab"}, - {file = "coverage-6.1.2-cp310-cp310-win32.whl", hash = "sha256:cd2d11a59afa5001ff28073ceca24ae4c506da4355aba30d1e7dd2bd0d2206dc"}, - {file = "coverage-6.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:96129e41405887a53a9cc564f960d7f853cc63d178f3a182fdd302e4cab2745b"}, - {file = "coverage-6.1.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1de9c6f5039ee2b1860b7bad2c7bc3651fbeb9368e4c4d93e98a76358cdcb052"}, - {file = "coverage-6.1.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:80cb70264e9a1d04b519cdba3cd0dc42847bf8e982a4d55c769b9b0ee7cdce1e"}, - {file = "coverage-6.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:ba6125d4e55c0b8e913dad27b22722eac7abdcb1f3eab1bd090eee9105660266"}, - {file = "coverage-6.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8492d37acdc07a6eac6489f6c1954026f2260a85a4c2bb1e343fe3d35f5ee21a"}, - {file = "coverage-6.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66af99c7f7b64d050d37e795baadf515b4561124f25aae6e1baa482438ecc388"}, - {file = "coverage-6.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ebcc03e1acef4ff44f37f3c61df478d6e469a573aa688e5a162f85d7e4c3860d"}, - {file = "coverage-6.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d44a8136eebbf544ad91fef5bd2b20ef0c9b459c65a833c923d9aa4546b204"}, - {file = "coverage-6.1.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c18725f3cffe96732ef96f3de1939d81215fd6d7d64900dcc4acfe514ea4fcbf"}, - {file = "coverage-6.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c8e9c4bcaaaa932be581b3d8b88b677489975f845f7714efc8cce77568b6711c"}, - {file = "coverage-6.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:06d009e8a29483cbc0520665bc46035ffe9ae0e7484a49f9782c2a716e37d0a0"}, - {file = "coverage-6.1.2-cp36-cp36m-win32.whl", hash = "sha256:e5432d9c329b11c27be45ee5f62cf20a33065d482c8dec1941d6670622a6fb8f"}, - {file = "coverage-6.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:82fdcb64bf08aa5db881db061d96db102c77397a570fbc112e21c48a4d9cb31b"}, - {file = "coverage-6.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:94f558f8555e79c48c422045f252ef41eb43becdd945e9c775b45ebfc0cbd78f"}, - {file = "coverage-6.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:046647b96969fda1ae0605f61288635209dd69dcd27ba3ec0bf5148bc157f954"}, - {file = "coverage-6.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cc799916b618ec9fd00135e576424165691fec4f70d7dc12cfaef09268a2478c"}, - {file = "coverage-6.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62646d98cf0381ffda301a816d6ac6c35fc97aa81b09c4c52d66a15c4bef9d7c"}, - {file = "coverage-6.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:27a3df08a855522dfef8b8635f58bab81341b2fb5f447819bc252da3aa4cf44c"}, - {file = "coverage-6.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:610c0ba11da8de3a753dc4b1f71894f9f9debfdde6559599f303286e70aeb0c2"}, - {file = "coverage-6.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:35b246ae3a2c042dc8f410c94bcb9754b18179cdb81ff9477a9089dbc9ecc186"}, - {file = "coverage-6.1.2-cp37-cp37m-win32.whl", hash = "sha256:0cde7d9fe2fb55ff68ebe7fb319ef188e9b88e0a3d1c9c5db7dd829cd93d2193"}, - {file = "coverage-6.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:958ac66272ff20e63d818627216e3d7412fdf68a2d25787b89a5c6f1eb7fdd93"}, - {file = "coverage-6.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a300b39c3d5905686c75a369d2a66e68fd01472ea42e16b38c948bd02b29e5bd"}, - {file = "coverage-6.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d3855d5d26292539861f5ced2ed042fc2aa33a12f80e487053aed3bcb6ced13"}, - {file = "coverage-6.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:586d38dfc7da4a87f5816b203ff06dd7c1bb5b16211ccaa0e9788a8da2b93696"}, - {file = "coverage-6.1.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a34fccb45f7b2d890183a263578d60a392a1a218fdc12f5bce1477a6a68d4373"}, - {file = "coverage-6.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bc1ee1318f703bc6c971da700d74466e9b86e0c443eb85983fb2a1bd20447263"}, - {file = "coverage-6.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3f546f48d5d80a90a266769aa613bc0719cb3e9c2ef3529d53f463996dd15a9d"}, - {file = "coverage-6.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd92ece726055e80d4e3f01fff3b91f54b18c9c357c48fcf6119e87e2461a091"}, - {file = "coverage-6.1.2-cp38-cp38-win32.whl", hash = "sha256:24ed38ec86754c4d5a706fbd5b52b057c3df87901a8610d7e5642a08ec07087e"}, - {file = "coverage-6.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:97ef6e9119bd39d60ef7b9cd5deea2b34869c9f0b9777450a7e3759c1ab09b9b"}, - {file = "coverage-6.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e5a8c947a2a89c56655ecbb789458a3a8e3b0cbf4c04250331df8f647b3de59"}, - {file = "coverage-6.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a39590d1e6acf6a3c435c5d233f72f5d43b585f5be834cff1f21fec4afda225"}, - {file = "coverage-6.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9d2c2e3ce7b8cc932a2f918186964bd44de8c84e2f9ef72dc616f5bb8be22e71"}, - {file = "coverage-6.1.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3348865798c077c695cae00da0924136bb5cc501f236cfd6b6d9f7a3c94e0ec4"}, - {file = "coverage-6.1.2-cp39-cp39-win32.whl", hash = "sha256:fae3fe111670e51f1ebbc475823899524e3459ea2db2cb88279bbfb2a0b8a3de"}, - {file = "coverage-6.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:af45eea024c0e3a25462fade161afab4f0d9d9e0d5a5d53e86149f74f0a35ecc"}, - {file = "coverage-6.1.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:eab14fdd410500dae50fd14ccc332e65543e7b39f6fc076fe90603a0e5d2f929"}, - {file = "coverage-6.1.2.tar.gz", hash = "sha256:d9a635114b88c0ab462e0355472d00a180a5fbfd8511e7f18e4ac32652e7d972"}, -] -cryptography = [ - {file = "cryptography-35.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9"}, - {file = "cryptography-35.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992"}, - {file = "cryptography-35.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6"}, - {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d"}, - {file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6"}, - {file = "cryptography-35.0.0-cp36-abi3-win32.whl", hash = "sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8"}, - {file = "cryptography-35.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588"}, - {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953"}, - {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6"}, - {file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2"}, - {file = "cryptography-35.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c"}, - {file = "cryptography-35.0.0.tar.gz", hash = "sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d"}, -] -decorator = [ - {file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"}, - {file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"}, -] -flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, -] -ghp-import = [ - {file = "ghp-import-2.0.2.tar.gz", hash = "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071"}, - {file = "ghp_import-2.0.2-py3-none-any.whl", hash = "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46"}, -] -h11 = [ - {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, - {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, -] -httpcore = [ - {file = "httpcore-0.13.7-py3-none-any.whl", hash = "sha256:369aa481b014cf046f7067fddd67d00560f2f00426e79569d99cb11245134af0"}, - {file = "httpcore-0.13.7.tar.gz", hash = "sha256:036f960468759e633574d7c121afba48af6419615d36ab8ede979f1ad6276fa3"}, -] -httpx = [ - {file = "httpx-0.18.2-py3-none-any.whl", hash = "sha256:979afafecb7d22a1d10340bafb403cf2cb75aff214426ff206521fc79d26408c"}, - {file = "httpx-0.18.2.tar.gz", hash = "sha256:9f99c15d33642d38bce8405df088c1c4cfd940284b4290cacbfb02e64f4877c6"}, -] -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"}, -] -ipython = [ - {file = "ipython-7.29.0-py3-none-any.whl", hash = "sha256:a658beaf856ce46bc453366d5dc6b2ddc6c481efd3540cb28aa3943819caac9f"}, - {file = "ipython-7.29.0.tar.gz", hash = "sha256:4f69d7423a5a1972f6347ff233e38bbf4df6a150ef20fbb00c635442ac3060aa"}, -] -isodate = [ - {file = "isodate-0.6.0-py2.py3-none-any.whl", hash = "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81"}, - {file = "isodate-0.6.0.tar.gz", hash = "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8"}, -] -isort = [ - {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, -] -jedi = [ - {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, - {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, -] -jinja2 = [ - {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, - {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, -] -lxml = [ - {file = "lxml-4.6.5-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:abcf7daa5ebcc89328326254f6dd6d566adb483d4d00178892afd386ab389de2"}, - {file = "lxml-4.6.5-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3884476a90d415be79adfa4e0e393048630d0d5bcd5757c4c07d8b4b00a1096b"}, - {file = "lxml-4.6.5-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:add017c5bd6b9ec3a5f09248396b6ee2ce61c5621f087eb2269c813cd8813808"}, - {file = "lxml-4.6.5-cp27-cp27m-win32.whl", hash = "sha256:a702005e447d712375433ed0499cb6e1503fadd6c96a47f51d707b4d37b76d3c"}, - {file = "lxml-4.6.5-cp27-cp27m-win_amd64.whl", hash = "sha256:da07c7e7fc9a3f40446b78c54dbba8bfd5c9100dfecb21b65bfe3f57844f5e71"}, - {file = "lxml-4.6.5-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a708c291900c40a7ecf23f1d2384ed0bc0604e24094dd13417c7e7f8f7a50d93"}, - {file = "lxml-4.6.5-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f33d8efb42e4fc2b31b3b4527940b25cdebb3026fb56a80c1c1c11a4271d2352"}, - {file = "lxml-4.6.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:f6befb83bca720b71d6bd6326a3b26e9496ae6649e26585de024890fe50f49b8"}, - {file = "lxml-4.6.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:59d77bfa3bea13caee95bc0d3f1c518b15049b97dd61ea8b3d71ce677a67f808"}, - {file = "lxml-4.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:68a851176c931e2b3de6214347b767451243eeed3bea34c172127bbb5bf6c210"}, - {file = "lxml-4.6.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7790a273225b0c46e5f859c1327f0f659896cc72eaa537d23aa3ad9ff2a1cc1"}, - {file = "lxml-4.6.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6548fc551de15f310dd0564751d9dc3d405278d45ea9b2b369ed1eccf142e1f5"}, - {file = "lxml-4.6.5-cp310-cp310-win32.whl", hash = "sha256:dc8a0dbb2a10ae8bb609584f5c504789f0f3d0d81840da4849102ec84289f952"}, - {file = "lxml-4.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:1ccbfe5d17835db906f2bab6f15b34194db1a5b07929cba3cf45a96dbfbfefc0"}, - {file = "lxml-4.6.5-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca9a40497f7e97a2a961c04fa8a6f23d790b0521350a8b455759d786b0bcb203"}, - {file = "lxml-4.6.5-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e5b4b0d9440046ead3bd425eb2b852499241ee0cef1ae151038e4f87ede888c4"}, - {file = "lxml-4.6.5-cp35-cp35m-win32.whl", hash = "sha256:87f8f7df70b90fbe7b49969f07b347e3f978f8bd1046bb8ecae659921869202b"}, - {file = "lxml-4.6.5-cp35-cp35m-win_amd64.whl", hash = "sha256:ce52aad32ec6e46d1a91ff8b8014a91538800dd533914bfc4a82f5018d971408"}, - {file = "lxml-4.6.5-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:8021eeff7fabde21b9858ed058a8250ad230cede91764d598c2466b0ba70db8b"}, - {file = "lxml-4.6.5-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:cab343b265e38d4e00649cbbad9278b734c5715f9bcbb72c85a1f99b1a58e19a"}, - {file = "lxml-4.6.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:3534d7c468c044f6aef3c0aff541db2826986a29ea73f2ca831f5d5284d9b570"}, - {file = "lxml-4.6.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdb98f4c9e8a1735efddfaa995b0c96559792da15d56b76428bdfc29f77c4cdb"}, - {file = "lxml-4.6.5-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5ea121cb66d7e5cb396b4c3ca90471252b94e01809805cfe3e4e44be2db3a99c"}, - {file = "lxml-4.6.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:121fc6f71c692b49af6c963b84ab7084402624ffbe605287da362f8af0668ea3"}, - {file = "lxml-4.6.5-cp36-cp36m-win32.whl", hash = "sha256:1a2a7659b8eb93c6daee350a0d844994d49245a0f6c05c747f619386fb90ba04"}, - {file = "lxml-4.6.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2f77556266a8fe5428b8759fbfc4bd70be1d1d9c9b25d2a414f6a0c0b0f09120"}, - {file = "lxml-4.6.5-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:558485218ee06458643b929765ac1eb04519ca3d1e2dcc288517de864c747c33"}, - {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ba0006799f21d83c3717fe20e2707a10bbc296475155aadf4f5850f6659b96b9"}, - {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:916d457ad84e05b7db52700bad0a15c56e0c3000dcaf1263b2fb7a56fe148996"}, - {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c580c2a61d8297a6e47f4d01f066517dbb019be98032880d19ece7f337a9401d"}, - {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a21b78af7e2e13bec6bea12fc33bc05730197674f3e5402ce214d07026ccfebd"}, - {file = "lxml-4.6.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:46515773570a33eae13e451c8fcf440222ef24bd3b26f40774dd0bd8b6db15b2"}, - {file = "lxml-4.6.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:124f09614f999551ac65e5b9875981ce4b66ac4b8e2ba9284572f741935df3d9"}, - {file = "lxml-4.6.5-cp37-cp37m-win32.whl", hash = "sha256:b4015baed99d046c760f09a4c59d234d8f398a454380c3cf0b859aba97136090"}, - {file = "lxml-4.6.5-cp37-cp37m-win_amd64.whl", hash = "sha256:12ae2339d32a2b15010972e1e2467345b7bf962e155671239fba74c229564b7f"}, - {file = "lxml-4.6.5-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:76b6c296e4f7a1a8a128aec42d128646897f9ae9a700ef6839cdc9b3900db9b5"}, - {file = "lxml-4.6.5-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:534032a5ceb34bba1da193b7d386ac575127cc39338379f39a164b10d97ade89"}, - {file = "lxml-4.6.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:60aeb14ff9022d2687ef98ce55f6342944c40d00916452bb90899a191802137a"}, - {file = "lxml-4.6.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9801bcd52ac9c795a7d81ea67471a42cffe532e46cfb750cd5713befc5c019c0"}, - {file = "lxml-4.6.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3b95fb7e6f9c2f53db88f4642231fc2b8907d854e614710996a96f1f32018d5c"}, - {file = "lxml-4.6.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:642eb4cabd997c9b949a994f9643cd8ae00cf4ca8c5cd9c273962296fadf1c44"}, - {file = "lxml-4.6.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:af4139172ff0263d269abdcc641e944c9de4b5d660894a3ec7e9f9db63b56ac9"}, - {file = "lxml-4.6.5-cp38-cp38-win32.whl", hash = "sha256:57cf05466917e08f90e323f025b96f493f92c0344694f5702579ab4b7e2eb10d"}, - {file = "lxml-4.6.5-cp38-cp38-win_amd64.whl", hash = "sha256:4f415624cf8b065796649a5e4621773dc5c9ea574a944c76a7f8a6d3d2906b41"}, - {file = "lxml-4.6.5-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7679bb6e4d9a3978a46ab19a3560e8d2b7265ef3c88152e7fdc130d649789887"}, - {file = "lxml-4.6.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c34234a1bc9e466c104372af74d11a9f98338a3f72fae22b80485171a64e0144"}, - {file = "lxml-4.6.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4b9390bf973e3907d967b75be199cf1978ca8443183cf1e78ad80ad8be9cf242"}, - {file = "lxml-4.6.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fcc849b28f584ed1dbf277291ded5c32bb3476a37032df4a1d523b55faa5f944"}, - {file = "lxml-4.6.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:46f21f2600d001af10e847df9eb3b832e8a439f696c04891bcb8a8cedd859af9"}, - {file = "lxml-4.6.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:99cf827f5a783038eb313beee6533dddb8bdb086d7269c5c144c1c952d142ace"}, - {file = "lxml-4.6.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:925174cafb0f1179a7fd38da90302555d7445e34c9ece68019e53c946be7f542"}, - {file = "lxml-4.6.5-cp39-cp39-win32.whl", hash = "sha256:12d8d6fe3ddef629ac1349fa89a638b296a34b6529573f5055d1cb4e5245f73b"}, - {file = "lxml-4.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:a52e8f317336a44836475e9c802f51c2dc38d612eaa76532cb1d17690338b63b"}, - {file = "lxml-4.6.5-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:11ae552a78612620afd15625be9f1b82e3cc2e634f90d6b11709b10a100cba59"}, - {file = "lxml-4.6.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:473701599665d874919d05bb33b56180447b3a9da8d52d6d9799f381ce23f95c"}, - {file = "lxml-4.6.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7f00cc64b49d2ef19ddae898a3def9dd8fda9c3d27c8a174c2889ee757918e71"}, - {file = "lxml-4.6.5-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:73e8614258404b2689a26cb5d002512b8bc4dfa18aca86382f68f959aee9b0c8"}, - {file = "lxml-4.6.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ff44de36772b05c2eb74f2b4b6d1ae29b8f41ed5506310ce1258d44826ee38c1"}, - {file = "lxml-4.6.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:5d5254c815c186744c8f922e2ce861a2bdeabc06520b4b30b2f7d9767791ce6e"}, - {file = "lxml-4.6.5.tar.gz", hash = "sha256:6e84edecc3a82f90d44ddee2ee2a2630d4994b8471816e226d2b771cda7ac4ca"}, -] -markdown = [ - {file = "Markdown-3.3.5-py3-none-any.whl", hash = "sha256:0d2d09f75cb8d1ffc6770c65c61770b23a61708101f47bda416a002a0edbc480"}, - {file = "Markdown-3.3.5.tar.gz", hash = "sha256:26e9546bfbcde5fcd072bd8f612c9c1b6e2677cb8aadbdf65206674f46dde069"}, -] -markupsafe = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, - {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, -] -matplotlib-inline = [ - {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, - {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mergedeep = [ - {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, - {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, -] -mkdocs = [ - {file = "mkdocs-1.2.3-py3-none-any.whl", hash = "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072"}, - {file = "mkdocs-1.2.3.tar.gz", hash = "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1"}, -] -mkdocs-material = [ - {file = "mkdocs-material-7.3.6.tar.gz", hash = "sha256:1b1dbd8ef2508b358d93af55a5c5db3f141c95667fad802301ec621c40c7c217"}, - {file = "mkdocs_material-7.3.6-py2.py3-none-any.whl", hash = "sha256:1b6b3e9e09f922c2d7f1160fe15c8f43d4adc0d6fb81aa6ff0cbc7ef5b78ec75"}, -] -mkdocs-material-extensions = [ - {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, - {file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"}, -] -mypy = [ - {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"}, - {file = "mypy-0.812-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c"}, - {file = "mypy-0.812-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521"}, - {file = "mypy-0.812-cp35-cp35m-win_amd64.whl", hash = "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb"}, - {file = "mypy-0.812-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a"}, - {file = "mypy-0.812-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c"}, - {file = "mypy-0.812-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6"}, - {file = "mypy-0.812-cp36-cp36m-win_amd64.whl", hash = "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064"}, - {file = "mypy-0.812-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56"}, - {file = "mypy-0.812-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8"}, - {file = "mypy-0.812-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7"}, - {file = "mypy-0.812-cp37-cp37m-win_amd64.whl", hash = "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564"}, - {file = "mypy-0.812-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506"}, - {file = "mypy-0.812-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5"}, - {file = "mypy-0.812-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66"}, - {file = "mypy-0.812-cp38-cp38-win_amd64.whl", hash = "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e"}, - {file = "mypy-0.812-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a"}, - {file = "mypy-0.812-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a"}, - {file = "mypy-0.812-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97"}, - {file = "mypy-0.812-cp39-cp39-win_amd64.whl", hash = "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df"}, - {file = "mypy-0.812-py3-none-any.whl", hash = "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4"}, - {file = "mypy-0.812.tar.gz", hash = "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119"}, -] -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"}, -] -oauthlib = [ - {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"}, - {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"}, -] -orjson = [ - {file = "orjson-3.6.4-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:fc01a15f3101628fd619158daec79b30d7461149735e73542ca8c13be6b835be"}, - {file = "orjson-3.6.4-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:c840e6ca222f76e7f13e9ee2f0650c9ee449e5e4aae38c73ab6ecaf3077ea21c"}, - {file = "orjson-3.6.4-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:48a69fed90f551bf9e9bb7a63e363fed4f67fc7c6e6bfb057054dc78f6721e9e"}, - {file = "orjson-3.6.4-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:3722f02f50861d5e2a6be9d50bfe8da27a5155bb60043118a4e1ceb8c7040cf7"}, - {file = "orjson-3.6.4-cp310-none-win_amd64.whl", hash = "sha256:231a99a728322d0271e970b149c57deb67315e6837e6cd4166cf51d30161700c"}, - {file = "orjson-3.6.4-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:6cd300421b41f7e84e388b1792a18c3fc4c440ae3039434b9320956be05f0102"}, - {file = "orjson-3.6.4-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e55ef66ee1d35b1c43db275aff3a1ba7e0408b31e624912a612bd799df14e73e"}, - {file = "orjson-3.6.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef8d332af8e6f7d6d2c1f3b5384c8d239800c1405b136da5f1710e802918d57"}, - {file = "orjson-3.6.4-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:8896e242a92733e454378e22711bd43a55fda4e80604fcefcc064ca977623673"}, - {file = "orjson-3.6.4-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:bdfa6f29f7b6aad70ce14591b99fba651008afa6bc3759f158887bcdc568b452"}, - {file = "orjson-3.6.4-cp37-none-win_amd64.whl", hash = "sha256:7c16c44872d33da0b97050a9ea8f7bc04e930c56e8185657bc200e1875a671da"}, - {file = "orjson-3.6.4-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:b467551f3be1dd08aff70c261cc883b63483eb0e31861ffe2cd8dac4fec7cfa9"}, - {file = "orjson-3.6.4-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7bf61afef12f6416db3ea377f3491ca8ac677d3cac6db1ebffb7a5fe92cce3ca"}, - {file = "orjson-3.6.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:014ea74d4a5dd6a7e98540768072d5bd8c2fedbcbbedcbbaecbb614e66080e81"}, - {file = "orjson-3.6.4-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:705cb90c536b4b9336c06b4a62c3c62e50354ddf20a2e48eb62bf34fb93d5b1f"}, - {file = "orjson-3.6.4-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:159e2240fc36720a5cb51a1cbc9905dcb8758aad50b3e7f14f6178ce2e842004"}, - {file = "orjson-3.6.4-cp38-none-win_amd64.whl", hash = "sha256:d2ae087866a1050de83c2a28490850badb41aeeb8a4605c84dd6004d4e58b5a4"}, - {file = "orjson-3.6.4-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:b4a7efe039b1154b23e5df8787ac01e4621213aed303b6304a5f8ad89c01455d"}, - {file = "orjson-3.6.4-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7b24f97ed76005f447e152b0e493abce8c60f010131998295175446312a71caf"}, - {file = "orjson-3.6.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1121187e2a721864b52e5dbb3cf8dd4a4546519a5fef1e13fa777347fb8884a2"}, - {file = "orjson-3.6.4-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:4edffd9e2298ff4f4f939aa67248eba043dc65c9e7d940c28a62c5502c6f2aa8"}, - {file = "orjson-3.6.4-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:e236fe94d8a77532f0065870fe265bd53e229012f39af99f79f5f1d4a8b0067c"}, - {file = "orjson-3.6.4-cp39-none-win_amd64.whl", hash = "sha256:5448cc1edd4c4bafc968404f92f0e9a582b4326ca442346bd1d1179a6faf52d9"}, - {file = "orjson-3.6.4.tar.gz", hash = "sha256:f8dbc428fc6d7420f231a7133d8dff4c882e64acb585dcf2fda74bdcfe1a6d9d"}, -] -packaging = [ - {file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"}, - {file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"}, -] -parso = [ - {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, - {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, -] -pathspec = [ - {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, - {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, -] -pexpect = [ - {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, - {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, -] -pickleshare = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] -platformdirs = [ - {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, - {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -prompt-toolkit = [ - {file = "prompt_toolkit-3.0.22-py3-none-any.whl", hash = "sha256:48d85cdca8b6c4f16480c7ce03fd193666b62b0a21667ca56b4bb5ad679d1170"}, - {file = "prompt_toolkit-3.0.22.tar.gz", hash = "sha256:449f333dd120bd01f5d296a8ce1452114ba3a71fae7288d2f0ae2c918764fa72"}, -] -ptyprocess = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -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"}, -] -pydantic = [ - {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, - {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, - {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, - {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, - {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, - {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, - {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, - {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, - {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, - {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, - {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, - {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, - {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, - {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, - {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, - {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, - {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, - {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, - {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, - {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, - {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, - {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, -] -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"}, -] -pymdown-extensions = [ - {file = "pymdown-extensions-9.1.tar.gz", hash = "sha256:74247f2c80f1d9e3c7242abe1c16317da36c6f26c7ad4b8a7f457f0ec20f0365"}, - {file = "pymdown_extensions-9.1-py3-none-any.whl", hash = "sha256:b03e66f91f33af4a6e7a0e20c740313522995f69a03d86316b1449766c473d0e"}, -] -pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, -] -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-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, - {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -pytz = [ - {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, - {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -pyyaml-env-tag = [ - {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, - {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, -] -regex = [ - {file = "regex-2021.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf"}, - {file = "regex-2021.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0"}, - {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9ed0b1e5e0759d6b7f8e2f143894b2a7f3edd313f38cf44e1e15d360e11749b"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:473e67837f786404570eae33c3b64a4b9635ae9f00145250851a1292f484c063"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2fee3ed82a011184807d2127f1733b4f6b2ff6ec7151d83ef3477f3b96a13d03"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d5fd67df77bab0d3f4ea1d7afca9ef15c2ee35dfb348c7b57ffb9782a6e4db6e"}, - {file = "regex-2021.11.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5d408a642a5484b9b4d11dea15a489ea0928c7e410c7525cd892f4d04f2f617b"}, - {file = "regex-2021.11.10-cp310-cp310-win32.whl", hash = "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a"}, - {file = "regex-2021.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12"}, - {file = "regex-2021.11.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23"}, - {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:74cbeac0451f27d4f50e6e8a8f3a52ca074b5e2da9f7b505c4201a57a8ed6286"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:3598893bde43091ee5ca0a6ad20f08a0435e93a69255eeb5f81b85e81e329264"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:50a7ddf3d131dc5633dccdb51417e2d1910d25cbcf842115a3a5893509140a3a"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:61600a7ca4bcf78a96a68a27c2ae9389763b5b94b63943d5158f2a377e09d29a"}, - {file = "regex-2021.11.10-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:563d5f9354e15e048465061509403f68424fef37d5add3064038c2511c8f5e00"}, - {file = "regex-2021.11.10-cp36-cp36m-win32.whl", hash = "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4"}, - {file = "regex-2021.11.10-cp36-cp36m-win_amd64.whl", hash = "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e"}, - {file = "regex-2021.11.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e"}, - {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:42b50fa6666b0d50c30a990527127334d6b96dd969011e843e726a64011485da"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6e1d2cc79e8dae442b3fa4a26c5794428b98f81389af90623ffcc650ce9f6732"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:0416f7399e918c4b0e074a0f66e5191077ee2ca32a0f99d4c187a62beb47aa05"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:ce298e3d0c65bd03fa65ffcc6db0e2b578e8f626d468db64fdf8457731052942"}, - {file = "regex-2021.11.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dc07f021ee80510f3cd3af2cad5b6a3b3a10b057521d9e6aaeb621730d320c5a"}, - {file = "regex-2021.11.10-cp37-cp37m-win32.whl", hash = "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec"}, - {file = "regex-2021.11.10-cp37-cp37m-win_amd64.whl", hash = "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4"}, - {file = "regex-2021.11.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83"}, - {file = "regex-2021.11.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe"}, - {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f5be7805e53dafe94d295399cfbe5227f39995a997f4fd8539bf3cbdc8f47ca8"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a955b747d620a50408b7fdf948e04359d6e762ff8a85f5775d907ceced715129"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:139a23d1f5d30db2cc6c7fd9c6d6497872a672db22c4ae1910be22d4f4b2068a"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ca49e1ab99593438b204e00f3970e7a5f70d045267051dfa6b5f4304fcfa1dbf"}, - {file = "regex-2021.11.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:96fc32c16ea6d60d3ca7f63397bff5c75c5a562f7db6dec7d412f7c4d2e78ec0"}, - {file = "regex-2021.11.10-cp38-cp38-win32.whl", hash = "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc"}, - {file = "regex-2021.11.10-cp38-cp38-win_amd64.whl", hash = "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d"}, - {file = "regex-2021.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b"}, - {file = "regex-2021.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b"}, - {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cd410a1cbb2d297c67d8521759ab2ee3f1d66206d2e4328502a487589a2cb21b"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e6096b0688e6e14af6a1b10eaad86b4ff17935c49aa774eac7c95a57a4e8c296"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:529801a0d58809b60b3531ee804d3e3be4b412c94b5d267daa3de7fadef00f49"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f594b96fe2e0821d026365f72ac7b4f0b487487fb3d4aaf10dd9d97d88a9737"}, - {file = "regex-2021.11.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2409b5c9cef7054dde93a9803156b411b677affc84fca69e908b1cb2c540025d"}, - {file = "regex-2021.11.10-cp39-cp39-win32.whl", hash = "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a"}, - {file = "regex-2021.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29"}, - {file = "regex-2021.11.10.tar.gz", hash = "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6"}, -] -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-file = [ - {file = "requests-file-1.5.1.tar.gz", hash = "sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e"}, - {file = "requests_file-1.5.1-py2.py3-none-any.whl", hash = "sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"}, -] -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"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -sniffio = [ - {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, - {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, -] -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"}, -] -traitlets = [ - {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"}, - {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"}, -] -typed-ast = [ - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, - {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, - {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, - {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, - {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, - {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, - {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, - {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, - {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, - {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, - {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, - {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, - {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, - {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, - {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, - {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, -] -typing-extensions = [ - {file = "typing_extensions-4.0.0-py3-none-any.whl", hash = "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"}, - {file = "typing_extensions-4.0.0.tar.gz", hash = "sha256:2cdf80e4e04866a9b3689a51869016d36db0814d84b8d8a568d22781d45d27ed"}, -] -urllib3 = [ - {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, - {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, -] -watchdog = [ - {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"}, - {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b"}, - {file = "watchdog-2.1.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542"}, - {file = "watchdog-2.1.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669"}, - {file = "watchdog-2.1.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660"}, - {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3"}, - {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04"}, - {file = "watchdog-2.1.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b"}, - {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604"}, - {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6"}, - {file = "watchdog-2.1.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9"}, - {file = "watchdog-2.1.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_armv7l.whl", hash = "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_i686.whl", hash = "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_s390x.whl", hash = "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15"}, - {file = "watchdog-2.1.6-py3-none-win32.whl", hash = "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d"}, - {file = "watchdog-2.1.6-py3-none-win_amd64.whl", hash = "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5"}, - {file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"}, - {file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"}, -] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, -] -zeep = [ - {file = "zeep-4.1.0-py2.py3-none-any.whl", hash = "sha256:81c491092b71f5b276de8c63dfd452be3f322622c48a54f3a497cf913bdfb2f4"}, - {file = "zeep-4.1.0.tar.gz", hash = "sha256:5867f2eadd6b028d9751f4155af590d3aaf9280e3a0ed5e15a53343921c956e5"}, -] -zipp = [ - {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, - {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, -] From 1b2d4d6906d23517cc1bd3e2562491d61ddf4267 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:14:43 +0200 Subject: [PATCH 37/47] ci: Update pre-commit config --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5230d62..2009a93 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,18 +5,18 @@ default_language_version: repos: - repo: https://gitlab.com/pycqa/flake8 - rev: 3.9.0 + rev: 4.0.1 hooks: - id: flake8 - repo: https://github.com/pycqa/isort - rev: 5.8.0 + rev: 5.10.1 hooks: - id: isort - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 22.3.0 hooks: - id: black - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.812 + rev: v0.960 hooks: - id: mypy From 352bdd6c9c63d1d2f9b0107921d35ca690dad82d Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:19:06 +0200 Subject: [PATCH 38/47] docs: Add example project for soap requests --- examples/soap-api.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 examples/soap-api.py diff --git a/examples/soap-api.py b/examples/soap-api.py new file mode 100644 index 0000000..d6b2c8f --- /dev/null +++ b/examples/soap-api.py @@ -0,0 +1,34 @@ +import argparse +import asyncio + +from netsuite import Config +from netsuite.constants import DEFAULT_INI_PATH, DEFAULT_INI_SECTION +from netsuite.soap_api.client import NetSuiteSoapApi + +parser = argparse.ArgumentParser() +parser.add_argument("-p", "--config-path", default=DEFAULT_INI_PATH, dest="path") +parser.add_argument( + "-c", "--config-section", default=DEFAULT_INI_SECTION, dest="section" +) + + +async def main(): + args = parser.parse_args() + + config = Config.from_ini(**vars(args)) + api = NetSuiteSoapApi(config) + + resp1 = await api.getList("customer", internalIds=[1]) + print(resp1) + + # Re-use same connection + async with api: + resp2 = await api.getList("salesOrder", internalIds=[1]) + resp3 = await api.getList("salesOrder", internalIds=[2]) + + print(resp2) + print(resp3) + + +if __name__ == "__main__": + asyncio.run(main()) From b9982bf23ebf051ba994c0c1f8b0ba3f88b31cbc Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:19:51 +0200 Subject: [PATCH 39/47] fix: Don't require existing config to check version --- netsuite/cli/main.py | 5 +++++ netsuite/cli/misc.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/netsuite/cli/main.py b/netsuite/cli/main.py index b13954d..6e381c2 100644 --- a/netsuite/cli/main.py +++ b/netsuite/cli/main.py @@ -51,6 +51,11 @@ def main(): subparser_name = sys.argv[-1] + # Call version directly to avoid loading of config + if subparser_name == "version": + print(args.func()) + return + # Show help section instead of an error when no arguments were passed... if subparser_name == "rest-api": rest_api_parser.print_help() diff --git a/netsuite/cli/misc.py b/netsuite/cli/misc.py index 51e2181..41cda27 100644 --- a/netsuite/cli/misc.py +++ b/netsuite/cli/misc.py @@ -9,5 +9,5 @@ def add_parser(parser, subparser): return (version_parser, None) -def version(config, args) -> str: +def version() -> str: return pkg_resources.get_distribution("netsuite").version From 1956b4db748dd321fe310c3542f3e29c0f2161eb Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:20:58 +0200 Subject: [PATCH 40/47] fix: Update dependencies Fixes #39 Fixes #40 --- .github/workflows/cd.yml | 2 +- .github/workflows/ci.yml | 5 ++--- .github/workflows/codecov.yml | 2 +- .github/workflows/docs.yml | 2 +- pyproject.toml | 35 +++++++++++++++++++---------------- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index e8cd15a..eac71e2 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: 3.10 - uses: abatilo/actions-poetry@v2.1.2 with: poetry-version: 1.1.6 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fe25a8..8c3bc32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,6 @@ name: ci on: push: - branches-ignore: [] tags-ignore: ['v*.*'] # Ignore version tags from triggering CI run pull_request: @@ -11,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.9, 3.8, 3.7] + python-version: [3.10, 3.9, 3.8, 3.7] extras: [all, soap_api, orjson, cli] os: [ubuntu-latest, macos-latest, windows-latest] steps: @@ -28,7 +27,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.9] + python-version: [3.10] extras: [all] os: [ubuntu-latest] steps: diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 40104e1..863e9ed 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: 3.10 - uses: abatilo/actions-poetry@v2.1.2 with: poetry-version: 1.1.6 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index aa4d871..fdcc2ee 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: 3.10 - uses: abatilo/actions-poetry@v2.1.2 with: poetry-version: 1.1.6 diff --git a/pyproject.toml b/pyproject.toml index acd85e2..12b665c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netsuite" -version = "0.8.0" +version = "0.9.0" description = "Make async requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets" authors = ["Jacob Magnusson "] license = "MIT" @@ -18,18 +18,19 @@ classifiers = [ "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", ] [tool.poetry.dependencies] python = "^3.7" -authlib = "^0.15.3" +authlib = "~1" # As per httpx recommendation we will lock to a fixed minor version until 1.0 is released -httpx = "~0.18" -pydantic = "^1.8" -orjson = {version = "^3", optional = true} -ipython = {version = "^7", optional = true} -zeep = {version = "^4.0.0", optional = true, extras = ["async"]} -oauthlib = "^3.1.0" +httpx = "~0.23" +pydantic = "~1" +orjson = {version = "~3", optional = true} +ipython = {version = "~8", optional = true, python = "^3.8"} +zeep = {version = "~4", optional = true, extras = ["async"]} +oauthlib = "~3" [tool.poetry.extras] soap_api = ["zeep"] @@ -38,13 +39,15 @@ orjson = ["orjson"] all = ["zeep", "ipython", "orjson"] [tool.poetry.dev-dependencies] -black = "^21.11b0" -flake8 = "^3.9" -isort = "^5.8" -mkdocs-material = "^7" -mypy = "^0.812" -pytest = "^6" -pytest-cov = "^2.11" +black = "~22" +flake8 = "~4" +isort = "~5" +mkdocs-material = "~8" +mypy = "~0" +pytest = "~7" +pytest-cov = "~3" +types-setuptools = "^57.4.17" +types-requests = "^2.27.30" [tool.poetry.scripts] netsuite = 'netsuite.cli:main' @@ -55,7 +58,7 @@ build-backend = "poetry.core.masonry.api" [tool.black] line-length = 88 -target-version = ["py37", "py38"] +target-version = ["py37", "py38", "py39", "py310"] [tool.isort] profile = "black" From c0074a08edbef352b8b707ae1a6a7a21a25ce3d1 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:21:53 +0200 Subject: [PATCH 41/47] fix: Regression in `openapi serve` CLI command --- netsuite/cli/rest_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netsuite/cli/rest_api.py b/netsuite/cli/rest_api.py index 31da524..626c6f6 100644 --- a/netsuite/cli/rest_api.py +++ b/netsuite/cli/rest_api.py @@ -264,7 +264,7 @@ async def rest_api_openapi_serve(config, args): tempdir = pathlib.Path(tempfile.mkdtemp()) openapi_file = tempdir / "openapi.json" html_file = tempdir / "index.html" - openapi_file.write_bytes(json.dumps(spec)) + openapi_file.write_bytes(json.dumps(spec).encode("utf-8")) html = """ From 28e2b8e387ae4d30d65540901714f69a0b6248ab Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:22:33 +0200 Subject: [PATCH 42/47] fix: Async connections sessions were being closed --- docs/index.md | 11 ++++-- netsuite/soap_api/client.py | 77 +++++++++++++------------------------ 2 files changed, 35 insertions(+), 53 deletions(-) diff --git a/docs/index.md b/docs/index.md index 56c2d9e..22bad13 100644 --- a/docs/index.md +++ b/docs/index.md @@ -41,13 +41,18 @@ ns = NetSuite(config) async def async_main(): - # NOTE: SOAP needs `pip install netsuite[soap_api]` - soap_api_results = await ns.soap_api.getList('customer', internalIds=[1337]) - rest_api_results = await ns.rest_api.get("/record/v1/salesOrder") restlet_results = await ns.restlet.get(987, deploy=2) + # NOTE: SOAP needs `pip install netsuite[soap_api]` + soap_api_results = await ns.soap_api.getList('customer', internalIds=[1337]) + + # Multiple requests, using the same underlying connection + async with ns.soap_api: + customers = await ns.soap_api.getList('customer', internalIds=[1, 2, 3]) + sales_orders = await ns.soap_api.getList('salesOrder', internalIds=[1, 2]) + if __name__ == "__main__": asyncio.run(async_main()) diff --git a/netsuite/soap_api/client.py b/netsuite/soap_api/client.py index b4f564b..97db3f7 100644 --- a/netsuite/soap_api/client.py +++ b/netsuite/soap_api/client.py @@ -15,24 +15,6 @@ __all__ = ("NetSuiteSoapApi",) -# TODO: Submit PR for the following changes (1170 uses a different method) -# This avoids the following warning on asyncio loop shutdown: -# -# UserWarning: Unclosed . -# See https://www.python-httpx.org/async/#opening-and-closing-clients -# for details. -# -class _AsyncClient(zeep.client.AsyncClient): - async def __aenter__(self): - await self.transport.client.__aenter__() - return self - - async def __aexit__(self, exc_type=None, exc_value=None, traceback=None) -> None: - await self.transport.client.__aexit__( - exc_type=exc_type, exc_value=exc_value, traceback=traceback - ) - - class NetSuiteSoapApi: version = "2021.1.0" wsdl_url_tmpl = "https://{account_slug}.suitetalk.api.netsuite.com/wsdl/v{underscored_version}/netsuite.wsdl" @@ -44,16 +26,15 @@ def __init__( version: str = None, wsdl_url: str = None, cache: zeep.cache.Base = None, - session: zeep.requests.Session = None, ) -> None: self._ensure_required_dependencies() if version is not None: assert re.match(r"\d+\.\d+\.\d+", version) self.version = version - self.config = config - self.__wsdl_url = wsdl_url - self.__cache = cache - self.__session = session + self.config: Config = config + self._wsdl_url: Optional[str] = wsdl_url + self._cache: Optional[zeep.cache.Base] = cache + self._client: Optional[zeep.client.AsyncClient] = None def __repr__(self) -> str: return f"<{self.__class__.__name__} {self.hostname}({self.version})>" @@ -66,26 +47,30 @@ async def __aexit__(self, exc_type=None, exc_value=None, traceback=None) -> None await self.client.__aexit__( exc_type=exc_type, exc_value=exc_value, traceback=traceback ) + # Connection is now closed by zeep. Generate a new one + self._client = self._generate_client() - @cached_property + @property def wsdl_url(self) -> str: - return self.__wsdl_url or self._generate_wsdl_url() + if self._wsdl_url is None: + self._wsdl_url = self._generate_wsdl_url() + return self._wsdl_url - @cached_property + @property def cache(self) -> zeep.cache.Base: - return self.__cache or self._generate_cache() + if self._cache is None: + self._cache = self._generate_cache() + return self._cache - @cached_property - def session(self) -> zeep.requests.Session: - return self.__session or self._generate_session() - - @cached_property - def client(self) -> _AsyncClient: - return self._generate_client() + @property + def client(self) -> zeep.client.AsyncClient: + if self._client is None: + self._client = self._generate_client() + return self._client - @cached_property + @property def transport(self): - return self._generate_transport() + return self.client.transport @cached_property def hostname(self) -> str: @@ -117,8 +102,8 @@ def _generate_session(self) -> zeep.requests.Session: def _generate_transport(self) -> zeep.transports.AsyncTransport: return AsyncNetSuiteTransport( - self._generate_wsdl_url(), - session=self.session, + self.wsdl_url, + session=self._generate_session(), cache=self.cache, ) @@ -135,10 +120,10 @@ def with_timeout(self, timeout: int): with self.transport.settings(timeout=timeout): yield - def _generate_client(self) -> _AsyncClient: - return _AsyncClient( + def _generate_client(self) -> zeep.client.AsyncClient: + return zeep.client.AsyncClient( self.wsdl_url, - transport=self.transport, + transport=self._generate_transport(), ) def _get_namespace(self, name: str, sub_namespace: str) -> str: @@ -358,15 +343,7 @@ async def request(self, service_name: str, *args, **kw): The response from NetSuite """ svc = getattr(self.service, service_name) - # NOTE: Using httpx context manager here - # This avoids the following error on asyncio close: - # - # UserWarning: Unclosed . - # See https://www.python-httpx.org/async/#opening-and-closing-clients - # for details. - # - async with self: - return await svc(*args, _soapheaders=self.generate_passport(), **kw) + return await svc(*args, _soapheaders=self.generate_passport(), **kw) @WebServiceCall( "body.readResponseList.readResponse", From 20987ced4f492bf7bcf9dba58af482170ee0b679 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:22:44 +0200 Subject: [PATCH 43/47] chore: Release 0.9.0 --- CHANGELOG.md | 11 +++++++++++ README.md | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34cb046..e3dd638 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nothing +## [0.9.0](https://github.com/jacobsvante/netsuite/compare/v0.8.0...v0.9.0) - 2022-06-02 + +### Fixed +- SOAP: Circumvent `zeep` closing the underlying client after the first request + +### Changed +- Update dependencies, including major version bump of `authlib` and `ipython` + +### Removed +- Remove `session` parameter to NetSuiteSoapApi + ## [0.8.0](https://github.com/jacobsvante/netsuite/compare/v0.7.0...v0.8.0) - 2021-04-28 ### Changed diff --git a/README.md b/README.md index 5cc27df..aadc955 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Make async requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets ## Beta quality disclaimer -The project's API is still very much in fluctuation. Please consider pinning your dependency to this package to a minor version (e.g. `poetry add netsuite~0.7` or `pipenv install netsuite~=0.7.0`), which is guaranteed to have no breaking changes. From 1.0 and forward we will keep a stable API. +The project's API is still very much in fluctuation. Please consider pinning your dependency to this package to a minor version (e.g. `poetry add netsuite~0.9` or `pipenv install netsuite~=0.9.0`), which is guaranteed to have no breaking changes. From 1.0 and forward we will keep a stable API. ## Installation From 35a89bb1ca5ece5172d424d28b295210ea0fe5d6 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:37:13 +0200 Subject: [PATCH 44/47] ci: Introduce release-please and prepare for first auto-release --- .github/workflows/cd.yml | 40 +++++++++++++++++++---------------- .github/workflows/ci.yml | 29 +++++++++++++------------ .github/workflows/codecov.yml | 13 ++++++------ .github/workflows/docs.yml | 13 ++++++------ CHANGELOG.md | 19 ----------------- pyproject.toml | 2 +- 6 files changed, 52 insertions(+), 64 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index eac71e2..1651c5b 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,29 +1,33 @@ name: cd on: push: - tags: - - v* + branches: [main] + jobs: - cd: + release-please: runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: google-github-actions/release-please-action@v3 + id: release with: - python-version: 3.10 - - uses: abatilo/actions-poetry@v2.1.2 + release-type: python + package-name: netsuite + bump-minor-pre-major: true + cd: + runs-on: ubuntu-latest + needs: [release-please] + if: needs.release-please.outputs.release_created + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: - poetry-version: 1.1.6 - - name: Build - run: poetry build - - uses: marvinpinto/action-automatic-releases@latest + python-version: '3.10' + - uses: abatilo/actions-poetry@v2.1.4 with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: false - files: | - dist/*.whl - dist/*.tar.gz - - name: Publish to PyPI + poetry-version: '1.1.13' + - run: poetry build + - run: poetry publish env: POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} - run: poetry publish diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c3bc32..4ce1b9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,42 +2,45 @@ name: ci on: push: - tags-ignore: ['v*.*'] # Ignore version tags from triggering CI run + branches: [main] pull_request: jobs: unittests: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: python-version: [3.10, 3.9, 3.8, 3.7] - extras: [all, soap_api, orjson, cli] + extras: ['', all, soap_api, orjson, cli] os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - - uses: abatilo/actions-poetry@v2.1.2 + - uses: abatilo/actions-poetry@v2.1.4 with: - poetry-version: 1.1.6 + poetry-version: '1.1.13' - run: poetry install --extras ${{ matrix.extras }} + if: matrix.extras != '' + - run: poetry install + if: matrix.extras == '' - run: poetry run pytest -v - linting: + + style: runs-on: ubuntu-latest strategy: matrix: python-version: [3.10] extras: [all] - os: [ubuntu-latest] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - - uses: abatilo/actions-poetry@v2.1.2 + - uses: abatilo/actions-poetry@v2.1.4 with: - poetry-version: 1.1.6 + poetry-version: '1.1.13' - run: poetry install --extras ${{ matrix.extras }} - run: poetry run flake8 - run: poetry run mypy --ignore-missing-imports . diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 863e9ed..be24631 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -2,21 +2,22 @@ name: codecov on: push: + branches: [main] pull_request: jobs: code_coverage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: - python-version: 3.10 - - uses: abatilo/actions-poetry@v2.1.2 + python-version: '3.10' + - uses: abatilo/actions-poetry@v2.1.4 with: - poetry-version: 1.1.6 + poetry-version: '1.1.13' - run: poetry install --extras all - run: poetry run pytest --cov=netsuite --cov-report=xml --cov-report=term - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v3 with: files: ./coverage.xml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index fdcc2ee..a87a315 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,20 +1,19 @@ name: docs on: push: - branches: - - main + branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: - python-version: 3.10 - - uses: abatilo/actions-poetry@v2.1.2 + python-version: '3.10' + - uses: abatilo/actions-poetry@v2.1.4 with: - poetry-version: 1.1.6 + poetry-version: '1.1.13' - run: poetry install --extras all - run: poetry run mkdocs build - uses: peaceiris/actions-gh-pages@v3.7.3 diff --git a/CHANGELOG.md b/CHANGELOG.md index e3dd638..38e3658 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,4 @@ # Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -- Nothing - -## [0.9.0](https://github.com/jacobsvante/netsuite/compare/v0.8.0...v0.9.0) - 2022-06-02 - -### Fixed -- SOAP: Circumvent `zeep` closing the underlying client after the first request - -### Changed -- Update dependencies, including major version bump of `authlib` and `ipython` - -### Removed -- Remove `session` parameter to NetSuiteSoapApi ## [0.8.0](https://github.com/jacobsvante/netsuite/compare/v0.7.0...v0.8.0) - 2021-04-28 diff --git a/pyproject.toml b/pyproject.toml index 12b665c..aff9286 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netsuite" -version = "0.9.0" +version = "0.8.0" description = "Make async requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets" authors = ["Jacob Magnusson "] license = "MIT" From 7bbd0f8bf4481200c1536395ce4cf2cc6cded162 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:40:20 +0200 Subject: [PATCH 45/47] New minor version Release-As: 0.9.0 From 91808971e3ab737ab0cd5bde8eda6a5b04bdd2e3 Mon Sep 17 00:00:00 2001 From: Jacob Magnusson Date: Thu, 2 Jun 2022 11:44:27 +0200 Subject: [PATCH 46/47] ci: Fix yaml miss Release-As: 0.9.0 --- .github/workflows/cd.yml | 1 + .github/workflows/ci.yml | 10 ++++++---- .github/workflows/codecov.yml | 1 + .github/workflows/docs.yml | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 1651c5b..cfbcc38 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -24,6 +24,7 @@ jobs: - uses: actions/setup-python@v3 with: python-version: '3.10' + architecture: x64 - uses: abatilo/actions-poetry@v2.1.4 with: poetry-version: '1.1.13' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ce1b9a..183b7bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,14 +10,15 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.10, 3.9, 3.8, 3.7] + python-version: ['3.10', '3.9', '3.8', '3.7'] extras: ['', all, soap_api, orjson, cli] os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 with: - python-version: ${{ matrix.python-version }} + python-version: '${{ matrix.python-version }}' + architecture: x64 - uses: abatilo/actions-poetry@v2.1.4 with: poetry-version: '1.1.13' @@ -31,13 +32,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.10] + python-version: ['3.10'] extras: [all] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 with: - python-version: ${{ matrix.python-version }} + python-version: '${{ matrix.python-version }}' + architecture: x64 - uses: abatilo/actions-poetry@v2.1.4 with: poetry-version: '1.1.13' diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index be24631..a1f665d 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -13,6 +13,7 @@ jobs: - uses: actions/setup-python@v3 with: python-version: '3.10' + architecture: x64 - uses: abatilo/actions-poetry@v2.1.4 with: poetry-version: '1.1.13' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a87a315..65bb96b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,7 +10,8 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 with: - python-version: '3.10' + python-version: '3.9' + architecture: x64 - uses: abatilo/actions-poetry@v2.1.4 with: poetry-version: '1.1.13' From 9d7bdb39bdf50c246b6b0abd6fa12d65f8e57d61 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 2 Jun 2022 09:48:25 +0000 Subject: [PATCH 47/47] chore(main): release 0.9.0 --- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38e3658..d2f56b2 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## [0.9.0](https://github.com/jacobsvante/netsuite/compare/v0.8.0...v0.9.0) (2022-06-02) + + +### Bug Fixes + +* Async connections sessions were being closed ([28e2b8e](https://github.com/jacobsvante/netsuite/commit/28e2b8e387ae4d30d65540901714f69a0b6248ab)) +* Don't require existing config to check version ([b9982bf](https://github.com/jacobsvante/netsuite/commit/b9982bf23ebf051ba994c0c1f8b0ba3f88b31cbc)) +* Regression in `openapi serve` CLI command ([c0074a0](https://github.com/jacobsvante/netsuite/commit/c0074a08edbef352b8b707ae1a6a7a21a25ce3d1)) +* Update dependencies ([1956b4d](https://github.com/jacobsvante/netsuite/commit/1956b4db748dd321fe310c3542f3e29c0f2161eb)), closes [#39](https://github.com/jacobsvante/netsuite/issues/39) [#40](https://github.com/jacobsvante/netsuite/issues/40) + + +### Documentation + +* Add example project for soap requests ([352bdd6](https://github.com/jacobsvante/netsuite/commit/352bdd6c9c63d1d2f9b0107921d35ca690dad82d)) + + +### Continuous Integration + +* Fix yaml miss ([9180897](https://github.com/jacobsvante/netsuite/commit/91808971e3ab737ab0cd5bde8eda6a5b04bdd2e3)) + ## [0.8.0](https://github.com/jacobsvante/netsuite/compare/v0.7.0...v0.8.0) - 2021-04-28 ### Changed diff --git a/pyproject.toml b/pyproject.toml index aff9286..12b665c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netsuite" -version = "0.8.0" +version = "0.9.0" description = "Make async requests to NetSuite SuiteTalk SOAP/REST Web Services and Restlets" authors = ["Jacob Magnusson "] license = "MIT"