diff --git a/.github/workflows/publish-docs-beta.yml b/.github/workflows/publish-docs-beta.yml new file mode 100644 index 0000000..16e3724 --- /dev/null +++ b/.github/workflows/publish-docs-beta.yml @@ -0,0 +1,35 @@ +name: Build and Deploy Docs Site (for beta) + +on: + workflow_dispatch: + inputs: + version: + description: Document version e.g. 1.1 (beta), 1.2 (beta) + required: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: recursive + + - uses: actions/cache@v2 + with: + key: ${{ github.ref }} + path: .cache + + - name: Install dependencies + run: pip install mkdocs mkdocs-material mkdocs-minify-plugin mike + + - name: Configure Git user + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + + - name: Publish site + run: | + mike deploy --push --update-aliases ${{ inputs.version }} diff --git a/dbterd/adapters/algos/base.py b/dbterd/adapters/algos/base.py index ab24c88..72206a4 100644 --- a/dbterd/adapters/algos/base.py +++ b/dbterd/adapters/algos/base.py @@ -1,6 +1,50 @@ import copy -from dbterd.adapters.meta import Column, Table +import click + +from dbterd.adapters.meta import Column, Ref, Table +from dbterd.constants import ( + DEFAULT_ALGO_RULE, + TEST_META_IGNORE_IN_ERD, + TEST_META_RELATIONSHIP_TYPE, +) +from dbterd.helpers.log import logger + + +def get_tables_from_metadata(data=[], **kwargs): + """Extract tables from dbt metadata + + Args: + data (dict): dbt metadata query result + + Returns: + List[Table]: All parsed tables + """ + tables = [] + table_exposures = get_node_exposures_from_metadata(data=data) + # Model + if "model" in kwargs.get("resource_type", []): + for data_item in data: + for model in data_item.get("models", {}).get("edges", []): + table = get_table_from_metadata( + model_metadata=model, + exposures=table_exposures, + **kwargs, + ) + tables.append(table) + + # Source + if "source" in kwargs.get("resource_type", []): + for data_item in data: + for model in data_item.get("sources", {}).get("edges", []): + table = get_table_from_metadata( + model_metadata=model, + exposures=table_exposures, + **kwargs, + ) + tables.append(table) + + return tables def get_tables(manifest, catalog, **kwargs): @@ -77,7 +121,67 @@ def enrich_tables_from_relationships(tables, relationships): return copied_tables -def get_table(node_name, manifest_node, catalog_node=None, exposures=[], **kwargs): +def get_table_from_metadata(model_metadata, exposures=[], **kwargs) -> Table: + """Construct a single Table object (for Metadata) + + Args: + model_metadata (dict): Metadata model node + exposures (list, optional): List of parsed exposures. Defaults to []. + + Returns: + Table: Parsed table + """ + node_name = model_metadata.get("node", {}).get("uniqueId") + node_description = model_metadata.get("node", {}).get("description") + node_database = model_metadata.get("node", {}).get("database").lower() + node_schema = model_metadata.get("node", {}).get("schema").lower() + node_name_parts = node_name.split(".") + table = Table( + name=get_table_name( + format=kwargs.get("entity_name_format"), + **dict( + resource=node_name_parts[0], + package=node_name_parts[1], + model=node_name_parts[2], + database=node_database, + schema=model_metadata.get("node", {}).get("schema").lower(), + table=( + model_metadata.get("node", {}).get("alias") + or model_metadata.get("node", {}).get("name") + ).lower(), + ), + ), + node_name=node_name, + raw_sql="TODO", + database=node_database, + schema=node_schema, + columns=[], + resource_type=node_name.split(".")[0], + exposures=[ + x.get("exposure_name") for x in exposures if x.get("node_name") == node_name + ], + description=node_description, + ) + + # columns + for column in model_metadata.get("node", {}).get("catalog", {}).get("columns", []): + table.columns.append( + Column( + name=column.get("name", "").lower(), + data_type=column.get("type", "").lower(), + description=column.get("description", ""), + ) + ) + + if not table.columns: + table.columns.append(Column()) + + return table + + +def get_table( + node_name, manifest_node, catalog_node=None, exposures=[], **kwargs +) -> Table: """Construct a single Table object Args: @@ -182,6 +286,33 @@ def get_compiled_sql(manifest_node): return manifest_node.raw_sql # fallback to raw dbt code +def get_node_exposures_from_metadata(data=[], **kwargs): + """Get the mapping of table name and exposure name (for Metadata) + + Args: + data (list, optional): Metadata result list. Defaults to []. + + Returns: + list: List of maping dict {table_name:..., exposure_name=...} + """ + exposures = [] + for data_item in data: + for exposure in data_item.get("exposures", {}).get("edges", []): + name = exposure.get("node", {}).get("name") + parent_nodes = exposure.get("node", {}).get("parents") + for node in parent_nodes: + node_name = node.get("uniqueId", "") + if node_name.split(".")[0] in kwargs.get("resource_type", []): + exposures.append( + dict( + node_name=node_name, + exposure_name=name, + ) + ) + + return exposures + + def get_node_exposures(manifest): """Get the mapping of table name and exposure name @@ -189,7 +320,7 @@ def get_node_exposures(manifest): manifest (dict): dbt manifest json Returns: - dict: Maping dict {table_name:..., exposure_name=...} + list: List of maping dict {table_name:..., exposure_name=...} """ exposures = [] @@ -216,3 +347,264 @@ def get_table_name(format: str, **kwargs) -> str: str: Qualified table name """ return ".".join([kwargs.get(x.lower()) or "KEYNOTFOUND" for x in format.split(".")]) + + +def get_relationships_from_metadata(data=[], **kwargs) -> list[Ref]: + """Extract relationships from Metadata result list on test relationship + + Args: + data (_type_): Metadata result list. Defaults to []. + + Returns: + list[Ref]: List of parsed relationship + """ + refs = [] + rule = get_algo_rule(**kwargs) + + for data_item in data: + for test in data_item.get("tests", {}).get("edges", []): + test_id = test.get("node", {}).get("uniqueId", "") + test_meta = test.get("node", {}).get("meta", {}) + if ( + test_id.startswith("test") + and rule.get("name").lower() in test_id.lower() + and test_meta.get(TEST_META_IGNORE_IN_ERD, "0") == "0" + ): + test_metadata_kwargs = ( + test.get("node", {}).get("testMetadata", {}).get("kwargs", {}) + ) + refs.append( + Ref( + name=test_id, + table_map=get_table_map_from_metadata(test_node=test, **kwargs), + column_map=[ + ( + test_metadata_kwargs.get(rule.get("c_to")) + or "_and_".join( + test_metadata_kwargs.get( + f'{rule.get("c_to")}s', "unknown" + ) + ) + ) + .replace('"', "") + .lower(), + ( + test_metadata_kwargs.get("columnName") + or test_metadata_kwargs.get(rule.get("c_from")) + or "_and_".join( + test_metadata_kwargs.get( + f'{rule.get("c_from")}s', "unknown" + ) + ) + ) + .replace('"', "") + .lower(), + ], + type=get_relationship_type( + test_meta.get(TEST_META_RELATIONSHIP_TYPE, "") + ), + ) + ) + + return get_unique_refs(refs=refs) + + +def get_relationships(manifest, **kwargs): + """Extract relationships from dbt artifacts based on test relationship + + Args: + manifest (dict): Manifest json + + Returns: + List[Ref]: List of parsed relationship + """ + rule = get_algo_rule(**kwargs) + refs = [ + Ref( + name=x, + table_map=get_table_map(test_node=manifest.nodes[x], **kwargs), + column_map=[ + str( + manifest.nodes[x].test_metadata.kwargs.get(rule.get("c_to")) + or "_and_".join( + manifest.nodes[x].test_metadata.kwargs.get( + f'{rule.get("c_to")}s', "unknown" + ) + ) + ) + .replace('"', "") + .lower(), + str( + manifest.nodes[x].test_metadata.kwargs.get("column_name") + or manifest.nodes[x].test_metadata.kwargs.get(rule.get("c_from")) + or "_and_".join( + manifest.nodes[x].test_metadata.kwargs.get( + f'{rule.get("c_from")}s', "unknown" + ) + ) + ) + .replace('"', "") + .lower(), + ], + type=get_relationship_type( + manifest.nodes[x].meta.get(TEST_META_RELATIONSHIP_TYPE, "") + ), + ) + for x in manifest.nodes + if ( + x.startswith("test") + and rule.get("name").lower() in x.lower() + and manifest.nodes[x].meta.get(TEST_META_IGNORE_IN_ERD, "0") == "0" + ) + ] + + return get_unique_refs(refs=refs) + + +def get_unique_refs(refs: list[Ref] = []) -> list[Ref]: + """Remove duplicates in the Relationship list + + Args: + refs (list[Ref], optional): List of parsed relationship. Defaults to []. + + Returns: + list[Ref]: Distinct parsed relationship + """ + if not refs: + return [] + + distinct_list = [refs[0]] + for ref in refs: + distinct_maps = [str((x.table_map, x.column_map)) for x in distinct_list] + if str((ref.table_map, ref.column_map)) not in distinct_maps: + distinct_list.append(ref) + + return distinct_list + + +def get_algo_rule(**kwargs): + """Extract rule from the --algo option + + Args: + kwargs.algo (str): | + Algorithm name and optional rules. + Samples: + - test_relationship + - test_relationship:(name:relationship|c_from:column_name|c_to:field) + - test_relationship:(name:foreign_key|c_from:fk_column_name|c_to:pk_column_name) + - test_relationship:( + name:foreign_key| + c_from:fk_column_name| + c_to:pk_column_name| + t_to:pk_table_name + ) + + Returns: + dict: Rule object ( + name [default 'relationship', use contains], + c_from [default 'column_name'], + c_to [default 'field'], + t_to [default 'to'] + ) + """ + algo_parts = (kwargs.get("algo") or "").replace(" ", "").split(":", 1) + rules, _ = ( + algo_parts[1] if len(algo_parts) > 1 else DEFAULT_ALGO_RULE, + algo_parts[0], + ) + rules = rules[1:-1] # remove brackets + rules = dict(arg.split(":") for arg in rules.split("|")) + return rules + + +def get_table_map_from_metadata(test_node, **kwargs): + """Get the table map with order of [to, from] guaranteed + (for Metadata) + + Args: + test_node (dict): Metadata test node + + Raises: + click.BadParameter: A Ref must have 2 parents + + Returns: + list: [to model, from model] + """ + rule = get_algo_rule(**kwargs) + + test_parents = [] + for parent in test_node.get("node", {}).get("parents", []): + parent_id = parent.get("uniqueId", "") + if parent_id.split(".")[0] in kwargs.get("resource_type", []): + test_parents.append(parent_id) + + if len(test_parents) == 0: + return ["", ""] # return dummies - need to be excluded manually + + if len(test_parents) != 2: + logger.debug(f"Collected test parents: {test_parents}") + raise click.BadParameter( + "Relationship test unexpectedly doesn't have 2 parents" + ) + + test_metadata_to = ( + test_node.get("node", {}) + .get("testMetadata", {}) + .get("kwargs", {}) + .get(rule.get("t_to", "to"), "") + ) + + first_test_parent_parts = test_parents[0].split(".") + first_test_parent_resource_type = ( + "ref" if first_test_parent_parts[0] != "source" else first_test_parent_parts[0] + ) + to_model_possible_values = [ + f"{first_test_parent_resource_type}('{first_test_parent_parts[2]}','{first_test_parent_parts[-1]}')", + f"{first_test_parent_resource_type}('{first_test_parent_parts[-1]}')", + ] + if test_metadata_to in to_model_possible_values: + return test_parents + + return list(reversed(test_parents)) + + +def get_table_map(test_node, **kwargs): + """Get the table map with order of [to, from] guaranteed + + Args: + test_node (dict): Manifest Test node + + Returns: + list: [to model, from model] + """ + map = test_node.depends_on.nodes or [] + rule = get_algo_rule(**kwargs) + to_model = str(test_node.test_metadata.kwargs.get(rule.get("t_to", "to"), {})) + if f'("{map[1].split(".")[-1]}")'.lower() in to_model.replace("'", '"').lower(): + return [map[1], map[0]] + + return map + + +def get_relationship_type(meta: str) -> str: + """Get short form of the relationship type configured in test meta + + Args: + meta (str): meta value + + Returns: + str: | + Short relationship type. Accepted values: '0n','01','11','nn','n1' and '1n'. + And `1n` is default/fallback value + """ + if meta.lower() == "zero-to-many": + return "0n" + if meta.lower() == "zero-to-one": + return "01" + if meta.lower() == "one-to-one": + return "11" + if meta.lower() == "many-to-many": + return "nn" + if meta.lower() == "one-to-many": + return "1n" + return "n1" # "many-to-one" diff --git a/dbterd/adapters/algos/test_relationship.py b/dbterd/adapters/algos/test_relationship.py index 301f3cb..fa93fa9 100644 --- a/dbterd/adapters/algos/test_relationship.py +++ b/dbterd/adapters/algos/test_relationship.py @@ -1,14 +1,52 @@ from dbterd.adapters.algos import base from dbterd.adapters.filter import is_selected_table from dbterd.adapters.meta import Ref -from dbterd.constants import ( - DEFAULT_ALGO_RULE, - TEST_META_IGNORE_IN_ERD, - TEST_META_RELATIONSHIP_TYPE, -) from dbterd.helpers.log import logger +def parse_metadata(data, **kwargs): + """Get all information (tables, relationships) needed for building diagram + (from Metadata) + + Args: + data (dict): metadata dict + + Returns: + Tuple(List[Table], List[Ref]): Info of parsed tables and relationships + """ + tables = [] + relationships = [] + + # Parse Table + tables = base.get_tables_from_metadata(data=data, **kwargs) + + # Apply selection + tables = [ + table + for table in tables + if is_selected_table( + table=table, + select_rules=kwargs.get("select") or [], + resource_types=kwargs.get("resource_type", []), + exclude_rules=kwargs.get("exclude") or [], + ) + ] + + # Parse Ref + relationships = base.get_relationships_from_metadata(data=data, **kwargs) + node_names = [x.node_name for x in tables] + relationships = [ + x + for x in relationships + if x.table_map[0] in node_names and x.table_map[1] in node_names + ] + + logger.info( + f"Collected {len(tables)} table(s) and {len(relationships)} relationship(s)" + ) + return (tables, relationships) + + def parse(manifest, catalog, **kwargs): """Get all information (tables, relationships) needed for building diagram @@ -19,6 +57,10 @@ def parse(manifest, catalog, **kwargs): Returns: Tuple(List[Table], List[Ref]): Info of parsed tables and relationships """ + # Parse metadata + if catalog == "metadata": + return parse_metadata(data=manifest, **kwargs) + # Parse Table tables = base.get_tables(manifest=manifest, catalog=catalog, **kwargs) @@ -35,7 +77,7 @@ def parse(manifest, catalog, **kwargs): ] # Parse Ref - relationships = get_relationships(manifest=manifest, **kwargs) + relationships = base.get_relationships(manifest=manifest, **kwargs) node_names = [x.node_name for x in tables] relationships = [ Ref( @@ -60,142 +102,3 @@ def parse(manifest, catalog, **kwargs): f"Collected {len(tables)} table(s) and {len(relationships)} relationship(s)" ) return (tables, relationships) - - -def get_relationships(manifest, **kwargs): - """Extract relationships from dbt artifacts based on test relationship - - Args: - manifest (dict): Manifest json - - Returns: - List[Ref]: List of parsed relationship - """ - rule = get_algo_rule(**kwargs) - refs = [ - Ref( - name=x, - table_map=get_table_map(test_node=manifest.nodes[x], **kwargs), - column_map=[ - str( - manifest.nodes[x].test_metadata.kwargs.get(rule.get("c_to")) - or "_and_".join( - manifest.nodes[x].test_metadata.kwargs.get( - f'{rule.get("c_to")}s', "unknown" - ) - ) - ) - .replace('"', "") - .lower(), - str( - manifest.nodes[x].test_metadata.kwargs.get("column_name") - or manifest.nodes[x].test_metadata.kwargs.get(rule.get("c_from")) - or "_and_".join( - manifest.nodes[x].test_metadata.kwargs.get( - f'{rule.get("c_from")}s', "unknown" - ) - ) - ) - .replace('"', "") - .lower(), - ], - type=get_relationship_type( - manifest.nodes[x].meta.get(TEST_META_RELATIONSHIP_TYPE, "") - ), - ) - for x in manifest.nodes - if ( - x.startswith("test") - and rule.get("name").lower() in x.lower() - and manifest.nodes[x].meta.get(TEST_META_IGNORE_IN_ERD, "0") == "0" - ) - ] - - # remove duplicates - if refs: - distinct_list = [refs[0]] - for ref in refs: - distinct_maps = [str((x.table_map, x.column_map)) for x in distinct_list] - if str((ref.table_map, ref.column_map)) not in distinct_maps: - distinct_list.append(ref) - - return distinct_list - - return [] - - -def get_algo_rule(**kwargs): - """Extract rule from the --algo option - - Args: - kwargs.algo (str): | - Algorithm name and optional rules. - Samples: - - test_relationship - - test_relationship:(name:relationship|c_from:column_name|c_to:field) - - test_relationship:(name:foreign_key|c_from:fk_column_name|c_to:pk_column_name) - - test_relationship:( - name:foreign_key| - c_from:fk_column_name| - c_to:pk_column_name| - t_to:pk_table_name - ) - - Returns: - dict: Rule object ( - name [default 'relationship', use contains], - c_from [default 'column_name'], - c_to [default 'field'], - t_to [default 'to'] - ) - """ - algo_parts = (kwargs.get("algo") or "").replace(" ", "").split(":", 1) - rules, _ = ( - algo_parts[1] if len(algo_parts) > 1 else DEFAULT_ALGO_RULE, - algo_parts[0], - ) - rules = rules[1:-1] # remove brackets - rules = dict(arg.split(":") for arg in rules.split("|")) - return rules - - -def get_table_map(test_node, **kwargs): - """Get the table map with order of [to, from] guaranteed - - Args: - test_node (dict): Manifest Test node - - Returns: - list: [to model, from model] - """ - map = test_node.depends_on.nodes or [] - rule = get_algo_rule(**kwargs) - to_model = str(test_node.test_metadata.kwargs.get(rule.get("t_to", "to"), {})) - if f'("{map[1].split(".")[-1]}")'.lower() in to_model.replace("'", '"').lower(): - return [map[1], map[0]] - - return map - - -def get_relationship_type(meta: str) -> str: - """Get short form of the relationship type configured in test meta - - Args: - meta (str): meta value - - Returns: - str: | - Short relationship type. Accepted values: '0n','01','11','nn','n1' and '1n'. - And `1n` is default/fallback value - """ - if meta.lower() == "zero-to-many": - return "0n" - if meta.lower() == "zero-to-one": - return "01" - if meta.lower() == "one-to-one": - return "11" - if meta.lower() == "many-to-many": - return "nn" - if meta.lower() == "one-to-many": - return "1n" - return "n1" # "many-to-one" diff --git a/dbterd/adapters/base.py b/dbterd/adapters/base.py index 17d99ce..9aaea71 100644 --- a/dbterd/adapters/base.py +++ b/dbterd/adapters/base.py @@ -5,8 +5,9 @@ from dbterd import default from dbterd.adapters import adapter -from dbterd.adapters.dbt_cloud import DbtCloudArtifact -from dbterd.adapters.dbt_invocation import DbtInvocation +from dbterd.adapters.dbt_cloud.administrative import DbtCloudArtifact +from dbterd.adapters.dbt_cloud.discovery import DbtCloudMetadata +from dbterd.adapters.dbt_core.dbt_invocation import DbtInvocation from dbterd.adapters.filter import has_unsupported_rule from dbterd.helpers import cli_messaging from dbterd.helpers import file as file_handlers @@ -26,16 +27,18 @@ def __init__(self, ctx) -> None: self.dbt: DbtInvocation = None def run(self, **kwargs): - """Main function helps to run by the target strategy""" + """Generate ERD from files""" kwargs = self.evaluate_kwargs(**kwargs) self.__run_by_strategy(**kwargs) + def run_metadata(self, **kwargs): + """Generate ERD from API metadata""" + kwargs = self.evaluate_kwargs(**kwargs) + self.__run_metadata_by_strategy(**kwargs) + def evaluate_kwargs(self, **kwargs) -> dict: """Re-calculate the options - - trigger `dbt ls` for re-calculate the Selection if `--dbt` enabled - - trigger `dbt docs generate` for re-calculate the artifact direction if `--dbt-atu-artifacts` enabled - Raises: click.UsageError: Not Supported exception @@ -43,34 +46,35 @@ def evaluate_kwargs(self, **kwargs) -> dict: dict: kwargs dict """ artifacts_dir, dbt_project_dir = self.__get_dir(**kwargs) - logger.info(f"Using dbt project dir at: {dbt_project_dir}") + command = self.ctx.command.name select = list(kwargs.get("select")) or [] exclude = list(kwargs.get("exclude")) or [] - if kwargs.get("dbt"): - self.dbt = DbtInvocation( - dbt_project_dir=kwargs.get("dbt_project_dir"), - dbt_target=kwargs.get("dbt_target"), - ) - select = self.__get_selection(**kwargs) - exclude = [] - - if kwargs.get("dbt_auto_artifacts"): - self.dbt.get_artifacts_for_erd() + unsupported, rule = has_unsupported_rule( + rules=select.extend(exclude) if exclude else select + ) + if unsupported: + message = f"Unsupported Selection found: {rule}" + logger.error(message) + raise click.UsageError(message) + + if command == "run": + if kwargs.get("dbt"): + logger.info(f"Using dbt project dir at: {dbt_project_dir}") + self.dbt = DbtInvocation( + dbt_project_dir=kwargs.get("dbt_project_dir"), + dbt_target=kwargs.get("dbt_target"), + ) + select = self.__get_selection(**kwargs) + exclude = [] + + if kwargs.get("dbt_auto_artifacts"): + self.dbt.get_artifacts_for_erd() + artifacts_dir = f"{dbt_project_dir}/target" + elif kwargs.get("dbt_cloud"): artifacts_dir = f"{dbt_project_dir}/target" - elif kwargs.get("dbt_cloud"): - artifacts_dir = f"{dbt_project_dir}/target" - DbtCloudArtifact(**kwargs).get(artifacts_dir=artifacts_dir) - else: - unsupported, rule = has_unsupported_rule( - rules=select.extend(exclude) if exclude else select - ) - if unsupported: - message = f"Unsupported Selection found: {rule}" - logger.error(message) - raise click.UsageError(message) + logger.info(f"Using dbt artifact dir at: {artifacts_dir}") - logger.info(f"Using dbt artifact dir at: {artifacts_dir}") kwargs["artifacts_dir"] = artifacts_dir kwargs["dbt_project_dir"] = dbt_project_dir kwargs["select"] = select @@ -144,8 +148,12 @@ def __read_catalog(self, cp: str, cv: int = None): with cli_messaging.handle_read_errors(self.filename_catalog): return file_handlers.read_catalog(path=cp, version=cv) - def __run_by_strategy(self, **kwargs): - """Read artifacts and export the diagram file following the target""" + def __get_operation(self, kwargs): + """Get target's operation (aka.`parse` function) + + Returns: + func: Operation function + """ target = adapter.load_executor(name=kwargs["target"]) # import {target} run_operation_dispatcher = getattr(target, "run_operation_dispatcher") operation_default = getattr(target, "run_operation_default") @@ -154,6 +162,31 @@ def __run_by_strategy(self, **kwargs): operation_default, ) + return operation + + def __save_result(self, path, data): + """Save ERD data to file + + Args: + path (str): Output file path + data (dict): ERD data + + Raises: + click.FileError: Can not save the file + """ + try: + with open(f"{path}/{data[0]}", "w") as f: + logger.info(path) + f.write(data[1]) + except Exception as e: + logger.error(str(e)) + raise click.FileError(f"Could not save the output: {str(e)}") + + def __run_by_strategy(self, **kwargs): + """Local File - Read artifacts and export the diagram file following the target""" + if kwargs.get("dbt_cloud"): + DbtCloudArtifact(**kwargs).get(artifacts_dir=kwargs.get("artifacts_dir")) + manifest = self.__read_manifest( mp=kwargs.get("artifacts_dir"), mv=kwargs.get("manifest_version"), @@ -163,12 +196,14 @@ def __run_by_strategy(self, **kwargs): cv=kwargs.get("catalog_version"), ) + operation = self.__get_operation(kwargs) result = operation(manifest=manifest, catalog=catalog, **kwargs) - path = kwargs.get("output") + f"/{result[0]}" - try: - with open(path, "w") as f: - logger.info(path) - f.write(result[1]) - except Exception as e: - logger.error(str(e)) - raise click.FileError(f"Could not save the output: {str(e)}") + self.__save_result(path=kwargs.get("output"), data=result) + + def __run_metadata_by_strategy(self, **kwargs): + """Metadata - Read artifacts and export the diagram file following the target""" + data = DbtCloudMetadata(**kwargs).query_erd_data() + operation = self.__get_operation(kwargs) + + result = operation(manifest=data, catalog="metadata", **kwargs) + self.__save_result(path=kwargs.get("output"), data=result) diff --git a/dbterd/adapters/dbt_cloud/__init__.py b/dbterd/adapters/dbt_cloud/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dbterd/adapters/dbt_cloud.py b/dbterd/adapters/dbt_cloud/administrative.py similarity index 96% rename from dbterd/adapters/dbt_cloud.py rename to dbterd/adapters/dbt_cloud/administrative.py index 7d7d57b..ea7b6f3 100644 --- a/dbterd/adapters/dbt_cloud.py +++ b/dbterd/adapters/dbt_cloud/administrative.py @@ -70,10 +70,10 @@ def download_artifact(self, artifact: str, artifacts_dir: str) -> bool: bool: True is success, False if any errors """ artifact_api_endpoint = getattr(self, f"{artifact}_api_endpoint") - logger.info(f"Dowloading...[URL: {artifact_api_endpoint}]") + logger.debug(f"Dowloading...[URL: {artifact_api_endpoint}]") try: r = requests.get(url=artifact_api_endpoint, headers=self.request_headers) - logger.info(f"Completed [status: {r.status_code}]") + logger.debug(f"Completed [status: {r.status_code}]") if r.status_code != 200: logger.error(f"Failed to retrieve artifacts [error: {vars(r)}]") diff --git a/dbterd/adapters/dbt_cloud/discovery.py b/dbterd/adapters/dbt_cloud/discovery.py new file mode 100644 index 0000000..a000ae0 --- /dev/null +++ b/dbterd/adapters/dbt_cloud/discovery.py @@ -0,0 +1,169 @@ +from dbterd.adapters.dbt_cloud.graphql import GraphQLHelper +from dbterd.adapters.dbt_cloud.query import Query +from dbterd.helpers.log import logger + + +class DbtCloudMetadata: + """Class for managing the Discovery API via GraphQL""" + + def __init__(self, **kwargs) -> None: + """Initialize the GraphQL Helper and the corresponding ERD query""" + self.graphql = GraphQLHelper(**kwargs) + self.environment_id = kwargs.get("dbt_cloud_environment_id") + self.erd_query = Query().take( + file_path=kwargs.get("dbt_cloud_query_file_path", None) + ) + self.last_cursor = {} + + def query_erd_data(self, page_size: int = 500, poll_until_end: bool = True): + """Perform query to get the ERD dict result + + Args: + page_size (int, optional): Pagination size. Defaults to 500. + poll_until_end (bool, optional): Decide to pull all data. Defaults to True. + + Returns: + list[dict]: Metadata result list + """ + variables = { + "environment_id": self.environment_id, + "model_first": page_size, + "source_first": page_size, + "exposure_first": page_size, + "test_first": page_size, + } + data = [ + self.extract_data( + graphql_data=self.graphql.query(query=self.erd_query, **variables) + ) + ] + self.show_counts(data=data[-1]) + if not poll_until_end: + return data + + while any( + [ + self.has_data(data=data[-1], resource_type="model"), + self.has_data(data=data[-1], resource_type="source"), + self.has_data(data=data[-1], resource_type="exposure"), + self.has_data(data=data[-1], resource_type="test"), + ] + ): + variables["model_after"] = self.get_last_cursor( + data=data[-1], resource_type="model" + ) + variables["source_after"] = self.get_last_cursor( + data=data[-1], resource_type="source" + ) + variables["exposure_after"] = self.get_last_cursor( + data=data[-1], resource_type="exposure" + ) + variables["test_after"] = self.get_last_cursor( + data=data[-1], resource_type="test" + ) + + self.save_last_cursor(data=data[-1]) + data.append( + self.extract_data( + graphql_data=self.graphql.query(query=self.erd_query, **variables) + ) + ) + self.show_counts(data=data[-1]) + + return data + + def extract_data(self, graphql_data: dict): + """Extract the core nested dict only: + environment: + applied: <-- HERE + models + sources + tests + exposures + + Args: + graphql_data (dict): Metadata result + + Returns: + dict: Applied data + """ + return graphql_data.get("environment", {}).get("applied", {}) + + def has_data(self, data, resource_type: str = "model") -> bool: + """Check if there is still having data to poll more given the resource type. + + Resource types: + - model + - source + - exposure + - test + + Args: + data (dict): Metadata result + resource_type (str, optional): Resource type. Defaults to "model". + + Returns: + bool: True if has data need polling more + """ + return ( + data.get(f"{resource_type}s", {}) + .get("pageInfo", {}) + .get("hasNextPage", False) + ) + + def save_last_cursor( + self, data, resource_types=["model", "source", "exposure", "test"] + ): + """Save last poll's cursor of all resource types. + + Args: + data (dict): Metadata result + resource_types (list, optional): | + Resource types. + Defaults to ["model", "source", "exposure", "test"]. + """ + for resource_type in resource_types: + self.last_cursor[resource_type] = self.get_last_cursor( + data=data, resource_type=resource_type + ) or self.last_cursor.get(resource_type, None) + + def get_last_cursor(self, data, resource_type: str = "model") -> str: + """Retrieve the last cursor of given resource type + + Args: + data (dict): Metadata result + resource_type (str, optional): Resource type. Defaults to "model". + + Returns: + str: Cursor value + """ + return ( + data.get(f"{resource_type}s", {}).get("pageInfo", {}).get("endCursor", None) + ) or (self.last_cursor.get(f"{resource_type}", None)) + + def get_count(self, data, resource_type: str = "model") -> int: + """Get metadata result count of given resource type + + Args: + data (dict): Metadata result + resource_type (str, optional): Resource type. Defaults to "model". + + Returns: + int: Number of metadata nodes given resource type + """ + return len(data.get(f"{resource_type}s", {}).get("edges", [])) + + def show_counts(self, data, resource_types=["model", "source", "exposure", "test"]): + """Print the metadata result count for all resource types + + Args: + data (dict): Metadata result + resource_types (list, optional): | + Resource types. + Defaults to ["model", "source", "exposure", "test"]. + """ + results = [ + f"{self.get_count(data=data, resource_type=x)} {x}(s)" + for x in resource_types + ] + logger.info(f"Metadata result: {', '.join(results)}") diff --git a/dbterd/adapters/dbt_cloud/graphql.py b/dbterd/adapters/dbt_cloud/graphql.py new file mode 100644 index 0000000..2e8fb90 --- /dev/null +++ b/dbterd/adapters/dbt_cloud/graphql.py @@ -0,0 +1,57 @@ +import requests + +from dbterd.helpers.log import logger + + +class GraphQLHelper: + """GraphQL Helper class""" + + def __init__(self, **kwargs) -> None: + """Initilize the required inputs: + - Host URL + - Bearer Token + """ + self.host_url = kwargs.get("dbt_cloud_host_url") + self.service_token = kwargs.get("dbt_cloud_service_token") + + @property + def request_headers(self) -> dict: + """API Header""" + return { + "authorization": f"Bearer {self.service_token}", + "content-type": "application/json", + } + + @property + def api_endpoint(self) -> dict: + """Base GraphQL API endpoint""" + return f"https://{self.host_url}/graphql/" + + def query(self, query: str, **variables): + """POST Graph QL query + + Args: + query (str): query string + + Returns: + dict: Query data responsed. None if any exceptions + """ + try: + logger.debug( + f"Getting erd data...[URL: {self.api_endpoint}, VARS: {variables}]" + ) + r = requests.post( + self.api_endpoint, + headers=self.request_headers, + json={"query": query, "variables": variables}, + ) + logger.debug(f"Completed [status: {r.status_code}]") + + if r.status_code != 200: + logger.error(f"Failed to query [error: {vars(r)}]") + return None + except Exception as e: + logger.error(f"Error occurred while querying [error: {str(e)}]") + return None + + return r.json().get("data") diff --git a/dbterd/adapters/dbt_cloud/include/erd_query.gql b/dbterd/adapters/dbt_cloud/include/erd_query.gql new file mode 100644 index 0000000..02c76c3 --- /dev/null +++ b/dbterd/adapters/dbt_cloud/include/erd_query.gql @@ -0,0 +1,80 @@ +query ( + $environment_id: BigInt!, + $model_first: Int!, + $model_after: String, + $source_first: Int!, + $source_after: String, + $exposure_first: Int!, + $exposure_after: String, + $test_first: Int!, + $test_after: String +) { + environment(id: $environment_id){ + applied { + models(first: $model_first, after: $model_after){ + edges { + node { + uniqueId, name, description, + database, schema, alias, + catalog {columns {name, description, type}}, + } + } + pageInfo { + startCursor, + endCursor, + hasNextPage + } + totalCount + } + sources(first: $source_first, after: $source_after){ + edges { + node { + uniqueId, name, description, + database, schema, + catalog {columns {name, description, type}}, + } + } + pageInfo { + startCursor, + endCursor, + hasNextPage + } + totalCount + } + exposures(first: $exposure_first, after: $exposure_after) { + edges { + node { + uniqueId, name, description, + parents { uniqueId } + } + } + pageInfo { + startCursor, + endCursor, + hasNextPage + } + totalCount + } + tests(first: $test_first, after: $test_after) { + edges { + node { + uniqueId, + columnName, + meta, + testMetadata { + name, + kwargs + }, + parents { uniqueId, resourceType } + } + } + pageInfo { + startCursor, + endCursor, + hasNextPage + } + totalCount + } + } + } +} diff --git a/dbterd/adapters/dbt_cloud/query.py b/dbterd/adapters/dbt_cloud/query.py new file mode 100644 index 0000000..bb1705a --- /dev/null +++ b/dbterd/adapters/dbt_cloud/query.py @@ -0,0 +1,42 @@ +import os + +from dbterd.helpers.log import logger + + +class Query: + """ERD Query file helper""" + + def __init__(self) -> None: + """Initilize the required input: + - Query directory + """ + self.dir = f"{os.path.dirname(os.path.realpath(__file__))}/include" + + def take(self, file_path: str = None) -> str: + """Read the given file path and return the content as the query string + + Args: + file_path (str, optional): File path. Defaults to `(query dir)/erd_query.gql` + + Returns: + str: Query string + """ + query_file = file_path or f"{self.dir}/erd_query.gql" + logger.info(f"Looking for the query in: {query_file}") + return self.get_file_content(file_path=query_file) + + def get_file_content(self, file_path: str) -> str: + """Read content of the given file path + + Args: + file_path (str): Query file path + + Returns: + str: Query string + """ + try: + with open(file_path, "r") as content: + return content.read() + except Exception as e: + logger.error(f"Cannot read file: [{file_path}] with error: {str(e)}") + return None diff --git a/dbterd/adapters/dbt_core/__init__.py b/dbterd/adapters/dbt_core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dbterd/adapters/dbt_invocation.py b/dbterd/adapters/dbt_core/dbt_invocation.py similarity index 100% rename from dbterd/adapters/dbt_invocation.py rename to dbterd/adapters/dbt_core/dbt_invocation.py diff --git a/dbterd/cli/main.py b/dbterd/cli/main.py index 6cd5fd9..e92ddd2 100644 --- a/dbterd/cli/main.py +++ b/dbterd/cli/main.py @@ -54,22 +54,35 @@ def dbterd(ctx, **kwargs): logger.info(f"Run with dbterd=={__version__}") +# dbterd run +@dbterd.command(name="run") +@click.pass_context +@params.run_params +def run(ctx, **kwargs): + """ + Generate ERD file from reading dbt artifact files, + optionally downloading from Administrative API (dbt Cloud) befor hands + """ + Executor(ctx).run(**kwargs) + + +# dbterd run_metadata +@dbterd.command(name="run-metadata") +@click.pass_context +@params.run_metadata_params +def run_metadata(ctx, **kwargs): + """Generate ERD file from reading Discovery API (dbt Cloud)""" + Executor(ctx).run_metadata(**kwargs) + + # dbterd debug @dbterd.command(name="debug") @click.pass_context -@params.common_params -def debug(ctx, **kwargs): +@params.run_params +@params.run_metadata_params +def debugx(ctx, **kwargs): """Inspect the hidden magics""" logger.info("**Arguments used**") logger.debug(jsonify.to_json(kwargs)) logger.info("**Arguments evaluated**") logger.debug(jsonify.to_json(Executor(ctx).evaluate_kwargs(**kwargs))) - - -# dbterd run -@dbterd.command(name="run") -@click.pass_context -@params.common_params -def run(ctx, **kwargs): - """Run the convert""" - Executor(ctx).run(**kwargs) diff --git a/dbterd/cli/params.py b/dbterd/cli/params.py index 21a3c4a..934f526 100644 --- a/dbterd/cli/params.py +++ b/dbterd/cli/params.py @@ -7,21 +7,6 @@ def common_params(func): - @click.option( - "--artifacts-dir", - "-ad", - help="Specified the path to dbt artifact directory which known as /target directory", - default="", - type=click.STRING, - ) - @click.option( - "--output", - "-o", - help="Output the result file. Default to the cwd/target", - default=default.default_output_path(), - show_default=True, - type=click.STRING, - ) @click.option( "--select", "-s", @@ -46,6 +31,14 @@ def common_params(func): show_default=True, type=click.STRING, ) + @click.option( + "--resource-type", + "-rt", + help="Specified dbt resource type(seed, model, source, snapshot),default:model, use examples, -rt model -rt source", + default=["model"], + multiple=True, + type=click.STRING, + ) @click.option( "--algo", "-a", @@ -54,6 +47,38 @@ def common_params(func): show_default=True, type=click.STRING, ) + @click.option( + "--entity-name-format", + "-enf", + help="Specified the format of the entity node's name", + default="resource.package.model", + show_default=True, + type=click.STRING, + ) + @click.option( + "--output", + "-o", + help="Output the result file. Default to the cwd/target", + default=default.default_output_path(), + show_default=True, + type=click.STRING, + ) + @functools.wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) # pragma: no cover + + return wrapper + + +def run_params(func): + @common_params + @click.option( + "--artifacts-dir", + "-ad", + help="Specified the path to dbt artifact directory which known as /target directory", + default="", + type=click.STRING, + ) @click.option( "--manifest-version", "-mv", @@ -68,14 +93,6 @@ def common_params(func): default=None, type=click.STRING, ) - @click.option( - "--resource-type", - "-rt", - help="Specified dbt resource type(seed, model, source, snapshot),default:model, use examples, -rt model -rt source", - default=["model"], - multiple=True, - type=click.STRING, - ) @click.option( "--dbt", help="Flag to indicate the Selecton to follow dbt's one leveraging Programmatic Invocation", @@ -105,14 +122,6 @@ def common_params(func): default=False, show_default=True, ) - @click.option( - "--entity-name-format", - "-enf", - help="Specified the format of the entity node's name", - default="resource.package.model", - show_default=True, - type=click.STRING, - ) @click.option( "--dbt-cloud", help=( @@ -185,3 +194,51 @@ def wrapper(*args, **kwargs): return func(*args, **kwargs) # pragma: no cover return wrapper + + +def run_metadata_params(func): + @common_params + @click.option( + "--dbt-cloud-host-url", + help=( + "Configure dbt Cloud's Host URL. " + "Try to get OS environment variable (DBTERD_DBT_CLOUD_HOST_URL) if not specified. " + "Sample dbt Cloud Run URL: " + "https:///deploy//projects/irrelevant/runs/" + ), + default=os.environ.get("DBTERD_DBT_CLOUD_HOST_URL", "cloud.getdbt.com"), + show_default=True, + ) + @click.option( + "--dbt-cloud-service-token", + help=( + "Configure dbt Service Token (Permissions: Job Admin). " + "Try to get OS environment variable (DBTERD_DBT_CLOUD_SERVICE_TOKEN) if not specified. " + "Visit https://docs.getdbt.com/docs/dbt-cloud-apis/service-tokens to see how to generate it. " + ), + default=os.environ.get("DBTERD_DBT_CLOUD_SERVICE_TOKEN"), + show_default=True, + ) + @click.option( + "--dbt-cloud-environment-id", + help=( + "Configure dbt Cloud Environment ID - Used for Metadata (Discovery) API. " + "Try to get OS environment variable (DBTERD_DBT_CLOUD_ENVIRONMENT_ID) if not specified." + ), + default=os.environ.get("DBTERD_DBT_CLOUD_ENVIRONMENT_ID"), + show_default=True, + ) + @click.option( + "--dbt-cloud-query-file-path", + help=( + "Configure dbt Cloud GraphQL query file path - Used for Metadata (Discovery) API. " + "Try to get OS environment variable (DBTERD_DBT_CLOUD_QUERY_FILE_PATH) if not specified." + ), + default=os.environ.get("DBTERD_DBT_CLOUD_QUERY_FILE_PATH"), + show_default=True, + ) + @functools.wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) # pragma: no cover + + return wrapper diff --git a/docs/nav/guide/cli-references.md b/docs/nav/guide/cli-references.md index d3a8a63..fa41bce 100644 --- a/docs/nav/guide/cli-references.md +++ b/docs/nav/guide/cli-references.md @@ -387,6 +387,17 @@ Check [Download artifacts from a Job Run](./dbt-cloud/download-artifact-from-a-j dbterd run --dbt-cloud --select wildcard:*transaction* ``` +## dbterd run-metadata + +Command to generate diagram-as-a-code file by connecting to dbt Cloud Discovery API using GraphQL connection + +**Examples:** +=== "CLI" + + ```bash + dbterd run-metadata [-t dbml or -t mermaid] + ``` + ## dbterd debug Shows hidden configured values, which will help us to see what configs are passed into and how they are evaluated to be used. diff --git a/docs/nav/guide/dbt-cloud/read-artifact-from-an-environment.md b/docs/nav/guide/dbt-cloud/read-artifact-from-an-environment.md new file mode 100644 index 0000000..84d6796 --- /dev/null +++ b/docs/nav/guide/dbt-cloud/read-artifact-from-an-environment.md @@ -0,0 +1,92 @@ +# Read the latest artifacts from an environment + +This is a guideline on how to query the dbt cloud metadata given an environment by using [dbt CLoud Discovery API](https://docs.getdbt.com/docs/dbt-cloud-apis/discovery-api). It's neither not requiring `JOB ID` nor `JOB RUN ID`, this is the dbt Cloud's `ENVIRONMENT ID`. Especially, with this method, `dbterd` doesn't require to download files before hands anymore, the ERD will be generated on fly 🚀. + +`dbterd` is now understanding GraphQL connection which is exposed by dbt CLoud Discovery API endpoint: + +```log +https://metadata.YOUR_ACCESS_URL/graphql +``` + +> Replace `{YOUR_ACCESS_URL}` with the appropriate [Access URL](https://docs.getdbt.com/docs/cloud/about-cloud/regions-ip-addresses) for your region and plan + +!!! note "Prerequisites" + - dbt Cloud [multi-tenant](https://docs.getdbt.com/docs/cloud/about-cloud/tenancy#multi-tenant) or [single tenant](https://docs.getdbt.com/docs/cloud/about-cloud/tenancy#single-tenant) account ☁️ + - You must be on a [Team or Enterprise plan](https://www.getdbt.com/pricing/) 💰 + - Your projects must be on dbt version 1.0 or later 🏃 + +The assumption is that you've already get the dbt Cloud project ready and is having at least 1 environment, and 1 job run successfully in this environment. + +## 1. Prepare the environment variables + +As mentioned above, the API Endpoint will look like: + +```log +https://metadata.YOUR_ACCESS_URL/graphql +``` + +For example, if your multi-tenant region is North America, your endpoint is `https://metadata.cloud.getdbt.com/graphql`. If your multi-tenant region is EMEA, your endpoint is `https://metadata.emea.dbt.com/graphql`. + +And the dbt Cloud's Environment will have the URL constructed as: + +```log +https:///deploy/irrelevant/projects/irrelevant/environments/ +``` + +In the above: + +| URL Part | Environment Variable | CLI Option | Description | +|-------------------|---------------------------------|---------------------------|---------------------------------------------------------------------------| +| `host_url` | `DBTERD_DBT_CLOUD_HOST_URL` | `--dbt-cloud-host-url` | Host URL (also known as [Access URL](https://docs.getdbt.com/docs/cloud/about-cloud/regions-ip-addresses)) with prefix of `metadata.` | +| `environment_id` | `DBTERD_DBT_CLOUD_ENVIRONMENT_ID` | `--dbt-cloud-environment-id` | dbt Cloud environment ID | + +Besides, we need another one which is very important, the service token: + +- Go to **Account settings** / **Service tokens**. Click _+ New token_ +- Enter _Service token name_ e.g. "ST_dbterd_metadata" +- Click _Add_ and select `Metadata Only` permission. Optionally, select the right project or all by default +- Click _Save_ +- Copy token & Pass it to the Environment Variable (`DBTERD_DBT_CLOUD_SERVICE_TOKEN`) or the CLI Option (`--dbt-cloud-service-token`) + +Finally, fill in `your_value` and execute the (Linux or Macos) command below: + +```bash +export DBTERD_DBT_CLOUD_HOST_URL=your_value e.g. metadata.cloud.getdbt.com +export DBTERD_DBT_CLOUD_SERVICE_TOKEN=your_value +export DBTERD_DBT_CLOUD_ENVIRONMENT_ID=your_value +``` + +Or in Powershell: + +```bash +$env:DBTERD_DBT_CLOUD_HOST_URL="your_value" +$env:DBTERD_DBT_CLOUD_SERVICE_TOKEN="your_value" +$env:DBTERD_DBT_CLOUD_ENVIRONMENT_ID="your_value" +``` + +## 2. Genrate ERD file + +We're going to use a new command as `dbterd run-metadata` to tell `dbterd` to use dbt Cloud Discovery API with all above variables. + +The command will be looks like: + +```bash +dbterd run-metadata [-s ] +``` + +> Behind the scenes, it will try use to the ERD GraphQL query buit-in at [include/erd_query.gql](https://github.com/datnguye/dbterd/tree/main/dbterd/adapters/dbt_cloud/include/erd_query.gql) + +and then, here is the sample console log: + +```log +2024-02-03 19:57:57,514 - dbterd - INFO - Run with dbterd==1.0.0 (main.py:54) +2024-02-03 19:57:57,515 - dbterd - INFO - Looking for the query in: (hidden)/dbterd/adapters/dbt_cloud/include/erd_query.gql (query.py:25) +2024-02-03 19:57:57,516 - dbterd - DEBUG - Getting erd data...[URL: https://metadata.cloud.getdbt.com/graphql/, VARS: {'environment_id': '(hidden)', 'model_first': 500, 'source_first': 500, 'exposure_first': 500, 'test_first': 500}] (graphql.py:40) +2024-02-03 19:57:58,865 - dbterd - DEBUG - Completed [status: 200] (graphql.py:48) +2024-02-03 19:57:58,868 - dbterd - INFO - Metadata result: 5 model(s), 2 source(s), 1 exposure(s), 21 test(s) (discovery.py:169) +2024-02-03 19:57:58,880 - dbterd - INFO - Collected 5 table(s) and 1 relationship(s) (test_relationship.py:44) +2024-02-03 19:57:58,881 - dbterd - INFO - (hidden)\target (base.py:179) +``` + + +Voila! Happy ERD _with dbt Cloud Metadata_ 🎉! diff --git a/mkdocs.yml b/mkdocs.yml index 1ac070e..7e0f60f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,6 +26,7 @@ nav: - dbt Cloud: - Download artifacts from a Job Run: nav/guide/dbt-cloud/download-artifact-from-a-job-run.md - Download the latest artifacts from a Job: nav/guide/dbt-cloud/download-artifact-from-a-job.md + - Read the latest artifacts from an environment: nav/guide/dbt-cloud/read-artifact-from-an-environment.md - Contribution Guideline ❤️: nav/development/contributing-guide.md - License: license.md - Change Log ↗️: https://github.com/datnguye/dbterd/releases" target="_blank diff --git a/poetry.lock b/poetry.lock index 54ce30b..76f24ba 100644 --- a/poetry.lock +++ b/poetry.lock @@ -421,13 +421,13 @@ test = ["black (==21.9b0)", "flake8 (>=3.8.3,<4.0.0)", "isort (>=5.0.6,<6.0.0)", [[package]] name = "dbt-core" -version = "1.7.4" +version = "1.7.6" description = "With dbt, data analysts and engineers can build analytics the way engineers build applications." optional = false python-versions = ">=3.8" files = [ - {file = "dbt-core-1.7.4.tar.gz", hash = "sha256:769b95949210cb0d1eafdb7be48b01e59984650403f86510fdee65bd0f70f76d"}, - {file = "dbt_core-1.7.4-py3-none-any.whl", hash = "sha256:50050ae44fe9bad63e1b639810ed3629822cdc7a2af0eff6e08461c94c4527c0"}, + {file = "dbt-core-1.7.6.tar.gz", hash = "sha256:8a8d14feedc695fe4c57e772a0e649e02ec37b7427d48420f4de90214fa34e4e"}, + {file = "dbt_core-1.7.6-py3-none-any.whl", hash = "sha256:b2c6b6b75650a8a905f9fa6391d64b8f8db779f5ff746547964820f539ce903e"}, ] [package.dependencies] @@ -482,18 +482,18 @@ files = [ [[package]] name = "dbt-postgres" -version = "1.7.4" +version = "1.7.6" description = "The postgres adapter plugin for dbt (data build tool)" optional = false python-versions = ">=3.8" files = [ - {file = "dbt-postgres-1.7.4.tar.gz", hash = "sha256:16185b8de36d1a2052a2e4b85512306ab55085b1ea323a353d0dc3628473208d"}, - {file = "dbt_postgres-1.7.4-py3-none-any.whl", hash = "sha256:d414b070ca5e48925ea9ab12706bbb9e2294f7d4509c28e7af42268596334044"}, + {file = "dbt-postgres-1.7.6.tar.gz", hash = "sha256:c0c7f0cf66194aa2f65c51332bc26264640e3f2dd124ebb2775c4cd2ec309f0b"}, + {file = "dbt_postgres-1.7.6-py3-none-any.whl", hash = "sha256:1b2edf3c8b4f5cd3c6c1ed6aa2067dac4f8209b475d0caa146f38a8b5524f1b6"}, ] [package.dependencies] agate = "*" -dbt-core = "1.7.4" +dbt-core = "1.7.6" psycopg2-binary = ">=2.8,<3.0" [[package]] @@ -700,13 +700,13 @@ files = [ [[package]] name = "jsonschema" -version = "4.20.0" +version = "4.21.1" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.20.0-py3-none-any.whl", hash = "sha256:ed6231f0429ecf966f5bc8dfef245998220549cbbcf140f913b7464c52c3b6b3"}, - {file = "jsonschema-4.20.0.tar.gz", hash = "sha256:4f614fd46d8d61258610998997743ec5492a648b33cf478c1ddc23ed4598a5fa"}, + {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, + {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, ] [package.dependencies] @@ -796,71 +796,71 @@ testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.4" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, + {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, ] [[package]] @@ -974,13 +974,13 @@ min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-imp [[package]] name = "mkdocs-material" -version = "9.5.3" +version = "9.5.5" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.3-py3-none-any.whl", hash = "sha256:76c93a8525cceb0b395b9cedab3428bf518cf6439adef2b940f1c1574b775d89"}, - {file = "mkdocs_material-9.5.3.tar.gz", hash = "sha256:5899219f422f0a6de784232d9d40374416302ffae3c160cacc72969fcc1ee372"}, + {file = "mkdocs_material-9.5.5-py3-none-any.whl", hash = "sha256:ac50b2431a79a3b160fdefbba37c9132485f1a69166aba115ad49fafdbbbc5df"}, + {file = "mkdocs_material-9.5.5.tar.gz", hash = "sha256:4480d9580faf42fed0123d0465502bfc1c0c239ecc9c4d66159cf0459ea1b4ae"}, ] [package.dependencies] @@ -998,7 +998,7 @@ requests = ">=2.26,<3.0" [package.extras] git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2,<2.0)"] -imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=9.4,<10.0)"] +imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] [[package]] @@ -1219,13 +1219,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1404,47 +1404,47 @@ files = [ [[package]] name = "pydantic" -version = "1.10.13" +version = "1.10.14" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737"}, - {file = "pydantic-1.10.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01"}, - {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548"}, - {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8"}, - {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69"}, - {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17"}, - {file = "pydantic-1.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f"}, - {file = "pydantic-1.10.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653"}, - {file = "pydantic-1.10.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe"}, - {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9"}, - {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80"}, - {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580"}, - {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0"}, - {file = "pydantic-1.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0"}, - {file = "pydantic-1.10.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4b03e42ec20286f052490423682016fd80fda830d8e4119f8ab13ec7464c0132"}, - {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f59ef915cac80275245824e9d771ee939133be38215555e9dc90c6cb148aaeb5"}, - {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a1f9f747851338933942db7af7b6ee8268568ef2ed86c4185c6ef4402e80ba8"}, - {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:97cce3ae7341f7620a0ba5ef6cf043975cd9d2b81f3aa5f4ea37928269bc1b87"}, - {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854223752ba81e3abf663d685f105c64150873cc6f5d0c01d3e3220bcff7d36f"}, - {file = "pydantic-1.10.13-cp37-cp37m-win_amd64.whl", hash = "sha256:b97c1fac8c49be29486df85968682b0afa77e1b809aff74b83081cc115e52f33"}, - {file = "pydantic-1.10.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c958d053453a1c4b1c2062b05cd42d9d5c8eb67537b8d5a7e3c3032943ecd261"}, - {file = "pydantic-1.10.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c5370a7edaac06daee3af1c8b1192e305bc102abcbf2a92374b5bc793818599"}, - {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6f6e7305244bddb4414ba7094ce910560c907bdfa3501e9db1a7fd7eaea127"}, - {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3a3c792a58e1622667a2837512099eac62490cdfd63bd407993aaf200a4cf1f"}, - {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c636925f38b8db208e09d344c7aa4f29a86bb9947495dd6b6d376ad10334fb78"}, - {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:678bcf5591b63cc917100dc50ab6caebe597ac67e8c9ccb75e698f66038ea953"}, - {file = "pydantic-1.10.13-cp38-cp38-win_amd64.whl", hash = "sha256:6cf25c1a65c27923a17b3da28a0bdb99f62ee04230c931d83e888012851f4e7f"}, - {file = "pydantic-1.10.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6"}, - {file = "pydantic-1.10.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691"}, - {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd"}, - {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1"}, - {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96"}, - {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d"}, - {file = "pydantic-1.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d"}, - {file = "pydantic-1.10.13-py3-none-any.whl", hash = "sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687"}, - {file = "pydantic-1.10.13.tar.gz", hash = "sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340"}, + {file = "pydantic-1.10.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4fcec873f90537c382840f330b90f4715eebc2bc9925f04cb92de593eae054"}, + {file = "pydantic-1.10.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e3a76f571970fcd3c43ad982daf936ae39b3e90b8a2e96c04113a369869dc87"}, + {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d886bd3c3fbeaa963692ef6b643159ccb4b4cefaf7ff1617720cbead04fd1d"}, + {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:798a3d05ee3b71967844a1164fd5bdb8c22c6d674f26274e78b9f29d81770c4e"}, + {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:23d47a4b57a38e8652bcab15a658fdb13c785b9ce217cc3a729504ab4e1d6bc9"}, + {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9f674b5c3bebc2eba401de64f29948ae1e646ba2735f884d1594c5f675d6f2a"}, + {file = "pydantic-1.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:24a7679fab2e0eeedb5a8924fc4a694b3bcaac7d305aeeac72dd7d4e05ecbebf"}, + {file = "pydantic-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d578ac4bf7fdf10ce14caba6f734c178379bd35c486c6deb6f49006e1ba78a7"}, + {file = "pydantic-1.10.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa7790e94c60f809c95602a26d906eba01a0abee9cc24150e4ce2189352deb1b"}, + {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad4e10efa5474ed1a611b6d7f0d130f4aafadceb73c11d9e72823e8f508e663"}, + {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1245f4f61f467cb3dfeced2b119afef3db386aec3d24a22a1de08c65038b255f"}, + {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:21efacc678a11114c765eb52ec0db62edffa89e9a562a94cbf8fa10b5db5c046"}, + {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:412ab4a3f6dbd2bf18aefa9f79c7cca23744846b31f1d6555c2ee2b05a2e14ca"}, + {file = "pydantic-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:e897c9f35281f7889873a3e6d6b69aa1447ceb024e8495a5f0d02ecd17742a7f"}, + {file = "pydantic-1.10.14-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d604be0f0b44d473e54fdcb12302495fe0467c56509a2f80483476f3ba92b33c"}, + {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42c7d17706911199798d4c464b352e640cab4351efe69c2267823d619a937e5"}, + {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:596f12a1085e38dbda5cbb874d0973303e34227b400b6414782bf205cc14940c"}, + {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bfb113860e9288d0886e3b9e49d9cf4a9d48b441f52ded7d96db7819028514cc"}, + {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bc3ed06ab13660b565eed80887fcfbc0070f0aa0691fbb351657041d3e874efe"}, + {file = "pydantic-1.10.14-cp37-cp37m-win_amd64.whl", hash = "sha256:ad8c2bc677ae5f6dbd3cf92f2c7dc613507eafe8f71719727cbc0a7dec9a8c01"}, + {file = "pydantic-1.10.14-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c37c28449752bb1f47975d22ef2882d70513c546f8f37201e0fec3a97b816eee"}, + {file = "pydantic-1.10.14-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49a46a0994dd551ec051986806122767cf144b9702e31d47f6d493c336462597"}, + {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e3819bd20a42470d6dd0fe7fc1c121c92247bca104ce608e609b59bc7a77ee"}, + {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbb503bbbbab0c588ed3cd21975a1d0d4163b87e360fec17a792f7d8c4ff29f"}, + {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:336709883c15c050b9c55a63d6c7ff09be883dbc17805d2b063395dd9d9d0022"}, + {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ae57b4d8e3312d486e2498d42aed3ece7b51848336964e43abbf9671584e67f"}, + {file = "pydantic-1.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:dba49d52500c35cfec0b28aa8b3ea5c37c9df183ffc7210b10ff2a415c125c4a"}, + {file = "pydantic-1.10.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c66609e138c31cba607d8e2a7b6a5dc38979a06c900815495b2d90ce6ded35b4"}, + {file = "pydantic-1.10.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d986e115e0b39604b9eee3507987368ff8148222da213cd38c359f6f57b3b347"}, + {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:646b2b12df4295b4c3148850c85bff29ef6d0d9621a8d091e98094871a62e5c7"}, + {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282613a5969c47c83a8710cc8bfd1e70c9223feb76566f74683af889faadc0ea"}, + {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:466669501d08ad8eb3c4fecd991c5e793c4e0bbd62299d05111d4f827cded64f"}, + {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:13e86a19dca96373dcf3190fcb8797d40a6f12f154a244a8d1e8e03b8f280593"}, + {file = "pydantic-1.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:08b6ec0917c30861e3fe71a93be1648a2aa4f62f866142ba21670b24444d7fd8"}, + {file = "pydantic-1.10.14-py3-none-any.whl", hash = "sha256:8ee853cd12ac2ddbf0ecbac1c289f95882b2d4482258048079d13be700aa114c"}, + {file = "pydantic-1.10.14.tar.gz", hash = "sha256:46f17b832fe27de7850896f3afee50ea682220dd218f7e9c88d436788419dca6"}, ] [package.dependencies] @@ -1557,13 +1557,13 @@ six = ">=1.5" [[package]] name = "python-slugify" -version = "8.0.1" +version = "8.0.2" description = "A Python slugify application that also handles Unicode" optional = false python-versions = ">=3.7" files = [ - {file = "python-slugify-8.0.1.tar.gz", hash = "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27"}, - {file = "python_slugify-8.0.1-py2.py3-none-any.whl", hash = "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395"}, + {file = "python-slugify-8.0.2.tar.gz", hash = "sha256:a1a02b127a95c124fd84f8f88be730e557fd823774bf19b1cd5e8704e2ae0e5e"}, + {file = "python_slugify-8.0.2-py2.py3-none-any.whl", hash = "sha256:428ea9b00c977b8f6c097724398f190b2c18e2a6011094d1001285875ccacdbf"}, ] [package.dependencies] @@ -1807,110 +1807,110 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rpds-py" -version = "0.16.2" +version = "0.17.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.16.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:509b617ac787cd1149600e731db9274ebbef094503ca25158e6f23edaba1ca8f"}, - {file = "rpds_py-0.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:413b9c17388bbd0d87a329d8e30c1a4c6e44e2bb25457f43725a8e6fe4161e9e"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2946b120718eba9af2b4dd103affc1164a87b9e9ebff8c3e4c05d7b7a7e274e2"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35ae5ece284cf36464eb160880018cf6088a9ac5ddc72292a6092b6ef3f4da53"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc6a7620ba7639a3db6213da61312cb4aa9ac0ca6e00dc1cbbdc21c2aa6eb57"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8cb6fe8ecdfffa0e711a75c931fb39f4ba382b4b3ccedeca43f18693864fe850"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dace7b26a13353e24613417ce2239491b40a6ad44e5776a18eaff7733488b44"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bdbc5fcb04a7309074de6b67fa9bc4b418ab3fc435fec1f2779a0eced688d04"}, - {file = "rpds_py-0.16.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f42e25c016927e2a6b1ce748112c3ab134261fc2ddc867e92d02006103e1b1b7"}, - {file = "rpds_py-0.16.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eab36eae3f3e8e24b05748ec9acc66286662f5d25c52ad70cadab544e034536b"}, - {file = "rpds_py-0.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0474df4ade9a3b4af96c3d36eb81856cb9462e4c6657d4caecfd840d2a13f3c9"}, - {file = "rpds_py-0.16.2-cp310-none-win32.whl", hash = "sha256:84c5a4d1f9dd7e2d2c44097fb09fffe728629bad31eb56caf97719e55575aa82"}, - {file = "rpds_py-0.16.2-cp310-none-win_amd64.whl", hash = "sha256:2bd82db36cd70b3628c0c57d81d2438e8dd4b7b32a6a9f25f24ab0e657cb6c4e"}, - {file = "rpds_py-0.16.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:adc0c3d6fc6ae35fee3e4917628983f6ce630d513cbaad575b4517d47e81b4bb"}, - {file = "rpds_py-0.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ec23fcad480e77ede06cf4127a25fc440f7489922e17fc058f426b5256ee0edb"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07aab64e2808c3ebac2a44f67e9dc0543812b715126dfd6fe4264df527556cb6"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4ebb8b20bd09c5ce7884c8f0388801100f5e75e7f733b1b6613c713371feefc"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3d7e2ea25d3517c6d7e5a1cc3702cffa6bd18d9ef8d08d9af6717fc1c700eed"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f28ac0e8e7242d140f99402a903a2c596ab71550272ae9247ad78f9a932b5698"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19f00f57fdd38db4bb5ad09f9ead1b535332dbf624200e9029a45f1f35527ebb"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3da5a4c56953bdbf6d04447c3410309616c54433146ccdb4a277b9cb499bc10e"}, - {file = "rpds_py-0.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec2e1cf025b2c0f48ec17ff3e642661da7ee332d326f2e6619366ce8e221f018"}, - {file = "rpds_py-0.16.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e0441fb4fdd39a230477b2ca9be90868af64425bfe7b122b57e61e45737a653b"}, - {file = "rpds_py-0.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9f0350ef2fba5f34eb0c9000ea328e51b9572b403d2f7f3b19f24085f6f598e8"}, - {file = "rpds_py-0.16.2-cp311-none-win32.whl", hash = "sha256:5a80e2f83391ad0808b4646732af2a7b67550b98f0cae056cb3b40622a83dbb3"}, - {file = "rpds_py-0.16.2-cp311-none-win_amd64.whl", hash = "sha256:e04e56b4ca7a770593633556e8e9e46579d66ec2ada846b401252a2bdcf70a6d"}, - {file = "rpds_py-0.16.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:5e6caa3809e50690bd92fa490f5c38caa86082c8c3315aa438bce43786d5e90d"}, - {file = "rpds_py-0.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e53b9b25cac9065328901713a7e9e3b12e4f57ef4280b370fbbf6fef2052eef"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af27423662f32d7501a00c5e7342f7dbd1e4a718aea7a239781357d15d437133"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d4dd5fb16eb3825742bad8339d454054261ab59fed2fbac84e1d84d5aae7ba"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e061de3b745fe611e23cd7318aec2c8b0e4153939c25c9202a5811ca911fd733"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b811d182ad17ea294f2ec63c0621e7be92a1141e1012383461872cead87468f"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5552f328eaef1a75ff129d4d0c437bf44e43f9436d3996e8eab623ea0f5fcf73"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dcbe1f8dd179e4d69b70b1f1d9bb6fd1e7e1bdc9c9aad345cdeb332e29d40748"}, - {file = "rpds_py-0.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8aad80645a011abae487d356e0ceb359f4938dfb6f7bcc410027ed7ae4f7bb8b"}, - {file = "rpds_py-0.16.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6f5549d6ed1da9bfe3631ca9483ae906f21410be2445b73443fa9f017601c6f"}, - {file = "rpds_py-0.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d452817e0d9c749c431a1121d56a777bd7099b720b3d1c820f1725cb40928f58"}, - {file = "rpds_py-0.16.2-cp312-none-win32.whl", hash = "sha256:888a97002e986eca10d8546e3c8b97da1d47ad8b69726dcfeb3e56348ebb28a3"}, - {file = "rpds_py-0.16.2-cp312-none-win_amd64.whl", hash = "sha256:d8dda2a806dfa4a9b795950c4f5cc56d6d6159f7d68080aedaff3bdc9b5032f5"}, - {file = "rpds_py-0.16.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:071980663c273bf3d388fe5c794c547e6f35ba3335477072c713a3176bf14a60"}, - {file = "rpds_py-0.16.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:726ac36e8a3bb8daef2fd482534cabc5e17334052447008405daca7ca04a3108"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9e557db6a177470316c82f023e5d571811c9a4422b5ea084c85da9aa3c035fc"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:90123853fc8b1747f80b0d354be3d122b4365a93e50fc3aacc9fb4c2488845d6"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a61f659665a39a4d17d699ab3593d7116d66e1e2e3f03ef3fb8f484e91908808"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc97f0640e91d7776530f06e6836c546c1c752a52de158720c4224c9e8053cad"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a54e99a2b9693a37ebf245937fd6e9228b4cbd64b9cc961e1f3391ec6c7391"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4b677d929cf1f6bac07ad76e0f2d5de367e6373351c01a9c0a39f6b21b4a8b"}, - {file = "rpds_py-0.16.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:5ef00873303d678aaf8b0627e111fd434925ca01c657dbb2641410f1cdaef261"}, - {file = "rpds_py-0.16.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:349cb40897fd529ca15317c22c0eab67f5ac5178b5bd2c6adc86172045210acc"}, - {file = "rpds_py-0.16.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2ddef620e70eaffebed5932ce754d539c0930f676aae6212f8e16cd9743dd365"}, - {file = "rpds_py-0.16.2-cp38-none-win32.whl", hash = "sha256:882ce6e25e585949c3d9f9abd29202367175e0aab3aba0c58c9abbb37d4982ff"}, - {file = "rpds_py-0.16.2-cp38-none-win_amd64.whl", hash = "sha256:f4bd4578e44f26997e9e56c96dedc5f1af43cc9d16c4daa29c771a00b2a26851"}, - {file = "rpds_py-0.16.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:69ac7ea9897ec201ce68b48582f3eb34a3f9924488a5432a93f177bf76a82a7e"}, - {file = "rpds_py-0.16.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a9880b4656efe36ccad41edc66789e191e5ee19a1ea8811e0aed6f69851a82f4"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee94cb58c0ba2c62ee108c2b7c9131b2c66a29e82746e8fa3aa1a1effbd3dcf1"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24f7a2eb3866a9e91f4599851e0c8d39878a470044875c49bd528d2b9b88361c"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca57468da2d9a660bcf8961637c85f2fbb2aa64d9bc3f9484e30c3f9f67b1dd7"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccd4e400309e1f34a5095bf9249d371f0fd60f8a3a5c4a791cad7b99ce1fd38d"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80443fe2f7b3ea3934c5d75fb0e04a5dbb4a8e943e5ff2de0dec059202b70a8b"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d6a9f052e72d493efd92a77f861e45bab2f6be63e37fa8ecf0c6fd1a58fedb0"}, - {file = "rpds_py-0.16.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:35953f4f2b3216421af86fd236b7c0c65935936a94ea83ddbd4904ba60757773"}, - {file = "rpds_py-0.16.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:981d135c7cdaf6cd8eadae1c950de43b976de8f09d8e800feed307140d3d6d00"}, - {file = "rpds_py-0.16.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d0dd7ed2f16df2e129496e7fbe59a34bc2d7fc8db443a606644d069eb69cbd45"}, - {file = "rpds_py-0.16.2-cp39-none-win32.whl", hash = "sha256:703d95c75a72e902544fda08e965885525e297578317989fd15a6ce58414b41d"}, - {file = "rpds_py-0.16.2-cp39-none-win_amd64.whl", hash = "sha256:e93ec1b300acf89730cf27975ef574396bc04edecc358e9bd116fb387a123239"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:44627b6ca7308680a70766454db5249105fa6344853af6762eaad4158a2feebe"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3f91df8e6dbb7360e176d1affd5fb0246d2b88d16aa5ebc7db94fd66b68b61da"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d904c5693e08bad240f16d79305edba78276be87061c872a4a15e2c301fa2c0"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:290a81cfbe4673285cdf140ec5cd1658ffbf63ab359f2b352ebe172e7cfa5bf0"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b634c5ec0103c5cbebc24ebac4872b045cccb9456fc59efdcf6fe39775365bd2"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a297a4d08cc67c7466c873c78039d87840fb50d05473db0ec1b7b03d179bf322"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2e75e17bd0bb66ee34a707da677e47c14ee51ccef78ed6a263a4cc965a072a1"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f1b9d9260e06ea017feb7172976ab261e011c1dc2f8883c7c274f6b2aabfe01a"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:162d7cd9cd311c1b0ff1c55a024b8f38bd8aad1876b648821da08adc40e95734"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:9b32f742ce5b57201305f19c2ef7a184b52f6f9ba6871cc042c2a61f0d6b49b8"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac08472f41ea77cd6a5dae36ae7d4ed3951d6602833af87532b556c1b4601d63"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:495a14b72bbe217f2695dcd9b5ab14d4f8066a00f5d209ed94f0aca307f85f6e"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:8d6b6937ae9eac6d6c0ca3c42774d89fa311f55adff3970fb364b34abde6ed3d"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a61226465bda9283686db8f17d02569a98e4b13c637be5a26d44aa1f1e361c2"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5cf6af100ffb5c195beec11ffaa8cf8523057f123afa2944e6571d54da84cdc9"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6df15846ee3fb2e6397fe25d7ca6624af9f89587f3f259d177b556fed6bebe2c"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1be2f033df1b8be8c3167ba3c29d5dca425592ee31e35eac52050623afba5772"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96f957d6ab25a78b9e7fc9749d754b98eac825a112b4e666525ce89afcbd9ed5"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:088396c7c70e59872f67462fcac3ecbded5233385797021976a09ebd55961dfe"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4c46ad6356e1561f2a54f08367d1d2e70a0a1bb2db2282d2c1972c1d38eafc3b"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:47713dc4fce213f5c74ca8a1f6a59b622fc1b90868deb8e8e4d993e421b4b39d"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f811771019f063bbd0aa7bb72c8a934bc13ebacb4672d712fc1639cfd314cccc"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f19afcfc0dd0dca35694df441e9b0f95bc231b512f51bded3c3d8ca32153ec19"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4b682c5775d6a3d21e314c10124599976809455ee67020e8e72df1769b87bc3"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c647ca87fc0ebe808a41de912e9a1bfef9acb85257e5d63691364ac16b81c1f0"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:302bd4983bbd47063e452c38be66153760112f6d3635c7eeefc094299fa400a9"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf721ede3eb7b829e4a9b8142bd55db0bdc82902720548a703f7e601ee13bdc3"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:358dafc89ce3894c7f486c615ba914609f38277ef67f566abc4c854d23b997fa"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cad0f59ee3dc35526039f4bc23642d52d5f6616b5f687d846bfc6d0d6d486db0"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cffa76b385dfe1e38527662a302b19ffb0e7f5cf7dd5e89186d2c94a22dd9d0c"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:83640a5d7cd3bff694747d50436b8b541b5b9b9782b0c8c1688931d6ee1a1f2d"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:ed99b4f7179d2111702020fd7d156e88acd533f5a7d3971353e568b6051d5c97"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4022b9dc620e14f30201a8a73898a873c8e910cb642bcd2f3411123bc527f6ac"}, - {file = "rpds_py-0.16.2.tar.gz", hash = "sha256:781ef8bfc091b19960fc0142a23aedadafa826bc32b433fdfe6fd7f964d7ef44"}, + {file = "rpds_py-0.17.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4128980a14ed805e1b91a7ed551250282a8ddf8201a4e9f8f5b7e6225f54170d"}, + {file = "rpds_py-0.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ff1dcb8e8bc2261a088821b2595ef031c91d499a0c1b031c152d43fe0a6ecec8"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d65e6b4f1443048eb7e833c2accb4fa7ee67cc7d54f31b4f0555b474758bee55"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a71169d505af63bb4d20d23a8fbd4c6ce272e7bce6cc31f617152aa784436f29"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:436474f17733c7dca0fbf096d36ae65277e8645039df12a0fa52445ca494729d"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10162fe3f5f47c37ebf6d8ff5a2368508fe22007e3077bf25b9c7d803454d921"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:720215373a280f78a1814becb1312d4e4d1077b1202a56d2b0815e95ccb99ce9"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70fcc6c2906cfa5c6a552ba7ae2ce64b6c32f437d8f3f8eea49925b278a61453"}, + {file = "rpds_py-0.17.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:91e5a8200e65aaac342a791272c564dffcf1281abd635d304d6c4e6b495f29dc"}, + {file = "rpds_py-0.17.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:99f567dae93e10be2daaa896e07513dd4bf9c2ecf0576e0533ac36ba3b1d5394"}, + {file = "rpds_py-0.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24e4900a6643f87058a27320f81336d527ccfe503984528edde4bb660c8c8d59"}, + {file = "rpds_py-0.17.1-cp310-none-win32.whl", hash = "sha256:0bfb09bf41fe7c51413f563373e5f537eaa653d7adc4830399d4e9bdc199959d"}, + {file = "rpds_py-0.17.1-cp310-none-win_amd64.whl", hash = "sha256:20de7b7179e2031a04042e85dc463a93a82bc177eeba5ddd13ff746325558aa6"}, + {file = "rpds_py-0.17.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:65dcf105c1943cba45d19207ef51b8bc46d232a381e94dd38719d52d3980015b"}, + {file = "rpds_py-0.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:01f58a7306b64e0a4fe042047dd2b7d411ee82e54240284bab63e325762c1147"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:071bc28c589b86bc6351a339114fb7a029f5cddbaca34103aa573eba7b482382"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae35e8e6801c5ab071b992cb2da958eee76340e6926ec693b5ff7d6381441745"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149c5cd24f729e3567b56e1795f74577aa3126c14c11e457bec1b1c90d212e38"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e796051f2070f47230c745d0a77a91088fbee2cc0502e9b796b9c6471983718c"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e820ee1004327609b28db8307acc27f5f2e9a0b185b2064c5f23e815f248f8"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1957a2ab607f9added64478a6982742eb29f109d89d065fa44e01691a20fc20a"}, + {file = "rpds_py-0.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8587fd64c2a91c33cdc39d0cebdaf30e79491cc029a37fcd458ba863f8815383"}, + {file = "rpds_py-0.17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4dc889a9d8a34758d0fcc9ac86adb97bab3fb7f0c4d29794357eb147536483fd"}, + {file = "rpds_py-0.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2953937f83820376b5979318840f3ee47477d94c17b940fe31d9458d79ae7eea"}, + {file = "rpds_py-0.17.1-cp311-none-win32.whl", hash = "sha256:1bfcad3109c1e5ba3cbe2f421614e70439f72897515a96c462ea657261b96518"}, + {file = "rpds_py-0.17.1-cp311-none-win_amd64.whl", hash = "sha256:99da0a4686ada4ed0f778120a0ea8d066de1a0a92ab0d13ae68492a437db78bf"}, + {file = "rpds_py-0.17.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1dc29db3900cb1bb40353772417800f29c3d078dbc8024fd64655a04ee3c4bdf"}, + {file = "rpds_py-0.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82ada4a8ed9e82e443fcef87e22a3eed3654dd3adf6e3b3a0deb70f03e86142a"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d36b2b59e8cc6e576f8f7b671e32f2ff43153f0ad6d0201250a7c07f25d570e"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3677fcca7fb728c86a78660c7fb1b07b69b281964673f486ae72860e13f512ad"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:516fb8c77805159e97a689e2f1c80655c7658f5af601c34ffdb916605598cda2"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df3b6f45ba4515632c5064e35ca7f31d51d13d1479673185ba8f9fefbbed58b9"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a967dd6afda7715d911c25a6ba1517975acd8d1092b2f326718725461a3d33f9"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dbbb95e6fc91ea3102505d111b327004d1c4ce98d56a4a02e82cd451f9f57140"}, + {file = "rpds_py-0.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02866e060219514940342a1f84303a1ef7a1dad0ac311792fbbe19b521b489d2"}, + {file = "rpds_py-0.17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2528ff96d09f12e638695f3a2e0c609c7b84c6df7c5ae9bfeb9252b6fa686253"}, + {file = "rpds_py-0.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bd345a13ce06e94c753dab52f8e71e5252aec1e4f8022d24d56decd31e1b9b23"}, + {file = "rpds_py-0.17.1-cp312-none-win32.whl", hash = "sha256:2a792b2e1d3038daa83fa474d559acfd6dc1e3650ee93b2662ddc17dbff20ad1"}, + {file = "rpds_py-0.17.1-cp312-none-win_amd64.whl", hash = "sha256:292f7344a3301802e7c25c53792fae7d1593cb0e50964e7bcdcc5cf533d634e3"}, + {file = "rpds_py-0.17.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:8ffe53e1d8ef2520ebcf0c9fec15bb721da59e8ef283b6ff3079613b1e30513d"}, + {file = "rpds_py-0.17.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4341bd7579611cf50e7b20bb8c2e23512a3dc79de987a1f411cb458ab670eb90"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4eb548daf4836e3b2c662033bfbfc551db58d30fd8fe660314f86bf8510b93"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b686f25377f9c006acbac63f61614416a6317133ab7fafe5de5f7dc8a06d42eb"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e21b76075c01d65d0f0f34302b5a7457d95721d5e0667aea65e5bb3ab415c25"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b86b21b348f7e5485fae740d845c65a880f5d1eda1e063bc59bef92d1f7d0c55"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f175e95a197f6a4059b50757a3dca33b32b61691bdbd22c29e8a8d21d3914cae"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1701fc54460ae2e5efc1dd6350eafd7a760f516df8dbe51d4a1c79d69472fbd4"}, + {file = "rpds_py-0.17.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9051e3d2af8f55b42061603e29e744724cb5f65b128a491446cc029b3e2ea896"}, + {file = "rpds_py-0.17.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7450dbd659fed6dd41d1a7d47ed767e893ba402af8ae664c157c255ec6067fde"}, + {file = "rpds_py-0.17.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5a024fa96d541fd7edaa0e9d904601c6445e95a729a2900c5aec6555fe921ed6"}, + {file = "rpds_py-0.17.1-cp38-none-win32.whl", hash = "sha256:da1ead63368c04a9bded7904757dfcae01eba0e0f9bc41d3d7f57ebf1c04015a"}, + {file = "rpds_py-0.17.1-cp38-none-win_amd64.whl", hash = "sha256:841320e1841bb53fada91c9725e766bb25009cfd4144e92298db296fb6c894fb"}, + {file = "rpds_py-0.17.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f6c43b6f97209e370124baf2bf40bb1e8edc25311a158867eb1c3a5d449ebc7a"}, + {file = "rpds_py-0.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7d63ec01fe7c76c2dbb7e972fece45acbb8836e72682bde138e7e039906e2c"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81038ff87a4e04c22e1d81f947c6ac46f122e0c80460b9006e6517c4d842a6ec"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:810685321f4a304b2b55577c915bece4c4a06dfe38f6e62d9cc1d6ca8ee86b99"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25f071737dae674ca8937a73d0f43f5a52e92c2d178330b4c0bb6ab05586ffa6"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa5bfb13f1e89151ade0eb812f7b0d7a4d643406caaad65ce1cbabe0a66d695f"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfe07308b311a8293a0d5ef4e61411c5c20f682db6b5e73de6c7c8824272c256"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a000133a90eea274a6f28adc3084643263b1e7c1a5a66eb0a0a7a36aa757ed74"}, + {file = "rpds_py-0.17.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d0e8a6434a3fbf77d11448c9c25b2f25244226cfbec1a5159947cac5b8c5fa4"}, + {file = "rpds_py-0.17.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:efa767c220d94aa4ac3a6dd3aeb986e9f229eaf5bce92d8b1b3018d06bed3772"}, + {file = "rpds_py-0.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:dbc56680ecf585a384fbd93cd42bc82668b77cb525343170a2d86dafaed2a84b"}, + {file = "rpds_py-0.17.1-cp39-none-win32.whl", hash = "sha256:270987bc22e7e5a962b1094953ae901395e8c1e1e83ad016c5cfcfff75a15a3f"}, + {file = "rpds_py-0.17.1-cp39-none-win_amd64.whl", hash = "sha256:2a7b2f2f56a16a6d62e55354dd329d929560442bd92e87397b7a9586a32e3e76"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a3264e3e858de4fc601741498215835ff324ff2482fd4e4af61b46512dd7fc83"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f2f3b28b40fddcb6c1f1f6c88c6f3769cd933fa493ceb79da45968a21dccc920"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9584f8f52010295a4a417221861df9bea4c72d9632562b6e59b3c7b87a1522b7"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c64602e8be701c6cfe42064b71c84ce62ce66ddc6422c15463fd8127db3d8066"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:060f412230d5f19fc8c8b75f315931b408d8ebf56aec33ef4168d1b9e54200b1"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9412abdf0ba70faa6e2ee6c0cc62a8defb772e78860cef419865917d86c7342"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9737bdaa0ad33d34c0efc718741abaafce62fadae72c8b251df9b0c823c63b22"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9f0e4dc0f17dcea4ab9d13ac5c666b6b5337042b4d8f27e01b70fae41dd65c57"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1db228102ab9d1ff4c64148c96320d0be7044fa28bd865a9ce628ce98da5973d"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:d8bbd8e56f3ba25a7d0cf980fc42b34028848a53a0e36c9918550e0280b9d0b6"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:be22ae34d68544df293152b7e50895ba70d2a833ad9566932d750d3625918b82"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bf046179d011e6114daf12a534d874958b039342b347348a78b7cdf0dd9d6041"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a746a6d49665058a5896000e8d9d2f1a6acba8a03b389c1e4c06e11e0b7f40d"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0b8bf5b8db49d8fd40f54772a1dcf262e8be0ad2ab0206b5a2ec109c176c0a4"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7f4cb1f173385e8a39c29510dd11a78bf44e360fb75610594973f5ea141028b"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7fbd70cb8b54fe745301921b0816c08b6d917593429dfc437fd024b5ba713c58"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bdf1303df671179eaf2cb41e8515a07fc78d9d00f111eadbe3e14262f59c3d0"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad059a4bd14c45776600d223ec194e77db6c20255578bb5bcdd7c18fd169361"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3664d126d3388a887db44c2e293f87d500c4184ec43d5d14d2d2babdb4c64cad"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:698ea95a60c8b16b58be9d854c9f993c639f5c214cf9ba782eca53a8789d6b19"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:c3d2010656999b63e628a3c694f23020322b4178c450dc478558a2b6ef3cb9bb"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:938eab7323a736533f015e6069a7d53ef2dcc841e4e533b782c2bfb9fb12d84b"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e626b365293a2142a62b9a614e1f8e331b28f3ca57b9f05ebbf4cf2a0f0bdc5"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:380e0df2e9d5d5d339803cfc6d183a5442ad7ab3c63c2a0982e8c824566c5ccc"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b760a56e080a826c2e5af09002c1a037382ed21d03134eb6294812dda268c811"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5576ee2f3a309d2bb403ec292d5958ce03953b0e57a11d224c1f134feaf8c40f"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3c3461ebb4c4f1bbc70b15d20b565759f97a5aaf13af811fcefc892e9197ba"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:637b802f3f069a64436d432117a7e58fab414b4e27a7e81049817ae94de45d8d"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffee088ea9b593cc6160518ba9bd319b5475e5f3e578e4552d63818773c6f56a"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ac732390d529d8469b831949c78085b034bff67f584559340008d0f6041a049"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:93432e747fb07fa567ad9cc7aaadd6e29710e515aabf939dfbed8046041346c6"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7b7d9ca34542099b4e185b3c2a2b2eda2e318a7dbde0b0d83357a6d4421b5296"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:0387ce69ba06e43df54e43968090f3626e231e4bc9150e4c3246947567695f68"}, + {file = "rpds_py-0.17.1.tar.gz", hash = "sha256:0210b2668f24c078307260bf88bdac9d6f1093635df5123789bfee4d8d7fc8e7"}, ] [[package]] diff --git a/tests/unit/adapters/algos/test_test_relationship.py b/tests/unit/adapters/algos/test_test_relationship.py index 936c655..b9a7d1b 100644 --- a/tests/unit/adapters/algos/test_test_relationship.py +++ b/tests/unit/adapters/algos/test_test_relationship.py @@ -5,7 +5,6 @@ import pytest from dbterd.adapters.algos import base as base_algo -from dbterd.adapters.algos import test_relationship as algo from dbterd.adapters.meta import Column, Ref, Table @@ -371,7 +370,9 @@ def test_get_compiled(self, manifest, expected): ], ) def test_get_relationships(self, manifest, algorithm, expected): - assert algo.get_relationships(manifest=manifest, algo=algorithm) == expected + assert ( + base_algo.get_relationships(manifest=manifest, algo=algorithm) == expected + ) @pytest.mark.parametrize( "meta, type", @@ -386,7 +387,7 @@ def test_get_relationships(self, manifest, algorithm, expected): ], ) def test_get_relationship_type(self, meta, type): - assert algo.get_relationship_type(meta=meta) == type + assert base_algo.get_relationship_type(meta=meta) == type @pytest.mark.parametrize( "manifest, expected", diff --git a/tests/unit/adapters/targets/d2/test_d2_test_relationship.py b/tests/unit/adapters/targets/d2/test_d2_test_relationship.py index 2f0e511..875c5e0 100644 --- a/tests/unit/adapters/targets/d2/test_d2_test_relationship.py +++ b/tests/unit/adapters/targets/d2/test_d2_test_relationship.py @@ -239,7 +239,7 @@ def test_parse( return_value=tables, ) as mock_get_tables: with mock.patch( - "dbterd.adapters.algos.test_relationship.get_relationships", + "dbterd.adapters.algos.base.get_relationships", return_value=relationships, ) as mock_get_relationships: mermaid = engine.parse( diff --git a/tests/unit/adapters/targets/dbml/test_dbml_test_relationship.py b/tests/unit/adapters/targets/dbml/test_dbml_test_relationship.py index f46885e..2382a41 100644 --- a/tests/unit/adapters/targets/dbml/test_dbml_test_relationship.py +++ b/tests/unit/adapters/targets/dbml/test_dbml_test_relationship.py @@ -334,7 +334,7 @@ def test_parse( return_value=tables, ) as mock_get_tables: with mock.patch( - "dbterd.adapters.algos.test_relationship.get_relationships", + "dbterd.adapters.algos.base.get_relationships", return_value=relationships, ) as mock_get_relationships: dbml = engine.parse( diff --git a/tests/unit/adapters/targets/graphviz/test_graphviz_test_relationship.py b/tests/unit/adapters/targets/graphviz/test_graphviz_test_relationship.py index 502d5da..0683d69 100644 --- a/tests/unit/adapters/targets/graphviz/test_graphviz_test_relationship.py +++ b/tests/unit/adapters/targets/graphviz/test_graphviz_test_relationship.py @@ -355,7 +355,7 @@ def test_parse( return_value=tables, ) as mock_get_tables: with mock.patch( - "dbterd.adapters.algos.test_relationship.get_relationships", + "dbterd.adapters.algos.base.get_relationships", return_value=relationships, ) as mock_get_relationships: graphviz = engine.parse( diff --git a/tests/unit/adapters/targets/mermaid/test_mermaid_test_relationship.py b/tests/unit/adapters/targets/mermaid/test_mermaid_test_relationship.py index c885951..5c9d9cb 100644 --- a/tests/unit/adapters/targets/mermaid/test_mermaid_test_relationship.py +++ b/tests/unit/adapters/targets/mermaid/test_mermaid_test_relationship.py @@ -231,7 +231,7 @@ def test_parse( return_value=tables, ) as mock_get_tables: with mock.patch( - "dbterd.adapters.algos.test_relationship.get_relationships", + "dbterd.adapters.algos.base.get_relationships", return_value=relationships, ) as mock_get_relationships: mermaid = engine.parse( diff --git a/tests/unit/adapters/targets/plantuml/test_plantuml_test_relationship.py b/tests/unit/adapters/targets/plantuml/test_plantuml_test_relationship.py index e1fb254..836df83 100644 --- a/tests/unit/adapters/targets/plantuml/test_plantuml_test_relationship.py +++ b/tests/unit/adapters/targets/plantuml/test_plantuml_test_relationship.py @@ -237,7 +237,7 @@ def test_parse( return_value=tables, ) as mock_get_tables: with mock.patch( - "dbterd.adapters.algos.test_relationship.get_relationships", + "dbterd.adapters.algos.base.get_relationships", return_value=relationships, ) as mock_get_relationships: plantuml = engine.parse( diff --git a/tests/unit/adapters/test_base.py b/tests/unit/adapters/test_base.py index 2ee343f..d2065a2 100644 --- a/tests/unit/adapters/test_base.py +++ b/tests/unit/adapters/test_base.py @@ -6,7 +6,7 @@ from dbterd import default from dbterd.adapters.base import Executor -from dbterd.adapters.dbt_invocation import DbtInvocation +from dbterd.adapters.dbt_core.dbt_invocation import DbtInvocation class TestBase: @@ -106,7 +106,6 @@ def test__get_selection__error(self, mock_dbt_invocation): ), ], ) - @mock.patch("dbterd.adapters.dbt_cloud.DbtCloudArtifact.get") @mock.patch("dbterd.adapters.base.Executor._Executor__get_dir") @mock.patch("dbterd.adapters.base.Executor._Executor__get_selection") @mock.patch("dbterd.adapters.base.DbtInvocation.get_artifacts_for_erd") @@ -115,19 +114,16 @@ def test_evaluate_kwargs( mock_get_artifacts_for_erd, mock_get_selection, mock_get_dir, - mock_dbt_cloud_get, kwargs, expected, ): - worker = Executor(ctx=click.Context(command=click.BaseCommand("dummy"))) + worker = Executor(ctx=click.Context(command=click.BaseCommand("run"))) mock_get_dir.return_value = ("/path/ad", "/path/dpd") mock_get_selection.return_value = ["yolo"] assert expected == worker.evaluate_kwargs(**kwargs) mock_get_dir.assert_called_once() if kwargs.get("dbt_auto_artifacts"): mock_get_artifacts_for_erd.assert_called_once() - if kwargs.get("dbt_cloud"): - mock_dbt_cloud_get.assert_called_once() @pytest.mark.parametrize( "kwargs, mock_isfile_se, expected", diff --git a/tests/unit/adapters/test_dbt_cloud.py b/tests/unit/adapters/test_dbt_cloud.py index 5622548..a3f9900 100644 --- a/tests/unit/adapters/test_dbt_cloud.py +++ b/tests/unit/adapters/test_dbt_cloud.py @@ -4,7 +4,7 @@ import pytest import requests -from dbterd.adapters.dbt_cloud import DbtCloudArtifact +from dbterd.adapters.dbt_cloud.administrative import DbtCloudArtifact class MockResponse: @@ -106,8 +106,8 @@ def test_api_endpoint(self, kwargs, endpoint): dbt_cloud = DbtCloudArtifact(**kwargs) assert dbt_cloud.api_endpoint == endpoint - @mock.patch("dbterd.adapters.dbt_cloud.file.write_json") - @mock.patch("dbterd.adapters.dbt_cloud.requests.get") + @mock.patch("dbterd.adapters.dbt_cloud.administrative.file.write_json") + @mock.patch("dbterd.adapters.dbt_cloud.administrative.requests.get") def test_download_artifact_ok( self, mock_requests_get, mock_write_json, dbtCloudArtifact ): @@ -120,7 +120,7 @@ def test_download_artifact_ok( path="/irrelevant/path/manifest.json", ) - @mock.patch("dbterd.adapters.dbt_cloud.file.write_json") + @mock.patch("dbterd.adapters.dbt_cloud.administrative.file.write_json") def test_download_artifact_bad_parameters(self, mock_write_json, dbtCloudArtifact): with pytest.raises(AttributeError): dbtCloudArtifact.download_artifact( @@ -128,8 +128,8 @@ def test_download_artifact_bad_parameters(self, mock_write_json, dbtCloudArtifac ) assert mock_write_json.call_count == 0 - @mock.patch("dbterd.adapters.dbt_cloud.file.write_json") - @mock.patch("dbterd.adapters.dbt_cloud.requests.get") + @mock.patch("dbterd.adapters.dbt_cloud.administrative.file.write_json") + @mock.patch("dbterd.adapters.dbt_cloud.administrative.requests.get") def test_download_artifact_network_failed( self, mock_requests_get, mock_write_json, dbtCloudArtifact ): @@ -139,8 +139,8 @@ def test_download_artifact_network_failed( ) assert mock_write_json.call_count == 0 - @mock.patch("dbterd.adapters.dbt_cloud.file.write_json") - @mock.patch("dbterd.adapters.dbt_cloud.requests.get") + @mock.patch("dbterd.adapters.dbt_cloud.administrative.file.write_json") + @mock.patch("dbterd.adapters.dbt_cloud.administrative.requests.get") def test_download_artifact_failed_to_save_file( self, mock_requests_get, mock_write_json, dbtCloudArtifact ): @@ -151,8 +151,8 @@ def test_download_artifact_failed_to_save_file( ) assert mock_write_json.call_count == 1 - @mock.patch("dbterd.adapters.dbt_cloud.file.write_json") - @mock.patch("dbterd.adapters.dbt_cloud.requests.get") + @mock.patch("dbterd.adapters.dbt_cloud.administrative.file.write_json") + @mock.patch("dbterd.adapters.dbt_cloud.administrative.requests.get") def test_download_artifact_status_not_ok( self, mock_requests_get, mock_write_json, dbtCloudArtifact ): @@ -162,7 +162,9 @@ def test_download_artifact_status_not_ok( ) assert mock_write_json.call_count == 0 - @mock.patch("dbterd.adapters.dbt_cloud.DbtCloudArtifact.download_artifact") + @mock.patch( + "dbterd.adapters.dbt_cloud.administrative.DbtCloudArtifact.download_artifact" + ) def test_get(self, mock_download_artifact, dbtCloudArtifact): mock_download_artifact.return_value = True assert dbtCloudArtifact.get(artifacts_dir="/irrelevant/path") diff --git a/tests/unit/adapters/test_dbt_invocation.py b/tests/unit/adapters/test_dbt_invocation.py index 0b3f814..c9e6b02 100644 --- a/tests/unit/adapters/test_dbt_invocation.py +++ b/tests/unit/adapters/test_dbt_invocation.py @@ -5,7 +5,7 @@ import pytest from dbt.cli.main import dbtRunnerResult -from dbterd.adapters.dbt_invocation import DbtInvocation +from dbterd.adapters.dbt_core.dbt_invocation import DbtInvocation class TestDbtInvocation: @@ -33,7 +33,7 @@ def test__ensure_dbt_installed__no_dbt_installed(self, mock_find_spec): ), ], ) - @mock.patch("dbterd.adapters.dbt_invocation.dbtRunner.invoke") + @mock.patch("dbterd.adapters.dbt_core.dbt_invocation.dbtRunner.invoke") def test_get_selection( self, mock_dbtRunner_invoke, @@ -62,7 +62,7 @@ def test_get_selection( ] ) - @mock.patch("dbterd.adapters.dbt_invocation.dbtRunner.invoke") + @mock.patch("dbterd.adapters.dbt_core.dbt_invocation.dbtRunner.invoke") def test_get_selection__failed(self, mock_dbtRunner_invoke): mock_dbtRunner_invoke.return_value = dbtRunnerResult(success=False) with pytest.raises(click.UsageError): @@ -70,7 +70,7 @@ def test_get_selection__failed(self, mock_dbtRunner_invoke): select_rules=[], exclude_rules=[] ) - @mock.patch("dbterd.adapters.dbt_invocation.dbtRunner.invoke") + @mock.patch("dbterd.adapters.dbt_core.dbt_invocation.dbtRunner.invoke") def test_get_artifacts_for_erd(self, mock_dbtRunner_invoke): invoker = DbtInvocation() _ = invoker.get_artifacts_for_erd() diff --git a/tests/unit/cli/test_runner.py b/tests/unit/cli/test_runner.py index 100c96a..b04272a 100644 --- a/tests/unit/cli/test_runner.py +++ b/tests/unit/cli/test_runner.py @@ -60,10 +60,14 @@ def test_invoke_run_with_invalid_strategy(self, dbterd: dbterdRunner) -> None: "dbterd.adapters.base.Executor._Executor__read_catalog", return_value=None, ) as mock_read_c: - with pytest.raises(TypeError): + with mock.patch( + "dbterd.adapters.base.Executor._Executor__save_result", + return_value=None, + ) as mock_save: dbterd.invoke(["run", "--algo", invalid_strategy]) - mock_read_m.assert_called_once() - mock_read_c.assert_called_once() + mock_read_m.assert_called_once() + mock_read_c.assert_called_once() + mock_save.assert_called_once() @pytest.mark.parametrize( "target, output",