Skip to content

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

Reports code that Jython 2.7's parser accepts but its compiler rejects, with Jython's message:

  • return or yield outside a function (also in a class body);
  • break outside a loop, continue outside a loop (a loop's else clause is outside it);
  • a duplicate parameter name (def f(a, a), also inside tuple parameters);
  • return value (even return None) in a generator;
  • import * in a function that is nested or contains nested functions with free variables, unqualified exec in a function with free variables;
  • a bare except: that is not the last handler;
  • global x where x is a parameter;
  • del x where x is 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 yield lexically 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). A yield in the element, filter or later iterables of a set, dict or generator comprehension, and in the first iterable of a generator expression, compiles.
  • a yield in the exception type of an except clause (except (yield):, except E((yield)):; Jython's code generator fails with NegativeArraySizeException), or in an attribute or subscript target inside an unpacking target (b, a[(yield)] = 1, 2, also in for and with ... as targets; IllegalArgumentException). A single target (a[(yield)] = 1), an augmented assignment and del compile, and so does a yield inside a lambda there.

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 def header). Jython reads them ahead to look for an encoding declaration and fails with java.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 of pass, 3,283 of x = 1, 2,741 of xN = "vN" at module level, 1,880 of f(N, "a", b) and 680 of d["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.

The script never runs. In a library module every script that imports it fails.

def onMessageReceived(self, payload):
for row in payload["rows"]:
process(row)
else:
continue

Use 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.