From b34e044db623fe085c65f58cb2998dc567430c7c Mon Sep 17 00:00:00 2001 From: Jesse Whitehouse Date: Fri, 17 Feb 2023 16:36:52 -0600 Subject: [PATCH 1/3] Add SQLAlchemy dialect Signed-off-by: Jesse Whitehouse --- README.md | 23 +- examples/README.md | 3 +- examples/sqlalchemy.py | 92 +++++ src/databricks/sql/utils.py | 9 +- src/databricks/sqlalchemy/__init__.py | 1 + src/databricks/sqlalchemy/dialect/__init__.py | 315 ++++++++++++++++++ src/databricks/sqlalchemy/dialect/base.py | 17 + src/databricks/sqlalchemy/dialect/compiler.py | 38 +++ tests/e2e/sqlalchemy/test_basic.py | 254 ++++++++++++++ tests/sqlalchemy/demo_data/MOCK_DATA.xlsx | Bin 0 -> 59837 bytes tests/unit/test_param_escaper.py | 7 +- 11 files changed, 750 insertions(+), 9 deletions(-) create mode 100644 examples/sqlalchemy.py create mode 100644 src/databricks/sqlalchemy/__init__.py create mode 100644 src/databricks/sqlalchemy/dialect/__init__.py create mode 100644 src/databricks/sqlalchemy/dialect/base.py create mode 100644 src/databricks/sqlalchemy/dialect/compiler.py create mode 100644 tests/e2e/sqlalchemy/test_basic.py create mode 100644 tests/sqlalchemy/demo_data/MOCK_DATA.xlsx diff --git a/README.md b/README.md index 45251507f..b2b50ab8a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PyPI](https://img.shields.io/pypi/v/databricks-sql-connector?style=flat-square)](https://pypi.org/project/databricks-sql-connector/) [![Downloads](https://pepy.tech/badge/databricks-sql-connector)](https://pepy.tech/project/databricks-sql-connector) -The Databricks SQL Connector for Python allows you to develop Python applications that connect to Databricks clusters and SQL warehouses. It is a Thrift-based client with no dependencies on ODBC or JDBC. It conforms to the [Python DB API 2.0 specification](https://www.python.org/dev/peps/pep-0249/). +The Databricks SQL Connector for Python allows you to develop Python applications that connect to Databricks clusters and SQL warehouses. It is a Thrift-based client with no dependencies on ODBC or JDBC. It conforms to the [Python DB API 2.0 specification](https://www.python.org/dev/peps/pep-0249/) and exposes a [SQLAlchemy](https://www.sqlalchemy.org/) dialect for use with tools like `pandas` and `alembic` which use SQLAlchemy to execute DDL. This connector uses Arrow as the data-exchange format, and supports APIs to directly fetch Arrow tables. Arrow tables are wrapped in the `ArrowQueue` class to provide a natural API to get several rows at a time. @@ -24,16 +24,27 @@ For the latest documentation, see Install the library with `pip install databricks-sql-connector` -Example usage: +Note: Don't hard-code authentication secrets into your Python. Use environment variables + +```bash +export DATABRICKS_HOST=********.databricks.com +export DATABRICKS_HTTP_PATH=/sql/1.0/endpoints/**************** +export DATABRICKS_TOKEN=dapi******************************** +``` +Example usage: ```python +import os from databricks import sql -connection = sql.connect( - server_hostname='********.databricks.com', - http_path='/sql/1.0/endpoints/****************', - access_token='dapi********************************') +host = os.getenv("DATABRICKS_HOST) +http_path = os.getenv("DATABRICKS_HTTP_PATH) +access_token = os.getenv("DATABRICKS_ACCESS_TOKEN) +connection = sql.connect( + server_hostname=host, + http_path=http_path, + access_token=access_token) cursor = connection.cursor() diff --git a/examples/README.md b/examples/README.md index c4fe8ad68..d931e5642 100644 --- a/examples/README.md +++ b/examples/README.md @@ -36,4 +36,5 @@ To run all of these examples you can clone the entire repository to your disk. O - **`persistent_oauth.py`** shows a more advanced example of authenticating by OAuth while Bring Your Own IDP is in public preview. In this case, it shows how to use a sublcass of `OAuthPersistence` to reuse an OAuth token across script executions. - **`set_user_agent.py`** shows how to customize the user agent header used for Thrift commands. In this example the string `ExamplePartnerTag` will be added to the the user agent on every request. -- **`staging_ingestion.py`** shows how the connector handles Databricks' experimental staging ingestion commands `GET`, `PUT`, and `REMOVE`. \ No newline at end of file +- **`staging_ingestion.py`** shows how the connector handles Databricks' experimental staging ingestion commands `GET`, `PUT`, and `REMOVE`. +- **`sqlalchemy.py`** shows a basic example of connecting to Databricks with [SQLAlchemy](https://www.sqlalchemy.org/). diff --git a/examples/sqlalchemy.py b/examples/sqlalchemy.py new file mode 100644 index 000000000..b45d2aa49 --- /dev/null +++ b/examples/sqlalchemy.py @@ -0,0 +1,92 @@ +""" +databricks-sql-connector includes a SQLAlchemy dialect compatible with Databricks SQL. +It aims to be a drop-in replacement for the crflynn/sqlalchemy-databricks project, that implements +more of the Databricks API, particularly around table reflection, Alembic usage, and data +ingestion with pandas. + +Because of the extent of SQLAlchemy's capabilities it isn't feasible to provide examples of every +usage in a single script, so we only provide a basic one here. More examples are found in our test +suite at tests/e2e/sqlalchemy/test_basic.py and in the PR that implements this change: + +https://github.com/databricks/databricks-sql-python/pull/57 + +# What's already supported + +Most of the functionality is demonstrated in the e2e tests mentioned above. The below list we +derived from those test method names: + + - Create and drop tables with SQLAlchemy Core + - Create and drop tables with SQLAlchemy ORM + - Read created tables via reflection + - Modify column nullability + - Insert records manually + - Insert records with pandas.to_sql (note that this does not work for DataFrames with indexes) + +This connector also aims to support Alembic for programmatic delta table schema maintenance. This +behaviour is not yet backed by integration tests, which will follow in a subsequent PR as we learn +more about customer use cases there. That said, the following behaviours have been tested manually: + + - Autogenerate revisions with alembic revision --autogenerate + - Upgrade and downgrade between revisions with `alembic upgrade ` and + `alembic downgrade ` + +# Known Gaps + - MAP, ARRAY, and STRUCT types: this dialect can read these types out as strings. But you cannot + define a SQLAlchemy model with databricks.sqlalchemy.dialect.types.DatabricksMap (e.g.) because + we haven't implemented them yet. + - Constraints: with the addition of information_schema to Unity Catalog, Databricks SQL supports + foreign key and primary key constraints. This dialect can write these constraints but the ability + for alembic to reflect and modify them programmatically has not been tested. +""" + +import os +from sqlalchemy.orm import declarative_base, Session +from sqlalchemy import Column, String, Integer, BOOLEAN, create_engine, select + +host = os.getenv("DATABRICKS_SERVER_HOSTNAME") +http_path = os.getenv("DATABRICKS_HTTP_PATH") +access_token = os.getenv("DATABRICKS_TOKEN") +catalog = os.getenv("DATABRICKS_CATALOG") +schema = os.getenv("DATABRICKS_SCHEMA") + + +# Extra arguments are passed untouched to the driver +# See thrift_backend.py for complete list +extra_connect_args = { + "_tls_verify_hostname": True, + "_user_agent_entry": "PySQL Example Script", +} + +engine = create_engine( + f"databricks://token:{access_token}@{host}?http_path={http_path}&catalog={catalog}&schema={schema}", + connect_args=extra_connect_args, +) +session = Session(bind=engine) +base = declarative_base(bind=engine) + + +class SampleObject(base): + + __tablename__ = "mySampleTable" + + name = Column(String(255), primary_key=True) + episodes = Column(Integer) + some_bool = Column(BOOLEAN) + + +base.metadata.create_all() + +sample_object_1 = SampleObject(name="Bim Adewunmi", episodes=6, some_bool=True) +sample_object_2 = SampleObject(name="Miki Meek", episodes=12, some_bool=False) + +session.add(sample_object_1) +session.add(sample_object_2) + +session.commit() + +stmt = select(SampleObject).where(SampleObject.name.in_(["Bim Adewunmi", "Miki Meek"])) + +output = [i for i in session.scalars(stmt)] +assert len(output) == 2 + +base.metadata.drop_all() diff --git a/src/databricks/sql/utils.py b/src/databricks/sql/utils.py index ae411c7dc..ed5581367 100644 --- a/src/databricks/sql/utils.py +++ b/src/databricks/sql/utils.py @@ -1,10 +1,12 @@ from collections import namedtuple, OrderedDict from collections.abc import Iterable -import datetime +import datetime, decimal from enum import Enum from typing import Dict import pyarrow +from databricks.sql import exc + class ArrowQueue: def __init__( @@ -157,6 +159,9 @@ def escape_datetime(self, item, format, cutoff=0): formatted = dt_str[:-cutoff] if cutoff and format.endswith(".%f") else dt_str return "'{}'".format(formatted) + def escape_decimal(self, item): + return str(item) + def escape_item(self, item): if item is None: return "NULL" @@ -170,6 +175,8 @@ def escape_item(self, item): return self.escape_datetime(item, self._DATETIME_FORMAT) elif isinstance(item, datetime.date): return self.escape_datetime(item, self._DATE_FORMAT) + elif isinstance(item, decimal.Decimal): + return self.escape_decimal(item) else: raise exc.ProgrammingError("Unsupported object {}".format(item)) diff --git a/src/databricks/sqlalchemy/__init__.py b/src/databricks/sqlalchemy/__init__.py new file mode 100644 index 000000000..1df1e1d4d --- /dev/null +++ b/src/databricks/sqlalchemy/__init__.py @@ -0,0 +1 @@ +from databricks.sqlalchemy.dialect import DatabricksDialect diff --git a/src/databricks/sqlalchemy/dialect/__init__.py b/src/databricks/sqlalchemy/dialect/__init__.py new file mode 100644 index 000000000..58f3094e7 --- /dev/null +++ b/src/databricks/sqlalchemy/dialect/__init__.py @@ -0,0 +1,315 @@ +"""This module's layout loosely follows example of SQLAlchemy's postgres dialect +""" + +import decimal, re, datetime +from dateutil.parser import parse + +from sqlalchemy import types, processors, event +from sqlalchemy.engine import default, Engine +from sqlalchemy.exc import DatabaseError +from sqlalchemy.engine import reflection + +from databricks import sql + + +from databricks.sqlalchemy.dialect.base import ( + DatabricksDDLCompiler, + DatabricksIdentifierPreparer, +) +from databricks.sqlalchemy.dialect.compiler import DatabricksTypeCompiler + +try: + import alembic +except ImportError: + pass +else: + from alembic.ddl import DefaultImpl + + class DatabricksImpl(DefaultImpl): + __dialect__ = "databricks" + + +class DatabricksDecimal(types.TypeDecorator): + """Translates strings to decimals""" + + impl = types.DECIMAL + + def process_result_value(self, value, dialect): + if value is not None: + return decimal.Decimal(value) + else: + return None + + +class DatabricksTimestamp(types.TypeDecorator): + """Translates timestamp strings to datetime objects""" + + impl = types.TIMESTAMP + + def process_result_value(self, value, dialect): + return processors.str_to_datetime(value) + + def adapt(self, impltype, **kwargs): + return self.impl + + +class DatabricksDate(types.TypeDecorator): + """Translates date strings to date objects""" + + impl = types.DATE + + def process_result_value(self, value, dialect): + return processors.str_to_date(value) + + def adapt(self, impltype, **kwargs): + return self.impl + + +class DatabricksDialect(default.DefaultDialect): + """This dialect implements only those methods required to pass our e2e tests""" + + # Possible attributes are defined here: https://docs.sqlalchemy.org/en/14/core/internals.html#sqlalchemy.engine.Dialect + name: str = "databricks" + driver: str = "databricks-sql-python" + default_schema_name: str = "default" + + preparer = DatabricksIdentifierPreparer + type_compiler = DatabricksTypeCompiler + ddl_compiler = DatabricksDDLCompiler + supports_statement_cache: bool = True + supports_multivalues_insert: bool = True + supports_native_decimal: bool = True + supports_sane_rowcount: bool = False + + @classmethod + def dbapi(cls): + return sql + + def create_connect_args(self, url): + # TODO: can schema be provided after HOST? + # Expected URI format is: databricks+thrift://token:dapi***@***.cloud.databricks.com?http_path=/sql/*** + + kwargs = { + "server_hostname": url.host, + "access_token": url.password, + "http_path": url.query.get("http_path"), + "catalog": url.query.get("catalog"), + "schema": url.query.get("schema"), + } + + self.schema = kwargs["schema"] + self.catalog = kwargs["catalog"] + + return [], kwargs + + def get_columns(self, connection, table_name, schema=None, **kwargs): + """Return information about columns in `table_name`. + + Given a :class:`_engine.Connection`, a string + `table_name`, and an optional string `schema`, return column + information as a list of dictionaries with these keys: + + name + the column's name + + type + [sqlalchemy.types#TypeEngine] + + nullable + boolean + + default + the column's default value + + autoincrement + boolean + + sequence + a dictionary of the form + {'name' : str, 'start' :int, 'increment': int, 'minvalue': int, + 'maxvalue': int, 'nominvalue': bool, 'nomaxvalue': bool, + 'cycle': bool, 'cache': int, 'order': bool} + + Additional column attributes may be present. + """ + + _type_map = { + "boolean": types.Boolean, + "smallint": types.SmallInteger, + "int": types.Integer, + "bigint": types.BigInteger, + "float": types.Float, + "double": types.Float, + "string": types.String, + "varchar": types.String, + "char": types.String, + "binary": types.String, + "array": types.String, + "map": types.String, + "struct": types.String, + "uniontype": types.String, + "decimal": DatabricksDecimal, + "timestamp": DatabricksTimestamp, + "date": DatabricksDate, + } + + with self.get_driver_connection( + connection + )._dbapi_connection.dbapi_connection.cursor() as cur: + resp = cur.columns( + catalog_name=self.catalog, + schema_name=schema or self.schema, + table_name=table_name, + ).fetchall() + + columns = [] + + for col in resp: + + # Taken from PyHive. This removes added type info from decimals and maps + _col_type = re.search(r"^\w+", col.TYPE_NAME).group(0) + this_column = { + "name": col.COLUMN_NAME, + "type": _type_map[_col_type.lower()], + "nullable": bool(col.NULLABLE), + "default": col.COLUMN_DEF, + "autoincrement": False if col.IS_AUTO_INCREMENT == "NO" else True, + } + columns.append(this_column) + + return columns + + def get_pk_constraint(self, connection, table_name, schema=None, **kw): + """Return information about the primary key constraint on + table_name`. + + Given a :class:`_engine.Connection`, a string + `table_name`, and an optional string `schema`, return primary + key information as a dictionary with these keys: + + constrained_columns + a list of column names that make up the primary key + + name + optional name of the primary key constraint. + + """ + # TODO: implement this behaviour + return {"constrained_columns": []} + + def get_foreign_keys(self, connection, table_name, schema=None, **kw): + """Return information about foreign_keys in `table_name`. + + Given a :class:`_engine.Connection`, a string + `table_name`, and an optional string `schema`, return foreign + key information as a list of dicts with these keys: + + name + the constraint's name + + constrained_columns + a list of column names that make up the foreign key + + referred_schema + the name of the referred schema + + referred_table + the name of the referred table + + referred_columns + a list of column names in the referred table that correspond to + constrained_columns + """ + # TODO: Implement this behaviour + return [] + + def get_indexes(self, connection, table_name, schema=None, **kw): + """Return information about indexes in `table_name`. + + Given a :class:`_engine.Connection`, a string + `table_name` and an optional string `schema`, return index + information as a list of dictionaries with these keys: + + name + the index's name + + column_names + list of column names in order + + unique + boolean + """ + # TODO: Implement this behaviour + return [] + + def get_table_names(self, connection, schema=None, **kwargs): + TABLE_NAME = 1 + with self.get_driver_connection( + connection + )._dbapi_connection.dbapi_connection.cursor() as cur: + sql_str = "SHOW TABLES FROM {}".format( + ".".join([self.catalog, schema or self.schema]) + ) + data = cur.execute(sql_str).fetchall() + _tables = [i[TABLE_NAME] for i in data] + + return _tables + + def get_view_names(self, connection, schema=None, **kwargs): + VIEW_NAME = 1 + with self.get_driver_connection( + connection + )._dbapi_connection.dbapi_connection.cursor() as cur: + sql_str = "SHOW VIEWS FROM {}".format( + ".".join([self.catalog, schema or self.schema]) + ) + data = cur.execute(sql_str).fetchall() + _tables = [i[VIEW_NAME] for i in data] + + return _tables + + def do_rollback(self, dbapi_connection): + # Databricks SQL Does not support transactions + pass + + def has_table(self, connection, table_name, schema=None, **kwargs) -> bool: + """SQLAlchemy docstrings say dialect providers must implement this method""" + + schema = schema or "default" + + # DBR >12.x uses underscores in error messages + DBR_LTE_12_NOT_FOUND_STRING = "Table or view not found" + DBR_GT_12_NOT_FOUND_STRING = "TABLE_OR_VIEW_NOT_FOUND" + + try: + res = connection.execute(f"DESCRIBE TABLE {table_name}") + return True + except DatabaseError as e: + if (DBR_GT_12_NOT_FOUND_STRING in str(e) or DBR_LTE_12_NOT_FOUND_STRING in str(e)): + return False + else: + raise e + + @reflection.cache + def get_schema_names(self, connection, **kw): + # Equivalent to SHOW DATABASES + + # TODO: replace with call to cursor.schemas() once its performance matches raw SQL + return [row[0] for row in connection.execute("SHOW SCHEMAS")] + + +@event.listens_for(Engine, "do_connect") +def receive_do_connect(dialect, conn_rec, cargs, cparams): + """Helpful for DS on traffic from clients using SQLAlchemy in particular + """ + + # Ignore connect invocations that don't use our dialect + if not dialect.name == "databricks": + return + + if "_user_agent_entry" in cparams: + new_user_agent = f"sqlalchemy + {cparams['_user_agent_entry']}" + else: + new_user_agent = "sqlalchemy" + + cparams["_user_agent_entry"] = new_user_agent diff --git a/src/databricks/sqlalchemy/dialect/base.py b/src/databricks/sqlalchemy/dialect/base.py new file mode 100644 index 000000000..080f04106 --- /dev/null +++ b/src/databricks/sqlalchemy/dialect/base.py @@ -0,0 +1,17 @@ +import re +from sqlalchemy.sql import compiler + + +class DatabricksIdentifierPreparer(compiler.IdentifierPreparer): + # SparkSQL identifier specification: + # ref: https://spark.apache.org/docs/latest/sql-ref-identifier.html + + legal_characters = re.compile(r"^[A-Z0-9_]+$", re.I) + + def __init__(self, dialect): + super().__init__(dialect, initial_quote="`") + + +class DatabricksDDLCompiler(compiler.DDLCompiler): + def post_create_table(self, table): + return " USING DELTA" diff --git a/src/databricks/sqlalchemy/dialect/compiler.py b/src/databricks/sqlalchemy/dialect/compiler.py new file mode 100644 index 000000000..f77807ed4 --- /dev/null +++ b/src/databricks/sqlalchemy/dialect/compiler.py @@ -0,0 +1,38 @@ +from sqlalchemy.sql import compiler + + +class DatabricksTypeCompiler(compiler.GenericTypeCompiler): + """Originally forked from pyhive""" + + def visit_INTEGER(self, type_): + return "INT" + + def visit_NUMERIC(self, type_): + return "DECIMAL" + + def visit_CHAR(self, type_): + return "STRING" + + def visit_VARCHAR(self, type_): + return "STRING" + + def visit_NCHAR(self, type_): + return "STRING" + + def visit_TEXT(self, type_): + return "STRING" + + def visit_CLOB(self, type_): + return "STRING" + + def visit_BLOB(self, type_): + return "BINARY" + + def visit_TIME(self, type_): + return "TIMESTAMP" + + def visit_DATE(self, type_): + return "DATE" + + def visit_DATETIME(self, type_): + return "TIMESTAMP" diff --git a/tests/e2e/sqlalchemy/test_basic.py b/tests/e2e/sqlalchemy/test_basic.py new file mode 100644 index 000000000..4f4df91b6 --- /dev/null +++ b/tests/e2e/sqlalchemy/test_basic.py @@ -0,0 +1,254 @@ +import os, datetime, decimal +import pytest +from unittest import skipIf +from sqlalchemy import create_engine, select, insert, Column, MetaData, Table +from sqlalchemy.orm import declarative_base, Session +from sqlalchemy.types import SMALLINT, Integer, BOOLEAN, String, DECIMAL, Date + + +USER_AGENT_TOKEN = "PySQL e2e Tests" + + +@pytest.fixture +def db_engine(): + + HOST = os.environ.get("host") + HTTP_PATH = os.environ.get("http_path") + ACCESS_TOKEN = os.environ.get("access_token") + CATALOG = os.environ.get("catalog") + SCHEMA = os.environ.get("schema") + + connect_args = {"_user_agent_entry": USER_AGENT_TOKEN} + + engine = create_engine( + f"databricks://token:{ACCESS_TOKEN}@{HOST}?http_path={HTTP_PATH}&catalog={CATALOG}&schema={SCHEMA}", + connect_args=connect_args, + ) + return engine + + +@pytest.fixture() +def base(db_engine): + return declarative_base(bind=db_engine) + + +@pytest.fixture() +def session(db_engine): + return Session(bind=db_engine) + + +@pytest.fixture() +def metadata_obj(db_engine): + return MetaData(bind=db_engine) + + +def test_can_connect(db_engine): + simple_query = "SELECT 1" + result = db_engine.execute(simple_query).fetchall() + assert len(result) == 1 + + +def test_connect_args(db_engine): + """Verify that extra connect args passed to sqlalchemy.create_engine are passed to DBAPI + + This will most commonly happen when partners supply a user agent entry + """ + + conn = db_engine.connect() + connection_headers = conn.connection.thrift_backend._transport._headers + user_agent = connection_headers["User-Agent"] + + expected = f"(sqlalchemy + {USER_AGENT_TOKEN})" + assert expected in user_agent + + +def test_pandas_upload(db_engine, metadata_obj): + + import pandas as pd + + SCHEMA = os.environ.get("schema") + try: + df = pd.read_excel("tests/sqlalchemy/demo_data/MOCK_DATA.xlsx") + df.to_sql( + "mock_data", + db_engine, + schema=SCHEMA, + index=False, + method="multi", + if_exists="replace", + ) + + df_after = pd.read_sql_table("mock_data", db_engine, schema=SCHEMA) + assert len(df) == len(df_after) + except Exception as e: + raise e + finally: + db_engine.execute("DROP TABLE mock_data") + + +def test_create_table_not_null(db_engine, metadata_obj): + + table_name = "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s")) + + SampleTable = Table( + table_name, + metadata_obj, + Column("name", String(255)), + Column("episodes", Integer), + Column("some_bool", BOOLEAN, nullable=False), + ) + + metadata_obj.create_all() + + columns = db_engine.dialect.get_columns( + connection=db_engine.connect(), table_name=table_name + ) + + name_column_description = columns[0] + some_bool_column_description = columns[2] + + assert name_column_description.get("nullable") is True + assert some_bool_column_description.get("nullable") is False + + metadata_obj.drop_all() + + +def test_bulk_insert_with_core(db_engine, metadata_obj, session): + + import random + + num_to_insert = random.choice(range(10_000, 20_000)) + + table_name = "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s")) + + names = ["Bim", "Miki", "Sarah", "Ira"] + + SampleTable = Table( + table_name, metadata_obj, Column("name", String(255)), Column("number", Integer) + ) + + rows = [ + {"name": names[i % 3], "number": random.choice(range(10000))} + for i in range(num_to_insert) + ] + + metadata_obj.create_all() + db_engine.execute(insert(SampleTable).values(rows)) + + rows = db_engine.execute(select(SampleTable)).fetchall() + + assert len(rows) == num_to_insert + + +def test_create_insert_drop_table_core(base, db_engine, metadata_obj: MetaData): + """ """ + + SampleTable = Table( + "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s")), + metadata_obj, + Column("name", String(255)), + Column("episodes", Integer), + Column("some_bool", BOOLEAN), + Column("dollars", DECIMAL(10, 2)), + ) + + metadata_obj.create_all() + + insert_stmt = insert(SampleTable).values( + name="Bim Adewunmi", episodes=6, some_bool=True, dollars=decimal.Decimal(125) + ) + + with db_engine.connect() as conn: + conn.execute(insert_stmt) + + select_stmt = select(SampleTable) + resp = db_engine.execute(select_stmt) + + result = resp.fetchall() + + assert len(result) == 1 + + metadata_obj.drop_all() + + +# ORM tests are made following this tutorial +# https://docs.sqlalchemy.org/en/14/orm/quickstart.html + + +@skipIf(False, "Unity catalog must be supported") +def test_create_insert_drop_table_orm(base, session: Session): + """ORM classes built on the declarative base class must have a primary key. + This is restricted to Unity Catalog. + """ + + class SampleObject(base): + + __tablename__ = "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s")) + + name = Column(String(255), primary_key=True) + episodes = Column(Integer) + some_bool = Column(BOOLEAN) + + base.metadata.create_all() + + sample_object_1 = SampleObject(name="Bim Adewunmi", episodes=6, some_bool=True) + sample_object_2 = SampleObject(name="Miki Meek", episodes=12, some_bool=False) + session.add(sample_object_1) + session.add(sample_object_2) + session.commit() + + stmt = select(SampleObject).where( + SampleObject.name.in_(["Bim Adewunmi", "Miki Meek"]) + ) + + output = [i for i in session.scalars(stmt)] + assert len(output) == 2 + + base.metadata.drop_all() + + +def test_dialect_type_mappings(base, db_engine, metadata_obj: MetaData): + """Confirms that we get back the same time we declared in a model and inserted using Core""" + + SampleTable = Table( + "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s")), + metadata_obj, + Column("string_example", String(255)), + Column("integer_example", Integer), + Column("boolean_example", BOOLEAN), + Column("decimal_example", DECIMAL(10, 2)), + Column("date_example", Date), + ) + + string_example = "" + integer_example = 100 + boolean_example = True + decimal_example = decimal.Decimal(125) + date_example = datetime.date(2013, 1, 1) + + metadata_obj.create_all() + + insert_stmt = insert(SampleTable).values( + string_example=string_example, + integer_example=integer_example, + boolean_example=boolean_example, + decimal_example=decimal_example, + date_example=date_example, + ) + + with db_engine.connect() as conn: + conn.execute(insert_stmt) + + select_stmt = select(SampleTable) + resp = db_engine.execute(select_stmt) + + result = resp.fetchall() + this_row = result[0] + + assert this_row["string_example"] == string_example + assert this_row["integer_example"] == integer_example + assert this_row["boolean_example"] == boolean_example + assert this_row["decimal_example"] == decimal_example + assert this_row["date_example"] == date_example + + metadata_obj.drop_all() diff --git a/tests/sqlalchemy/demo_data/MOCK_DATA.xlsx b/tests/sqlalchemy/demo_data/MOCK_DATA.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e080689a9d978891664c1848474f64401a453165 GIT binary patch literal 59837 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 Ki0m!^!2bbc3S|iZ literal 0 HcmV?d00001 diff --git a/tests/unit/test_param_escaper.py b/tests/unit/test_param_escaper.py index cb5758aa7..6c1f1770e 100644 --- a/tests/unit/test_param_escaper.py +++ b/tests/unit/test_param_escaper.py @@ -1,5 +1,5 @@ from datetime import date, datetime -import unittest, pytest +import unittest, pytest, decimal from databricks.sql.utils import ParamEscaper, inject_parameters @@ -18,6 +18,11 @@ def test_escape_number_float(self): """ assert pe.escape_number(100.1234) == 100.1234 + def test_escape_number_decimal(self): + """This behaviour uses the string representation of a decimal + """ + assert pe.escape_decimal(decimal.Decimal("124.32")) == "124.32" + def test_escape_string_normal(self): """ """ From f436815c43d0b6278c2c0cc9e1f59011742c8744 Mon Sep 17 00:00:00 2001 From: Jesse Whitehouse Date: Fri, 17 Feb 2023 16:42:26 -0600 Subject: [PATCH 2/3] Update poetry after rebase onto main Signed-off-by: Jesse Whitehouse --- poetry.lock | 537 ++++++++++++++++++++++++++++++++++++++++++------- pyproject.toml | 6 + 2 files changed, 465 insertions(+), 78 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3ab1c15b7..44a151df8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,20 @@ +[[package]] +name = "alembic" +version = "1.9.4" +description = "A database migration tool for SQLAlchemy." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.9\""} +importlib-resources = {version = "*", markers = "python_version < \"3.9\""} +Mako = "*" +SQLAlchemy = ">=1.3.0" + +[package.extras] +tz = ["python-dateutil"] + [[package]] name = "astroid" version = "2.11.7" @@ -61,14 +78,11 @@ python-versions = ">=3.6" [[package]] name = "charset-normalizer" -version = "2.1.1" +version = "3.0.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false -python-versions = ">=3.6.0" - -[package.extras] -unicode-backport = ["unicodedata2"] +python-versions = "*" [[package]] name = "click" @@ -101,6 +115,14 @@ python-versions = ">=3.7" [package.extras] graph = ["objgraph (>=1.7.2)"] +[[package]] +name = "et-xmlfile" +version = "1.1.0" +description = "An implementation of lxml.xmlfile for the standard library" +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "exceptiongroup" version = "1.1.0" @@ -112,6 +134,18 @@ python-versions = ">=3.7" [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "greenlet" +version = "2.0.2" +description = "Lightweight in-process concurrent programming" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" + +[package.extras] +docs = ["Sphinx", "docutils (<0.18)"] +test = ["objgraph", "psutil"] + [[package]] name = "idna" version = "3.4" @@ -124,7 +158,7 @@ python-versions = ">=3.5" name = "importlib-metadata" version = "6.0.0" description = "Read metadata from Python packages" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -137,6 +171,21 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker perf = ["ipython"] testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +[[package]] +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -147,7 +196,7 @@ python-versions = ">=3.7" [[package]] name = "isort" -version = "5.11.4" +version = "5.11.5" description = "A Python utility / library to sort Python imports." category = "dev" optional = false @@ -155,7 +204,7 @@ python-versions = ">=3.7.0" [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile-deprecated-finder = ["pipreqs", "requirementslib"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] plugins = ["setuptools"] requirements-deprecated-finder = ["pip-api", "pipreqs"] @@ -180,6 +229,31 @@ docs = ["sphinx (>=1.6.0)", "sphinx-bootstrap-theme"] flake8 = ["flake8"] tests = ["psutil", "pytest (!=3.3.0)", "pytest-cov"] +[[package]] +name = "mako" +version = "1.2.4" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" + [[package]] name = "mccabe" version = "0.7.0" @@ -209,11 +283,11 @@ reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" [[package]] name = "numpy" @@ -244,6 +318,17 @@ rsa = ["cryptography (>=3.0.0)"] signals = ["blinker (>=1.4.0)"] signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] +[[package]] +name = "openpyxl" +version = "3.1.1" +description = "A Python library to read/write Excel 2010 xlsx/xlsm files" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +et-xmlfile = "*" + [[package]] name = "packaging" version = "23.0" @@ -275,7 +360,7 @@ test = ["hypothesis (>=3.58)", "pytest (>=6.0)", "pytest-xdist"] [[package]] name = "pathspec" -version = "0.10.3" +version = "0.11.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false @@ -283,7 +368,7 @@ python-versions = ">=3.7" [[package]] name = "platformdirs" -version = "2.6.2" +version = "3.0.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false @@ -293,8 +378,8 @@ python-versions = ">=3.7" typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} [package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" @@ -313,7 +398,7 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pyarrow" -version = "10.0.1" +version = "11.0.0" description = "Python library for Apache Arrow" category = "main" optional = false @@ -345,7 +430,7 @@ testutil = ["gitpython (>3)"] [[package]] name = "pytest" -version = "7.2.0" +version = "7.2.1" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -377,7 +462,7 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2022.7" +version = "2022.7.1" description = "World timezone definitions, modern and historical" category = "main" optional = false @@ -385,7 +470,7 @@ python-versions = "*" [[package]] name = "requests" -version = "2.28.1" +version = "2.28.2" description = "Python HTTP for Humans." category = "main" optional = false @@ -393,7 +478,7 @@ python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" +charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" @@ -403,14 +488,14 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "setuptools" -version = "65.6.3" +version = "67.3.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] @@ -422,6 +507,39 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "sqlalchemy" +version = "1.4.46" +description = "Database Abstraction Library" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] +mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +pymysql = ["pymysql", "pymysql (<1)"] +sqlcipher = ["sqlcipher3_binary"] + [[package]] name = "thrift" version = "0.16.0" @@ -456,15 +574,15 @@ python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "4.4.0" +version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" [[package]] name = "urllib3" -version = "1.26.13" +version = "1.26.14" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -485,22 +603,26 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "zipp" -version = "3.11.0" +version = "3.13.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" python-versions = "^3.7.1" -content-hash = "2ce16d6f44e05796d057e57efebf9f9c2dfde68a04d6fed6aef2687acb5ce202" +content-hash = "4d3a82b79d42a0a4642dc54954a62013aca022f44f449c02fe70b2a07c686964" [metadata.files] +alembic = [ + {file = "alembic-1.9.4-py3-none-any.whl", hash = "sha256:6f1c2207369bf4f49f952057a33bb017fbe5c148c2a773b46906b806ea6e825f"}, + {file = "alembic-1.9.4.tar.gz", hash = "sha256:4d3bd32ecdbb7bbfb48a9fe9e6d6fd6a831a1b59d03e26e292210237373e7db5"}, +] astroid = [ {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"}, @@ -528,8 +650,94 @@ certifi = [ {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, - {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, + {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-win32.whl", hash = "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-win32.whl", hash = "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-win32.whl", hash = "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-win32.whl", hash = "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-win32.whl", hash = "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"}, + {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, ] click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, @@ -543,10 +751,76 @@ dill = [ {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, ] +et-xmlfile = [ + {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, + {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, +] exceptiongroup = [ {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, ] +greenlet = [ + {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"}, + {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"}, + {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"}, + {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"}, + {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"}, + {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, + {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, + {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, + {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, + {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"}, + {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"}, + {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"}, + {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"}, + {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"}, + {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"}, + {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"}, + {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"}, + {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"}, + {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"}, + {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, + {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, + {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, + {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, + {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, + {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, + {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, +] idna = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, @@ -555,13 +829,17 @@ importlib-metadata = [ {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, ] +importlib-resources = [ + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, +] iniconfig = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] isort = [ - {file = "isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"}, - {file = "isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"}, + {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, + {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, ] lazy-object-proxy = [ {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, @@ -638,6 +916,62 @@ lz4 = [ {file = "lz4-4.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:4caedeb19e3ede6c7a178968b800f910db6503cb4cb1e9cc9221157572139b49"}, {file = "lz4-4.3.2.tar.gz", hash = "sha256:e1431d84a9cfb23e6773e72078ce8e65cad6745816d4cbf9ae67da5ea419acda"}, ] +mako = [ + {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, + {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, +] +markupsafe = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] mccabe = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -668,8 +1002,8 @@ mypy = [ {file = "mypy-0.950.tar.gz", hash = "sha256:1b333cfbca1762ff15808a0ef4f71b5d3eed8528b23ea1c3fb50543c867d68de"}, ] 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"}, + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] numpy = [ {file = "numpy-1.21.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38e8648f9449a549a7dfe8d8755a5979b45b3538520d1e735637ef28e8c2dc50"}, @@ -733,6 +1067,10 @@ oauthlib = [ {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, ] +openpyxl = [ + {file = "openpyxl-3.1.1-py2.py3-none-any.whl", hash = "sha256:a0266e033e65f33ee697254b66116a5793c15fc92daf64711080000df4cfe0a8"}, + {file = "openpyxl-3.1.1.tar.gz", hash = "sha256:f06d44e2c973781068bce5ecf860a09bcdb1c7f5ce1facd5e9aa82c92c93ae72"}, +] packaging = [ {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, @@ -765,72 +1103,115 @@ pandas = [ {file = "pandas-1.3.5.tar.gz", hash = "sha256:1e4285f5de1012de20ca46b188ccf33521bff61ba5c5ebd78b4fb28e5416a9f1"}, ] pathspec = [ - {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, - {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, + {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, + {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, ] platformdirs = [ - {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, - {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, + {file = "platformdirs-3.0.0-py3-none-any.whl", hash = "sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567"}, + {file = "platformdirs-3.0.0.tar.gz", hash = "sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] pyarrow = [ - {file = "pyarrow-10.0.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:e00174764a8b4e9d8d5909b6d19ee0c217a6cf0232c5682e31fdfbd5a9f0ae52"}, - {file = "pyarrow-10.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f7a7dbe2f7f65ac1d0bd3163f756deb478a9e9afc2269557ed75b1b25ab3610"}, - {file = "pyarrow-10.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb627673cb98708ef00864e2e243f51ba7b4c1b9f07a1d821f98043eccd3f585"}, - {file = "pyarrow-10.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba71e6fc348c92477586424566110d332f60d9a35cb85278f42e3473bc1373da"}, - {file = "pyarrow-10.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:7b4ede715c004b6fc535de63ef79fa29740b4080639a5ff1ea9ca84e9282f349"}, - {file = "pyarrow-10.0.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:e3fe5049d2e9ca661d8e43fab6ad5a4c571af12d20a57dffc392a014caebef65"}, - {file = "pyarrow-10.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:254017ca43c45c5098b7f2a00e995e1f8346b0fb0be225f042838323bb55283c"}, - {file = "pyarrow-10.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70acca1ece4322705652f48db65145b5028f2c01c7e426c5d16a30ba5d739c24"}, - {file = "pyarrow-10.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb57334f2c57979a49b7be2792c31c23430ca02d24becd0b511cbe7b6b08649"}, - {file = "pyarrow-10.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:1765a18205eb1e02ccdedb66049b0ec148c2a0cb52ed1fb3aac322dfc086a6ee"}, - {file = "pyarrow-10.0.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:61f4c37d82fe00d855d0ab522c685262bdeafd3fbcb5fe596fe15025fbc7341b"}, - {file = "pyarrow-10.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e141a65705ac98fa52a9113fe574fdaf87fe0316cde2dffe6b94841d3c61544c"}, - {file = "pyarrow-10.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf26f809926a9d74e02d76593026f0aaeac48a65b64f1bb17eed9964bfe7ae1a"}, - {file = "pyarrow-10.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:443eb9409b0cf78df10ced326490e1a300205a458fbeb0767b6b31ab3ebae6b2"}, - {file = "pyarrow-10.0.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:f2d00aa481becf57098e85d99e34a25dba5a9ade2f44eb0b7d80c80f2984fc03"}, - {file = "pyarrow-10.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b1fc226d28c7783b52a84d03a66573d5a22e63f8a24b841d5fc68caeed6784d4"}, - {file = "pyarrow-10.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efa59933b20183c1c13efc34bd91efc6b2997377c4c6ad9272da92d224e3beb1"}, - {file = "pyarrow-10.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:668e00e3b19f183394388a687d29c443eb000fb3fe25599c9b4762a0afd37775"}, - {file = "pyarrow-10.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1bc6e4d5d6f69e0861d5d7f6cf4d061cf1069cb9d490040129877acf16d4c2a"}, - {file = "pyarrow-10.0.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:42ba7c5347ce665338f2bc64685d74855900200dac81a972d49fe127e8132f75"}, - {file = "pyarrow-10.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b069602eb1fc09f1adec0a7bdd7897f4d25575611dfa43543c8b8a75d99d6874"}, - {file = "pyarrow-10.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fb4a0c12a2ac1ed8e7e2aa52aade833772cf2d3de9dde685401b22cec30002"}, - {file = "pyarrow-10.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db0c5986bf0808927f49640582d2032a07aa49828f14e51f362075f03747d198"}, - {file = "pyarrow-10.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0ec7587d759153f452d5263dbc8b1af318c4609b607be2bd5127dcda6708cdb1"}, - {file = "pyarrow-10.0.1.tar.gz", hash = "sha256:1a14f57a5f472ce8234f2964cd5184cccaa8df7e04568c64edc33b23eb285dd5"}, + {file = "pyarrow-11.0.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:40bb42afa1053c35c749befbe72f6429b7b5f45710e85059cdd534553ebcf4f2"}, + {file = "pyarrow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7c28b5f248e08dea3b3e0c828b91945f431f4202f1a9fe84d1012a761324e1ba"}, + {file = "pyarrow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a37bc81f6c9435da3c9c1e767324ac3064ffbe110c4e460660c43e144be4ed85"}, + {file = "pyarrow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7c53def8dbbc810282ad308cc46a523ec81e653e60a91c609c2233ae407689"}, + {file = "pyarrow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:25aa11c443b934078bfd60ed63e4e2d42461682b5ac10f67275ea21e60e6042c"}, + {file = "pyarrow-11.0.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:e217d001e6389b20a6759392a5ec49d670757af80101ee6b5f2c8ff0172e02ca"}, + {file = "pyarrow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad42bb24fc44c48f74f0d8c72a9af16ba9a01a2ccda5739a517aa860fa7e3d56"}, + {file = "pyarrow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d942c690ff24a08b07cb3df818f542a90e4d359381fbff71b8f2aea5bf58841"}, + {file = "pyarrow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f010ce497ca1b0f17a8243df3048055c0d18dcadbcc70895d5baf8921f753de5"}, + {file = "pyarrow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:2f51dc7ca940fdf17893227edb46b6784d37522ce08d21afc56466898cb213b2"}, + {file = "pyarrow-11.0.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:1cbcfcbb0e74b4d94f0b7dde447b835a01bc1d16510edb8bb7d6224b9bf5bafc"}, + {file = "pyarrow-11.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaee8f79d2a120bf3e032d6d64ad20b3af6f56241b0ffc38d201aebfee879d00"}, + {file = "pyarrow-11.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:410624da0708c37e6a27eba321a72f29d277091c8f8d23f72c92bada4092eb5e"}, + {file = "pyarrow-11.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2d53ba72917fdb71e3584ffc23ee4fcc487218f8ff29dd6df3a34c5c48fe8c06"}, + {file = "pyarrow-11.0.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:f12932e5a6feb5c58192209af1d2607d488cb1d404fbc038ac12ada60327fa34"}, + {file = "pyarrow-11.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:41a1451dd895c0b2964b83d91019e46f15b5564c7ecd5dcb812dadd3f05acc97"}, + {file = "pyarrow-11.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:becc2344be80e5dce4e1b80b7c650d2fc2061b9eb339045035a1baa34d5b8f1c"}, + {file = "pyarrow-11.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f40be0d7381112a398b93c45a7e69f60261e7b0269cc324e9f739ce272f4f70"}, + {file = "pyarrow-11.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:362a7c881b32dc6b0eccf83411a97acba2774c10edcec715ccaab5ebf3bb0835"}, + {file = "pyarrow-11.0.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:ccbf29a0dadfcdd97632b4f7cca20a966bb552853ba254e874c66934931b9841"}, + {file = "pyarrow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e99be85973592051e46412accea31828da324531a060bd4585046a74ba45854"}, + {file = "pyarrow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69309be84dcc36422574d19c7d3a30a7ea43804f12552356d1ab2a82a713c418"}, + {file = "pyarrow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da93340fbf6f4e2a62815064383605b7ffa3e9eeb320ec839995b1660d69f89b"}, + {file = "pyarrow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:caad867121f182d0d3e1a0d36f197df604655d0b466f1bc9bafa903aa95083e4"}, + {file = "pyarrow-11.0.0.tar.gz", hash = "sha256:5461c57dbdb211a632a48facb9b39bbeb8a7905ec95d768078525283caef5f6d"}, ] pylint = [ {file = "pylint-2.13.9-py3-none-any.whl", hash = "sha256:705c620d388035bdd9ff8b44c5bcdd235bfb49d276d488dd2c8ff1736aa42526"}, {file = "pylint-2.13.9.tar.gz", hash = "sha256:095567c96e19e6f57b5b907e67d265ff535e588fe26b12b5ebe1fc5645b2c731"}, ] pytest = [ - {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, - {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, + {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, + {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, ] 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-2022.7-py2.py3-none-any.whl", hash = "sha256:93007def75ae22f7cd991c84e02d434876818661f8df9ad5df9e950ff4e52cfd"}, - {file = "pytz-2022.7.tar.gz", hash = "sha256:7ccfae7b4b2c067464a6733c6261673fdb8fd1be905460396b97a073e9fa683a"}, + {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, + {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, ] requests = [ - {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, - {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, + {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, + {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, ] setuptools = [ - {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, - {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, + {file = "setuptools-67.3.2-py3-none-any.whl", hash = "sha256:bb6d8e508de562768f2027902929f8523932fcd1fb784e6d573d2cafac995a48"}, + {file = "setuptools-67.3.2.tar.gz", hash = "sha256:95f00380ef2ffa41d9bba85d95b27689d923c93dfbafed4aecd7cf988a25e012"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +sqlalchemy = [ + {file = "SQLAlchemy-1.4.46-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:7001f16a9a8e06488c3c7154827c48455d1c1507d7228d43e781afbc8ceccf6d"}, + {file = "SQLAlchemy-1.4.46-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c7a46639ba058d320c9f53a81db38119a74b8a7a1884df44d09fbe807d028aaf"}, + {file = "SQLAlchemy-1.4.46-cp27-cp27m-win32.whl", hash = "sha256:c04144a24103135ea0315d459431ac196fe96f55d3213bfd6d39d0247775c854"}, + {file = "SQLAlchemy-1.4.46-cp27-cp27m-win_amd64.whl", hash = "sha256:7b81b1030c42b003fc10ddd17825571603117f848814a344d305262d370e7c34"}, + {file = "SQLAlchemy-1.4.46-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:939f9a018d2ad04036746e15d119c0428b1e557470361aa798e6e7d7f5875be0"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b7f4b6aa6e87991ec7ce0e769689a977776db6704947e562102431474799a857"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbf17ac9a61e7a3f1c7ca47237aac93cabd7f08ad92ac5b96d6f8dea4287fc1"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7f8267682eb41a0584cf66d8a697fef64b53281d01c93a503e1344197f2e01fe"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cb0ad8a190bc22d2112001cfecdec45baffdf41871de777239da6a28ed74b6"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-win32.whl", hash = "sha256:5f752676fc126edc1c4af0ec2e4d2adca48ddfae5de46bb40adbd3f903eb2120"}, + {file = "SQLAlchemy-1.4.46-cp310-cp310-win_amd64.whl", hash = "sha256:31de1e2c45e67a5ec1ecca6ec26aefc299dd5151e355eb5199cd9516b57340be"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d68e1762997bfebf9e5cf2a9fd0bcf9ca2fdd8136ce7b24bbd3bbfa4328f3e4a"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d112b0f3c1bc5ff70554a97344625ef621c1bfe02a73c5d97cac91f8cd7a41e"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69fac0a7054d86b997af12dc23f581cf0b25fb1c7d1fed43257dee3af32d3d6d"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-win32.whl", hash = "sha256:887865924c3d6e9a473dc82b70977395301533b3030d0f020c38fd9eba5419f2"}, + {file = "SQLAlchemy-1.4.46-cp311-cp311-win_amd64.whl", hash = "sha256:984ee13543a346324319a1fb72b698e521506f6f22dc37d7752a329e9cd00a32"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:9167d4227b56591a4cc5524f1b79ccd7ea994f36e4c648ab42ca995d28ebbb96"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d61e9ecc849d8d44d7f80894ecff4abe347136e9d926560b818f6243409f3c86"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3ec187acf85984263299a3f15c34a6c0671f83565d86d10f43ace49881a82718"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9883f5fae4fd8e3f875adc2add69f8b945625811689a6c65866a35ee9c0aea23"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-win32.whl", hash = "sha256:535377e9b10aff5a045e3d9ada8a62d02058b422c0504ebdcf07930599890eb0"}, + {file = "SQLAlchemy-1.4.46-cp36-cp36m-win_amd64.whl", hash = "sha256:18cafdb27834fa03569d29f571df7115812a0e59fd6a3a03ccb0d33678ec8420"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:a1ad90c97029cc3ab4ffd57443a20fac21d2ec3c89532b084b073b3feb5abff3"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4847f4b1d822754e35707db913396a29d874ee77b9c3c3ef3f04d5a9a6209618"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c5a99282848b6cae0056b85da17392a26b2d39178394fc25700bcf967e06e97a"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4b1cc7835b39835c75cf7c20c926b42e97d074147c902a9ebb7cf2c840dc4e2"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-win32.whl", hash = "sha256:c522e496f9b9b70296a7675272ec21937ccfc15da664b74b9f58d98a641ce1b6"}, + {file = "SQLAlchemy-1.4.46-cp37-cp37m-win_amd64.whl", hash = "sha256:ae067ab639fa499f67ded52f5bc8e084f045d10b5ac7bb928ae4ca2b6c0429a5"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:e3c1808008124850115a3f7e793a975cfa5c8a26ceeeb9ff9cbb4485cac556df"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d164df3d83d204c69f840da30b292ac7dc54285096c6171245b8d7807185aa"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b33ffbdbbf5446cf36cd4cc530c9d9905d3c2fe56ed09e25c22c850cdb9fac92"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d94682732d1a0def5672471ba42a29ff5e21bb0aae0afa00bb10796fc1e28dd"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-win32.whl", hash = "sha256:f8cb80fe8d14307e4124f6fad64dfd87ab749c9d275f82b8b4ec84c84ecebdbe"}, + {file = "SQLAlchemy-1.4.46-cp38-cp38-win_amd64.whl", hash = "sha256:07e48cbcdda6b8bc7a59d6728bd3f5f574ffe03f2c9fb384239f3789c2d95c2e"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1b1e5e96e2789d89f023d080bee432e2fef64d95857969e70d3cadec80bd26f0"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3714e5b33226131ac0da60d18995a102a17dddd42368b7bdd206737297823ad"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:955162ad1a931fe416eded6bb144ba891ccbf9b2e49dc7ded39274dd9c5affc5"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6e4cb5c63f705c9d546a054c60d326cbde7421421e2d2565ce3e2eee4e1a01f"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-win32.whl", hash = "sha256:51e1ba2884c6a2b8e19109dc08c71c49530006c1084156ecadfaadf5f9b8b053"}, + {file = "SQLAlchemy-1.4.46-cp39-cp39-win_amd64.whl", hash = "sha256:315676344e3558f1f80d02535f410e80ea4e8fddba31ec78fe390eff5fb8f466"}, + {file = "SQLAlchemy-1.4.46.tar.gz", hash = "sha256:6913b8247d8a292ef8315162a51931e2b40ce91681f1b6f18f697045200c4a30"}, +] thrift = [ {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, ] @@ -865,12 +1246,12 @@ typed-ast = [ {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] typing-extensions = [ - {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, - {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, ] urllib3 = [ - {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"}, - {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"}, + {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, + {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, ] wrapt = [ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, @@ -939,6 +1320,6 @@ wrapt = [ {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] zipp = [ - {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, - {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, + {file = "zipp-3.13.0-py3-none-any.whl", hash = "sha256:e8b2a36ea17df80ffe9e2c4fda3f693c3dad6df1697d3cd3af232db680950b0b"}, + {file = "zipp-3.13.0.tar.gz", hash = "sha256:23f70e964bc11a34cef175bc90ba2914e1e4545ea1e3e2f67c079671883f9cb6"}, ] diff --git a/pyproject.toml b/pyproject.toml index 4de252714..ef9fc674b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,9 @@ numpy = [ {version = "1.21.1", python = ">=3.7,<3.8"}, {version = "1.23.4", python = ">=3.8"} ] +sqlalchemy = "^1.4.44" +openpyxl = "^3.0.10" +alembic = "^1.8.1" [tool.poetry.dev-dependencies] pytest = "^7.1.2" @@ -34,6 +37,9 @@ black = "^22.3.0" "Homepage" = "https://github.com/databricks/databricks-sql-python" "Bug Tracker" = "https://github.com/databricks/databricks-sql-python/issues" +[tool.poetry.plugins."sqlalchemy.dialects"] +"databricks" = "databricks.sqlalchemy:DatabricksDialect" + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" From 44d3b22a7468e73b64ee4d326c4911a2e8190c98 Mon Sep 17 00:00:00 2001 From: Jesse Whitehouse Date: Fri, 17 Feb 2023 16:46:49 -0600 Subject: [PATCH 3/3] Black source files Signed-off-by: Jesse Whitehouse --- src/databricks/sqlalchemy/dialect/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/databricks/sqlalchemy/dialect/__init__.py b/src/databricks/sqlalchemy/dialect/__init__.py index 58f3094e7..bc30723aa 100644 --- a/src/databricks/sqlalchemy/dialect/__init__.py +++ b/src/databricks/sqlalchemy/dialect/__init__.py @@ -276,7 +276,7 @@ def has_table(self, connection, table_name, schema=None, **kwargs) -> bool: """SQLAlchemy docstrings say dialect providers must implement this method""" schema = schema or "default" - + # DBR >12.x uses underscores in error messages DBR_LTE_12_NOT_FOUND_STRING = "Table or view not found" DBR_GT_12_NOT_FOUND_STRING = "TABLE_OR_VIEW_NOT_FOUND" @@ -285,7 +285,9 @@ def has_table(self, connection, table_name, schema=None, **kwargs) -> bool: res = connection.execute(f"DESCRIBE TABLE {table_name}") return True except DatabaseError as e: - if (DBR_GT_12_NOT_FOUND_STRING in str(e) or DBR_LTE_12_NOT_FOUND_STRING in str(e)): + if DBR_GT_12_NOT_FOUND_STRING in str( + e + ) or DBR_LTE_12_NOT_FOUND_STRING in str(e): return False else: raise e @@ -300,8 +302,7 @@ def get_schema_names(self, connection, **kw): @event.listens_for(Engine, "do_connect") def receive_do_connect(dialect, conn_rec, cargs, cparams): - """Helpful for DS on traffic from clients using SQLAlchemy in particular - """ + """Helpful for DS on traffic from clients using SQLAlchemy in particular""" # Ignore connect invocations that don't use our dialect if not dialect.name == "databricks":