Jens Geyer created THRIFT-6116:
----------------------------------

             Summary: Python generator still misses keyword escaping in six 
more spots (consts, enum-value defaults, required-field checks, 
type_hints/twisted/enum modes)
                 Key: THRIFT-6116
                 URL: https://issues.apache.org/jira/browse/THRIFT-6116
             Project: Thrift
          Issue Type: Bug
          Components: Python - Compiler
            Reporter: Jens Geyer


Fifth and (per a full audit of every get_name()-family call in 
t_py_generator.cc, 105 sites classified) hopefully final follow-up in the 
THRIFT-5927 chain (THRIFT-6113: regression test wasn't running in CI; 
THRIFT-6114: service module filename, "__all__", and -remote script; 
THRIFT-6115: service extends and cross-module type references).

Six more spots never got maybe_escape_identifier() wired in. Each confirmed 
with a real SyntaxError via py_compile/ast.parse, not just inferred from 
reading:

1. t_py_generator.cc:598, generate_const(): a top-level "const i32 break = 42" 
const whose own name is a keyword renders "break = 42" in constants.py.

2. t_py_generator.cc:647, render_const_value()'s enum branch: an unescaped enum 
*value* reference (e.g. "Lambda.None") in constant rendering. Reached from 
top-level consts AND, via render_field_default_value() calling the same 
function, from any field's default value -- struct __init__ defaults, 
thrift_spec default-value entries, and type-hint parameter defaults all funnel 
through this one line. One fix covers all four manifestations (verified by 
regenerating a probe fixture under every relevant mode post-fix).

3. t_py_generator.cc:925, generate_py_struct_definition() (-gen py:enum + a 
"(python.immutable = "")" struct): the __setattr__ override's 
"EnumType.__members__.get(<field>)" call uses the raw field name, inconsistent 
with the escaped uses of the same identifier earlier in the same statement.

4. t_py_generator.cc:1276, generate_py_struct_required_validator(): "if 
self.<field> is None:" for a required field uses the raw field name.

5. t_py_generator.cc:2179-2183, generate_process_function() (-gen py:twisted): 
the "except <Type> as <name>:" clause and its two subsequent uses use the raw 
exception field name (xname), unescaped -- inconsistent with the tornado-style 
and default-style equivalents at :2246/:2248 and :2322/:2326, which already 
call maybe_escape_identifier(xname). The exception *type* half of this line was 
already fixed by THRIFT-6115; only the "as" binding was still broken.

6. t_py_generator.cc:855, generate_py_struct_definition() (-gen 
py:type_hints,enum + immutable struct): the class-level type-hint annotation 
("<field>: typing.Optional[...]") uses the raw field name.

Four of these six only surface under non-default generator options 
(gen_enum_/gen_type_hints_/gen_twisted_), which is why test_keyword_escape.py's 
existing single "-gen py" run never caught them -- same class of blind spot as 
THRIFT-6113 (a check that only ever exercises the default path). Fixed by 
extending the test to loop the compiler over three generation modes ("py", 
"py:type_hints,enum", "py:twisted") and running every existing static check 
(compile, __all__, keyword-literal-except) against each.

New fixture additions to Thrift5927.thrift: a keyword-named plain const, a 
keyword-named const of enum type (reusing the existing Lambda enum's None 
value), a struct with a required keyword-named field, and a struct with a 
keyword-named, enum-typed, default-valued field marked "(python.immutable = 
"")".

Const-reference note: consts referencing other consts (e.g. "const i32 B = A;") 
are resolved to their literal value by the compiler at parse time ("B = 5", not 
"B = A"), so there's no separate const-reference call site needing escaping 
beyond the definition itself.

Explicitly out of scope (different bug category, not an identifier-escaping 
gap): get_real_py_module()'s fallback to program->get_name() when no "namespace 
py ..." is declared -- if a .thrift file's own base name collides with a Python 
keyword, the generated top-level package name would too. This is a 
package/namespace-naming concern, not an identifier-escaping one, and the 
project already has an established workaround pattern for exactly this (see the 
"namespace d share" comment in tutorial/shared.thrift, choosing a different 
namespace name rather than auto-escaping).

Branched from THRIFT-6115 (not master): the twisted except-fix (item 5) is 
layered on top of THRIFT-6115's type_name() fix (the exception type half of 
that same line), so this depends on THRIFT-6115 (PR #3660), which in turn 
depends on THRIFT-6114 (PR #3659).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to