mirror of
https://github.com/esphome/esphome.git
synced 2024-12-01 19:24:14 +01:00
722 lines
23 KiB
Python
722 lines
23 KiB
Python
import inspect
|
|
|
|
import math
|
|
|
|
# pylint: disable=unused-import, wrong-import-order
|
|
from typing import Any, Generator, List, Optional, Tuple, Type, Union, Dict, Callable # noqa
|
|
|
|
from esphome.core import ( # noqa
|
|
CORE, HexInt, ID, Lambda, TimePeriod, TimePeriodMicroseconds,
|
|
TimePeriodMilliseconds, TimePeriodMinutes, TimePeriodSeconds, coroutine, Library, Define,
|
|
EnumValue)
|
|
from esphome.helpers import cpp_string_escape, indent_all_but_first_and_last
|
|
from esphome.py_compat import integer_types, string_types, text_type
|
|
from esphome.util import OrderedDict
|
|
|
|
|
|
class Expression(object):
|
|
def __str__(self):
|
|
raise NotImplementedError
|
|
|
|
|
|
SafeExpType = Union[Expression, bool, str, text_type, int, float, TimePeriod,
|
|
Type[bool], Type[int], Type[float], List[Any]]
|
|
|
|
|
|
class RawExpression(Expression):
|
|
def __init__(self, text): # type: (Union[str, unicode]) -> None
|
|
super(RawExpression, self).__init__()
|
|
self.text = text
|
|
|
|
def __str__(self):
|
|
return str(self.text)
|
|
|
|
|
|
# pylint: disable=redefined-builtin
|
|
class AssignmentExpression(Expression):
|
|
def __init__(self, type, modifier, name, rhs, obj):
|
|
super(AssignmentExpression, self).__init__()
|
|
self.type = type
|
|
self.modifier = modifier
|
|
self.name = name
|
|
self.rhs = safe_exp(rhs)
|
|
self.obj = obj
|
|
|
|
def __str__(self):
|
|
if self.type is None:
|
|
return u"{} = {}".format(self.name, self.rhs)
|
|
return u"{} {}{} = {}".format(self.type, self.modifier, self.name, self.rhs)
|
|
|
|
|
|
class VariableDeclarationExpression(Expression):
|
|
def __init__(self, type, modifier, name):
|
|
super(VariableDeclarationExpression, self).__init__()
|
|
self.type = type
|
|
self.modifier = modifier
|
|
self.name = name
|
|
|
|
def __str__(self):
|
|
return u"{} {}{}".format(self.type, self.modifier, self.name)
|
|
|
|
|
|
class ExpressionList(Expression):
|
|
def __init__(self, *args):
|
|
super(ExpressionList, self).__init__()
|
|
# Remove every None on end
|
|
args = list(args)
|
|
while args and args[-1] is None:
|
|
args.pop()
|
|
self.args = [safe_exp(arg) for arg in args]
|
|
|
|
def __str__(self):
|
|
text = u", ".join(text_type(x) for x in self.args)
|
|
return indent_all_but_first_and_last(text)
|
|
|
|
def __iter__(self):
|
|
return iter(self.args)
|
|
|
|
|
|
class TemplateArguments(Expression):
|
|
def __init__(self, *args): # type: (*SafeExpType) -> None
|
|
super(TemplateArguments, self).__init__()
|
|
self.args = ExpressionList(*args)
|
|
|
|
def __str__(self):
|
|
return u'<{}>'.format(self.args)
|
|
|
|
def __iter__(self):
|
|
return iter(self.args)
|
|
|
|
|
|
class CallExpression(Expression):
|
|
def __init__(self, base, *args): # type: (Expression, *SafeExpType) -> None
|
|
super(CallExpression, self).__init__()
|
|
self.base = base
|
|
if args and isinstance(args[0], TemplateArguments):
|
|
self.template_args = args[0]
|
|
args = args[1:]
|
|
else:
|
|
self.template_args = None
|
|
self.args = ExpressionList(*args)
|
|
|
|
def __str__(self):
|
|
if self.template_args is not None:
|
|
return u'{}{}({})'.format(self.base, self.template_args, self.args)
|
|
return u'{}({})'.format(self.base, self.args)
|
|
|
|
|
|
class StructInitializer(Expression):
|
|
def __init__(self, base, *args): # type: (Expression, *Tuple[str, SafeExpType]) -> None
|
|
super(StructInitializer, self).__init__()
|
|
self.base = base
|
|
if not isinstance(args, OrderedDict):
|
|
args = OrderedDict(args)
|
|
self.args = OrderedDict()
|
|
for key, value in args.items():
|
|
if value is None:
|
|
continue
|
|
exp = safe_exp(value)
|
|
self.args[key] = exp
|
|
|
|
def __str__(self):
|
|
cpp = u'{}{{\n'.format(self.base)
|
|
for key, value in self.args.items():
|
|
cpp += u' .{} = {},\n'.format(key, value)
|
|
cpp += u'}'
|
|
return cpp
|
|
|
|
|
|
class ArrayInitializer(Expression):
|
|
def __init__(self, *args, **kwargs): # type: (*Any, **Any) -> None
|
|
super(ArrayInitializer, self).__init__()
|
|
self.multiline = kwargs.get('multiline', False)
|
|
self.args = []
|
|
for arg in args:
|
|
if arg is None:
|
|
continue
|
|
exp = safe_exp(arg)
|
|
self.args.append(exp)
|
|
|
|
def __str__(self):
|
|
if not self.args:
|
|
return u'{}'
|
|
if self.multiline:
|
|
cpp = u'{\n'
|
|
for arg in self.args:
|
|
cpp += u' {},\n'.format(arg)
|
|
cpp += u'}'
|
|
else:
|
|
cpp = u'{' + u', '.join(str(arg) for arg in self.args) + u'}'
|
|
return cpp
|
|
|
|
|
|
class ParameterExpression(Expression):
|
|
def __init__(self, type, id):
|
|
super(ParameterExpression, self).__init__()
|
|
self.type = safe_exp(type)
|
|
self.id = id
|
|
|
|
def __str__(self):
|
|
return u"{} {}".format(self.type, self.id)
|
|
|
|
|
|
class ParameterListExpression(Expression):
|
|
def __init__(self, *parameters):
|
|
super(ParameterListExpression, self).__init__()
|
|
self.parameters = []
|
|
for parameter in parameters:
|
|
if not isinstance(parameter, ParameterExpression):
|
|
parameter = ParameterExpression(*parameter)
|
|
self.parameters.append(parameter)
|
|
|
|
def __str__(self):
|
|
return u", ".join(text_type(x) for x in self.parameters)
|
|
|
|
|
|
class LambdaExpression(Expression):
|
|
def __init__(self, parts, parameters, capture='=', return_type=None):
|
|
super(LambdaExpression, self).__init__()
|
|
self.parts = parts
|
|
if not isinstance(parameters, ParameterListExpression):
|
|
parameters = ParameterListExpression(*parameters)
|
|
self.parameters = parameters
|
|
self.capture = capture
|
|
self.return_type = safe_exp(return_type) if return_type is not None else None
|
|
|
|
def __str__(self):
|
|
cpp = u'[{}]({})'.format(self.capture, self.parameters)
|
|
if self.return_type is not None:
|
|
cpp += u' -> {}'.format(self.return_type)
|
|
cpp += u' {{\n{}\n}}'.format(self.content)
|
|
return indent_all_but_first_and_last(cpp)
|
|
|
|
@property
|
|
def content(self):
|
|
return u''.join(text_type(part) for part in self.parts)
|
|
|
|
|
|
class Literal(Expression):
|
|
def __str__(self):
|
|
raise NotImplementedError
|
|
|
|
|
|
class StringLiteral(Literal):
|
|
def __init__(self, string): # type: (Union[str, unicode]) -> None
|
|
super(StringLiteral, self).__init__()
|
|
self.string = string
|
|
|
|
def __str__(self):
|
|
return u'{}'.format(cpp_string_escape(self.string))
|
|
|
|
|
|
class IntLiteral(Literal):
|
|
def __init__(self, i): # type: (Union[int, long]) -> None
|
|
super(IntLiteral, self).__init__()
|
|
self.i = i
|
|
|
|
def __str__(self):
|
|
if self.i > 4294967295:
|
|
return u'{}ULL'.format(self.i)
|
|
if self.i > 2147483647:
|
|
return u'{}UL'.format(self.i)
|
|
if self.i < -2147483648:
|
|
return u'{}LL'.format(self.i)
|
|
return text_type(self.i)
|
|
|
|
|
|
class BoolLiteral(Literal):
|
|
def __init__(self, binary): # type: (bool) -> None
|
|
super(BoolLiteral, self).__init__()
|
|
self.binary = binary
|
|
|
|
def __str__(self):
|
|
return u"true" if self.binary else u"false"
|
|
|
|
|
|
class HexIntLiteral(Literal):
|
|
def __init__(self, i): # type: (int) -> None
|
|
super(HexIntLiteral, self).__init__()
|
|
self.i = HexInt(i)
|
|
|
|
def __str__(self):
|
|
return str(self.i)
|
|
|
|
|
|
class FloatLiteral(Literal):
|
|
def __init__(self, value): # type: (float) -> None
|
|
super(FloatLiteral, self).__init__()
|
|
self.float_ = value
|
|
|
|
def __str__(self):
|
|
if math.isnan(self.float_):
|
|
return u"NAN"
|
|
return u"{}f".format(self.float_)
|
|
|
|
|
|
# pylint: disable=bad-continuation
|
|
def safe_exp(
|
|
obj # type: Union[Expression, bool, str, unicode, int, long, float, TimePeriod, list]
|
|
):
|
|
# type: (...) -> Expression
|
|
"""Try to convert obj to an expression by automatically converting native python types to
|
|
expressions/literals.
|
|
"""
|
|
from esphome.cpp_types import bool_, float_, int32
|
|
|
|
if isinstance(obj, Expression):
|
|
return obj
|
|
if isinstance(obj, EnumValue):
|
|
return safe_exp(obj.enum_value)
|
|
if isinstance(obj, bool):
|
|
return BoolLiteral(obj)
|
|
if isinstance(obj, string_types):
|
|
return StringLiteral(obj)
|
|
if isinstance(obj, HexInt):
|
|
return HexIntLiteral(obj)
|
|
if isinstance(obj, integer_types):
|
|
return IntLiteral(obj)
|
|
if isinstance(obj, float):
|
|
return FloatLiteral(obj)
|
|
if isinstance(obj, TimePeriodMicroseconds):
|
|
return IntLiteral(int(obj.total_microseconds))
|
|
if isinstance(obj, TimePeriodMilliseconds):
|
|
return IntLiteral(int(obj.total_milliseconds))
|
|
if isinstance(obj, TimePeriodSeconds):
|
|
return IntLiteral(int(obj.total_seconds))
|
|
if isinstance(obj, TimePeriodMinutes):
|
|
return IntLiteral(int(obj.total_minutes))
|
|
if isinstance(obj, (tuple, list)):
|
|
return ArrayInitializer(*[safe_exp(o) for o in obj])
|
|
if obj is bool:
|
|
return bool_
|
|
if obj is int:
|
|
return int32
|
|
if obj is float:
|
|
return float_
|
|
if isinstance(obj, ID):
|
|
raise ValueError(u"Object {} is an ID. Did you forget to register the variable?"
|
|
u"".format(obj))
|
|
if inspect.isgenerator(obj):
|
|
raise ValueError(u"Object {} is a coroutine. Did you forget to await the expression with "
|
|
u"'yield'?".format(obj))
|
|
raise ValueError(u"Object is not an expression", obj)
|
|
|
|
|
|
class Statement(object):
|
|
def __init__(self):
|
|
pass
|
|
|
|
def __str__(self):
|
|
raise NotImplementedError
|
|
|
|
|
|
class RawStatement(Statement):
|
|
def __init__(self, text):
|
|
super(RawStatement, self).__init__()
|
|
self.text = text
|
|
|
|
def __str__(self):
|
|
return self.text
|
|
|
|
|
|
class ExpressionStatement(Statement):
|
|
def __init__(self, expression):
|
|
super(ExpressionStatement, self).__init__()
|
|
self.expression = safe_exp(expression)
|
|
|
|
def __str__(self):
|
|
return u"{};".format(self.expression)
|
|
|
|
|
|
class LineComment(Statement):
|
|
def __init__(self, value): # type: (unicode) -> None
|
|
super(LineComment, self).__init__()
|
|
self._value = value
|
|
|
|
def __str__(self):
|
|
parts = self._value.split(u'\n')
|
|
parts = [u'// {}'.format(x) for x in parts]
|
|
return u'\n'.join(parts)
|
|
|
|
|
|
class ProgmemAssignmentExpression(AssignmentExpression):
|
|
def __init__(self, type, name, rhs, obj):
|
|
super(ProgmemAssignmentExpression, self).__init__(
|
|
type, '', name, rhs, obj
|
|
)
|
|
|
|
def __str__(self):
|
|
type_ = self.type
|
|
return u"static const {} {}[] PROGMEM = {}".format(type_, self.name, self.rhs)
|
|
|
|
|
|
def progmem_array(id, rhs):
|
|
rhs = safe_exp(rhs)
|
|
obj = MockObj(id, u'.')
|
|
assignment = ProgmemAssignmentExpression(id.type, id, rhs, obj)
|
|
CORE.add(assignment)
|
|
CORE.register_variable(id, obj)
|
|
return obj
|
|
|
|
|
|
def statement(expression): # type: (Union[Expression, Statement]) -> Statement
|
|
if isinstance(expression, Statement):
|
|
return expression
|
|
return ExpressionStatement(expression)
|
|
|
|
|
|
def variable(id, # type: ID
|
|
rhs, # type: SafeExpType
|
|
type=None # type: MockObj
|
|
):
|
|
# type: (...) -> MockObj
|
|
"""Declare a new variable (not pointer type) in the code generation.
|
|
|
|
:param id: The ID used to declare the variable.
|
|
:param rhs: The expression to place on the right hand side of the assignment.
|
|
:param type: Manually define a type for the variable, only use this when it's not possible
|
|
to do so during config validation phase (for example because of template arguments).
|
|
|
|
:returns The new variable as a MockObj.
|
|
"""
|
|
assert isinstance(id, ID)
|
|
rhs = safe_exp(rhs)
|
|
obj = MockObj(id, u'.')
|
|
if type is not None:
|
|
id.type = type
|
|
assignment = AssignmentExpression(id.type, '', id, rhs, obj)
|
|
CORE.add(assignment)
|
|
CORE.register_variable(id, obj)
|
|
return obj
|
|
|
|
|
|
def Pvariable(id, # type: ID
|
|
rhs, # type: SafeExpType
|
|
type=None # type: MockObj
|
|
):
|
|
# type: (...) -> MockObj
|
|
"""Declare a new pointer variable in the code generation.
|
|
|
|
:param id: The ID used to declare the variable.
|
|
:param rhs: The expression to place on the right hand side of the assignment.
|
|
:param type: Manually define a type for the variable, only use this when it's not possible
|
|
to do so during config validation phase (for example because of template arguments).
|
|
|
|
:returns The new variable as a MockObj.
|
|
"""
|
|
rhs = safe_exp(rhs)
|
|
obj = MockObj(id, u'->')
|
|
if type is not None:
|
|
id.type = type
|
|
decl = VariableDeclarationExpression(id.type, '*', id)
|
|
CORE.add_global(decl)
|
|
assignment = AssignmentExpression(None, None, id, rhs, obj)
|
|
CORE.add(assignment)
|
|
CORE.register_variable(id, obj)
|
|
return obj
|
|
|
|
|
|
def new_Pvariable(id, # type: ID
|
|
*args # type: *SafeExpType
|
|
):
|
|
"""Declare a new pointer variable in the code generation by calling it's constructor
|
|
with the given arguments.
|
|
|
|
:param id: The ID used to declare the variable (also specifies the type).
|
|
:param args: The values to pass to the constructor.
|
|
|
|
:returns The new variable as a MockObj.
|
|
"""
|
|
if args and isinstance(args[0], TemplateArguments):
|
|
id = id.copy()
|
|
id.type = id.type.template(args[0])
|
|
args = args[1:]
|
|
rhs = id.type.new(*args)
|
|
return Pvariable(id, rhs)
|
|
|
|
|
|
def add(expression, # type: Union[Expression, Statement]
|
|
):
|
|
# type: (...) -> None
|
|
"""Add an expression to the codegen section.
|
|
|
|
After this is called, the given given expression will
|
|
show up in the setup() function after this has been called.
|
|
"""
|
|
CORE.add(expression)
|
|
|
|
|
|
def add_global(expression, # type: Union[SafeExpType, Statement]
|
|
):
|
|
# type: (...) -> None
|
|
"""Add an expression to the codegen global storage (above setup())."""
|
|
CORE.add_global(expression)
|
|
|
|
|
|
def add_library(name, # type: str
|
|
version # type: Optional[str]
|
|
):
|
|
# type: (...) -> None
|
|
"""Add a library to the codegen library storage.
|
|
|
|
:param name: The name of the library (for example 'AsyncTCP')
|
|
:param version: The version of the library, may be None.
|
|
"""
|
|
CORE.add_library(Library(name, version))
|
|
|
|
|
|
def add_build_flag(build_flag, # type: str
|
|
):
|
|
# type: (...) -> None
|
|
"""Add a global build flag to the compiler flags."""
|
|
CORE.add_build_flag(build_flag)
|
|
|
|
|
|
def add_define(name, # type: str
|
|
value=None, # type: Optional[SafeExpType]
|
|
):
|
|
# type: (...) -> None
|
|
"""Add a global define to the auto-generated defines.h file.
|
|
|
|
Optionally define a value to set this define to.
|
|
"""
|
|
if value is None:
|
|
CORE.add_define(Define(name))
|
|
else:
|
|
CORE.add_define(Define(name, safe_exp(value)))
|
|
|
|
|
|
@coroutine
|
|
def get_variable(id): # type: (ID) -> Generator[MockObj]
|
|
"""
|
|
Wait for the given ID to be defined in the code generation and
|
|
return it as a MockObj.
|
|
|
|
This is a coroutine, you need to await it with a 'yield' expression!
|
|
|
|
:param id: The ID to retrieve
|
|
:return: The variable as a MockObj.
|
|
"""
|
|
var = yield CORE.get_variable(id)
|
|
yield var
|
|
|
|
|
|
@coroutine
|
|
def get_variable_with_full_id(id): # type: (ID) -> Generator[ID, MockObj]
|
|
"""
|
|
Wait for the given ID to be defined in the code generation and
|
|
return it as a MockObj.
|
|
|
|
This is a coroutine, you need to await it with a 'yield' expression!
|
|
|
|
:param id: The ID to retrieve
|
|
:return: The variable as a MockObj.
|
|
"""
|
|
full_id, var = yield CORE.get_variable_with_full_id(id)
|
|
yield full_id, var
|
|
|
|
|
|
@coroutine
|
|
def process_lambda(value, # type: Lambda
|
|
parameters, # type: List[Tuple[SafeExpType, str]]
|
|
capture='=', # type: str
|
|
return_type=None # type: Optional[SafeExpType]
|
|
):
|
|
# type: (...) -> Generator[LambdaExpression]
|
|
"""Process the given lambda value into a LambdaExpression.
|
|
|
|
This is a coroutine because lambdas can depend on other IDs,
|
|
you need to await it with 'yield'!
|
|
|
|
:param value: The lambda to process.
|
|
:param parameters: The parameters to pass to the Lambda, list of tuples
|
|
:param capture: The capture expression for the lambda, usually ''.
|
|
:param return_type: The return type of the lambda.
|
|
:return: The generated lambda expression.
|
|
"""
|
|
from esphome.components.globals import GlobalsComponent
|
|
|
|
if value is None:
|
|
yield
|
|
return
|
|
parts = value.parts[:]
|
|
for i, id in enumerate(value.requires_ids):
|
|
full_id, var = yield CORE.get_variable_with_full_id(id)
|
|
if full_id is not None and isinstance(full_id.type, MockObjClass) and \
|
|
full_id.type.inherits_from(GlobalsComponent):
|
|
parts[i * 3 + 1] = var.value()
|
|
continue
|
|
|
|
if parts[i * 3 + 2] == '.':
|
|
parts[i * 3 + 1] = var._
|
|
else:
|
|
parts[i * 3 + 1] = var
|
|
parts[i * 3 + 2] = ''
|
|
yield LambdaExpression(parts, parameters, capture, return_type)
|
|
|
|
|
|
def is_template(value):
|
|
"""Return if value is a lambda expression."""
|
|
return isinstance(value, Lambda)
|
|
|
|
|
|
@coroutine
|
|
def templatable(value, # type: Any
|
|
args, # type: List[Tuple[SafeExpType, str]]
|
|
output_type, # type: Optional[SafeExpType],
|
|
to_exp=None # type: Optional[Any]
|
|
):
|
|
"""Generate code for a templatable config option.
|
|
|
|
If `value` is a templated value, the lambda expression is returned.
|
|
Otherwise the value is returned as-is (optionally process with to_exp).
|
|
|
|
:param value: The value to process.
|
|
:param args: The arguments for the lambda expression.
|
|
:param output_type: The output type of the lambda expression.
|
|
:param to_exp: An optional callable to use for converting non-templated values.
|
|
:return: The potentially templated value.
|
|
"""
|
|
if is_template(value):
|
|
lambda_ = yield process_lambda(value, args, return_type=output_type)
|
|
yield lambda_
|
|
else:
|
|
if to_exp is None:
|
|
yield value
|
|
elif isinstance(to_exp, dict):
|
|
yield to_exp[value]
|
|
else:
|
|
yield to_exp(value)
|
|
|
|
|
|
class MockObj(Expression):
|
|
"""A general expression that can be used to represent any value.
|
|
|
|
Mostly consists of magic methods that allow ESPHome's codegen syntax.
|
|
"""
|
|
def __init__(self, base, op=u'.'):
|
|
self.base = base
|
|
self.op = op
|
|
super(MockObj, self).__init__()
|
|
|
|
def __getattr__(self, attr): # type: (str) -> MockObj
|
|
next_op = u'.'
|
|
if attr.startswith(u'P') and self.op not in ['::', '']:
|
|
attr = attr[1:]
|
|
next_op = u'->'
|
|
if attr.startswith(u'_'):
|
|
attr = attr[1:]
|
|
return MockObj(u'{}{}{}'.format(self.base, self.op, attr), next_op)
|
|
|
|
def __call__(self, *args): # type: (SafeExpType) -> MockObj
|
|
call = CallExpression(self.base, *args)
|
|
return MockObj(call, self.op)
|
|
|
|
def __str__(self): # type: () -> unicode
|
|
return text_type(self.base)
|
|
|
|
def __repr__(self):
|
|
return u'MockObj<{}>'.format(text_type(self.base))
|
|
|
|
@property
|
|
def _(self): # type: () -> MockObj
|
|
return MockObj(u'{}{}'.format(self.base, self.op))
|
|
|
|
@property
|
|
def new(self): # type: () -> MockObj
|
|
return MockObj(u'new {}'.format(self.base), u'->')
|
|
|
|
def template(self, *args): # type: (*SafeExpType) -> MockObj
|
|
if len(args) != 1 or not isinstance(args[0], TemplateArguments):
|
|
args = TemplateArguments(*args)
|
|
else:
|
|
args = args[0]
|
|
return MockObj(u'{}{}'.format(self.base, args))
|
|
|
|
def namespace(self, name): # type: (str) -> MockObj
|
|
return MockObj(u'{}{}'.format(self._, name), u'::')
|
|
|
|
def class_(self, name, *parents): # type: (str, *MockObjClass) -> MockObjClass
|
|
op = '' if self.op == '' else '::'
|
|
return MockObjClass(u'{}{}{}'.format(self.base, op, name), u'.', parents=parents)
|
|
|
|
def struct(self, name): # type: (str) -> MockObjClass
|
|
return self.class_(name)
|
|
|
|
def enum(self, name, is_class=False): # type: (str, bool) -> MockObj
|
|
return MockObjEnum(enum=name, is_class=is_class, base=self.base, op=self.op)
|
|
|
|
def operator(self, name): # type: (str) -> MockObj
|
|
if name == 'ref':
|
|
return MockObj(u'{} &'.format(self.base), u'')
|
|
if name == 'ptr':
|
|
return MockObj(u'{} *'.format(self.base), u'')
|
|
if name == "const":
|
|
return MockObj(u'const {}'.format(self.base), u'')
|
|
raise NotImplementedError
|
|
|
|
@property
|
|
def using(self): # type: () -> MockObj
|
|
assert self.op == '::'
|
|
return MockObj(u'using namespace {}'.format(self.base))
|
|
|
|
def __getitem__(self, item): # type: (Union[str, Expression]) -> MockObj
|
|
next_op = u'.'
|
|
if isinstance(item, str) and item.startswith(u'P'):
|
|
item = item[1:]
|
|
next_op = u'->'
|
|
return MockObj(u'{}[{}]'.format(self.base, item), next_op)
|
|
|
|
|
|
class MockObjEnum(MockObj):
|
|
def __init__(self, *args, **kwargs):
|
|
self._enum = kwargs.pop('enum')
|
|
self._is_class = kwargs.pop('is_class')
|
|
base = kwargs.pop('base')
|
|
if self._is_class:
|
|
base = base + '::' + self._enum
|
|
kwargs['op'] = '::'
|
|
kwargs['base'] = base
|
|
MockObj.__init__(self, *args, **kwargs)
|
|
|
|
def __str__(self): # type: () -> unicode
|
|
if self._is_class:
|
|
return super(MockObjEnum, self).__str__()
|
|
return u'{}{}{}'.format(self.base, self.op, self._enum)
|
|
|
|
def __repr__(self):
|
|
return u'MockObj<{}>'.format(text_type(self.base))
|
|
|
|
|
|
class MockObjClass(MockObj):
|
|
def __init__(self, *args, **kwargs):
|
|
parens = kwargs.pop('parents')
|
|
MockObj.__init__(self, *args, **kwargs)
|
|
self._parents = []
|
|
for paren in parens:
|
|
if not isinstance(paren, MockObjClass):
|
|
raise ValueError
|
|
self._parents.append(paren)
|
|
# pylint: disable=protected-access
|
|
self._parents += paren._parents
|
|
|
|
def inherits_from(self, other): # type: (MockObjClass) -> bool
|
|
if self == other:
|
|
return True
|
|
for parent in self._parents:
|
|
if parent == other:
|
|
return True
|
|
return False
|
|
|
|
def template(self, *args):
|
|
# type: (*SafeExpType) -> MockObjClass
|
|
if len(args) != 1 or not isinstance(args[0], TemplateArguments):
|
|
args = TemplateArguments(*args)
|
|
else:
|
|
args = args[0]
|
|
new_parents = self._parents[:]
|
|
new_parents.append(self)
|
|
return MockObjClass(u'{}{}'.format(self.base, args), parents=new_parents)
|
|
|
|
def __repr__(self):
|
|
return u'MockObjClass<{}, parents={}>'.format(text_type(self.base), self._parents)
|