Skip to content

Commit 802362f

Browse files
committed
Add static typing annotations to opencolorio_config_aces.utilities sub-package.
Signed-off-by: Thomas Mansencal <[email protected]>
1 parent d479054 commit 802362f

File tree

1 file changed

+57
-40
lines changed

1 file changed

+57
-40
lines changed

opencolorio_config_aces/utilities/common.py

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
Defines common utilities objects that don't fall in any specific category.
88
"""
99

10+
from __future__ import annotations
11+
1012
import datetime
1113
import functools
1214
import logging
@@ -15,12 +17,13 @@
1517
import subprocess
1618
import unicodedata
1719
from collections import defaultdict
20+
from collections.abc import Callable, Iterable
1821
from html.parser import HTMLParser
1922
from itertools import chain
2023
from pathlib import Path
2124
from pprint import PrettyPrinter
2225
from textwrap import TextWrapper
23-
from typing import TypeVar
26+
from typing import Any, TypeVar
2427

2528
import requests
2629

@@ -64,8 +67,8 @@
6467

6568
# Monkey-patching the "PrettyPrinter" mapping to handle the "TypeError"
6669
# exception raised with "instancemethod": https://bugs.python.org/issue33395
67-
class _dispatch(dict):
68-
def get(self, key, default=None):
70+
class _dispatch(dict[Any, Any]):
71+
def get(self, key: Any, default: Any = None) -> Any:
6972
try:
7073
return self.__get__(key, default)
7174
except Exception as error: # noqa: F841, S110
@@ -74,22 +77,22 @@ def get(self, key, default=None):
7477

7578
PrettyPrinter._dispatch = _dispatch() # pyright: ignore
7679

77-
ROOT_BUILD_DEFAULT = (Path(__file__) / ".." / ".." / ".." / "build").resolve()
80+
ROOT_BUILD_DEFAULT: Path = (Path(__file__) / ".." / ".." / ".." / "build").resolve()
7881
"""
7982
Default build root directory.
8083
8184
ROOT_BUILD_DEFAULT : str
8285
"""
8386

8487

85-
class DocstringDict(dict):
88+
class DocstringDict(dict[str, Any]):
8689
"""
8790
A :class:`dict` sub-class that allows settings a docstring to :class:`dict`
8891
instances.
8992
"""
9093

9194

92-
def first_item(iterable, default=None):
95+
def first_item(iterable: Iterable[Any], default: Any = None) -> Any:
9396
"""
9497
Return the first item of given iterable.
9598
@@ -115,7 +118,7 @@ def first_item(iterable, default=None):
115118
return None
116119

117120

118-
def common_ancestor(*args):
121+
def common_ancestor(*args: Any) -> Any:
119122
"""
120123
Return the common ancestor of given iterables.
121124
@@ -148,7 +151,7 @@ def common_ancestor(*args):
148151
return ancestor
149152

150153

151-
def paths_common_ancestor(*args):
154+
def paths_common_ancestor(*args: str) -> str:
152155
"""
153156
Return the common ancestor path from given paths.
154157
@@ -174,7 +177,7 @@ def paths_common_ancestor(*args):
174177
return path_ancestor
175178

176179

177-
def vivification():
180+
def vivification() -> defaultdict[Any, Any]:
178181
"""
179182
Implement supports for vivification of the underlying dict like
180183
data-structure, magical!
@@ -196,7 +199,9 @@ def vivification():
196199
return defaultdict(vivification)
197200

198201

199-
def vivified_to_dict(vivified):
202+
def vivified_to_dict(
203+
vivified: defaultdict[Any, Any] | dict[Any, Any],
204+
) -> dict[Any, Any]:
200205
"""
201206
Convert given vivified data-structure to dictionary.
202207
@@ -222,7 +227,12 @@ def vivified_to_dict(vivified):
222227
return vivified
223228

224229

