codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import logging4import os5from typing import ClassVar6 7 8class TypeData:9 def __init__(self, default_type, as_type) -> None:10 self.default_type = default_type11 self.as_type = as_type12 13 def __repr__(self) -> str:14 return f"{self.__class__.__name__}(base={self.default_type}, as={self.as_type})"15 16 def convert(self, value):17 return self.default_type(value)18 19 20class BoolType(TypeData):21 BOOLEAN_STATES: ClassVar[dict[str, bool]] = {22 "1": True,23 "yes": True,24 "true": True,25 "on": True,26 "0": False,27 "no": False,28 "false": False,29 "off": False,30 }31 32 def convert(self, value):33 if value.lower() not in self.BOOLEAN_STATES:34 msg = f"Not a boolean: {value}"35 raise ValueError(msg)36 return self.BOOLEAN_STATES[value.lower()]37 38 39class NoneType(TypeData):40 def convert(self, value):41 if not value:42 return None43 return str(value)44 45 46class ListType(TypeData):47 def _validate(self):48 """no op."""49 50 def convert(self, value, flatten=True): # noqa: ARG002, FBT00251 values = self.split_values(value)52 result = []53 for a_value in values:54 sub_values = a_value.split(os.pathsep)55 result.extend(sub_values)56 return [self.as_type(i) for i in result]57 58 def split_values(self, value):59 """60 Split the provided value into a list.61 62 First this is done by newlines. If there were no newlines in the text,63 then we next try to split by comma.64 """65 if isinstance(value, (str, bytes)):66 # Use `splitlines` rather than a custom check for whether there is67 # more than one line. This ensures that the full `splitlines()`68 # logic is supported here.69 values = value.splitlines()70 if len(values) <= 1:71 values = value.split(",")72 values = filter(None, [x.strip() for x in values])73 else:74 values = list(value)75 76 return values77 78 79def convert(value, as_type, source):80 """Convert the value as a given type where the value comes from the given source."""81 try:82 return as_type.convert(value)83 except Exception as exception:84 logging.warning("%s failed to convert %r as %r because %r", source, value, as_type, exception)85 raise86 87 88_CONVERT = {bool: BoolType, type(None): NoneType, list: ListType}89 90 91def get_type(action):92 default_type = type(action.default)93 as_type = default_type if action.type is None else action.type94 return _CONVERT.get(default_type, TypeData)(default_type, as_type)95 96 97__all__ = [98 "convert",99 "get_type",100]101 