On 10/7/20 4:50 AM, Markus Armbruster wrote:
> John Snow <jsnow@redhat.com> writes:
>
>> Code style tools really dislike the use of global keywords, because it
>> generally involves re-binding the name at runtime which can have strange
>> effects depending on when and how that global name is referenced in
>> other modules.
>>
>> Make a little indent level manager instead.
>>
>> Signed-off-by: John Snow <jsnow@redhat.com>
>> Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
>> Reviewed-by: Cleber Rosa <crosa@redhat.com>
>
> Intentation is a job for QAPIGen (and its subtypes). But if this patch
> is easier to achieve this series' goal, I don't mind.
>
I agree, but refactoring it properly is beyond my capacity right now.
This was the dumbest thing I could do to get pylint/mypy passing, which
required the elimination (or suppression) of the global keyword.
Creating a stateful object was the fastest way from A to B.
>> ---
>> scripts/qapi/common.py | 49 ++++++++++++++++++++++++++++--------------
>> scripts/qapi/visit.py | 7 +++---
>> 2 files changed, 36 insertions(+), 20 deletions(-)
>>
>> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
>> index cee63eb95c7..b35318b72cf 100644
>> --- a/scripts/qapi/common.py
>> +++ b/scripts/qapi/common.py
>> @@ -93,33 +93,50 @@ def c_name(name, protect=True):
>> pointer_suffix = ' *' + eatspace
>>
>>
>> -def genindent(count):
>> - ret = ''
>> - for _ in range(count):
>> - ret += ' '
>> - return ret
>> +class Indentation:
>> + """
>> + Indentation level management.
>>
>> + :param initial: Initial number of spaces, default 0.
>> + """
>> + def __init__(self, initial: int = 0) -> None:
>> + self._level = initial
>>
>> -indent_level = 0
>> + def __int__(self) -> int:
>> + return self._level
>>
>> + def __repr__(self) -> str:
>> + return "{}({:d})".format(type(self).__name__, self._level)
>>
>> -def push_indent(indent_amount=4):
>> - global indent_level
>> - indent_level += indent_amount
>> + def __str__(self) -> str:
>> + """Return the current indentation as a string of spaces."""
>> + return ' ' * self._level
>>
>> + def __bool__(self) -> bool:
>> + """True when there is a non-zero indentation."""
>> + return bool(self._level)
>>
>> -def pop_indent(indent_amount=4):
>> - global indent_level
>> - indent_level -= indent_amount
>> + def increase(self, amount: int = 4) -> None:
>> + """Increase the indentation level by ``amount``, default 4."""
>> + self._level += amount
>> +
>> + def decrease(self, amount: int = 4) -> None:
>> + """Decrease the indentation level by ``amount``, default 4."""
>> + if self._level < amount:
>> + raise ArithmeticError(
>> + f"Can't remove {amount:d} spaces from {self!r}")
>
> Raise a fancy error when there's an actual need for it. You're not
> coding a framework thousands of people you never heard of will put to
> uses you cannot imagine.
>
It's not fancy, it's just a normal built-in exception, like
AssertionError or any other:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArithmeticError: Can't remove 4 spaces from Indent(0)
vs
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: Can't remove 4 spaces from Indent(0)
I feel like it's kind of a lateral move? I realize you feel this is an
overfancy class doing what we hope is a temporary job, and that I have
overengineered the hell out of a tiny do-nothing class... but I suppose
that's also why I feel weird changing it around so much to accomplish so
little.
Differences in style, I suppose.
Feel free to change it around to suit your tastes, I don't think it's
worth spending a lot of ping-pong time on this paintsink in particular.
--js
>> + self._level -= amount
>> +
>> +
>> +indent = Indentation()
>>
>>
>> # Generate @code with @kwds interpolated.
>> -# Obey indent_level, and strip eatspace.
>> +# Obey indent, and strip eatspace.
>> def cgen(code, **kwds):
>> raw = code % kwds
>> - if indent_level:
>> - indent = genindent(indent_level)
>> - raw = re.sub(r'^(?!(#|$))', indent, raw, flags=re.MULTILINE)
>> + if indent:
>> + raw = re.sub(r'^(?!(#|$))', str(indent), raw, flags=re.MULTILINE)
>> return re.sub(re.escape(eatspace) + r' *', '', raw)
>>
>>
>> diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
>> index 808410d6f1b..14f30c228b7 100644
>> --- a/scripts/qapi/visit.py
>> +++ b/scripts/qapi/visit.py
>> @@ -18,9 +18,8 @@
>> c_name,
>> gen_endif,
>> gen_if,
>> + indent,
>> mcgen,
>> - pop_indent,
>> - push_indent,
>> )
>> from .gen import QAPISchemaModularCVisitor, ifcontext
>> from .schema import QAPISchemaObjectType
>> @@ -69,7 +68,7 @@ def gen_visit_object_members(name, base, members, variants):
>> if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
>> ''',
>> name=memb.name, c_name=c_name(memb.name))
>> - push_indent()
>> + indent.increase()
>> ret += mcgen('''
>> if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
>> return false;
>> @@ -78,7 +77,7 @@ def gen_visit_object_members(name, base, members, variants):
>> c_type=memb.type.c_name(), name=memb.name,
>> c_name=c_name(memb.name))
>> if memb.optional:
>> - pop_indent()
>> + indent.decrease()
>> ret += mcgen('''
>> }
>> ''')