Jens Geyer created THRIFT-6114:
----------------------------------

             Summary: Python service/function names that are Python keywords 
generate unimportable modules and a broken -remote script
                 Key: THRIFT-6114
                 URL: https://issues.apache.org/jira/browse/THRIFT-6114
             Project: Thrift
          Issue Type: Bug
          Components: Python - Compiler
            Reporter: Jens Geyer


Follow-up to THRIFT-5927 (Python keyword-escaping) and THRIFT-6113 (regression 
test wasn't actually running in CI).

THRIFT-5927 switched Python codegen from hard-rejecting reserved-keyword 
identifiers at compile time (validate_id() against the full keyword set) to 
escaping them with a trailing underscore. That escaping was wired into 
struct/enum/field/exception generation and the service Client/Processor class 
bodies, but was missed in two places that are exercised by the existing 
Thrift5927.thrift test fixture (service continue { import return(1: False 
while) throws (1: True yield) }):

1. t_py_generator::generate_service() builds the service's own module filename 
from the raw, unescaped service name (t_py_generator.cc:1293: "package_dir_ + 
"/" + service_name_ + ".py""). For a service named "continue" this produces 
continue.py. That file compiles fine in isolation, but it can only be reached 
with importlib -- neither statement form a normal consumer (or Thrift's own 
generated -remote script) would use actually parses:

     >>> import ast
     >>> ast.parse('import thrift5927.continue')
     SyntaxError: invalid syntax
     >>> ast.parse('from thrift5927 import continue')
     SyntaxError: invalid syntax

2. t_py_generator::generate_service_remote() (t_py_generator.cc:1765-1949), 
which emits the "<service>-remote" CLI helper script, never calls 
maybe_escape_identifier() at all. For Thrift5927.thrift this produces a script 
with three genuine syntax errors, reproduced with py_compile:

     from thrift5927 import continue      # line 19 -- import target is a 
keyword
     client = continue.Client(protocol)   # line 104 -- keyword used as an 
identifier
     client.return(eval(args[0]),))       # line 111 -- keyword used as a 
method name

   py_compile.compile() on this file raises "SyntaxError: invalid syntax" at 
line 19.

Net effect: for a keyword-named service or function, THRIFT-5927 replaced a 
safe, loud compile-time rejection with a silent generation of broken Python. 
This is a regression introduced by THRIFT-5927, not a pre-existing issue -- 
prior to that fix, compiling Thrift5927.thrift's service/function names would 
have failed validate_id() outright.

The existing test (lib/py/test/test_compiler/test_keyword_escape.py) does not 
catch this because it only globs **/*.py for compileability; the -remote script 
is intentionally generated without a .py extension (matching every other 
language's convention for the executable helper script) and is invisible to 
that glob.

Proposed fix (scoped to what's covered by the existing fixture):
- Escape service_name_ at its three real identifier-position usages: the module 
filename (t_py_generator.cc:1293), the "from <module> import <service>" line in 
generate_service_remote (:1793), and the "client = <service>.Client(...)" line 
(:1896). The CLI script's own filename (<service>-remote, :1776) is left as-is 
since it is a shell-invoked filename, not a Python identifier -- keeping the 
literal IDL name there is more useful for users typing it at a shell.
- Escape the function name used for the client dispatch call in 
generate_service_remote (:1920, "client.<function>(...)"), matching the 
already-escaped method name on the generated Client class. The other get_name() 
uses in that function are inside string literals (help text, the sys.argv 
command-word comparison) and correctly stay unescaped since they reflect the 
original IDL name a CLI user would type.
- Extend test_keyword_escape.py to also compile-check generated "*-remote" 
files, not just "*.py".

Out of scope for this ticket (found while investigating, but not covered by the 
current fixture and needs its own fixture + a broader look at the shared 
type_name()/type_to_spec_args() helpers): t_service extends (inheritance) 
references and cross-module (included-file) type references also build Python 
import/attribute-access strings from unescaped names 
(t_py_generator.cc:1298-1301, 1387-1388, 1432-1433, 1978-1979, and the 
type_name()/type_to_spec_args() helpers generally). Flagging for a separate 
follow-up rather than bundling an unverified, untested code path into this fix.



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

Reply via email to