예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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))
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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)
예제 #7
0
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)
예제 #8
0
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)
예제 #9
0
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
예제 #10
0
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