Document utils.py
This commit is contained in:
parent
746a9259db
commit
a7e505a387
|
@ -1,3 +1,5 @@
|
||||||
|
"""Contains various utilities that are used throughout the package."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import collections
|
import collections
|
||||||
import html
|
import html
|
||||||
|
@ -18,13 +20,22 @@ CANCELLABLE_FUTURES: Dict[Tuple[Any, Callable], asyncio.Future] = {}
|
||||||
|
|
||||||
|
|
||||||
class AutoStrEnum(Enum):
|
class AutoStrEnum(Enum):
|
||||||
|
"""An Enum where auto() assigns the member's name instead of an int.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> class Fruits(AutoStrEnum): apple = auto()
|
||||||
|
>>> Fruits.apple.value
|
||||||
|
"apple"
|
||||||
|
"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate_next_value_(name, *_):
|
def _generate_next_value_(name, *_):
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def dict_update_recursive(dict1, dict2):
|
def dict_update_recursive(dict1: dict, dict2: dict) -> None:
|
||||||
|
"""Recursive version of dict.update()."""
|
||||||
# https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
|
# https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
|
||||||
|
|
||||||
for k in dict2:
|
for k in dict2:
|
||||||
if (k in dict1 and isinstance(dict1[k], dict) and
|
if (k in dict1 and isinstance(dict1[k], dict) and
|
||||||
isinstance(dict2[k], collections.Mapping)):
|
isinstance(dict2[k], collections.Mapping)):
|
||||||
|
@ -34,6 +45,8 @@ def dict_update_recursive(dict1, dict2):
|
||||||
|
|
||||||
|
|
||||||
def is_svg(file: Union[IO, bytes, str]) -> bool:
|
def is_svg(file: Union[IO, bytes, str]) -> bool:
|
||||||
|
"""Return True if the file is a SVG. Uses lxml for detection."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_, element = next(xml_etree.iterparse(file, ("start",)))
|
_, element = next(xml_etree.iterparse(file, ("start",)))
|
||||||
return element.tag == "{http://www.w3.org/2000/svg}svg"
|
return element.tag == "{http://www.w3.org/2000/svg}svg"
|
||||||
|
@ -42,6 +55,10 @@ def is_svg(file: Union[IO, bytes, str]) -> bool:
|
||||||
|
|
||||||
|
|
||||||
def svg_dimensions(file: Union[IO, bytes, str]) -> Tuple[int, int]:
|
def svg_dimensions(file: Union[IO, bytes, str]) -> Tuple[int, int]:
|
||||||
|
"""Return the width & height or viewBox width & height for a SVG.
|
||||||
|
If these properties are missing (broken file), ``(256, 256)`` is returned.
|
||||||
|
"""
|
||||||
|
|
||||||
attrs = xml_etree.parse(file).getroot().attrib
|
attrs = xml_etree.parse(file).getroot().attrib
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -58,6 +75,10 @@ def svg_dimensions(file: Union[IO, bytes, str]) -> Tuple[int, int]:
|
||||||
|
|
||||||
|
|
||||||
def guess_mime(file: IO) -> str:
|
def guess_mime(file: IO) -> str:
|
||||||
|
"""Return the mime type for a file, or application/octet-stream if it
|
||||||
|
can't be guessed.
|
||||||
|
"""
|
||||||
|
|
||||||
if is_svg(file):
|
if is_svg(file):
|
||||||
return "image/svg+xml"
|
return "image/svg+xml"
|
||||||
|
|
||||||
|
@ -66,12 +87,20 @@ def guess_mime(file: IO) -> str:
|
||||||
|
|
||||||
|
|
||||||
def plain2html(text: str) -> str:
|
def plain2html(text: str) -> str:
|
||||||
|
"""Transform plain text into HTML, this converts \n and \t."""
|
||||||
|
|
||||||
return html.escape(text)\
|
return html.escape(text)\
|
||||||
.replace("\n", "<br>")\
|
.replace("\n", "<br>")\
|
||||||
.replace("\t", " " * 4)
|
.replace("\t", " " * 4)
|
||||||
|
|
||||||
|
|
||||||
def serialize_value_for_qml(value: Any) -> Any:
|
def serialize_value_for_qml(value: Any) -> Any:
|
||||||
|
"""Transform a value to make it easier to use from QML.
|
||||||
|
|
||||||
|
Currently, this transforms Enum members to their actual value and Path
|
||||||
|
objects to their string version.
|
||||||
|
"""
|
||||||
|
|
||||||
if hasattr(value, "__class__") and issubclass(value.__class__, Enum):
|
if hasattr(value, "__class__") and issubclass(value.__class__, Enum):
|
||||||
return value.value
|
return value.value
|
||||||
|
|
||||||
|
@ -82,6 +111,8 @@ def serialize_value_for_qml(value: Any) -> Any:
|
||||||
|
|
||||||
|
|
||||||
def classes_defined_in(module: ModuleType) -> Dict[str, Type]:
|
def classes_defined_in(module: ModuleType) -> Dict[str, Type]:
|
||||||
|
"""Return a {name: class} dict of all the classes a module defines."""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
m[0]: m[1] for m in inspect.getmembers(module, inspect.isclass)
|
m[0]: m[1] for m in inspect.getmembers(module, inspect.isclass)
|
||||||
if not m[0].startswith("_") and
|
if not m[0].startswith("_") and
|
||||||
|
@ -90,6 +121,10 @@ def classes_defined_in(module: ModuleType) -> Dict[str, Type]:
|
||||||
|
|
||||||
|
|
||||||
def cancel_previous(async_func):
|
def cancel_previous(async_func):
|
||||||
|
"""When the wrapped coroutine is called, cancel any previous instance
|
||||||
|
of that coroutine that may still be running.
|
||||||
|
"""
|
||||||
|
|
||||||
async def wrapper(*args, **kwargs):
|
async def wrapper(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
arg0_is_self = inspect.getfullargspec(async_func).args[0] == "self"
|
arg0_is_self = inspect.getfullargspec(async_func).args[0] == "self"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user