
Security News
npm Adopts OIDC for Trusted Publishing in CI/CD Workflows
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
OQS (Open Quick Script) is a Python library for interpreting versatile expressions, supporting basic to advanced operations, custom functions, and performance monitoring. It efficiently handles fundamental types and operations, interprets expressions using variables from dictionaries or JSON, and adheres to robust error handling standards. OQS enhances Python's expression evaluation capabilities, making it ideal for diverse applications.
OQS
or Open Quick Script
is a streamlined and system-neutral expression language designed for universal adaptability. It excels in processing fundamental types and operations and can interpret expressions, optionally accompanied by a dictionary, map, or JSON of variables, to produce consistent and logical outcomes.
The OQS
Python Implementation was built following the OQS Specification
located at in the main repository.
To install the Python implementation of OQS, run the following command:
pip install oqs
from oqs import oqs_engine
# Simple expression evaluation
result: dict[str, dict[str, any]] = oqs_engine(expression="2 + 2")
print(result)
# Expression with variables
result: dict[str, dict[str, any]] = oqs_engine(expression="a + b", variables={"a": 1, "b": 2})
print(result)
OQS
supports complex operations, including lists, string manipulations, and custom functions. Here's an example of using OQS
with more advanced features including evaluating multiple expressions:
from oqs import (oqs_engine, ExpressionInput)
# Evaluating multiple expressions at once
multi_expressions: list[ExpressionInput] = [
ExpressionInput(expression="1 + 2"),
ExpressionInput(expression="a - b", variables={"a": 5, "b": 3}),
ExpressionInput(expression="<{2 * c}>", variables={"c": 4}, string_embedded=True)
]
results: dict[str, dict[str, any]] = oqs_engine(evaluate_multiple=True, expression_inputs=multi_expressions)
print(results)
The OQS
engine provides detailed error messages for various error types, including syntax errors, type errors, and undefined variables. Here's how to handle errors gracefully:
from oqs import oqs_engine
result: dict[str, dict[str, any]] = oqs_engine(expression="invalid syntax")
if "error" in result:
print("Error encountered:", result["error"]["message"])
else:
print(result)
ADD(argument1, argument2, ...)
- Adds Numbers, concatenates Strings, merges Lists or merges KVSs:
Number
Temporal
String
List
KVS
Decimal
, it will return a Decimal
.SUBTRACT(argument1, argument2)
- Subtracts numbers or removes instances from strings/lists:
Number
.String
or List
.Temporal
and the second argument should be a Duration
.MULTIPLY(argument1, argument2, ...)
- Multiplies numbers or repeats strings/lists:
Number
or the first String
/List
and the rest Number
.DIVIDE(argument1, argument2)
- Divides the first number by the second:
Number
.Number
.EXPONENTIATE(base, exponent)
- Raises a number to the power of another:
Number
.Number
.MODULO(number1, number2)
- Calculates the remainder of division:
Number
.Number
.LESS_THAN(argument1, argument2, ...)
- Compares if each preceding argument is less than its following argument:
Number
or all inputs must be of the same Temporal
subtype.Boolean
- Returns true
if each argument is less than the next one, otherwise false
.GREATER_THAN(argument1, argument2, ...)
- Compares if each preceding argument is greater than its following argument:
Number
or all inputs must be of the same Temporal
subtype.Boolean
- Returns true
if each argument is greater than the next one, otherwise false
.LESS_THAN_OR_EQUAL(argument1, argument2, ...)
- Compares if each preceding argument is less than or equal to its following argument:
Number
or all inputs must be of the same Temporal
subtype.Boolean
- Returns true
if each argument is less than or equal to the next one, otherwise false
.GREATER_THAN_OR_EQUAL(argument1, argument2, ...)
- Compares if each preceding argument is greater than or equal to its following argument:
Number
or all inputs must be of the same Temporal
subtype.Boolean
- Returns true
if each argument is greater than or equal to the next one, otherwise false
.EQUALS(argument1, argument2, ...)
- Compares if all arguments are equal:
Boolean
- Returns true
if all arguments are equal, otherwise false
.NOT_EQUALS(argument1, argument2, ...)
- Compares if any of the arguments are not equal:
Boolean
- Returns true
if any argument is not equal to the others, otherwise false
.STRICTLY_EQUALS(argument1, argument2, ...)
- Compares if all arguments are strictly equal (identical in type and value):
Boolean
- Returns true
if all arguments are strictly equal, otherwise false
.STRICTLY_NOT_EQUALS(argument1, argument2, ...)
- Compares if any of the arguments are strictly not equal (different in type or value):
Boolean
- Returns true
if any argument is strictly not equal to the others, otherwise false
.AND(argument1, argument2, ...)
- Performs a logical AND operation on all provided arguments:
Boolean
- Returns true
if all arguments are truthy, otherwise false
.AND(true, 1, "text")
Output: true
AND(true, 0)
Output: false
OR(argument1, argument2, ...)
- Performs a logical OR operation on all provided arguments:
Boolean
- Returns true
if at least one argument is truthy, otherwise false
.OR(false, 0, null, "text")
Output: true
OR(false, 0, "")
Output: false
NOT(argument)
- Performs a logical NOT operation on the provided argument:
Boolean
- Returns true
if the argument is falsy, otherwise false
.NOT(true)
Output: false
NOT(0)
Output: true
NOT("text")
Output: false
(since "text" is truthy)NOT(null)
Output: true
INTEGER(argument)
- Converts to an integer representation:
Decimal
, String
, Integer
, or Boolean
.Integer
.DECIMAL(argument)
- Converts to a decimal representation:
Integer
, String
, or Decimal
.Decimal
.STRING(argument)
- Converts to a string representation:
String
.LIST(argument1, argument2, ...)
- Creates a list from provided arguments:
List
.KVS(key1, value1, key2, value2, ..., keyN, valueN)
- Creates a key-value store:
String
, values can be any type.KVS
.BOOLEAN(argument)
/ BOOL(argument)
- Evaluates the truthiness of an argument:
Boolean
.Great! To include the AND
and OR
functions in the OQS
language guidelines, we can expand the "Built-in Functions" section. These functions will provide an alternative way to perform logical operations, particularly useful for handling multiple operands or integrating into more complex expressions.KEYS(kvs)
- Retrieves a list of all keys in a KVS:
KVS
.List
of keys.VALUES(kvs)
- Retrieves a list of all values in a KVS:
KVS
.List
of values.UNIQUE(list)
- Returns a list of unique values:
List
.List
containing unique elements.REVERSE(list)
- Reverses the order of a list:
List
.List
in reverse order.MAX(number1, number2, ..., numberN)
- Finds the maximum number:
Number
or all inputs must be of the same Temporal
subtype.Number
.MIN(number1, number2, ..., numberN)
- Finds the minimum number:
Number
or all inputs must be of the same Temporal
subtype.Number
.SUM(list)
- Adds up items in a list:
List
with all elements of the same base type.LENGTH(object)
/LEN(object)
- Returns the count of items or characters:
List
, String
, Integer
, or Decimal
.Integer
.APPEND(list, item)
- Appends an item to a list:
List
, second can be any type.List
.UPDATE(kvs/list, key/index, value)
- Updates a KVS or List with a new value:
List
, second Integer
(index), third any type.KVS
, second and third any type (key and value).List
or KVS
.REMOVE_ITEM(list/kvs, item, max_occurrences=unlimited)
- Removes an item from a list or KVS:
List
or KVS
, second input is the item to remove, third (optional) is Integer
for maximum occurrences.List
or KVS
.REMOVE(list/kvs, index/key)
- Removes an item from a list or KVS by index or key:
List
, second Integer
(index).KVS
, second String
(key).List
or KVS
.ACCESS(list/kvs, index/key, [optional default value])
- Accesses an item in a list or KVS:
List
, second Integer
(index).KVS
, second String
(key), third (optional) any type (default value).IF(condition1, result1, ..., conditionN, resultN, [else_result])
- Evaluates conditions and returns corresponding results:
else
result.TYPE(argument)
- Determines the type of the given argument:
String
representing the type of the argument, such as "number", "integer", "decimal", "boolean", "list", "string", "function", "null", or "kvs".TYPE(5)
Output: "integer"
TYPE([1, 2, 3])
Output: "list"
IS_TYPE(argument, type_string)
- Evaluates whether the argument's type matches the specified type string:
String
.type_string
input is case-insensitive.Boolean
indicating the type match.true
for "number".IS_TYPE(5, "number")
Output: true
IS_TYPE("hello", "string")
Output: true
TRY(expression, error_type1, result1, ..., error_typeN, resultN)
- Attempts to evaluate an expression and handles specific errors with corresponding fallback expressions:
String
error types and their corresponding expressions.error_type
inputs are case-insensitive.TRY(1/0, "Division By Zero Error", "Infinity", "Syntax Error", "Check expression")
Output: "Infinity"
RANGE(start, stop, step)
- Generates a list of integers starting from start
, ending before stop
, incrementing by step
:
stop
with start
defaulting to 0 and step
defaulting to 1.List
of integers.RANGE(3)
Output: [0, 1, 2]
RANGE(1, 3)
Output: [1, 2]
RANGE(2, 10, 2)
Output: [2, 4, 6, 8]
FOR(list, variable_name, expression)
/ MAP(list, variable_name, expression)
- Iterates over each item in a list, executing an expression for each item:
List
, second a String
for the variable name that will be set to the current item from the list, and third an expression.List
of the results from evaluating the expression for each list item.FOR([1, 2, 3], FOR_LIST_ITEM * 2)
Output: [2, 4, 6]
RAISE(error_name, error_message)
- Triggers a specified error or creates a custom error:
String
.error_name
is not a predefined error, a custom error with that name is raised.RAISE("Syntax Error", "Invalid syntax")
Output: Raises a Syntax Error with the message "Invalid syntax".RAISE("NewError", "Custom error occurred")
Output: Raises a custom error named "NewError" with the message "Custom error occurred".FILTER(list/kvs, variable_name, predicate)
- Filters elements of a List
or key-value pairs of a KVS
based on a provided predicate expression:
List
or KVS
to be filtered.String
representing the name of the variable that will be assigned each item or key-value pair during evaluation.Boolean
value. It is evaluated for each item (or key-value pair in the case of KVS
) in the List
/KVS
.List
or KVS
containing only those elements (or key-value pairs) for which the predicate returns true
.FILTER([1, 2, 3, 4], "x", x > 2)
Output: [3, 4]
FILTER({"a": 1, "b": 2, "c": 3}, "value", value == 2)
Output: {"b": 2}
SORT(list, variable_name, key_expression, [descending=false])
- Sorts a List
based on a key generated by an expression for each element:
List
to be sorted.String
representing the name of the variable that will be assigned each item during evaluation.List
.Boolean
indicating whether the sort should be in descending order. Defaults to false
.List
sorted based on the keys generated by the key_expression
.SORT([1, 2, 3, 4], "x", x, true)
Output: [4, 3, 2, 1]
SORT(["apple", "banana", "cherry"], "fruit", LEN(fruit))
Output: ["apple", "cherry", "banana"]
FLATTEN(list)
- Flattens a nested List
(a List
of List
s) into a single-level List
:
List
potentially containing other List
s as elements.List
where all elements are not List
s.FLATTEN([[1, 2], [3, 4], [5]])
Output: [1, 2, 3, 4, 5]
FLATTEN([[["a", "b"], "c"], ["d"]])
Output: ["a", "b", "c", "d"]
SLICE(list/string, start, [end])
- Extracts a subsection of a List
or String
:
List
or String
from which a subsection is to be extracted.Integer
representing the starting index of the subsection (inclusive).Integer
representing the ending index of the subsection (exclusive). If omitted, the slice includes all elements from the start to the end of the List
/String
.List
or String
that is a subsection of the input List
/String
.SLICE([1, 2, 3, 4, 5], 1, 3)
Output: [2, 3]
SLICE("Hello World", 6)
Output: "World"
IN(value, list/kvs)
- Checks if a given value is present in a List
or if a given key exists in a KVS
:
List
or KVS
to be searched. If a List
is provided, the function checks for the presence of the value in the List
. If a KVS
is provided, the function checks if the value is a key in the KVS
.Boolean
. Returns true
if the value is found in the List
or if the value is a key in the KVS
. Returns false
otherwise.IN(3, [1, 2, 3, 4])
Output: true
IN("b", {"a": 1, "b": 2, "c": 3})
Output: true
IN("z", [1, 2, 3, 4])
Output: false
IN("d", {"a": 1, "b": 2, "c": 3})
Output: false
DATE(year, month, day)
- Creates a Date
from specified year, month, and day:
Integer
.Date
.TIME(hour, minute, second, [millisecond])
- Creates a Time
from specified hour, minute, second, and optionally millisecond:
Integer
.Time
.DATETIME(year, month, day, hour, minute, second, [millisecond])
- Creates a DateTime
from specified year, month, day, hour, minute, second, and optionally millisecond:
Integer
.DateTime
.DURATION(days, hours, minutes, seconds, [milliseconds])
- Creates a Duration
from specified days, hours, minutes, seconds, and optionally milliseconds:
Integer
.Duration
.NOW()
- Returns the current UTC DateTime
:
DateTime
.TODAY()
- Returns the current UTC Date
.
Date
.TIME_NOW()
- Returns the current UTC Time
.
Time
.PARSE_TEMPORAL(string, type, [format])
- Converts a String
to the appropriate Temporal
type (DateTime
, Date
, Time
, Duration
), optionally using a specified format. The optional format input will be ignored if the specified type is Duration
:
String
, second String
one of the Temporal subtypes (case-insensitive), third (optional) String
(format pattern).Temporal
type based on the input String
.PARSE_TEMPORAL("2023-12-25T15:30:00", "DateTime")
Output: DateTime(2023, 12, 25, 15, 30, 0)
PARSE_TEMPORAL("2023-12-25", "Date")
Output: Date(2023, 12, 25)
PARSE_TEMPORAL("15:30:00", "Time")
Output: Time(15, 30, 0)
PARSE_TEMPORAL("1 02:15:30", "Duration")
Output: Duration(1, 2, 15, 30)
FORMAT_TEMPORAL(temporal, format)
- Formats a Temporal
(Date
, Time
, DateTime
, Duration
) into a String
using the specified format:
Temporal
(Date
, Time
, DateTime
, Duration
), second String
(format pattern).String
.EXTRACT_DATE(datetime)
- Extracts the Date
component from a DateTime
:
DateTime
.Date
.EXTRACT_TIME(datetime)
- Extracts the Time
component from a DateTime
:
DateTime
.Time
.Function calls are completed by putting the function name first and following it by putting an open parentheses (
followed by any number of arguments separated by commas ,
and then followed by a closing parentheses )
.
Enable performance monitoring to track CPU usage time.
from oqs import oqs_engine
result: dict[str, dict[str, any]] = oqs_engine(expression="2 + 2", report_usage=True)
print("Result:", result)
print("CPU Time (ns):", result.get("cpu_time_ns", "N/A"))
Extend OQS
capabilities by adding custom functions.
from oqs import (oqs_engine, OQSInterpreter, FunctionNode)
from oqs.errors import OQSTypeError
from oqs.utils.shortcuts import get_oqs_type
def custom_multiply(interpreter: OQSInterpreter, node: FunctionNode) -> int | float:
if not (2 < len(node.args) < 2):
raise OQSInvalidArgumentQuantityError(
function_name=node.name, expected_min=2, expected_max=2, actual=len(node.args)
)
arg_1, arg_2 = [interpreter.evaluatate(arg) for arg in node.args]
if isinstance(arg_1, (int, float)) and isinstance(arg_2, (int, float)):
return arg_1 * arg_2
else:
raise OQSTypeError(f"Cannot multiply '{get_oqs_type(arg_1)}' by '{get_oqs_type(arg_2)}'.")
result: dict[str, dict[str, any]] = oqs_engine(expression="custom_multiply(2, 3)", additional_functions=[("custom_multiply", custom_multiply)])
print(result)
OQS
supports its own data types. They are as follows:
Number
: A parent type to the two following types:
Integer
: Any value not surrounded by quotations "
'
that includes only digits and underscores such as 1
or 1_000
.Decimal
: Any Integer containing a Decimal Point .
such as 1.0
or .0
or 1.
or 1_000.0
. However, it is important to note that decimals containing an underscore after the decimal or more than one decimal will raise an error.String
: Any value starting with a quotation "
or '
and going until that quotation appears again unless it's already in a string. For example "This is a string"
or This is also a single 'string''
.List
: A value surrounded by square brackets separating its values using commas ,
such as []
or ["hi", 1]
. Lists can contain any number of values of any type including Lists. Nested lists are supported such as [[1, 2], 3].
Boolean
: The values true
and false
.KVS
or Key-Value Store
: A value surrounded by curly brackets and separating its keys and values using a colon :
and separating its pairs using commas ,
and such as {}
or {"hi": 1, "hey": "hello"}
. KVSs can contain any number of values of any type including Lists. Its keys must be Strings. Nested KVSs are supported such as {"kvs": {"1": 1, "2": 2}, "3": 3}.
Null
: The value null
.OQS
has advanced error reporting with the following errors:
OQS
, function names are not case-sensitive. For example, add
, Add
, and ADD
are treated as the same function.OQS
are case-sensitive. This means var
, Var
, and VAR
are considered different variables.Variables in OQS
can be used to store data that can be referred to in your expressions. They are defined as key-value pairs and passed to the oqs_engine
. Here's an example:
from oqs import oqs_engine
# Using variables in expressions
variables: dict[str, any] = {"x": 10, "y": 20}
result: dict[str, dict[str, any]] = oqs_engine(expression="x * y", variables=variables)
print(result)
OQS
fully supports nested expressions and evaluations anywhere within an expression, including in function calls. Here's how it works:
from oqs import oqs_engine
# Nested expressions
result: dict[str, dict[str, any]] = oqs_engine(expression="ADD(1, MULTIPLY(x, y))", variables={"x": 2, "y": 3})
print(result)
Unpacking in OQS
is done using the ***
notation. It's used to expand lists or KVSs directly into function arguments or to create new lists/KVSs. Here's an example:
from oqs import oqs_engine
# Unpacking a list into function arguments
variables: dict[str, any] = {"numbers": [1, 2, 3]}
result: dict[str, dict[str, any]] = oqs_engine(expression="SUM(***numbers)", variables=variables)
print(result)
When OQS
evaluates an expression, the final types are translated to Python types for seamless integration. Here's how OQS
types map to Python types:
Number
(both Integer
and Decimal
): Translated to Python's int
or float
.
String
: Becomes Python's str
.
List
: Translated to Python's list
.
Boolean
: Becomes Python's bool
.
KVS
(Key-Value Store): Translated to Python's dict
.
Null
: Becomes Python's None
.
For example, an OQS
list [1, "hello", true]
would be translated to the Python list [1, "hello", True]
.
Contributions to the OQS
Python implementation are welcome. Please follow the guidelines in the main OQS
repository for contributing.
This project is licensed under the Creative Commons Attribution 4.0 International License. More details about that can be found in the Main Repository License.
The OQS
Python Implementation was built and is being maintained through the support of Infuzu
Core Contributors of the OQS
Python Implementation are as follows:
FAQs
OQS (Open Quick Script) is a Python library for interpreting versatile expressions, supporting basic to advanced operations, custom functions, and performance monitoring. It efficiently handles fundamental types and operations, interprets expressions using variables from dictionaries or JSON, and adheres to robust error handling standards. OQS enhances Python's expression evaluation capabilities, making it ideal for diverse applications.
We found that oqs demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
Research
/Security News
A RubyGems malware campaign used 60 malicious packages posing as automation tools to steal credentials from social media and marketing tool users.
Security News
The CNA Scorecard ranks CVE issuers by data completeness, revealing major gaps in patch info and software identifiers across thousands of vulnerabilities.