class LutColor(Color): classdef.comparable('index') classdef.repr('index') def __init__(self, color_index: int) -> None: self.index = color_index def as_foreground(self) -> str: return '38;5;' + str(self.index) def as_background(self) -> str: return '48;5;' + str(self.index) @classmethod def from_rgb(cls, r: int, g: int, b: int) -> 'LutColor': """ Given RGB values in the range of [0..5], returns a #LutColor pointing to the color index that resembles the specified color coordinates. """ def _check(name, value): if not (0 <= value < 6): raise ValueError( 'bad value for parameter "{}": {} ∉ [0..5]'.format( name, value)) _check('r', r) _check('g', g) _check('b', b) return cls((16 + 36 * r) + (6 * g) + b)
class HttpData: """ Represents HTTP route parameters consisting of a method and the path. """ classdef.comparable(['method', 'path']) _METHODS = ['GET', 'HEAD', 'POST', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'] _PARSE_REGEX = re.compile(r'({})\s+(/.*)'.format('|'.join(_METHODS))) @classmethod def parse(cls, s: str): assert isinstance(s, str), type(s) match = cls._PARSE_REGEX.match(s) if not match: raise ValueError('invalid http specifier: {!r}'.format(s)) return cls(match.group(1), Path(match.group(2))) def __init__(self, method, path): self.method = method self.path = path def __str__(self): return '{} {}'.format(self.method, self.path) def __repr__(self): return 'HttpData(method={!r}, path={!r})'.format(self.method, self.path)
class Path: """ Represents a parametrized path. Parameters are defined by enclosing them in curly braces. >>> path = Path("/my-endpoint/{myParameter}") >>> path.parameters ['myParameter'] """ classdef.comparable(['components']) def __init__(self, path): self.components = [] self.parameters = [] offset = 0 for match in re.finditer(r'\{([\w\d]+)\}', path): if offset < match.start(): self.components.append({'type': 'text', 'text': path[offset:match.start()]}) parameter = match.group(1) if parameter in self.parameters: raise ValueError('duplicate parameter in path string: {!r}'.format( parameter)) self.components.append({'type': 'parameter', 'parameter': parameter}) self.parameters.append(parameter) offset = match.end() if offset < len(path): self.components.append({'type': 'text', 'text': path[offset:]}) def __str__(self): parts = [] for component in self.components: if component['type'] == 'text': parts.append(component['text']) else: parts.append('{' + component['parameter'] + '}') return ''.join(parts) def __repr__(self): return 'Path({!r})'.format(str(self)) def __add__(self, other: 'Path') -> 'Path': self = copy.copy(self) self.components = self.components + other.components self.parameters = self.parameters + other.parameters return self def sub(self, map_function): self = copy.copy(self) self.components = list(map(map_function, self.components)) self.parameters = [x['parameter'] for x in self.components if x['type'] == 'parameter'] return self def render(self, parameters: Dict[str, str]) -> str: def func(x): if x['type'] == 'parameter': return {'type': 'text', 'text': parameters[x['parameter']]} return x return str(self.sub(func))
class _ColorWrapper(Component): classdef.comparable('color') classdef.repr('color') def __init__(self, color: Union[Color, str]): if isinstance(color, str): color = parse_color(color) self.color = color
class RouteData: """ Route data that is attached to a function with the #Route decorator. """ classdef.comparable(['http', 'content_type']) classdef.repr(['http', 'content_type']) def __init__(self, http: HttpData, content_type: str): self.http = http self.content_type = content_type
class TrueColor(Color): classdef.comparable('r,g,b') classdef.repr('r,g,b') def __init__(self, r: int, g: int, b: int) -> None: self.r = r self.g = g self.b = b def as_foreground(self) -> str: return '38;2;{};{};{}'.format(self.r, self.g, self.b) def as_background(self) -> str: return '48;2;{};{};{}'.format(self.r, self.g, self.b)
class SgrColor(Color): classdef.comparable('name,bright') classdef.repr('name,bright') def __init__(self, name: SgrColorName, bright: bool = False) -> None: if isinstance(name, str): name = SgrColorName[name.upper()] self.name = name self.bright = bright def as_foreground(self) -> str: return str((90 if self.bright else 30) + self.name.value) def as_background(self) -> str: return str((100 if self.bright else 40) + self.name.value)
class Style: classdef.comparable('fg,bg,attrs') classdef.repr('fg,bg,attrs') def __init__(self, fg: Union[Color, str] = None, bg: Union[Color, str] = None, attrs: List[Attribute] = None) -> None: if isinstance(fg, str): fg = parse_color(fg) if isinstance(bg, str): bg = parse_color(bg) attrs = [ Attribute[k.upper()] if isinstance(k, str) else k for k in attrs or () ] assert fg is None or isinstance(fg, Color), type(fg) assert bg is None or isinstance(bg, Color), type(bg) self.fg = fg self.bg = bg self.attrs = attrs def __str__(self): fg = Foreground(self.fg) if self.fg else None bg = Background(self.bg) if self.bg else None return to_escape_sequence(fg, bg, *self.attrs) def merge(self, other: 'Style') -> 'Style': """ Merge two styles, taking precedence to the color and attributes of *self*. """ return Style(self.fg or other.fg, self.bg or other.bg, other.attrs + self.attrs) def replace(self, fg=NotSet, bg=NotSet, attrs=NotSet) -> 'Style': fg = self.fg if fg is NotSet else fg bg = self.bg if bg is NotSet else bg attrs = self.attrs if attrs is NotSet else attrs return Style(fg, bg, attrs)
class ChainTypeResolver: """ Chain multiple #IUnionTypeResolver instances. """ classdef.comparable('_resolvers') def __init__(self, *resolvers: IUnionTypeResolver) -> None: self._resolvers = resolvers def resolve(self, type_name: str) -> IUnionTypeMember: for resolver in self._resolvers: try: return resolver.resolve(type_name) except UnknownUnionTypeError: pass raise UnknownUnionTypeError(type_name) def reverse(self, value: Any) -> IUnionTypeMember: for resolver in self._resolvers: try: return resolver.reverse(value) except UnknownUnionTypeError: pass raise UnknownUnionTypeError(type(value)) def members(self) -> Iterable[IUnionTypeMember]: has_members = False for resolver in self._resolvers: try: yield from resolver.members() has_members = True except NotImplementedError: pass if not has_members: raise NotImplementedError
class ParametrizedRoute: """ A route that contains the resolved parameter information and a reference to the interface where the route is defined. This is returned from the #get_routes() function. """ classdef.comparable(['name', 'signature', 'route', 'interface', 'parameters', 'return_']) classdef.repr(['name', 'signature', 'route', 'interface', 'parameters', 'return_']) def __init__( self, name: str, signature: inspect.Signature, route: RouteData, interface: Type[Interface], parameters: Dict[str, 'RouteParam'], return_: 'RouteReturn' ) -> None: self.name = name self.signature = signature self.route = route self.interface = interface self.parameters = parameters self.return_ = return_ @property def fqn(self) -> str: return self.interface.__module__ + '.' + self.interface.__name__ + ':' + self.name @classmethod def from_function( cls, route: RouteData, interface: Type[Interface], func: Callable, ) -> 'ParametrizedRoute': """ Creates a #ParametrizedRoute from a function and the #RouteData that was associated with that function. This will generate the *parameters* for the #ParametrizedRoute constructor. """ sig = inspect.signature(func) # Determine the return-type of the route from the annotation. if sig.return_annotation in (inspect._empty, None): return_ = RouteReturn.Void() elif sig.return_annotation is Response: return_ = RouteReturn.Passthrough() else: return_ = RouteReturn.Mapped(sig.return_annotation, route.content_type) parameters = {} # Route path parameters. for parameter in route.http.path.parameters: if parameter not in sig.parameters: raise TypeError('function "{}.{}" does not provide parameter {}' .format(interface.__name__, func.__name__, parameter)) annotation = sig.parameters[parameter].annotation if annotation is inspect._empty: annotation = NotSet parameters[parameter] = RouteParam.Path(annotation, parameter) # Other parameter types. for parameter in sig.parameters.values(): if parameter.name in parameters or parameter.name == 'self': continue if isinstance(parameter.annotation, RouteParam): if getattr(parameter.annotation, 'name', '???') is None: parameter.annotation.name = parameter.name parameters[parameter.name] = parameter.annotation elif isinstance(parameter.annotation, type) and \ issubclass(parameter.annotation, RouteParam): parameters[parameter.name] = parameter.annotation() else: parameters[parameter.name] = RouteParam.Body(parameter.annotation) # Fill in name/default/content_type. param = parameters[parameter.name] if hasattr(param, 'name') and not param.name: param.name = parameter.name if hasattr(param, 'default') and parameter.default is not inspect._empty: if parameter.default is None and not isinstance(param.type_annotation, OptionalType): param.type_annotation = OptionalType(param.type_annotation) param.default = parameter.default if hasattr(param, 'content_type') and not param.content_type: param.content_type = route.content_type body_params = list(k for k, v in parameters.items() if isinstance(v, RouteParam.Body)) if len(body_params) > 1: raise TypeError('found multiple unmatched parameters that could serve ' 'as candiates for the request body, but there can be only one or no ' 'body parameter, candidates are: {!r}'.format(body_params)) return cls(func.__name__, sig, route, interface, parameters, return_) def build_parameters(self, visitor: 'ParameterVisitor') -> Dict[str, Any]: kwargs = {} for name, param in self.parameters.items(): kwargs[name] = visitor.dispatch(param) return kwargs