diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index da934315..6f7fdd0b 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -16,7 +16,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
- python-version: [3.7, 3.8, 3.9, "3.10", "3.11"]
+ python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
diff --git a/docs/generate_examples.py b/docs/generate_examples.py
index 001c2d18..a9b9ba52 100644
--- a/docs/generate_examples.py
+++ b/docs/generate_examples.py
@@ -1,9 +1,10 @@
"""Run this to populate the example cases from the code to Jekyll folder"""
+
import os
import re
import sys
from pathlib import Path
-from typing import TypedDict, List
+from typing import List, TypedDict
import yaml
@@ -13,7 +14,8 @@
from json_schema_for_humans.const import FileLikeType
from json_schema_for_humans.generate import generate_from_filename
-from json_schema_for_humans.generation_configuration import GenerationConfiguration
+from json_schema_for_humans.generation_configuration import \
+ GenerationConfiguration
EXAMPLES_DIR = os.path.join(CURRENT_DIR, "examples")
JSON_EXAMPLES_DIR = os.path.join(EXAMPLES_DIR, "cases")
@@ -191,18 +193,20 @@ def generate_each_template(examples_md_file: FileLikeType, case_path: str, case_
"""
print(f"Generating example {case_name}")
- examples_md_file.write(f"\n## --{case_name}--")
+ examples_md_file.write(f"\n## --{case_name}--") # type:ignore[arg-type]
if case_name in CASES_DESCRIPTION_DICT:
case_data = CASES_DESCRIPTION_DICT[case_name]
case_display_name = case_data["display_name"]
case_description = case_data["description"]
- examples_md_file.write(f"\n### {case_display_name}\n\n")
+ examples_md_file.write(f"\n### {case_display_name}\n\n") # type:ignore[arg-type]
if case_description:
- examples_md_file.write(f"**Description:** {case_description}\n")
+ examples_md_file.write(f"**Description:** {case_description}\n") # type:ignore[arg-type]
else:
- examples_md_file.write(f"\n### {case_name}\n")
+ examples_md_file.write(f"\n### {case_name}\n") # type:ignore[arg-type]
- examples_md_file.write(MD_EXAMPLE_JSON_TEMPLATE.format(file_url=case_url, title="Json schema"))
+ examples_md_file.write(
+ MD_EXAMPLE_JSON_TEMPLATE.format(file_url=case_url, title="Json schema") # type:ignore[arg-type]
+ )
for config in CONFIGURATIONS:
template_configuration = config["config"]
example_dir_name = config["dir_name"]
@@ -211,7 +215,7 @@ def generate_each_template(examples_md_file: FileLikeType, case_path: str, case_
examples_md_file.write(
config["md_example_template"].format(
file_url=f"examples/{example_dir_name}/{example_file_name}", title=config["title"]
- )
+ ) # type:ignore[arg-type]
)
example_dest_dir = os.path.join(EXAMPLES_DIR, example_dir_name)
diff --git a/json_schema_for_humans/cli.py b/json_schema_for_humans/cli.py
index 4784aadc..44e78613 100644
--- a/json_schema_for_humans/cli.py
+++ b/json_schema_for_humans/cli.py
@@ -1,10 +1,10 @@
from pathlib import Path
-from typing import Optional, List, Union
+from typing import List, Optional, Union
import click
-from json_schema_for_humans.const import FileLikeType, DEFAULT_CSS_FILE_NAME, DEFAULT_JS_FILE_NAME
-from json_schema_for_humans.generate import generate_schemas_doc, copy_additional_files_to_target
+from json_schema_for_humans.const import DEFAULT_CSS_FILE_NAME, DEFAULT_JS_FILE_NAME, FileLikeType
+from json_schema_for_humans.generate import copy_additional_files_to_target, generate_schemas_doc
from json_schema_for_humans.generation_configuration import get_final_config
from json_schema_for_humans.schema.schema_importer import get_result_output, get_schemas_to_render
from json_schema_for_humans.schema.schema_to_render import SchemaToRender
@@ -56,7 +56,7 @@ def main(
copy_js: bool,
link_to_reused_ref: bool,
) -> None:
- config = get_final_config(
+ final_config = get_final_config(
minify=minify,
deprecated_from_description=deprecated_from_description,
default_from_description=default_from_description,
@@ -69,22 +69,25 @@ def main(
)
schemas_to_render = get_schemas_to_render_from_cli_arguments(
- schema_files_or_dir, output_path_or_file, config.result_extension
+ schema_files_or_dir, output_path_or_file, final_config.result_extension
)
- template_renderer = TemplateRenderer(config)
+ template_renderer = TemplateRenderer(final_config)
generate_schemas_doc(schemas_to_render, template_renderer)
- copy_additional_files_to_target(schemas_to_render, config)
+ copy_additional_files_to_target(schemas_to_render, final_config)
def get_schemas_to_render_from_cli_arguments(
schema_files_or_dir: Union[str, Path], output_path_or_file: Optional[Path], result_extension: str
) -> List[SchemaToRender]:
- schema_files_or_dir = schema_files_or_dir.split(",")
- result_output = get_result_output(output_path_or_file, schema_files_or_dir, result_extension)
+ if isinstance(schema_files_or_dir, Path):
+ schema_files_or_dir_to_render = [str(schema_files_or_dir)]
+ else:
+ schema_files_or_dir_to_render = schema_files_or_dir.split(",")
+ result_output = get_result_output(output_path_or_file, schema_files_or_dir_to_render, result_extension)
schemas_to_render: List[SchemaToRender] = []
- for schema_file_or_dir_part in schema_files_or_dir:
+ for schema_file_or_dir_part in schema_files_or_dir_to_render:
schemas_to_render += get_schemas_to_render(schema_file_or_dir_part, result_output, result_extension)
return schemas_to_render
diff --git a/json_schema_for_humans/const.py b/json_schema_for_humans/const.py
index f2005a94..8d203fe5 100644
--- a/json_schema_for_humans/const.py
+++ b/json_schema_for_humans/const.py
@@ -1,6 +1,6 @@
from enum import Enum
-from io import TextIOWrapper, FileIO
-from typing import Union, TextIO
+from io import FileIO, TextIOWrapper
+from typing import TextIO, Union
TYPE_ARRAY = "array"
TYPE_BOOLEAN = "boolean"
@@ -45,6 +45,7 @@ def result_extension(self) -> str:
return "html"
if self in [self.MD, self.MD_NESTED]:
return "md"
+ return "html"
DEFAULT_TEMPLATE_FILE_NAME = "base.html"
diff --git a/json_schema_for_humans/generate.py b/json_schema_for_humans/generate.py
index 2bac8aae..2ddb515f 100644
--- a/json_schema_for_humans/generate.py
+++ b/json_schema_for_humans/generate.py
@@ -1,6 +1,7 @@
import shutil
from datetime import datetime
from pathlib import Path
+from tempfile import _TemporaryFileWrapper
from typing import Any, Dict, List, Optional, Union
from json_schema_for_humans.const import FileLikeType
@@ -18,7 +19,7 @@ def generate_from_schema(
default_from_description: bool = False,
expand_buttons: bool = False,
link_to_reused_ref: bool = True,
- config: GenerationConfiguration = None,
+ config: Optional[GenerationConfiguration] = None,
) -> str:
config = config or get_final_config(
minify=minify,
@@ -34,6 +35,8 @@ def generate_from_schema(
for rendered_content in generate_schemas_doc(schemas_to_render, template_renderer, loaded_schemas).items():
return rendered_content[1]
+ raise ValueError(f"Unable to find a schema to render from {schema_file}")
+
def generate_from_filename(
schema_file_name: Union[str, Path],
@@ -45,7 +48,7 @@ def generate_from_filename(
copy_css: bool = True,
copy_js: bool = True,
link_to_reused_ref: bool = True,
- config: GenerationConfiguration = None,
+ config: Optional[GenerationConfiguration] = None,
) -> None:
"""Generate the schema documentation from a filename"""
config = config or get_final_config(
@@ -66,8 +69,8 @@ def generate_from_filename(
def generate_from_file_object(
- schema_file: FileLikeType,
- result_file: FileLikeType,
+ schema_file: Union[FileLikeType, _TemporaryFileWrapper],
+ result_file: Union[FileLikeType, _TemporaryFileWrapper],
minify: bool = True,
deprecated_from_description: bool = False,
default_from_description: bool = False,
@@ -75,7 +78,7 @@ def generate_from_file_object(
copy_css: bool = True,
copy_js: bool = True,
link_to_reused_ref: bool = True,
- config: GenerationConfiguration = None,
+ config: Optional[GenerationConfiguration] = None,
) -> None:
"""Generate the JSON schema documentation from opened file objects for both input and output files. The
result_file should be opened in write mode.
diff --git a/json_schema_for_humans/generation_configuration.py b/json_schema_for_humans/generation_configuration.py
index 996c6225..a08cb7f4 100644
--- a/json_schema_for_humans/generation_configuration.py
+++ b/json_schema_for_humans/generation_configuration.py
@@ -4,19 +4,19 @@
from dataclasses import dataclass
from json import JSONDecodeError
from pathlib import Path
-from typing import Any, Union, Dict, List, Optional
+from typing import Any, Dict, List, Optional, Union
import yaml
from dataclasses_json import dataclass_json
from json_schema_for_humans.const import (
- DocumentationTemplate,
- FileLikeType,
DEFAULT_CSS_FILE_NAME,
DEFAULT_JS_FILE_NAME,
OFFLINE_CSS_FILE_NAMES,
OFFLINE_FONT_FILE_NAMES,
OFFLINE_JS_FILE_NAMES,
+ DocumentationTemplate,
+ FileLikeType,
)
DEFAULT_PROPERTIES_TABLE_COLUMNS = [
@@ -114,7 +114,7 @@ def result_extension(self) -> str:
raise ValueError("Trying to get extension for configuration with no template")
@property
- def template_path(self) -> Optional[Path]:
+ def template_path(self) -> Path:
if self.custom_template_path:
return Path(self.custom_template_path)
@@ -140,7 +140,7 @@ def get_final_config(
copy_css: bool = False,
copy_js: bool = False,
config: Optional[Union[str, Path, FileLikeType, Dict[str, Any], GenerationConfiguration]] = None,
- config_parameters: List[str] = None,
+ config_parameters: Optional[List[str]] = None,
) -> GenerationConfiguration:
if config:
final_config = _load_config(config)
@@ -214,5 +214,4 @@ def _apply_config_cli_parameters(
parameter_value = True
current_configuration_as_dict[parameter_name] = parameter_value
- # type: ignore
- return GenerationConfiguration.from_dict(current_configuration_as_dict)
+ return GenerationConfiguration.from_dict(current_configuration_as_dict) # type: ignore[attr-defined]
diff --git a/json_schema_for_humans/jinja_filters.py b/json_schema_for_humans/jinja_filters.py
index 98b763a7..dc8b4a61 100644
--- a/json_schema_for_humans/jinja_filters.py
+++ b/json_schema_for_humans/jinja_filters.py
@@ -1,22 +1,23 @@
-import re
import json
-
-import yaml
+import re
from datetime import datetime
-from typing import List, Any
+from typing import Any, List, Optional, cast
-from jinja2 import pass_environment, Environment
-from markdown2 import Markdown
-from markupsafe import Markup, escape as markupsafe_escape
+import yaml
+from jinja2 import Environment, pass_environment
+from markdown2 import Markdown # type: ignore
+from markupsafe import Markup
+from markupsafe import escape as markupsafe_escape
from pygments import highlight
from pygments.formatters.html import HtmlFormatter
-from pygments.lexers.javascript import JavascriptLexer
from pygments.lexers.data import YamlLexer
+from pygments.lexers.javascript import JavascriptLexer
from pytz import reference
from json_schema_for_humans import const
from json_schema_for_humans.generation_configuration import GenerationConfiguration
from json_schema_for_humans.schema.schema_node import SchemaNode
+from json_schema_for_humans.templating_utils import schema_keyword_convert_to_str, schema_keyword_to_str
SHORT_DESCRIPTION_NUMBER_OF_LINES = 8
DEFAULT_PATTERN = r"(\[Default - `([^`]+)`\])"
@@ -47,13 +48,14 @@ def is_deprecated_look_in_description(schema_node: SchemaNode) -> bool:
if const.DESCRIPTION not in schema_node.keywords:
return False
- return bool(DEPRECATED_MARKER in schema_node.keywords[const.DESCRIPTION].literal)
+ return bool(DEPRECATED_MARKER in (schema_node.keywords[const.DESCRIPTION].literal_str or ""))
def get_required_properties(schema_node: SchemaNode) -> List[str]:
- required_properties = schema_node.keywords.get("required") or []
- if required_properties:
- required_properties = [p.literal for p in required_properties.array_items]
+ required_properties_node: Optional[SchemaNode] = schema_node.keywords.get("required")
+ required_properties: List[str] = []
+ if required_properties_node:
+ required_properties = [p.literal_str for p in required_properties_node.array_items if p.literal_str]
return required_properties
@@ -96,7 +98,7 @@ def get_description(env: Environment, schema_node: SchemaNode) -> str:
def get_description_literal(env: Environment, description: str) -> str:
"""Filter. Get the description of a property or an empty string"""
- config: GenerationConfiguration = env.globals["jsfh_config"]
+ config: GenerationConfiguration = cast(GenerationConfiguration, env.globals["jsfh_config"])
if config.default_from_description:
match = re.match(DEFAULT_PATTERN, description)
if match:
@@ -112,7 +114,7 @@ def get_description_literal(env: Environment, description: str) -> str:
def get_default(schema_node: SchemaNode) -> str:
"""Filter. Return the default value for a property"""
- return schema_node.default_value
+ return str(schema_node.default_value) if schema_node.default_value is not None else ""
def get_default_look_in_description(schema_node: SchemaNode) -> str:
@@ -121,10 +123,9 @@ def get_default_look_in_description(schema_node: SchemaNode) -> str:
if default_value:
return default_value
- description = schema_node.keywords.get(const.DESCRIPTION)
+ description = schema_keyword_to_str(schema_node, const.DESCRIPTION)
if not description:
return ""
- description = description.literal
match = re.match(DEFAULT_PATTERN, description)
if not match:
@@ -135,21 +136,11 @@ def get_default_look_in_description(schema_node: SchemaNode) -> str:
def get_numeric_restrictions_text(schema_node: SchemaNode, before_value: str = "", after_value: str = "") -> str:
"""Filter. Get the text to display about restrictions on a numeric type(integer or number)"""
- multiple_of = schema_node.keywords.get(const.MULTIPLE_OF)
- if multiple_of:
- multiple_of = multiple_of.literal
- maximum = schema_node.keywords.get(const.MAXIMUM)
- if maximum:
- maximum = maximum.literal
- exclusive_maximum = schema_node.keywords.get(const.EXCLUSIVE_MAXIMUM)
- if exclusive_maximum:
- exclusive_maximum = exclusive_maximum.literal
- minimum = schema_node.keywords.get(const.MINIMUM)
- if minimum:
- minimum = minimum.literal
- exclusive_minimum = schema_node.keywords.get(const.EXCLUSIVE_MINIMUM)
- if exclusive_minimum:
- exclusive_minimum = exclusive_minimum.literal
+ multiple_of = schema_keyword_convert_to_str(schema_node, const.MULTIPLE_OF)
+ maximum = schema_keyword_convert_to_str(schema_node, const.MAXIMUM)
+ exclusive_maximum = schema_keyword_convert_to_str(schema_node, const.EXCLUSIVE_MAXIMUM)
+ minimum = schema_keyword_convert_to_str(schema_node, const.MINIMUM)
+ exclusive_minimum = schema_keyword_convert_to_str(schema_node, const.EXCLUSIVE_MINIMUM)
# Fix minimum and exclusive_minimum both there
if minimum is not None and exclusive_minimum is not None:
diff --git a/json_schema_for_humans/md_template.py b/json_schema_for_humans/md_template.py
index 0528cfa4..f98ed26f 100644
--- a/json_schema_for_humans/md_template.py
+++ b/json_schema_for_humans/md_template.py
@@ -1,20 +1,17 @@
-from typing import Dict, List, Union, Optional
-from urllib.parse import quote_plus, quote
+from typing import Dict, List, Optional, Union
+from urllib.parse import quote, quote_plus
import jinja2
from json_schema_for_humans import const, jinja_filters
from json_schema_for_humans.schema.schema_node import SchemaNode
+from json_schema_for_humans.templating_utils import schema_keyword_to_int
def get_numeric_maximum_restriction(schema_node: SchemaNode, default: str = "N/A") -> str:
"""Filter. Get the text to display about maximum restriction on a numeric type(integer or number)"""
- maximum = schema_node.keywords.get(const.MAXIMUM)
- if maximum:
- maximum = maximum.literal
- exclusive_maximum = schema_node.keywords.get(const.EXCLUSIVE_MAXIMUM)
- if exclusive_maximum:
- exclusive_maximum = exclusive_maximum.literal
+ maximum = schema_keyword_to_int(schema_node, const.MAXIMUM)
+ exclusive_maximum = schema_keyword_to_int(schema_node, const.EXCLUSIVE_MAXIMUM)
# Fix maximum and exclusive_maximum both there
if maximum is not None and exclusive_maximum is not None:
@@ -36,17 +33,13 @@ def escape_for_table(example_text: Optional[str]) -> str:
"""Filter. escape characters('|', '`') in string to be inserted into markdown table"""
if example_text is None:
return ""
- return example_text.translate(str.maketrans({"|": "\\|", "`": "\\`", "\n": "
"}))
+ return example_text.translate(str.maketrans({"|": "\\|", "`": "\\`", "\n": "
"})) # type:ignore[arg-type]
def get_numeric_minimum_restriction(schema_node: SchemaNode, default: str = "N/A") -> str:
"""Filter. Get the text to display about minimum restriction on a numeric type (integer or number)"""
- minimum = schema_node.keywords.get(const.MINIMUM)
- if minimum:
- minimum = minimum.literal
- exclusive_minimum = schema_node.keywords.get(const.EXCLUSIVE_MINIMUM)
- if exclusive_minimum:
- exclusive_minimum = exclusive_minimum.literal
+ minimum = schema_keyword_to_int(schema_node, const.MINIMUM)
+ exclusive_minimum = schema_keyword_to_int(schema_node, const.EXCLUSIVE_MINIMUM)
# Fix minimum and exclusive_minimum both there
if minimum is not None and exclusive_minimum is not None:
@@ -109,9 +102,9 @@ def restrictions_table(schema: SchemaNode) -> List[List[str]]:
restrictions.append(["**Min length**", str(schema.kw_min_length.literal)])
if schema.kw_max_length:
restrictions.append(["**Max length**", str(schema.kw_max_length.literal)])
- if schema.kw_pattern:
- pattern_code = schema.kw_pattern.literal.replace("|", "\\|")
- pattern_url = quote_plus(schema.kw_pattern.literal)
+ if schema.kw_pattern and schema.kw_pattern.literal_str:
+ pattern_code = schema.kw_pattern.literal_str.replace("|", "\\|")
+ pattern_url = quote_plus(schema.kw_pattern.literal_str)
example_url = ""
if len(schema.examples) > 0:
example_url = "&testString=" + quote_plus(schema.examples[0])
@@ -121,11 +114,11 @@ def restrictions_table(schema: SchemaNode) -> List[List[str]]:
f"```{pattern_code}``` [Test](https://regex101.com/?regex={pattern_url}{example_url})",
]
)
- if schema.keywords.get("multipleOf"):
- restrictions.append(["**Multiple of**", str(schema.keywords.get("multipleOf").literal)])
- if schema.keywords.get("minimum") or schema.keywords.get("exclusiveMinimum"):
+ if schema.kw_multiple_of and schema.kw_multiple_of.literal_to_str:
+ restrictions.append(["**Multiple of**", schema.kw_multiple_of.literal_to_str])
+ if schema.kw_minimum or schema.kw_exclusive_minimum:
restrictions.append(["**Minimum**", str(get_numeric_minimum_restriction(schema))])
- if schema.keywords.get("maximum") or schema.keywords.get("exclusiveMaximum"):
+ if schema.kw_maximum or schema.kw_exclusive_maximum:
restrictions.append(["**Maximum**", str(get_numeric_maximum_restriction(schema))])
if len(restrictions) > 0:
@@ -153,7 +146,7 @@ def array_items(schema: SchemaNode, title: str) -> List[List[str]]:
def first_line_fixed(example_text: str) -> str:
"""first_line but replace ` with ' to avoid unbalanced `s in markdown"""
- return jinja_filters.first_line(example_text).translate(str.maketrans({"`": "'"}))
+ return jinja_filters.first_line(example_text).translate(str.maketrans({"`": "'"})) # type:ignore[arg-type]
def array_items_restrictions(schema: SchemaNode) -> List[List[str]]:
@@ -350,6 +343,7 @@ def properties_table(self, schema: SchemaNode) -> List[List]:
elif field == "Definition":
# Link
if sub_property.should_be_a_link(self.config):
+ assert sub_property.links_to
line.append(
"Same as "
+ self.format_link(sub_property.links_to.link_name, sub_property.links_to.html_id)
@@ -392,6 +386,7 @@ def type_info_table(self, schema: SchemaNode) -> List[List]:
schema_format = schema.format
merged_schema = schema if not schema.refers_to else schema.refers_to_merged
+ assert merged_schema
type_info.append(["", ""])
type_info.append(
@@ -409,6 +404,7 @@ def type_info_table(self, schema: SchemaNode) -> List[List]:
if default_value:
type_info.append(["**Default**", f"`{default_value}`"])
if schema.should_be_a_link(self.config):
+ assert schema.links_to
schema_link_name = schema.links_to.link_name
html_id = schema.links_to.html_id
type_info.append(["**Same definition as**", f"[{ schema_link_name }](#{ html_id })"])
@@ -465,10 +461,12 @@ def array_restrictions(self, schema: SchemaNode) -> List[List[str]]:
],
[
"**Tuple validation**",
- "See below"
- if schema.array_items_def
- or schema.tuple_validation_items
- or (schema.kw_contains and schema.kw_contains.literal != {})
- else "N/A",
+ (
+ "See below"
+ if schema.array_items_def
+ or schema.tuple_validation_items
+ or (schema.kw_contains and schema.kw_contains.literal != {})
+ else "N/A"
+ ),
],
]
diff --git a/json_schema_for_humans/schema/intermediate_representation.py b/json_schema_for_humans/schema/intermediate_representation.py
index b720ec58..eb0dcafa 100644
--- a/json_schema_for_humans/schema/intermediate_representation.py
+++ b/json_schema_for_humans/schema/intermediate_representation.py
@@ -6,6 +6,7 @@
import urllib.parse
from collections import defaultdict
from pathlib import Path
+from tempfile import _TemporaryFileWrapper
from typing import Any, Dict, List, Optional, Tuple, Union
import requests
@@ -55,7 +56,7 @@ def _escape_html_id(config: GenerationConfiguration, html_id: str) -> str:
def build_intermediate_representation(
- schema_path: Union[str, Path, FileLikeType],
+ schema_path: Union[str, Path, FileLikeType, _TemporaryFileWrapper],
config: GenerationConfiguration,
loaded_schemas: Optional[Dict[str, Any]] = None,
) -> SchemaNode:
@@ -98,11 +99,11 @@ def defaultdict_list() -> Dict[Any, List]:
def _record_ref(
resolved_references: Dict[str, Dict[str, SchemaNode]],
schema_real_path: str,
- path_to_element: List[Union[str, int]],
+ path_to_element: List[str],
current_node: SchemaNode,
) -> None:
"""Record that the node is describing the schema at the provided path"""
- path_to_element_str = "/".join(str(e) for e in path_to_element)
+ path_to_element_str = "/".join(path_to_element)
resolved_references[schema_real_path][path_to_element_str] = current_node
@@ -152,7 +153,7 @@ def _find_ref(
return None, None
# Check if the node has already been documented (also for infinite loops)
- already_resolved = resolved_references.get(referenced_schema_path, {}).get(anchor_part, {})
+ already_resolved: Optional[SchemaNode] = resolved_references.get(referenced_schema_path, {}).get(anchor_part)
if already_resolved:
# The node is already documented elsewhere. Let's link to it
current_node.is_displayed = False
@@ -187,6 +188,8 @@ def _find_ref(
other_is_better = False
i_am_better = True
+ assert other_user
+
# There is at least one other node having the same reference as the current node.
if other_is_better:
# The other referencing node is nearer to the user, so it will now be displayed
@@ -316,7 +319,7 @@ def _resolve_ref(
return new_reference, new_reference
-def _get_schema_path(schema_path: Union[str, Path, FileLikeType]) -> str:
+def _get_schema_path(schema_path: Union[str, Path, FileLikeType, _TemporaryFileWrapper]) -> str:
if isinstance(schema_path, Path):
return str(schema_path.resolve())
elif isinstance(schema_path, str):
@@ -358,7 +361,7 @@ def _load_schema_from_uri(schema_uri: str, loaded_schemas: Dict[str, Any]) -> Op
def _load_schema(
- schema_uri: str, path_to_element: List[Union[str, int]], loaded_schemas: Dict[str, Any]
+ schema_uri: str, path_to_element: List[str], loaded_schemas: Dict[str, Any]
) -> Optional[Union[Dict, List, int, str]]:
"""Load the schema at the provided path or URL.
@@ -369,20 +372,28 @@ def _load_schema(
loaded_schema = _load_schema_from_uri(schema_uri, loaded_schemas)
if path_to_element:
+ path_to_element_str = "/".join(path_to_element)
for path_part in path_to_element:
if not path_part:
# Empty string
continue
- try:
- path_part_int = int(path_part)
- loaded_schema = loaded_schema[path_part_int]
- continue
- except (KeyError, ValueError):
- # KeyError: The part looks like a int but it is a string (property named "0" for example
- # ValueError: Normal case, the path part is a string
+
+ if isinstance(loaded_schema, list):
+ try:
+ path_part_int = int(path_part)
+ loaded_schema = loaded_schema[path_part_int]
+ continue
+ except (IndexError, ValueError):
+ pass
+ elif isinstance(loaded_schema, dict):
if path_part == ROOT_ID:
return loaded_schema
- loaded_schema = loaded_schema[path_part]
+
+ if path_part in loaded_schema:
+ loaded_schema = loaded_schema[path_part]
+ continue
+
+ logging.warning(f"Path {path_to_element_str} not found in schema at URI {schema_uri}")
return loaded_schema
@@ -409,7 +420,7 @@ def _build_node(
breadcrumb_name: str,
property_name: Optional[str],
schema_file_path: str,
- path_to_element: List[Union[str, int]],
+ path_to_element: List[str],
schema: Optional[Union[Dict, List, int, str]],
parent: Optional[SchemaNode] = None,
parent_key: Optional[str] = None,
@@ -488,15 +499,33 @@ def _build_node(
# Examples are rendered in JSON because they will be represented that way in the documentation,
# no need for a SchemaNode object
if schema_key == SchemaKeyword.EXAMPLES.value:
- keywords[schema_key] = [
- json.dumps(example, indent=4, separators=(",", ": "), ensure_ascii=False)
- for example in schema_value
- ]
+ keywords[schema_key] = SchemaNode(
+ depth=depth + 1,
+ file=schema_file_path,
+ path_to_element=path_to_element + [schema_key],
+ html_id=_add_html_id_part(html_id, schema_key),
+ array_items=[
+ SchemaNode(
+ depth=depth + 1,
+ file=schema_file_path,
+ path_to_element=path_to_element + [schema_key],
+ html_id=_add_html_id_part(html_id, schema_key),
+ literal=json.dumps(example, indent=4, separators=(",", ": "), ensure_ascii=False),
+ )
+ for example in schema_value
+ ],
+ )
continue
# The default value will be printed as-is, no need for a SchemaNode object
if schema_key == "default":
- keywords[schema_key] = json.dumps(schema_value, ensure_ascii=False)
+ keywords[schema_key] = SchemaNode(
+ depth=depth + 1,
+ file=schema_file_path,
+ path_to_element=path_to_element + [schema_key],
+ html_id=_add_html_id_part(html_id, schema_key),
+ literal=json.dumps(schema_value, ensure_ascii=False),
+ )
continue
if schema_key == SchemaKeyword.PROPERTIES.value:
@@ -658,7 +687,7 @@ def _build_node(
breadcrumb_name=f"item {i}",
property_name=None,
schema_file_path=schema_file_path,
- path_to_element=path_to_element + [i],
+ path_to_element=path_to_element + [str(i)],
schema=element,
parent=new_node,
)
diff --git a/json_schema_for_humans/schema/schema_importer.py b/json_schema_for_humans/schema/schema_importer.py
index 73685725..521079dd 100644
--- a/json_schema_for_humans/schema/schema_importer.py
+++ b/json_schema_for_humans/schema/schema_importer.py
@@ -1,6 +1,6 @@
import os
from pathlib import Path
-from typing import Optional, List, Union
+from typing import List, Optional, Union
import click
@@ -38,6 +38,9 @@ def get_schemas_to_render(
if not result_file_or_dir:
return [SchemaToRender(schema_file_path, None, None) for schema_file_path in schema_file_paths]
+ if isinstance(result_file_or_dir, str):
+ result_file_or_dir = Path(result_file_or_dir)
+
# Compute output_dir where additional files will be copied if necessary
result_path_is_dir = Path(result_file_or_dir).is_dir()
output_dir = result_file_or_dir if result_path_is_dir else result_file_or_dir.parent
diff --git a/json_schema_for_humans/schema/schema_keyword.py b/json_schema_for_humans/schema/schema_keyword.py
index fb9fad4e..9b103096 100644
--- a/json_schema_for_humans/schema/schema_keyword.py
+++ b/json_schema_for_humans/schema/schema_keyword.py
@@ -15,6 +15,11 @@ class SchemaKeyword(Enum):
MIN_ITEMS = "minItems"
MAX_LENGTH = "maxLength"
MIN_LENGTH = "minLength"
+ MULTIPLE_OF = "multipleOf"
+ MINIMUM = "minimum"
+ EXCLUSIVE_MINIMUM = "exclusiveMinimum"
+ MAXIMUM = "maximum"
+ EXCLUSIVE_MAXIMUM = "exclusiveMaximum"
PATTERN = "pattern"
CONST = "const"
ENUM = "enum"
diff --git a/json_schema_for_humans/schema/schema_node.py b/json_schema_for_humans/schema/schema_node.py
index 78bd486a..063577fd 100644
--- a/json_schema_for_humans/schema/schema_node.py
+++ b/json_schema_for_humans/schema/schema_node.py
@@ -3,9 +3,9 @@
from typing import Any, Dict, Iterable, Iterator, List, Optional, Set, Union, cast
from json_schema_for_humans import const
-from json_schema_for_humans.schema.schema_keyword import SchemaKeyword
-from json_schema_for_humans.templating_utils import get_type_name
from json_schema_for_humans.generation_configuration import GenerationConfiguration
+from json_schema_for_humans.schema.schema_keyword import SchemaKeyword
+from json_schema_for_humans.templating_utils import get_type_name, schema_keyword_to_str
circular_references: Dict["SchemaNode", bool] = {}
@@ -23,22 +23,22 @@ def __init__(
self,
depth: int,
file: str,
- path_to_element: List[Union[str, int]],
+ path_to_element: List[str],
html_id: str,
breadcrumb_name: str = "",
ref_path="",
- parent: "SchemaNode" = None,
- parent_key: str = None,
+ parent: Optional["SchemaNode"] = None,
+ parent_key: Optional[str] = None,
literal: Optional[Union[str, int, bool]] = None,
- keywords: Dict[str, Union["SchemaNode", str, List[str]]] = None,
- array_items: List["SchemaNode"] = None,
+ keywords: Optional[Dict[str, "SchemaNode"]] = None,
+ array_items: Optional[List["SchemaNode"]] = None,
array_items_def: Optional["SchemaNode"] = None,
array_additional_items_def: Optional["SchemaNode"] = None,
array_additional_items: bool = False,
tuple_validation_items: Optional[List["SchemaNode"]] = None,
property_name: Optional[str] = None,
- links_to: "SchemaNode" = None,
- refers_to: "SchemaNode" = None,
+ links_to: Optional["SchemaNode"] = None,
+ refers_to: Optional["SchemaNode"] = None,
is_displayed: bool = True,
):
"""
@@ -183,12 +183,12 @@ def required_properties(self) -> List[str]:
if not required_properties:
return []
- return [r.literal for r in required_properties.array_items]
+ return [r.literal_str for r in required_properties.array_items if r.literal_str]
@property
def is_required_property(self) -> bool:
"""Check if the current node represents a property and that this property is required by its parent"""
- return self.parent and self.property_name in self.parent.required_properties
+ return bool(self.parent and self.property_name in self.parent.required_properties)
@property
def nodes_from_root(self) -> Iterator["SchemaNode"]:
@@ -201,7 +201,7 @@ def nodes_from_root(self) -> Iterator["SchemaNode"]:
if len(nodes) == 1:
# Don't want to display "root" alone at the root
- return []
+ return iter([])
return reversed(nodes)
@@ -218,7 +218,7 @@ def path_to_property(self) -> str:
@property
def flat_path(self) -> str:
"""String representation of the path to this node from the root of the current schema"""
- return "/".join(str(part) for part in self.path_to_element)
+ return "/".join(self.path_to_element)
@property
def default_value(self) -> Optional[Any]:
@@ -226,7 +226,7 @@ def _default_value(node: SchemaNode) -> Optional[Any]:
default = node.keywords.get(const.DEFAULT)
if isinstance(default, SchemaNode) and default.is_a_property_node:
return None
- return default
+ return default.literal if default else ""
seen = set()
current_node = self
@@ -244,15 +244,12 @@ def _default_value(node: SchemaNode) -> Optional[Any]:
def format(self) -> Optional[str]:
format_val = self.keywords.get(const.FORMAT)
if format_val:
- return format_val.literal
+ return format_val.literal_str
return None
@property
def description(self) -> str:
- description = ""
- description_node = self.keywords.get(const.DESCRIPTION)
- if description_node:
- description = description_node.literal
+ description = schema_keyword_to_str(self, const.DESCRIPTION)
seen = set()
current_node = self
@@ -263,10 +260,24 @@ def description(self) -> str:
referenced_schema = current_node.refers_to
referenced_description_node = referenced_schema.keywords.get(const.DESCRIPTION)
if referenced_description_node:
- description = referenced_description_node.literal
+ description = referenced_description_node.literal_str
current_node = referenced_schema
- return description
+ return description or ""
+
+ @property
+ def literal_str(self) -> Optional[str]:
+ """Return the literal value if it is a str"""
+ if isinstance(self.literal, str):
+ return self.literal
+ return None
+
+ @property
+ def literal_to_str(self) -> Optional[str]:
+ """Return the literal value converted to str"""
+ if self.literal:
+ return str(self.literal)
+ return None
@property
def examples(self) -> List[str]:
@@ -277,7 +288,7 @@ def examples(self) -> List[str]:
if isinstance(possible_examples, SchemaNode) and possible_examples.is_a_property_node:
return []
- return possible_examples
+ return [example.literal_str for example in possible_examples.array_items if example.literal_str]
@property
def refers_to_merged(self) -> Optional["SchemaNode"]:
@@ -377,6 +388,26 @@ def kw_min_length(self) -> Optional["SchemaNode"]:
def kw_max_length(self) -> Optional["SchemaNode"]:
return self.get_keyword(SchemaKeyword.MAX_LENGTH)
+ @property
+ def kw_multiple_of(self) -> Optional["SchemaNode"]:
+ return self.get_keyword(SchemaKeyword.MULTIPLE_OF)
+
+ @property
+ def kw_minimum(self) -> Optional["SchemaNode"]:
+ return self.get_keyword(SchemaKeyword.MINIMUM)
+
+ @property
+ def kw_exclusive_minimum(self) -> Optional["SchemaNode"]:
+ return self.get_keyword(SchemaKeyword.EXCLUSIVE_MINIMUM)
+
+ @property
+ def kw_maximum(self) -> Optional["SchemaNode"]:
+ return self.get_keyword(SchemaKeyword.MAXIMUM)
+
+ @property
+ def kw_exclusive_maximum(self) -> Optional["SchemaNode"]:
+ return self.get_keyword(SchemaKeyword.EXCLUSIVE_MAXIMUM)
+
@property
def kw_items(self) -> Optional[List["SchemaNode"]]:
"""items can be either an object either a list of object"""
@@ -473,7 +504,10 @@ def enum_description(self, value: str) -> Optional["SchemaNode"]:
meta_enum_node = self.get_keyword(SchemaKeyword.META_ENUM)
if not meta_enum_node:
return None
- description = meta_enum_node.raw.get(value)
+ raw_node = meta_enum_node.raw
+ if not isinstance(raw_node, dict):
+ return None
+ description = raw_node.get(value)
if not description:
return None
return description
diff --git a/json_schema_for_humans/schema/schema_to_render.py b/json_schema_for_humans/schema/schema_to_render.py
index 8ac4934a..182e05d8 100644
--- a/json_schema_for_humans/schema/schema_to_render.py
+++ b/json_schema_for_humans/schema/schema_to_render.py
@@ -1,6 +1,8 @@
from dataclasses import dataclass
+from io import FileIO
from pathlib import Path
-from typing import Optional, Dict, Any, Union
+from tempfile import _TemporaryFileWrapper
+from typing import Any, Dict, Optional, TextIO, Union
from json_schema_for_humans.const import FileLikeType
from json_schema_for_humans.schema.intermediate_representation import build_intermediate_representation
@@ -15,8 +17,8 @@ class NoResultFileError(EnvironmentError):
class SchemaToRender:
def __init__(
self,
- schema_file: Union[Path, FileLikeType],
- result_file: Optional[Union[Path, FileLikeType]],
+ schema_file: Union[Path, FileLikeType, _TemporaryFileWrapper],
+ result_file: Optional[Union[Path, FileLikeType, _TemporaryFileWrapper]],
output_dir: Optional[Path],
):
self.schema_file = schema_file
@@ -68,6 +70,8 @@ def _write_to_disk(self, rendered: str):
if isinstance(self.result_file, Path):
with self.result_file.open("w", encoding="utf-8") as fp:
fp.write(rendered)
+ elif isinstance(self.result_file, FileIO):
+ self.result_file.write(rendered.encode("utf-8"))
else:
# self.result_file is file-like object
self.result_file.write(rendered)
diff --git a/json_schema_for_humans/template_renderer.py b/json_schema_for_humans/template_renderer.py
index 6245916d..6f25b630 100644
--- a/json_schema_for_humans/template_renderer.py
+++ b/json_schema_for_humans/template_renderer.py
@@ -1,8 +1,9 @@
import re
+from typing import Optional
-import htmlmin
+import htmlmin # type: ignore # No stubs available
import jinja2
-import markdown2
+import markdown2 # type: ignore # No stubs available
from jinja2 import FileSystemLoader, Template
from jinja2.ext import loopcontrols
@@ -21,7 +22,7 @@ def _minify(rendered: str, is_markdown: bool, is_html: bool) -> str:
class TemplateRenderer:
- def __init__(self, config: GenerationConfiguration, template: Template = None):
+ def __init__(self, config: GenerationConfiguration, template: Optional[Template] = None):
self.config = config
self.template = template or self._get_jinja_template()
diff --git a/json_schema_for_humans/templating_utils.py b/json_schema_for_humans/templating_utils.py
index e3cb1cca..8030f600 100644
--- a/json_schema_for_humans/templating_utils.py
+++ b/json_schema_for_humans/templating_utils.py
@@ -1,4 +1,4 @@
-from typing import List, Optional, Type, Union, TYPE_CHECKING
+from typing import TYPE_CHECKING, List, Optional, Type, Union
from json_schema_for_humans import const
@@ -6,10 +6,38 @@
from json_schema_for_humans.schema.schema_node import SchemaNode
+def schema_keyword_to_int(schema_node: "SchemaNode", keyword: str) -> Optional[int]:
+ """Extract an integer from a schema keyword"""
+ keyword_value = schema_node.keywords.get(keyword)
+ if keyword_value:
+ keyword_value_literal = keyword_value.literal
+ if isinstance(keyword_value_literal, int):
+ return keyword_value_literal
+ return None
+
+
+def schema_keyword_to_str(schema_node: "SchemaNode", keyword: str) -> Optional[str]:
+ """Extract a str from a schema keyword or return None"""
+ keyword_value = schema_node.keywords.get(keyword)
+ if keyword_value:
+ keyword_value_literal = keyword_value.literal
+ if isinstance(keyword_value_literal, str):
+ return keyword_value_literal
+ return None
+
+
+def schema_keyword_convert_to_str(schema_node: "SchemaNode", keyword: str) -> Optional[str]:
+ """Extract and convert to str from a schema keyword or return None"""
+ keyword_value = schema_node.keywords.get(keyword)
+ if keyword_value:
+ return str(keyword_value.literal)
+ return None
+
+
def get_type_name(schema_node: "SchemaNode") -> Optional[str]:
"""Filter. Return the type of a property taking into account the type of items for array and enum"""
- def _python_type_to_json_type(python_type: Type[Union[str, int, float, bool, list, dict]]) -> str:
+ def _python_type_to_json_type(python_type: Type[Union[str, int, float, bool, list, dict, None]]) -> str:
return {
str: const.TYPE_STRING,
int: const.TYPE_INTEGER,
@@ -36,9 +64,10 @@ def _add_subtype_if_array(type_name: str):
if not items:
return type_name
- subtype = items.keywords.get(const.TYPE)
- if subtype:
- subtype = subtype.literal
+ subtype_node = items.keywords.get(const.TYPE)
+ subtype: Optional[str] = None
+ if subtype_node:
+ subtype = subtype_node.literal_str
if const.TYPE_ENUM in items.keywords:
subtype = _enum_type(items.keywords[const.TYPE_ENUM].array_items)
@@ -56,16 +85,20 @@ def _add_subtype_if_array(type_name: str):
return _enum_type(schema_node.keywords[const.TYPE_ENUM].array_items)
type_node = schema_node.keywords.get(const.TYPE)
+ type_names: List[str] = []
if type_node:
if isinstance(type_node, str):
type_names = [type_node]
elif type_node.array_items:
- type_names = [node.literal for node in type_node.array_items]
- else:
- type_names = [type_node.literal]
+ type_names = [node.literal_str for node in type_node.array_items if node.literal_str]
+ elif type_node.literal_str:
+ type_names = [type_node.literal_str]
else:
return None
+ if not type_names:
+ return None
+
type_names = [_add_subtype_if_array(type_name) for type_name in type_names]
return ", ".join(type_names[:-1]) + (" or " if len(type_names) > 1 else "") + type_names[-1]
diff --git a/poetry.lock b/poetry.lock
index 944c2476..5bc9108b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "atomicwrites"
@@ -12,40 +12,41 @@ files = [
[[package]]
name = "attrs"
-version = "23.1.0"
+version = "23.2.0"
description = "Classes Without Boilerplate"
optional = false
python-versions = ">=3.7"
files = [
- {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"},
- {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"},
+ {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
+ {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
]
-[package.dependencies]
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
-
[package.extras]
cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
-dev = ["attrs[docs,tests]", "pre-commit"]
+dev = ["attrs[tests]", "pre-commit"]
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
tests = ["attrs[tests-no-zope]", "zope-interface"]
-tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
+tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
[[package]]
name = "beautifulsoup4"
-version = "4.12.2"
+version = "4.12.3"
description = "Screen-scraping library"
optional = false
python-versions = ">=3.6.0"
files = [
- {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"},
- {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"},
+ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"},
+ {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"},
]
[package.dependencies]
soupsieve = ">1.2"
[package.extras]
+cchardet = ["cchardet"]
+chardet = ["chardet"]
+charset-normalizer = ["charset-normalizer"]
html5lib = ["html5lib"]
lxml = ["lxml"]
@@ -76,7 +77,6 @@ mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
-typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""}
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
[package.extras]
@@ -87,13 +87,13 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "certifi"
-version = "2023.11.17"
+version = "2024.2.2"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
- {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
- {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
+ {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
+ {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
]
[[package]]
@@ -208,7 +208,6 @@ files = [
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
[[package]]
name = "colorama"
@@ -252,35 +251,15 @@ files = [
[[package]]
name = "idna"
-version = "3.6"
+version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
- {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
- {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
-]
-
-[[package]]
-name = "importlib-metadata"
-version = "6.7.0"
-description = "Read metadata from Python packages"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"},
- {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"},
+ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
+ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
-[package.dependencies]
-typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
-zipp = ">=0.5"
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
-
[[package]]
name = "iniconfig"
version = "2.0.0"
@@ -294,13 +273,13 @@ files = [
[[package]]
name = "jinja2"
-version = "3.1.2"
+version = "3.1.3"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
files = [
- {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
- {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
+ {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"},
+ {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
]
[package.dependencies]
@@ -311,13 +290,13 @@ i18n = ["Babel (>=2.7)"]
[[package]]
name = "markdown2"
-version = "2.4.12"
+version = "2.4.13"
description = "A fast and complete Python implementation of Markdown"
optional = false
python-versions = ">=3.5, <4"
files = [
- {file = "markdown2-2.4.12-py2.py3-none-any.whl", hash = "sha256:98f47591006f0ace0644cbece03fed6f3845513286f6c6e9f8bcf6a575174e2c"},
- {file = "markdown2-2.4.12.tar.gz", hash = "sha256:1bc8692696954d597778e0e25713c14ca56d87992070dedd95c17eddaf709204"},
+ {file = "markdown2-2.4.13-py2.py3-none-any.whl", hash = "sha256:855bde5cbcceb9beda7c80efdf7f406c23e6079172c497fcfce22fdce998e892"},
+ {file = "markdown2-2.4.13.tar.gz", hash = "sha256:18ceb56590da77f2c22382e55be48c15b3c8f0c71d6398def387275e6c347a9f"},
]
[package.extras]
@@ -327,81 +306,90 @@ wavedrom = ["wavedrom"]
[[package]]
name = "markupsafe"
-version = "2.1.3"
+version = "2.1.5"
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-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.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
+ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
]
[[package]]
name = "marshmallow"
-version = "3.19.0"
+version = "3.21.2"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "marshmallow-3.19.0-py3-none-any.whl", hash = "sha256:93f0958568da045b0021ec6aeb7ac37c81bfcccbb9a0e7ed8559885070b3a19b"},
- {file = "marshmallow-3.19.0.tar.gz", hash = "sha256:90032c0fd650ce94b6ec6dc8dfeb0e3ff50c144586462c389b81a07205bedb78"},
+ {file = "marshmallow-3.21.2-py3-none-any.whl", hash = "sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1"},
+ {file = "marshmallow-3.21.2.tar.gz", hash = "sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56"},
]
[package.dependencies]
packaging = ">=17.0"
[package.extras]
-dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"]
-docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.3.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
-lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)"]
+dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
+docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"]
tests = ["pytest", "pytz", "simplejson"]
[[package]]
@@ -418,6 +406,53 @@ files = [
[package.dependencies]
marshmallow = ">=2.0.0"
+[[package]]
+name = "mypy"
+version = "1.10.0"
+description = "Optional static typing for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"},
+ {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"},
+ {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"},
+ {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"},
+ {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"},
+ {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"},
+ {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"},
+ {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"},
+ {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"},
+ {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"},
+ {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"},
+ {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"},
+ {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"},
+ {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"},
+ {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"},
+ {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"},
+ {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"},
+ {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"},
+ {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"},
+ {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"},
+ {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"},
+ {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"},
+ {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"},
+ {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"},
+ {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"},
+ {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"},
+ {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"},
+]
+
+[package.dependencies]
+mypy-extensions = ">=1.0.0"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+typing-extensions = ">=4.1.0"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+install-types = ["pip"]
+mypyc = ["setuptools (>=50)"]
+reports = ["lxml"]
+
[[package]]
name = "mypy-extensions"
version = "1.0.0"
@@ -431,58 +466,53 @@ files = [
[[package]]
name = "packaging"
-version = "23.2"
+version = "24.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
- {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
- {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
+ {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
+ {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
]
[[package]]
name = "pathspec"
-version = "0.11.2"
+version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
- {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
+ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
+ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
]
[[package]]
name = "platformdirs"
-version = "4.0.0"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+version = "4.2.1"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"},
- {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"},
+ {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"},
+ {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"},
]
-[package.dependencies]
-typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""}
-
[package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
+docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
+type = ["mypy (>=1.8)"]
[[package]]
name = "pluggy"
-version = "1.2.0"
+version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"},
- {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"},
+ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+ {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
-[package.dependencies]
-importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
-
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
@@ -528,7 +558,6 @@ files = [
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
-importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
@@ -540,13 +569,13 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm
[[package]]
name = "pytz"
-version = "2023.3.post1"
+version = "2024.1"
description = "World timezone definitions, modern and historical"
optional = false
python-versions = "*"
files = [
- {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
- {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
+ {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
+ {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
]
[[package]]
@@ -621,13 +650,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "soupsieve"
-version = "2.4.1"
+version = "2.5"
description = "A modern CSS selector implementation for Beautiful Soup."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"},
- {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"},
+ {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
+ {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
]
[[package]]
@@ -653,64 +682,112 @@ files = [
]
[[package]]
-name = "typed-ast"
-version = "1.5.5"
-description = "a fork of Python 2 and 3 ast modules with type comment support"
+name = "types-beautifulsoup4"
+version = "4.12.0.20240229"
+description = "Typing stubs for beautifulsoup4"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
+files = [
+ {file = "types-beautifulsoup4-4.12.0.20240229.tar.gz", hash = "sha256:e37e4cfa11b03b01775732e56d2c010cb24ee107786277bae6bc0fa3e305b686"},
+ {file = "types_beautifulsoup4-4.12.0.20240229-py3-none-any.whl", hash = "sha256:000cdddb8aee4effb45a04be95654de8629fb8594a4f2f1231cff81108977324"},
+]
+
+[package.dependencies]
+types-html5lib = "*"
+
+[[package]]
+name = "types-docutils"
+version = "0.21.0.20240423"
+description = "Typing stubs for docutils"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "types-docutils-0.21.0.20240423.tar.gz", hash = "sha256:7716ec6c68b5179b7ba1738cace2f1326e64df9f44b7ab08d9904d32c23fc15f"},
+ {file = "types_docutils-0.21.0.20240423-py3-none-any.whl", hash = "sha256:7f6e84ba8fcd2454c5b8bb8d77384d091a901929cc2b31079316e10eb346580a"},
+]
+
+[[package]]
+name = "types-html5lib"
+version = "1.1.11.20240228"
+description = "Typing stubs for html5lib"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "types-html5lib-1.1.11.20240228.tar.gz", hash = "sha256:22736b7299e605ec4ba539d48691e905fd0c61c3ea610acc59922232dc84cede"},
+ {file = "types_html5lib-1.1.11.20240228-py3-none-any.whl", hash = "sha256:af5de0125cb0fe5667543b158db83849b22e25c0e36c9149836b095548bf1020"},
+]
+
+[[package]]
+name = "types-pygments"
+version = "2.17.0.20240310"
+description = "Typing stubs for Pygments"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "types-Pygments-2.17.0.20240310.tar.gz", hash = "sha256:b1d97e905ce36343c7283b0319182ae6d4f967188f361f45502a18ae43e03e1f"},
+ {file = "types_Pygments-2.17.0.20240310-py3-none-any.whl", hash = "sha256:b101ca9448aaff52af6966506f1fdd73b1e60a79b8a79a8bace3366cbf1f7ed9"},
+]
+
+[package.dependencies]
+types-docutils = "*"
+types-setuptools = "*"
+
+[[package]]
+name = "types-pytz"
+version = "2024.1.0.20240417"
+description = "Typing stubs for pytz"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "types-pytz-2024.1.0.20240417.tar.gz", hash = "sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981"},
+ {file = "types_pytz-2024.1.0.20240417-py3-none-any.whl", hash = "sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659"},
+]
+
+[[package]]
+name = "types-pyyaml"
+version = "6.0.12.20240311"
+description = "Typing stubs for PyYAML"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "types-PyYAML-6.0.12.20240311.tar.gz", hash = "sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342"},
+ {file = "types_PyYAML-6.0.12.20240311-py3-none-any.whl", hash = "sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6"},
+]
+
+[[package]]
+name = "types-requests"
+version = "2.31.0.20240406"
+description = "Typing stubs for requests"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"},
+ {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"},
+]
+
+[package.dependencies]
+urllib3 = ">=2"
+
+[[package]]
+name = "types-setuptools"
+version = "69.5.0.20240423"
+description = "Typing stubs for setuptools"
+optional = false
+python-versions = ">=3.8"
files = [
- {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"},
- {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"},
- {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"},
- {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"},
- {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"},
- {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"},
- {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"},
- {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"},
- {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"},
- {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"},
- {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"},
- {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"},
- {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"},
- {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"},
- {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"},
- {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"},
- {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"},
- {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"},
- {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"},
- {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"},
- {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"},
- {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"},
- {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"},
- {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"},
- {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"},
- {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"},
- {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"},
- {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"},
- {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"},
- {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"},
- {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"},
- {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"},
- {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"},
- {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"},
- {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"},
- {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"},
- {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"},
- {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"},
- {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"},
- {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"},
- {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"},
+ {file = "types-setuptools-69.5.0.20240423.tar.gz", hash = "sha256:a7ba908f1746c4337d13f027fa0f4a5bcad6d1d92048219ba792b3295c58586d"},
+ {file = "types_setuptools-69.5.0.20240423-py3-none-any.whl", hash = "sha256:a4381e041510755a6c9210e26ad55b1629bc10237aeb9cb8b6bd24996b73db48"},
]
[[package]]
name = "typing-extensions"
-version = "4.7.1"
-description = "Backported and Experimental Type Hints for Python 3.7+"
+version = "4.11.0"
+description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"},
- {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"},
+ {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
+ {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
]
[[package]]
@@ -730,37 +807,22 @@ typing-extensions = ">=3.7.4"
[[package]]
name = "urllib3"
-version = "2.0.7"
+version = "2.2.1"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"},
- {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"},
+ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
+ {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
-secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"]
+h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
-[[package]]
-name = "zipp"
-version = "3.15.0"
-description = "Backport of pathlib-compatible object wrapper for zip files"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"},
- {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"},
-]
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
-
[metadata]
lock-version = "2.0"
-python-versions = "^3.7"
-content-hash = "0bc0bc8334b3b0641dff9f7ce6115212e73a066f75198a773ca90e8c61c2a3f1"
+python-versions = "^3.8"
+content-hash = "365de98ef1c44eb069b28f0b2df72c2778f048ee830166a26b38c4c2f68fea95"
diff --git a/pyproject.toml b/pyproject.toml
index efe1b90d..1f6c445c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,7 +8,7 @@ readme = "README.md"
repository = "https://github.com/coveooss/json-schema-for-humans"
classifiers = [
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Development Status :: 4 - Beta",
@@ -23,7 +23,7 @@ include = [
[tool.poetry.dependencies]
-python = "^3.7"
+python = "^3.8"
click = "^8.0.1"
dataclasses-json = "^0.5.6"
htmlmin = "^0.1.12"
@@ -34,12 +34,9 @@ pytz = "*"
PyYAML = ">=5.4.1,<7"
requests = "^2.31.0"
MarkupSafe = "^2.0"
-
-
-[tool.poetry.dev-dependencies]
-beautifulsoup4 = "^4.10.0"
-black = "^22.10"
-pytest = "^6.2.5"
+types-beautifulsoup4 = "^4.12.0.20240229"
+types-pyyaml = "^6.0.12.20240311"
+types-pytz = "^2024.1.0.20240417"
@@ -47,6 +44,15 @@ pytest = "^6.2.5"
generate-schema-doc = "json_schema_for_humans.cli:main"
+[tool.poetry.group.dev.dependencies]
+beautifulsoup4 = "^4.10.0"
+black = "^22.10"
+pytest = "^6.2.5"
+mypy = "^1.10.0"
+types-requests = "^2.31.0"
+types-pygments = "^2.17.0.20240310"
+
+
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
@@ -54,7 +60,7 @@ build-backend = "poetry.core.masonry.api"
[tool.black]
line-length = 120
-target-version = ["py36", "py37"]
+target-version = ["py311"]
include = "(json_schema_for_humans|tests).*\\.pyi?$|setup.py"
exclude = """
(
diff --git a/tests/cli_test.py b/tests/cli_test.py
index d97e147a..feaf2f34 100644
--- a/tests/cli_test.py
+++ b/tests/cli_test.py
@@ -9,8 +9,8 @@
from tests.test_utils import (
assert_css_and_js_copied,
assert_css_and_js_not_copied,
- get_test_case_path,
get_nonexistent_output_path,
+ get_test_case_path,
)
diff --git a/tests/generate_test.py b/tests/generate_test.py
index 57a4e3ed..afafffe9 100644
--- a/tests/generate_test.py
+++ b/tests/generate_test.py
@@ -1,16 +1,18 @@
import os
import tempfile
-from typing import Dict, Any
+from io import TextIOWrapper
+from typing import Any, Dict, cast
+from unittest.mock import Mock
import pytest
from bs4 import BeautifulSoup
-from unittest.mock import Mock
import tests.html_schema_doc_asserts
-from json_schema_for_humans.schema.schema_to_render import SchemaToRender
-from json_schema_for_humans.template_renderer import TemplateRenderer
+from json_schema_for_humans.const import FileLikeType
from json_schema_for_humans.generate import generate_from_file_object
from json_schema_for_humans.generation_configuration import GenerationConfiguration
+from json_schema_for_humans.schema.schema_to_render import SchemaToRender
+from json_schema_for_humans.template_renderer import TemplateRenderer
from tests.test_utils import generate_case
current_dir = os.path.abspath(os.path.dirname(__file__))
diff --git a/tests/generation_configuration_test.py b/tests/generation_configuration_test.py
index 4689292d..2f3835a3 100644
--- a/tests/generation_configuration_test.py
+++ b/tests/generation_configuration_test.py
@@ -1,5 +1,5 @@
-from json_schema_for_humans.generation_configuration import GenerationConfiguration
from json_schema_for_humans.const import DocumentationTemplate
+from json_schema_for_humans.generation_configuration import GenerationConfiguration
def test_default_values() -> None:
@@ -80,7 +80,7 @@ def test_override_template_md_options() -> None:
# override badge_as_image key
config = GenerationConfiguration(
deprecated_from_description=True,
- template_name=DocumentationTemplate.MD,
+ template_name=DocumentationTemplate.MD.value,
template_md_options={"badge_as_image": "test"},
)
assert config.template_md_options is not None
@@ -89,7 +89,7 @@ def test_override_template_md_options() -> None:
# override badge_as_image key
config = GenerationConfiguration(
deprecated_from_description=True,
- template_name=DocumentationTemplate.MD,
+ template_name=DocumentationTemplate.MD.value,
template_md_options={"badge_as_image": True},
)
assert config.template_md_options is not None
diff --git a/tests/html_schema_doc_asserts.py b/tests/html_schema_doc_asserts.py
index 19320f9e..bda28063 100644
--- a/tests/html_schema_doc_asserts.py
+++ b/tests/html_schema_doc_asserts.py
@@ -1,6 +1,6 @@
-from typing import List, Optional
+from typing import List, Optional, Union
-from bs4 import BeautifulSoup, Tag
+from bs4 import BeautifulSoup, NavigableString, Tag
def assert_soup_results_text(soup: BeautifulSoup, class_name: str, texts: List[str]) -> None:
@@ -28,7 +28,11 @@ def assert_enum_values(soup: BeautifulSoup, enum_values: List[List[str]]) -> Non
def assert_title(soup: BeautifulSoup, title: str) -> None:
"""Assert the result file contains the provided title"""
+ assert soup.head
+ assert soup.head.title
assert soup.head.title.string == title
+ assert soup.body
+ assert soup.body.h1
assert soup.body.h1.string == title
@@ -98,7 +102,7 @@ def assert_basic_case(soup: BeautifulSoup) -> None:
assert_required(soup, [False] * 4)
-def get_ref_link(soup: BeautifulSoup, ref_html_id: str) -> Optional[Tag]:
+def get_ref_link(soup: BeautifulSoup, ref_html_id: str) -> Optional[Union[Tag, NavigableString]]:
"""Get a link for a reused ref that redirects to a specified HTML id"""
return soup.find("a", href=ref_html_id, class_="ref-link")
diff --git a/tests/interface_test.py b/tests/interface_test.py
index c9cb882b..0b44ccab 100644
--- a/tests/interface_test.py
+++ b/tests/interface_test.py
@@ -1,7 +1,7 @@
import logging
import os
from pathlib import Path
-from unittest.mock import patch, MagicMock
+from unittest.mock import MagicMock, patch
import pytest
import yaml
@@ -16,10 +16,7 @@
generate_from_schema,
generate_schemas_doc,
)
-from json_schema_for_humans.generation_configuration import (
- GenerationConfiguration,
- CONFIG_DEPRECATION_MESSAGE,
-)
+from json_schema_for_humans.generation_configuration import CONFIG_DEPRECATION_MESSAGE, GenerationConfiguration
from json_schema_for_humans.schema.schema_importer import get_schemas_to_render
from json_schema_for_humans.template_renderer import TemplateRenderer
from tests.html_schema_doc_asserts import assert_basic_case
diff --git a/tests/md_utils_asserts.py b/tests/md_utils_asserts.py
index 736b1dce..d713669d 100644
--- a/tests/md_utils_asserts.py
+++ b/tests/md_utils_asserts.py
@@ -1,9 +1,10 @@
import os
+import re
+from typing import Optional
from json_schema_for_humans.generate import generate_from_schema
from json_schema_for_humans.generation_configuration import GenerationConfiguration
from tests.test_utils import get_test_case_path
-import re
GENERATED_TIMESTAMP_REGEXP = re.compile(
r"on [0-9]{4}-[0-9]{2}-[0-9]{2} at [0-9]{2}:[0-9]{2}:[0-9]{2} \+[0-9]{4}", re.IGNORECASE | re.MULTILINE
@@ -15,7 +16,7 @@ def get_expected_test_case_path(self, example_dir: str, name: str) -> str:
"""Get the expected md for a test case"""
return os.path.realpath(os.path.join(example_dir, f"{name}.md"))
- def generate_case(self, case_name: str, config: GenerationConfiguration = None) -> str:
+ def generate_case(self, case_name: str, config: Optional[GenerationConfiguration] = None) -> str:
"""Get the generated markdown schema string for a given schema test case"""
return generate_from_schema(get_test_case_path(case_name), None, config=config)
@@ -29,7 +30,7 @@ def get_expected_case(self, example_dir: str, test_case: str, case_name: str) ->
return content
def assert_case_equals(
- self, example_dir: str, test_case: str, case_name: str, config: GenerationConfiguration = None
+ self, example_dir: str, test_case: str, case_name: str, config: Optional[GenerationConfiguration] = None
) -> None:
content = self.generate_case(case_name, config)
expected_content = self.get_expected_case(example_dir, test_case, case_name)
diff --git a/tests/schema/schema_importer_test.py b/tests/schema/schema_importer_test.py
index a4a65a09..8157774b 100644
--- a/tests/schema/schema_importer_test.py
+++ b/tests/schema/schema_importer_test.py
@@ -1,6 +1,6 @@
from dataclasses import dataclass
from pathlib import Path
-from typing import Union, Dict, Any, List, Tuple
+from typing import Any, Dict, List, Tuple, Union
import pytest
from _pytest.monkeypatch import MonkeyPatch
@@ -78,8 +78,8 @@ def test_get_schema_paths(is_path: bool, case: GetSchemaPathsTestCase, tmpdir: P
monkeypatch.chdir(tmpdir)
- get_schema_path_arg = Path(case.input_path) if is_path else str(case.input_path)
- assert [str(p) for p in _get_schema_paths(get_schema_path_arg)] == [
+ get_schema_path_arg = Path(case.input_path) if is_path else case.input_path
+ assert [str(p) for p in _get_schema_paths(get_schema_path_arg)] == [ # type:ignore[arg-type]
# Convert to path and back to ensure normalization (e.g. running on Windows)
str(Path(p).absolute())
for p in case.expected_schema_paths
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 9a0174f9..b98c5d24 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,9 +1,10 @@
import os
from pathlib import Path
+from typing import Optional
from bs4 import BeautifulSoup
-from json_schema_for_humans.const import DEFAULT_JS_FILE_NAME, DEFAULT_CSS_FILE_NAME
+from json_schema_for_humans.const import DEFAULT_CSS_FILE_NAME, DEFAULT_JS_FILE_NAME
from json_schema_for_humans.generate import generate_from_schema
from json_schema_for_humans.generation_configuration import GenerationConfiguration
@@ -39,7 +40,7 @@ def get_nonexistent_output_path(name: str) -> str:
return os.path.realpath(os.path.join(parent_dir, "not", "a", "path", f"{name}.html"))
-def generate_case(case_name: str, config: GenerationConfiguration = None) -> BeautifulSoup:
+def generate_case(case_name: str, config: Optional[GenerationConfiguration] = None) -> BeautifulSoup:
"""Get the BeautifulSoup object for a test case"""
return BeautifulSoup(
generate_from_schema(get_test_case_path(case_name), None, config=config),
diff --git a/tox.ini b/tox.ini
index 20889869..5ae1b522 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py{3.7,3.8,3.9,3.10}
+envlist = py{3.8,3.9,3.10,3.11,3.12}
isolated_build = True
allowlist_externals =
poetry
@@ -10,7 +10,9 @@ commands_pre =
allowlist_externals =
{[tox]allowlist_externals}
black
+ mypy
pytest
commands =
black . --line-length 120 --check
+ mypy .
pytest {posargs}