225-
def message_box(message, width=79, padding=3, print_callable=print):
230+
def message_box(
231+
message: str,
232+
width: int = 79,
233+
padding: int = 3,
234+
print_callable: Callable[[str], None] = print,
235+
) -> bool:
226236
"""
227237
Print a message inside a box.
228238
@@ -278,7 +288,7 @@ def message_box(message, width=79, padding=3, print_callable=print):
278288

279289
ideal_width = width - padding * 2 - 2
280290

281-
def inner(text):
291+
def inner(text: str) -> str:
282292
"""Format and pads inner text for the message box."""
283293

284294
return "*{0}{1}{2}{0}*".format(
@@ -303,7 +313,7 @@ def inner(text):
303313
return True
304314

305315

306-
def is_colour_installed(raise_exception=False):
316+
def is_colour_installed(raise_exception: bool = False) -> bool:
307317
"""
308318
Return if *Colour* is installed and available.
309319
@@ -335,7 +345,7 @@ def is_colour_installed(raise_exception=False):
335345
return False
336346

337347

338-
def is_jsonpickle_installed(raise_exception=False):
348+
def is_jsonpickle_installed(raise_exception: bool = False) -> bool:
339349
"""
340350
Return if *jsonpickle* is installed and available.
341351
@@ -368,7 +378,7 @@ def is_jsonpickle_installed(raise_exception=False):
368378
return False
369379

370380

371-
def is_networkx_installed(raise_exception=False):
381+
def is_networkx_installed(raise_exception: bool = False) -> bool:
372382
"""
373383
Return if *NetworkX* is installed and available.
374384
@@ -400,7 +410,7 @@ def is_networkx_installed(raise_exception=False):
400410
return False
401411

402412

