SCR002: compile-error
Script parses but Jython refuses to compile it
| Code | SCR002 |
| Name | compile-error |
| Category | Scripts (SCR) |
| Default severity | error |
| Enabled by default | yes |
| Fix available | no |
| ign-lint equivalent | none |
What it does
Section titled “What it does”Reports code that Jython 2.7's parser accepts but its compiler rejects, with Jython's message:
returnoryieldoutside a function (also in a class body);breakoutside a loop,continueoutside a loop (a loop'selseclause is outside it);- a duplicate parameter name (
def f(a, a), also inside tuple parameters); return value(evenreturn None) in a generator;import *in a function that is nested or contains nested functions with free variables, unqualifiedexecin a function with free variables;- a bare
except:that is not the last handler; global xwherexis a parameter;del xwherexis used by a nested function;- assigning to
__debug__; from __future__ import ...that is not at the top of a module (in Perspective and event scripts it never is: the script is the body of a generated function), or names an unknown feature;- assignment targets Jython's parser accepts but its compiler crashes on
(
-a = 1,for f() in x:,except E, a + b:); - a
yieldlexically inside a list comprehension, or in the first iterable of a set or dict comprehension ([(yield) for i in x],{a for a in (yield)}): Jython's code generator emits bytecode its own verifier rejects (java.lang.VerifyError). Ayieldin the element, filter or later iterables of a set, dict or generator comprehension, and in the first iterable of a generator expression, compiles. - a
yieldin the exception type of anexceptclause (except (yield):,except E((yield)):; Jython's code generator fails withNegativeArraySizeException), or in an attribute or subscript target inside an unpacking target (b, a[(yield)] = 1, 2, also inforandwith ... astargets;IllegalArgumentException). A single target (a[(yield)] = 1), an augmented assignment anddelcompile, and so does ayieldinside alambdathere.
It also reports scripts that are too large for Jython:
- the first two lines together are 100,000 characters or longer (UTF-16
code units, line ends included; in Perspective and event scripts the
first line is the generated
defheader). Jython reads them ahead to look for an encoding declaration and fails withjava.io.IOException: Mark invalid. Longer lines further down are fine; - a module, class or function body whose code certainly exceeds one Java
method (
Module or method too large: one body is one Java method of at most 65,535 bytes of bytecode). iglint adds, for every statement Jython generates code for, the least bytecode Jython's code generator emits for it and for each of its expressions (the line number update, every name, constant, attribute, subscript, call, operator, comparison, display, comprehension and branch), measured on Ignition 8.3.3's Jython and checked against the size of every body of thousands of generated programs, and reports a body only when that lower bound exceeds the limit. How many statements fit depends on what they hold: Jython fails from 9,380 lines ofpass, 3,283 ofx = 1, 2,741 ofxN = "vN"at module level, 1,880 off(N, "a", b)and 680 ofd["kN"] = {"a": N, "b": [1, 2, 3]}in a function, and iglint reports these from 9,382, 3,290, about 3,200, about 2,100 and about 740 lines; - a module with too many functions, classes, lambdas and generator
expressions for Jython's class initializer (
Module or method too large: the class's<init>builds one code object per body, with its variable names, and is one Java method too). iglint counts, per body, the instructions Jython's code generator emits for its code object at their shortest encoding (29 to 32 bytes, and 5 or 6 per parameter or assigned local variable of a function), checked against the<init>of hundreds of generated modules near the limit with 0 false reports. Jython fails from about 765 functions of eight parameters, and iglint reports these from 900; - a module with more than 32,767 distinct string literals (
Class too large: one module is one Java class, with at most 65,535 constants; Jython fails from 32,594); - a function or class whose name makes the name of its Java method too
long (
java.lang.IllegalArgumentException: UTF8 string too large): Jython names the method of a body<name>$<number>, with a private name mangled in a class, and a class file holds names of at most 65,535 bytes, so a function or class of the module fails from 65,534 characters. Long names elsewhere (variables, attributes, keywords, imports, docstrings) and long strings compile: Jython loads them in chunks.
Jython generates no code for the rest of a block after return, raise,
continue or an if/else whose every branch ends that way (but not
after break, a loop, try or with, and not for the module's own
statements). That dead code counts for neither size limit, and the errors
above that Jython's code generator raises (return, yield, break and
continue in the wrong place, import *, a bare except: that is not
last, del of a variable a nested function uses, invalid targets) are not
reported in it.
Invalid targets (-a = 1, for f() in x) crash Jython only where the
generated bytecode can be reached, so they are also not reported after
any statement that does not complete: break, a try whose body and
handlers (or whose finally) do not complete, a loop whose else does
not complete and that has no break. A new function, class or lambda
body is reachable again. That code is still generated: it counts for the
size limits, and the other errors are reported in it.
Every case was checked against Ignition's Jython with full compilation; iglint reports nothing that Jython compiles.
Why is this bad?
Section titled “Why is this bad?”The script never runs. In a library module every script that imports it fails.
Example
Section titled “Example”def onMessageReceived(self, payload): for row in payload["rows"]: process(row) else: continueUse instead:
def onMessageReceived(self, payload): for row in payload["rows"]: process(row)Generated from the rule metadata by iglint generate-docs; iglint rule SCR002 prints the same page in the terminal. Source: docs/rules/SCR002.md.