# Copyright 2026 jCloud Services GbR # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' The utilities for jcloud_docsgen ''' import pathlib import os import errno from typing import Union import types class ExistingDirectory(pathlib.Path): ''' pathlib.Path subclass that is a directory ExistingDirectory represents a filesystem path but it also checks whether the path exists and is not a file but a directory. It inherits from pathlib.Path. ''' def __init__(self, path, *args, **kwargs) -> None: if not os.path.exists(path): raise FileNotFoundError(errno.ENOENT, 'no such file or directory', path) if not os.path.isdir(path): raise NotADirectoryError(errno.ENOTDIR, 'not a directory', path) super().__init__(*(path, *args), **kwargs) def _quote(string: str, quotation_mark: str) -> str: ''' Quotes a string. :param string: The string to quote :type string: str :param quotation_mark: The quotation mark :type quotation_mark: str :return: The quoted string :rtype: str ''' return quotation_mark + string + quotation_mark def human_readable_list(ls: list, final_separator: str = 'and', quotation_mark: str = '') -> str: ''' Generates a human-readable list from a list. :param ls: The list. :type ls: list :param final_separator: The seperator between the last and the second-to-last element of the list. Default is 'and'. :type final_separator: str :param quotation_mark: The characters that are inserted before and after an element. Default is ''. :return: The human-readable list. :rtype: str ''' # empty list if not ls: return '' # list with one element if len(ls) == 1: return _quote(str(ls[0]), quotation_mark) return ', '.join( [ _quote(str(obj), quotation_mark) # quote the string representation of the object for obj in ls[:-1] # do not include the last element of the list ] ) + ' ' + final_separator + ' ' + _quote(str(ls[-1]), quotation_mark) def _list_type_names(types: list[type]) -> list[str]: ''' Converts a list of types into a list of their names (``__name__`` attribute) :param types: The list of types :type types: list[type] :return: The list of the names of the types :rtype: list[str] ''' return [tp.__name__ for tp in types] def assert_that_is_instance(obj: object, class_or_tuple: Union[type, types.UnionType, tuple[type, ...]]) -> None: if not isinstance(class_or_tuple, Union[type, types.UnionType, tuple]): raise TypeError(f'class_or_tuple: expected \'Union[type, types.UnionType, tuple[type, ...]]\', \'got {type(class_or_tuple).__name__}\'') if not isinstance(obj, class_or_tuple): if isinstance(class_or_tuple, (tuple, types.UnionType)): print('MORE') if isinstance(class_or_tuple, types.UnionType): class_or_tuple = class_or_tuple.__args__ print('LIST:', _list_type_names(class_or_tuple)) if len(class_or_tuple) > 1: exception_message_expected = 'either ' else: exception_message_expected = '' exception_message_expected += human_readable_list(_list_type_names(class_or_tuple), 'or', '\'') else: print('SINGLE') exception_message_expected = '\'' + class_or_tuple.__name__ + '\'' raise TypeError(f'expected {exception_message_expected}, got \'{type(obj).__name__}\'')