403-
REQUIREMENTS_TO_CALLABLE = DocstringDict(
413+
REQUIREMENTS_TO_CALLABLE: DocstringDict = DocstringDict(
404414
{
405415
"Colour": is_colour_installed,
406416
"jsonpickle": is_jsonpickle_installed,
@@ -415,7 +425,7 @@ def is_networkx_installed(raise_exception=False):
415425
"""
416426

417427

418-
def required(*requirements):
428+
def required(*requirements: str) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
419429
"""
420430
Decorate a function to check whether various ancillary package requirements
421431
are satisfied.
@@ -430,11 +440,11 @@ def required(*requirements):
430440
object
431441
"""
432442

433-
def wrapper(function):
443+
def wrapper(function: Callable[..., Any]) -> Callable[..., Any]:
434444
"""Wrap given function wrapper."""
435445

436446
@functools.wraps(function)
437-
def wrapped(*args, **kwargs):
447+
def wrapped(*args: Any, **kwargs: Any) -> Any:
438448
"""Wrap given function."""
439449

440450
for requirement in requirements:
@@ -447,7 +457,7 @@ def wrapped(*args, **kwargs):
447457
return wrapper
448458

449459

450-
def is_string(a):
460+
def is_string(a: Any) -> bool:
451461
"""
452462
Return if given :math:`a` variable is a *string* like variable.
453463
@@ -472,7 +482,7 @@ def is_string(a):
472482
return isinstance(a, str)
473483

474484

475-
def is_iterable(a):
485+
def is_iterable(a: Any) -> bool:
476486
"""
477487
Return if given :math:`a` variable is iterable.
478488
@@ -497,7 +507,7 @@ def is_iterable(a):
497507
return is_string(a) or (bool(getattr(a, "__iter__", False)))
498508

499509

500-
def git_describe():
510+
def git_describe() -> str:
501511
"""
502512
Describe the current *OpenColorIO Configuration for ACES* *git* version.
503513
@@ -512,7 +522,7 @@ def git_describe():
512522
try: # pragma: no cover
513523
version = subprocess.check_output(
514524
["git", "describe"], # noqa: S603, S607
515-
cwd=opencolorio_config_aces.__path__[0],
525+
cwd=opencolorio_config_aces.__path__[0], # pyright: ignore
516526
stderr=subprocess.STDOUT,
517527
).strip()
518528
version = version.decode("utf-8")
@@ -525,7 +535,7 @@ def git_describe():
525535
# TODO: Numpy currently comes via "Colour", we might want that to be an
526536
# explicit dependency in the future.
527537
@required("Colour")
528-
def matrix_3x3_to_4x4(M):
538+
def matrix_3x3_to_4x4(M: Any) -> list[float]:
529539
"""
530540
Convert given 3x3 matrix :math:`M` to a raveled 4x4 matrix.
531541
@@ -548,7 +558,7 @@ def matrix_3x3_to_4x4(M):
548558
return np.ravel(M_I).tolist()
549559

550560

551-
def multi_replace(name, patterns):
561+
def multi_replace(name: str, patterns: dict[str, str]) -> str:
552562
"""
553563
Update given name by applying in succession the given patterns and
554564
substitutions.
@@ -581,10 +591,10 @@ def multi_replace(name, patterns):
581591

582592

583593
def validate_method(
584-
method,
585-
valid_methods,
586-
message='"{0}" method is invalid, it must be one of {1}!',
587-
):
594+
method: str,
595+
valid_methods: list[str],
596+
message: str = '"{0}" method is invalid, it must be one of {1}!',
597+
) -> str:
588598
"""
589599
Validate whether given method exists in the given valid methods and
590600
returns the method lower cased.
@@ -623,7 +633,7 @@ def validate_method(
623633
return method_lower
624634

625635

626-
def google_sheet_title(url):
636+
def google_sheet_title(url: str) -> str:
627637
"""
628638
Return the title from given *Google Sheet* url.
629639
@@ -649,20 +659,24 @@ def google_sheet_title(url):
649659
"""
650660

651661
class Parser(HTMLParser):
652-
def __init__(self):
662+
def __init__(self) -> None:
653663
HTMLParser.__init__(self)
654-
self.in_title = []
655-
self.title = None
656-
657-
def handle_starttag(self, tag, attrs): # noqa: ARG002
664+
self.in_title: list[str] = []
665+
self.title: str | None = None
666+
667+
def handle_starttag(
668+
self,
669+
tag: str,
670+
attrs: list[tuple[str, str | None]], # noqa: ARG002
671+
) -> None:
658672
if tag == "title":
659673
self.in_title.append(tag)
660674

661-
def handle_endtag(self, tag):
675+
def handle_endtag(self, tag: str) -> None:
662676
if tag == "title":
663677
self.in_title.pop()
664678

665-
def handle_data(self, data):
679+
def handle_data(self, data: str) -> None:
666680
if self.in_title:
667681
self.title = data
668682

@@ -672,10 +686,13 @@ def handle_data(self, data):
672686
parser = Parser()
673687
parser.feed(requests.get(url, timeout=60).text)
674688

689+
if parser.title is None:
690+
raise ValueError(f"Could not extract title from URL: {url}")
691+
675692
return parser.title.rsplit("-", 1)[0].strip()
676693

677694

678-
def slugify(object_, allow_unicode=False):
695+
def slugify(object_: Any, allow_unicode: bool = False) -> str:
679696
"""
680697
Generate a *SEO* friendly and human-readable slug from given object.
681698
@@ -725,7 +742,7 @@ def slugify(object_, allow_unicode=False):
725742
return re.sub(r"[-\s]+", "-", value).strip("-_")
726743

727744

728-
def attest(condition, message=""):
745+
def attest(condition: bool, message: str = "") -> None:
729746
"""
730747
Provide the `assert` statement functionality without being disabled by
731748
optimised Python execution.
@@ -742,7 +759,7 @@ def attest(condition, message=""):
742759
raise AssertionError(message)
743760

744761

745-
def timestamp():
762+
def timestamp() -> str:
746763
"""
747764
Return a timestamp description.
748765

0 commit comments

Comments
 (0)