mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
added basic typehints
This commit is contained in:
parent
9bcc9b4019
commit
a80bbfbd57
1 changed files with 103 additions and 71 deletions
|
|
@ -2,8 +2,13 @@ import os
|
|||
import re
|
||||
import copy
|
||||
import numbers
|
||||
from typing import List
|
||||
import warnings
|
||||
from string import Formatter
|
||||
import typing
|
||||
from typing import List, Dict, Any, Set, Optional
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from typing import Union
|
||||
|
||||
SUB_DICT_PATTERN = re.compile(r"([^\[\]]+)")
|
||||
OPTIONAL_PATTERN = re.compile(r"(<.*?[^{0]*>)[^0-9]*?")
|
||||
|
|
@ -19,9 +24,7 @@ class TemplateUnsolved(Exception):
|
|||
def __init__(self, template, missing_keys, invalid_types):
|
||||
invalid_type_items = []
|
||||
for _key, _type in invalid_types.items():
|
||||
invalid_type_items.append(
|
||||
"\"{0}\" {1}".format(_key, str(_type))
|
||||
)
|
||||
invalid_type_items.append(f"\"{_key}\" {str(_type)}")
|
||||
|
||||
invalid_types_msg = ""
|
||||
if invalid_type_items:
|
||||
|
|
@ -34,20 +37,21 @@ class TemplateUnsolved(Exception):
|
|||
missing_keys_msg = self.missing_keys_msg.format(
|
||||
", ".join(missing_keys)
|
||||
)
|
||||
super(TemplateUnsolved, self).__init__(
|
||||
super().__init__(
|
||||
self.msg.format(template, missing_keys_msg, invalid_types_msg)
|
||||
)
|
||||
|
||||
|
||||
class StringTemplate:
|
||||
"""String that can be formatted."""
|
||||
def __init__(self, template):
|
||||
def __init__(self, template: str):
|
||||
if not isinstance(template, str):
|
||||
raise TypeError("<{}> argument must be a string, not {}.".format(
|
||||
self.__class__.__name__, str(type(template))
|
||||
))
|
||||
raise TypeError(
|
||||
f"<{self.__class__.__name__}> argument must be a string,"
|
||||
f" not {str(type(template))}."
|
||||
)
|
||||
|
||||
self._template = template
|
||||
self._template: str = template
|
||||
parts = []
|
||||
formatter = Formatter()
|
||||
|
||||
|
|
@ -78,15 +82,17 @@ class StringTemplate:
|
|||
if substr:
|
||||
new_parts.append(substr)
|
||||
|
||||
self._parts = self.find_optional_parts(new_parts)
|
||||
self._parts: List["Union[str, OptionalPart, FormattingPart]"] = (
|
||||
self.find_optional_parts(new_parts)
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self.template
|
||||
|
||||
def __repr__(self):
|
||||
return "<{}> {}".format(self.__class__.__name__, self.template)
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__.__name__}> {self.template}"
|
||||
|
||||
def __contains__(self, other):
|
||||
def __contains__(self, other: str) -> bool:
|
||||
return other in self.template
|
||||
|
||||
def replace(self, *args, **kwargs):
|
||||
|
|
@ -94,10 +100,10 @@ class StringTemplate:
|
|||
return self
|
||||
|
||||
@property
|
||||
def template(self):
|
||||
def template(self) -> str:
|
||||
return self._template
|
||||
|
||||
def format(self, data):
|
||||
def format(self, data: Dict[str, Any]) -> "TemplateResult":
|
||||
""" Figure out with whole formatting.
|
||||
|
||||
Separate advanced keys (*Like '{project[name]}') from string which must
|
||||
|
|
@ -109,6 +115,7 @@ class StringTemplate:
|
|||
Returns:
|
||||
TemplateResult: Filled or partially filled template containing all
|
||||
data needed or missing for filling template.
|
||||
|
||||
"""
|
||||
result = TemplatePartResult()
|
||||
for part in self._parts:
|
||||
|
|
@ -136,23 +143,29 @@ class StringTemplate:
|
|||
invalid_types
|
||||
)
|
||||
|
||||
def format_strict(self, *args, **kwargs):
|
||||
result = self.format(*args, **kwargs)
|
||||
def format_strict(self, data: Dict[str, Any]) -> "TemplateResult":
|
||||
result = self.format(data)
|
||||
result.validate()
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def format_template(cls, template, data):
|
||||
def format_template(
|
||||
cls, template: str, data: Dict[str, Any]
|
||||
) -> "TemplateResult":
|
||||
objected_template = cls(template)
|
||||
return objected_template.format(data)
|
||||
|
||||
@classmethod
|
||||
def format_strict_template(cls, template, data):
|
||||
def format_strict_template(
|
||||
cls, template: str, data: Dict[str, Any]
|
||||
) -> "TemplateResult":
|
||||
objected_template = cls(template)
|
||||
return objected_template.format_strict(data)
|
||||
|
||||
@staticmethod
|
||||
def find_optional_parts(parts):
|
||||
def find_optional_parts(
|
||||
parts: List["Union[str, FormattingPart]"]
|
||||
) -> List["Union[str, OptionalPart, FormattingPart]"]:
|
||||
new_parts = []
|
||||
tmp_parts = {}
|
||||
counted_symb = -1
|
||||
|
|
@ -217,11 +230,11 @@ class TemplateResult(str):
|
|||
of number.
|
||||
"""
|
||||
|
||||
used_values = None
|
||||
solved = None
|
||||
template = None
|
||||
missing_keys = None
|
||||
invalid_types = None
|
||||
used_values: Dict[str, Any] = None
|
||||
solved: bool = None
|
||||
template: str = None
|
||||
missing_keys: List[str] = None
|
||||
invalid_types: Dict[str, Any] = None
|
||||
|
||||
def __new__(
|
||||
cls, filled_template, template, solved,
|
||||
|
|
@ -249,7 +262,7 @@ class TemplateResult(str):
|
|||
self.invalid_types
|
||||
)
|
||||
|
||||
def copy(self):
|
||||
def copy(self) -> "TemplateResult":
|
||||
cls = self.__class__
|
||||
return cls(
|
||||
str(self),
|
||||
|
|
@ -260,7 +273,7 @@ class TemplateResult(str):
|
|||
self.invalid_types
|
||||
)
|
||||
|
||||
def normalized(self):
|
||||
def normalized(self) -> "TemplateResult":
|
||||
"""Convert to normalized path."""
|
||||
|
||||
cls = self.__class__
|
||||
|
|
@ -276,27 +289,28 @@ class TemplateResult(str):
|
|||
|
||||
class TemplatePartResult:
|
||||
"""Result to store result of template parts."""
|
||||
def __init__(self, optional=False):
|
||||
def __init__(self, optional: bool = False):
|
||||
# Missing keys or invalid value types of required keys
|
||||
self._missing_keys = set()
|
||||
self._invalid_types = {}
|
||||
self._missing_keys: Set[str] = set()
|
||||
self._invalid_types: Dict[str, Any] = {}
|
||||
# Missing keys or invalid value types of optional keys
|
||||
self._missing_optional_keys = set()
|
||||
self._invalid_optional_types = {}
|
||||
self._missing_optional_keys: Set[str] = set()
|
||||
self._invalid_optional_types: Dict[str, Any] = {}
|
||||
|
||||
# Used values stored by key with origin type
|
||||
# - key without any padding or key modifiers
|
||||
# - value from filling data
|
||||
# Example: {"version": 1}
|
||||
self._used_values = {}
|
||||
self._used_values: Dict[str, Any] = {}
|
||||
# Used values stored by key with all modifirs
|
||||
# - value is already formatted string
|
||||
# Example: {"version:0>3": "001"}
|
||||
self._really_used_values: Dict[str, Any] = {}
|
||||
# Concatenated string output after formatting
|
||||
self._output = ""
|
||||
self._output: str = ""
|
||||
# Is this result from optional part
|
||||
self._optional = True
|
||||
# TODO find out why we don't use 'optional' from args
|
||||
self._optional: bool = True
|
||||
|
||||
def add_output(self, other):
|
||||
if isinstance(other, str):
|
||||
|
|
@ -322,7 +336,7 @@ class TemplatePartResult:
|
|||
)
|
||||
|
||||
@property
|
||||
def solved(self):
|
||||
def solved(self) -> bool:
|
||||
if self.optional:
|
||||
if (
|
||||
len(self.missing_optional_keys) > 0
|
||||
|
|
@ -335,27 +349,27 @@ class TemplatePartResult:
|
|||
)
|
||||
|
||||
@property
|
||||
def optional(self):
|
||||
def optional(self) -> bool:
|
||||
return self._optional
|
||||
|
||||
@property
|
||||
def output(self):
|
||||
def output(self) -> str:
|
||||
return self._output
|
||||
|
||||
@property
|
||||
def missing_keys(self):
|
||||
def missing_keys(self) -> Set[str]:
|
||||
return self._missing_keys
|
||||
|
||||
@property
|
||||
def missing_optional_keys(self):
|
||||
def missing_optional_keys(self) -> Set[str]:
|
||||
return self._missing_optional_keys
|
||||
|
||||
@property
|
||||
def invalid_types(self):
|
||||
def invalid_types(self) -> Dict[str, Any]:
|
||||
return self._invalid_types
|
||||
|
||||
@property
|
||||
def invalid_optional_types(self):
|
||||
def invalid_optional_types(self) -> Dict[str, Any]:
|
||||
return self._invalid_optional_types
|
||||
|
||||
@property
|
||||
|
|
@ -372,11 +386,11 @@ class TemplatePartResult:
|
|||
return self._really_used_values
|
||||
|
||||
@property
|
||||
def used_values(self):
|
||||
def used_values(self) -> Dict[str, Any]:
|
||||
return self._used_values
|
||||
|
||||
@staticmethod
|
||||
def split_keys_to_subdicts(values):
|
||||
def split_keys_to_subdicts(values: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output = {}
|
||||
formatter = Formatter()
|
||||
for key, value in values.items():
|
||||
|
|
@ -391,7 +405,7 @@ class TemplatePartResult:
|
|||
data[last_key] = value
|
||||
return output
|
||||
|
||||
def get_clean_used_values(self):
|
||||
def get_clean_used_values(self) -> Dict[str, Any]:
|
||||
new_used_values = {}
|
||||
for key, value in self.used_values.items():
|
||||
if isinstance(value, FormatObject):
|
||||
|
|
@ -411,16 +425,16 @@ class TemplatePartResult:
|
|||
)
|
||||
self.add_really_used_value(key, value)
|
||||
|
||||
def add_used_value(self, key, value):
|
||||
def add_used_value(self, key: str, value: Any):
|
||||
self._used_values[key] = value
|
||||
|
||||
def add_missing_key(self, key):
|
||||
def add_missing_key(self, key: str):
|
||||
if self._optional:
|
||||
self._missing_optional_keys.add(key)
|
||||
else:
|
||||
self._missing_keys.add(key)
|
||||
|
||||
def add_invalid_type(self, key, value):
|
||||
def add_invalid_type(self, key: str, value: Any):
|
||||
if self._optional:
|
||||
self._invalid_optional_types[key] = type(value)
|
||||
else:
|
||||
|
|
@ -438,10 +452,10 @@ class FormatObject:
|
|||
def __format__(self, *args, **kwargs):
|
||||
return self.value.__format__(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__()
|
||||
|
||||
|
||||
|
|
@ -451,9 +465,17 @@ class FormattingPart:
|
|||
Containt only single key to format e.g. "{project[name]}".
|
||||
|
||||
Args:
|
||||
template(str): String containing the formatting key.
|
||||
field_name (str): Name of key.
|
||||
format_spec (str): Format specification.
|
||||
conversion (Union[str, None]): Conversion type.
|
||||
|
||||
"""
|
||||
def __init__(self, field_name, format_spec, conversion):
|
||||
def __init__(
|
||||
self,
|
||||
field_name: str,
|
||||
format_spec: str,
|
||||
conversion: "Union[str, None]",
|
||||
):
|
||||
format_spec_v = ""
|
||||
if format_spec:
|
||||
format_spec_v = f":{format_spec}"
|
||||
|
|
@ -461,26 +483,26 @@ class FormattingPart:
|
|||
if conversion:
|
||||
conversion_v = f"!{conversion}"
|
||||
|
||||
self._field_name = field_name
|
||||
self._format_spec = format_spec_v
|
||||
self._conversion = conversion_v
|
||||
self._field_name: str = field_name
|
||||
self._format_spec: str = format_spec_v
|
||||
self._conversion: str = conversion_v
|
||||
|
||||
template_base = f"{field_name}{format_spec_v}{conversion_v}"
|
||||
self._template_base = template_base
|
||||
self._template = f"{{{template_base}}}"
|
||||
self._template_base: str = template_base
|
||||
self._template: str = f"{{{template_base}}}"
|
||||
|
||||
@property
|
||||
def template(self):
|
||||
def template(self) -> str:
|
||||
return self._template
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return "<Format:{}>".format(self._template)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self._template
|
||||
|
||||
@staticmethod
|
||||
def validate_value_type(value):
|
||||
def validate_value_type(value: Any) -> bool:
|
||||
"""Check if value can be used for formatting of single key."""
|
||||
if isinstance(value, (numbers.Number, FormatObject)):
|
||||
return True
|
||||
|
|
@ -491,7 +513,7 @@ class FormattingPart:
|
|||
return False
|
||||
|
||||
@staticmethod
|
||||
def validate_key_is_matched(key):
|
||||
def validate_key_is_matched(key: str) -> bool:
|
||||
"""Validate that opening has closing at correct place.
|
||||
Future-proof, only square brackets are currently used in keys.
|
||||
|
||||
|
|
@ -528,12 +550,15 @@ class FormattingPart:
|
|||
joined_keys = "".join([f"[{key}]" for key in keys])
|
||||
return f"{template_base}{joined_keys}"
|
||||
|
||||
def format(self, data, result):
|
||||
def format(
|
||||
self, data: Dict[str, Any], result: TemplatePartResult
|
||||
) -> TemplatePartResult:
|
||||
"""Format the formattings string.
|
||||
|
||||
Args:
|
||||
data(dict): Data that should be used for formatting.
|
||||
result(TemplatePartResult): Object where result is stored.
|
||||
|
||||
"""
|
||||
key = self._template_base
|
||||
if key in result.really_used_values:
|
||||
|
|
@ -658,20 +683,27 @@ class OptionalPart:
|
|||
'FormattingPart'.
|
||||
"""
|
||||
|
||||
def __init__(self, parts):
|
||||
self._parts = parts
|
||||
def __init__(
|
||||
self,
|
||||
parts: List["Union[str, OptionalPart, FormattingPart]"]
|
||||
):
|
||||
self._parts: List["Union[str, OptionalPart, FormattingPart]"] = parts
|
||||
|
||||
@property
|
||||
def parts(self):
|
||||
def parts(self) -> List["Union[str, OptionalPart, FormattingPart]"]:
|
||||
return self._parts
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return "<{}>".format("".join([str(p) for p in self._parts]))
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return "<Optional:{}>".format("".join([str(p) for p in self._parts]))
|
||||
|
||||
def format(self, data, result):
|
||||
def format(
|
||||
self,
|
||||
data: Dict[str, Any],
|
||||
result: TemplatePartResult,
|
||||
) -> TemplatePartResult:
|
||||
new_result = TemplatePartResult(True)
|
||||
for part in self._parts:
|
||||
if isinstance(part, str):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue