import collections import html import inspect import xml.etree.cElementTree as xml_etree # FIXME: bandit warning from enum import Enum from enum import auto as autostr from pathlib import Path from types import ModuleType from typing import IO, Any, Dict, Tuple, Type, Union import filetype auto = autostr class AutoStrEnum(Enum): @staticmethod def _generate_next_value_(name, *_): return name def dict_update_recursive(dict1, dict2): # https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 for k in dict2: if (k in dict1 and isinstance(dict1[k], dict) and isinstance(dict2[k], collections.Mapping)): dict_update_recursive(dict1[k], dict2[k]) else: dict1[k] = dict2[k] def is_svg(file: Union[IO, bytes, str]) -> bool: try: _, element = next(xml_etree.iterparse(file, ("start",))) return element.tag == "{http://www.w3.org/2000/svg}svg" except (StopIteration, xml_etree.ParseError): return False def svg_dimensions(file: Union[IO, bytes, str]) -> Tuple[int, int]: attrs = xml_etree.parse(file).getroot().attrib try: width = round(float(attrs.get("width", attrs["viewBox"].split()[3]))) except (KeyError, IndexError, ValueError, TypeError): width = 256 try: height = round(float(attrs.get("height", attrs["viewBox"].split()[4]))) except (KeyError, IndexError, ValueError, TypeError): height = 256 return (width, height) def guess_mime(file: IO) -> str: if is_svg(file): return "image/svg+xml" file.seek(0, 0) return filetype.guess_mime(file) or "application/octet-stream" def plain2html(text: str) -> str: return html.escape(text)\ .replace("\n", "
")\ .replace("\t", " " * 4) def serialize_value_for_qml(value: Any) -> Any: if hasattr(value, "__class__") and issubclass(value.__class__, Enum): return value.value if isinstance(value, Path): return f"file://{value!s}" return value def classes_defined_in(module: ModuleType) -> Dict[str, Type]: return { m[0]: m[1] for m in inspect.getmembers(module, inspect.isclass) if not m[0].startswith("_") and m[1].__module__.startswith(module.__name__) }