Warning
THIS MODULE DOES NOT HAVE STABLE PUBLIC API
Exception raised when unsupported computing is detected inside requirement expression.
Exception raised when a resource could not be evaluated because it requires an unavailable resource.
Unlike the base class, this exception is raised before even running the expression. As in the base class the exception object is meant to have enough data to provide rich and meaningful error messages to the operator.
Exception raise when a resource expression failed to produce a true value.
This class is meant to be consumed by the UI layers to provide meaningful error messages to the operator. The expression attribute can be used to obtain the text of the expression that failed as well as the resource id that is used by that expression. The resource id can be used to lookup the (resource) job that produces such values.
Exception raised when an expression references multiple resources.
Exception raised when an expression does not reference any resources.
A NodeVisitor subclass used to analyze package requirement expressions.
A simple container for key-value data
Resource objects are used when evaluating expressions as containers for data read from resource scripts. Each RFC822 record produced by a resource script is converted to a new Resource object
Class representing a single line of an requirement program.
Each valid expression references exactly one resource. In practical terms each resource expression is a valid python expression that has no side effects (calls almost no methods, does not assign anything) that can be evaluated against a single variable which references a Resource object.
A NodeVisitor subclass used to analyze requirement expressions.
Warning
Implementation of this class requires understanding of some of the lower levels of python. The general idea is to use the ast (abstract syntax tree) module to allow the ResourceExpression class to execute safely (by not permitting various unsafe operations) and quickly (by knowing which resources are required so no O(n) operations over all resources are ever needed.
Resource expressions are written one per line, each line is like a separate min-program. This visitor will be applied to the root (module) node resulting from parsing each of those lines.
Each actual expression can only use a small subset of python syntax, most stuff is actually disallowed. Only basic expressions are permitted. Function calls are also disallowed, with the notable exception of ‘bool’, ‘int’, ‘float’ and ‘len’.
One very important aspect of each expression is the id of the resource it is computing against. This is visible as the ‘object’ the expressions are operating on, such as:
package.name == ‘fwts’
As a rule of a thumb exactly one such id is allowed per expression. This allows the code that evaluates this to know which resource to use. As resources are actually lists of records (where record values are available as object attribute) only one object/record is exposed to each expression. Using more than one object (by intent or simple typo) would lead to expression that will never match. This visitor class facilitates detecting that by computing the ids_seen set.
One notable fact is that storing is not allowed so it is (presumably) safe to evaluate the code in the context of the current python interpreter.
How this works:
Using the ast.NodeVisitor we can visit any node type by defining the visit_<class name> method. We care about Name and Call nodes and they have custom validation implemented. For all other nodes the generic_visit() method is called instead.
On each visit to ast.Name node we record the referenced ‘id’ (the id of the object being referenced, in simple terms)
On each visit to ast.Call node we check if the called function is in the allowed list of ids. This also takes care of stuff like foo()() which would call the return value of foo.
On each visit to any other ast.Node we check if the class is in the white-list.
All violation cause a CodeNotAllowed exception to be raised with the node that was rejected as argument.
Internal method of NodeVisitor.
Called for all ast.Node() subclasses that don’t have a dedicated visit_xxx() method here. Only needed to all the _check_node() method.
Class for storing and executing resource programs.
This is used by job requirement expressions
Evaluate the program with the given map of resources.
Raises a ExpressionFailedError exception if the any of the expressions that make up this program cannot be executed or executes but produces a non-true value.
Returns True
Resources must be a dictionary of mapping resource id to a list of Resource objects.