Package: oqs
Python Implementation of OQS (Open Quick Script)IntroductionOQSorOpen Quick Scriptis 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.TheOQSPython Implementation was built following theOQS Specificationlocated at in themain repository.InstallationPrerequisitesPython 3.10+pip package managerInstalling OQSTo install the Python implementation of OQS, run the following command:pipinstalloqsUsageBasic Usagefromoqsimportoqs_engine# Simple expression evaluationresult:dict[str,dict[str,any]]=oqs_engine(expression="2 + 2")print(result)# Expression with variablesresult:dict[str,dict[str,any]]=oqs_engine(expression="a + b",variables={"a":1,"b":2})print(result)Advanced UsageOQSsupports complex operations, including lists, string manipulations, and custom functions. Here's an example of usingOQSwith more advanced features including evaluating multiple expressions:fromoqsimport(oqs_engine,ExpressionInput)# Evaluating multiple expressions at oncemulti_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)Error HandlingTheOQSengine provides detailed error messages for various error types, including syntax errors, type errors, and undefined variables. Here's how to handle errors gracefully:fromoqsimportoqs_engineresult:dict[str,dict[str,any]]=oqs_engine(expression="invalid syntax")if"error"inresult:print("Error encountered:",result["error"]["message"])else:print(result)Built-in FunctionsADD(argument1, argument2, ...)- Adds Numbers, concatenates Strings, merges Lists or merges KVSs:Inputs:Amount: A minimum of two inputs with no maximum.Types: All input types must be of the same type being one of the following:NumberTemporalStringListKVSOutputs: The same type that the inputs were. If one of the inputs was aDecimal, it will return aDecimal.SUBTRACT(argument1, argument2)- Subtracts numbers or removes instances from strings/lists:Inputs:Amount: Exactly two inputs required.Types:For numbers: BothNumber.For strings/lists: BothStringorList.For temporal: First argument should be aTemporaland the second argument should be aDuration.Outputs: The same type as the inputs.MULTIPLY(argument1, argument2, ...)- Multiplies numbers or repeats strings/lists:Inputs:Amount: A minimum of two inputs with no maximum.Types: Either allNumberor the firstString/Listand the restNumber.Outputs: The same type as the first input.DIVIDE(argument1, argument2)- Divides the first number by the second:Inputs:Amount: Exactly two inputs.Types: Both inputs must beNumber.Error Handling: Raises an error if the second argument is zero.Outputs:Number.EXPONENTIATE(base, exponent)- Raises a number to the power of another:Inputs:Amount: Exactly two inputs.Types: Both inputs must beNumber.Outputs:Number.MODULO(number1, number2)- Calculates the remainder of division:Inputs:Amount: Exactly two inputs.Types: Both inputs must beNumber.Outputs:Number.LESS_THAN(argument1, argument2, ...)- Compares if each preceding argument is less than its following argument:Inputs:Amount: Two or more inputs.Types: All inputs must beNumberor all inputs must be of the sameTemporalsubtype.Outputs:Boolean- Returnstrueif each argument is less than the next one, otherwisefalse.GREATER_THAN(argument1, argument2, ...)- Compares if each preceding argument is greater than its following argument:Inputs:Amount: Two or more inputs.Types: All inputs must beNumberor all inputs must be of the sameTemporalsubtype.Outputs:Boolean- Returnstrueif each argument is greater than the next one, otherwisefalse.LESS_THAN_OR_EQUAL(argument1, argument2, ...)- Compares if each preceding argument is less than or equal to its following argument:Inputs:Amount: Two or more inputs.Types: All inputs must beNumberor all inputs must be of the sameTemporalsubtype.Outputs:Boolean- Returnstrueif each argument is less than or equal to the next one, otherwisefalse.GREATER_THAN_OR_EQUAL(argument1, argument2, ...)- Compares if each preceding argument is greater than or equal to its following argument:Inputs:Amount: Two or more inputs.Types: All inputs must beNumberor all inputs must be of the sameTemporalsubtype.Outputs:Boolean- Returnstrueif each argument is greater than or equal to the next one, otherwisefalse.EQUALS(argument1, argument2, ...)- Compares if all arguments are equal:Inputs:Amount: Two or more inputs.Types: Any types, but all must be of the same type.Outputs:Boolean- Returnstrueif all arguments are equal, otherwisefalse.NOT_EQUALS(argument1, argument2, ...)- Compares if any of the arguments are not equal:Inputs:Amount: Two or more inputs.Types: Any types, but all must be of the same type.Outputs:Boolean- Returnstrueif any argument is not equal to the others, otherwisefalse.STRICTLY_EQUALS(argument1, argument2, ...)- Compares if all arguments are strictly equal (identical in type and value):Inputs:Amount: Two or more inputs.Types: Any types, but all must be of the same type.Outputs:Boolean- Returnstrueif all arguments are strictly equal, otherwisefalse.STRICTLY_NOT_EQUALS(argument1, argument2, ...)- Compares if any of the arguments are strictly not equal (different in type or value):Inputs:Amount: Two or more inputs.Types: Any types, but all must be of the same type.Outputs:Boolean- Returnstrueif any argument is strictly not equal to the others, otherwisefalse.AND(argument1, argument2, ...)- Performs a logical AND operation on all provided arguments:Inputs:Amount: Two or more inputs.Types: Any types, evaluated for their truthiness.Outputs:Boolean- Returnstrueif all arguments are truthy, otherwisefalse.Examples:Input:AND(true, 1, "text")Output:trueInput:AND(true, 0)Output:falseOR(argument1, argument2, ...)- Performs a logical OR operation on all provided arguments:Inputs:Amount: Two or more inputs.Types: Any types, evaluated for their truthiness.Outputs:Boolean- Returnstrueif at least one argument is truthy, otherwisefalse.Examples:Input:OR(false, 0, null, "text")Output:trueInput:OR(false, 0, "")Output:falseNOT(argument)- Performs a logical NOT operation on the provided argument:Inputs:Amount: Exactly one input.Types: Any type, evaluated for its truthiness.Outputs:Boolean- Returnstrueif the argument is falsy, otherwisefalse.Examples:Input:NOT(true)Output:falseInput:NOT(0)Output:trueInput:NOT("text")Output:false(since "text" is truthy)Input:NOT(null)Output:trueINTEGER(argument)- Converts to an integer representation:Inputs:Amount: Exactly one input.Types:Decimal,String,Integer, orBoolean.Outputs:Integer.DECIMAL(argument)- Converts to a decimal representation:Inputs:Amount: Exactly one input.Types:Integer,String, orDecimal.Outputs:Decimal.STRING(argument)- Converts to a string representation:Inputs:Amount: Exactly one input.Types: Any single type.Outputs:String.LIST(argument1, argument2, ...)- Creates a list from provided arguments:Inputs:Amount: One or more inputs.Types: Any types.Outputs:List.KVS(key1, value1, key2, value2, ..., keyN, valueN)- Creates a key-value store:Inputs:Amount: Even number of inputs (pairs of keys and values).Types: Keys must beString, values can be any type.Error Handling: Raises an error if an odd number of arguments is provided.Outputs:KVS.BOOLEAN(argument)/BOOL(argument)- Evaluates the truthiness of an argument:Inputs:Amount: Exactly one input.Types: Any single type.Outputs:Boolean.Great! To include theANDandORfunctions in theOQSlanguage 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:Inputs:Amount: Exactly one input.Types:KVS.Outputs:Listof keys.VALUES(kvs)- Retrieves a list of all values in a KVS:Inputs:Amount: Exactly one input.Types:KVS.Outputs:Listof values.UNIQUE(list)- Returns a list of unique values:Inputs:Amount: Exactly one input.Types:List.Outputs:Listcontaining unique elements.REVERSE(list)- Reverses the order of a list:Inputs:Amount: Exactly one input.Types:List.Outputs:Listin reverse order.MAX(number1, number2, ..., numberN)- Finds the maximum number:Inputs:Amount: A minimum of two inputs with no maximum.Types: All inputs must beNumberor all inputs must be of the sameTemporalsubtype.Outputs:Number.MIN(number1, number2, ..., numberN)- Finds the minimum number:Inputs:Amount: A minimum of two inputs with no maximum.Types: All inputs must beNumberor all inputs must be of the sameTemporalsubtype.Outputs:Number.SUM(list)- Adds up items in a list:Inputs:Amount: Exactly one input.Types:Listwith all elements of the same base type.Error Handling: Raises an error for mixed types.Outputs: The sum or concatenation of list items.LENGTH(object)/LEN(object)- Returns the count of items or characters:Inputs:Amount: Exactly one input.Types:List,String,Integer, orDecimal.Outputs:Integer.APPEND(list, item)- Appends an item to a list:Inputs:Amount: Exactly two inputs.Types: First input must be aList, second can be any type.Outputs:List.UPDATE(kvs/list, key/index, value)- Updates a KVS or List with a new value:Inputs:Amount: Exactly three inputs.Types:For lists: FirstList, secondInteger(index), third any type.For KVS: FirstKVS, second and third any type (key and value).Error Handling: Raises an error if the index does not exist for lists. For KVS, adds or updates the key.Outputs: UpdatedListorKVS.REMOVE_ITEM(list/kvs, item, max_occurrences=unlimited)- Removes an item from a list or KVS:Inputs:Amount: Two or three inputs.Types: First input must beListorKVS, second input is the item to remove, third (optional) isIntegerfor maximum occurrences.Outputs: AdjustedListorKVS.REMOVE(list/kvs, index/key)- Removes an item from a list or KVS by index or key:Inputs:Amount: Exactly two inputs.Types:For lists: FirstList, secondInteger(index).For KVS: FirstKVS, secondString(key).Error Handling: Raises an error if the index does not exist for lists; does not raise an error if a key does not exist in KVS.Outputs: AdjustedListorKVS.ACCESS(list/kvs, index/key, [optional default value])- Accesses an item in a list or KVS:Inputs:Amount: Two or three inputs.Types:For lists: FirstList, secondInteger(index).For KVS: FirstKVS, secondString(key), third (optional) any type (default value).Error Handling: Raises an error if the index does not exist for lists; returns null or default value if the key does not exist in KVS.Outputs: The accessed item or default value.IF(condition1, result1, ..., conditionN, resultN, [else_result])- Evaluates conditions and returns corresponding results:Inputs:Amount: A minimum of two arguments up to an unlimited amount.Types: Alternating between conditions (any type evaluated for truthiness) and results (any type).Outputs: The result corresponding to the first true condition or theelseresult.TYPE(argument)- Determines the type of the given argument:Inputs:Amount: Exactly one input.Types: Any single type.Outputs: AStringrepresenting the type of the argument, such as "number", "integer", "decimal", "boolean", "list", "string", "function", "null", or "kvs".Examples:Input:TYPE(5)Output:"integer"Input:TYPE([1, 2, 3])Output:"list"IS_TYPE(argument, type_string)- Evaluates whether the argument's type matches the specified type string:Inputs:Amount: Exactly two inputs.Types: First input of any type, second input aString.Case Insensitivity: Thetype_stringinput is case-insensitive.Outputs: ABooleanindicating the type match.Superiority Rules: Types "integer" and "decimal" are considered subtypes of "number". An argument matching "integer" or "decimal" also returnstruefor "number".Examples:Input:IS_TYPE(5, "number")Output:trueInput:IS_TYPE("hello", "string")Output:trueTRY(expression, error_type1, result1, ..., error_typeN, resultN)- Attempts to evaluate an expression and handles specific errors with corresponding fallback expressions:Inputs:Amount: Minimum of three, odd number, no maximum.Types: The first input is an expression of any type, followed by alternatingStringerror types and their corresponding expressions.Case Insensitivity: Theerror_typeinputs are case-insensitive.Superiority Rules: Error types follow a hierarchy, with specific error types taking precedence over general ones.Outputs: The result of the first expression if no error occurs, or the result of the corresponding expression for the first true matching error.Examples:Input:TRY(1/0, "Division By Zero Error", "Infinity", "Syntax Error", "Check expression")Output:"Infinity"RANGE(start, stop, step)- Generates a list of integers starting fromstart, ending beforestop, incrementing bystep:Inputs:Amount: Between 1 and 3, all integers.Defaults: If only one argument is provided, it is considered asstopwithstartdefaulting to 0 andstepdefaulting to 1.Outputs: AListof integers.Examples:Input:RANGE(3)Output:[0, 1, 2]Input:RANGE(1, 3)Output:[1, 2]Input: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:Inputs:Amount: Exactly three inputs.Types: First input must be aList, second aStringfor the variable name that will be set to the current item from the list, and third an expression.Variable: During each iteration, the variable with the name set in the second argument is set to the current item from the list.Outputs: AListof the results from evaluating the expression for each list item.Examples:Input: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:Inputs:Amount: Exactly two inputs, bothString.Types: First input for the error name, second for the error message.Custom Error Handling: Iferror_nameis not a predefined error, a custom error with that name is raised.Outputs: Raises the specified error.Examples:Input:RAISE("Syntax Error", "Invalid syntax")Output: Raises a Syntax Error with the message "Invalid syntax".Input: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 aListor key-value pairs of aKVSbased on a provided predicate expression:Inputs:list/kvs: TheListorKVSto be filtered.variable_name: AStringrepresenting the name of the variable that will be assigned each item or key-value pair during evaluation.predicate: An expression that returns aBooleanvalue. It is evaluated for each item (or key-value pair in the case ofKVS) in theList/KVS.Outputs: A newListorKVScontaining only those elements (or key-value pairs) for which the predicate returnstrue.Examples:Input:FILTER([1, 2, 3, 4], "x", x > 2)Output:[3, 4]Input:FILTER({"a": 1, "b": 2, "c": 3}, "value", value == 2)Output:{"b": 2}SORT(list, variable_name, key_expression, [descending=false])- Sorts aListbased on a key generated by an expression for each element:Inputs:list: TheListto be sorted.variable_name: AStringrepresenting the name of the variable that will be assigned each item during evaluation.key_expression: An expression that computes a key for each element in theList.descending(optional): ABooleanindicating whether the sort should be in descending order. Defaults tofalse.Outputs: A newListsorted based on the keys generated by thekey_expression.Examples:Input:SORT([1, 2, 3, 4], "x", x, true)Output:[4, 3, 2, 1]Input:SORT(["apple", "banana", "cherry"], "fruit", LEN(fruit))Output:["apple", "cherry", "banana"]FLATTEN(list)- Flattens a nestedList(aListofLists) into a single-levelList:Inputs:list: AListpotentially containing otherLists as elements.Outputs: A newListwhere all elements are notLists.Examples:Input:FLATTEN([[1, 2], [3, 4], [5]])Output:[1, 2, 3, 4, 5]Input:FLATTEN([[["a", "b"], "c"], ["d"]])Output:["a", "b", "c", "d"]SLICE(list/string, start, [end])- Extracts a subsection of aListorString:Inputs:list/string: TheListorStringfrom which a subsection is to be extracted.start: AnIntegerrepresenting the starting index of the subsection (inclusive).end(optional): AnIntegerrepresenting the ending index of the subsection (exclusive). If omitted, the slice includes all elements from the start to the end of theList/String.Outputs: A newListorStringthat is a subsection of the inputList/String.Examples:Input:SLICE([1, 2, 3, 4, 5], 1, 3)Output:[2, 3]Input:SLICE("Hello World", 6)Output:"World"IN(value, list/kvs)- Checks if a given value is present in aListor if a given key exists in aKVS:Inputs:value: The value or key to be checked. This can be of any type.list/kvs: TheListorKVSto be searched. If aListis provided, the function checks for the presence of the value in theList. If aKVSis provided, the function checks if the value is a key in theKVS.Outputs:Boolean. Returnstrueif the value is found in theListor if the value is a key in theKVS. Returnsfalseotherwise.Examples:Input:IN(3, [1, 2, 3, 4])Output:trueInput:IN("b", {"a": 1, "b": 2, "c": 3})Output:trueInput:IN("z", [1, 2, 3, 4])Output:falseInput:IN("d", {"a": 1, "b": 2, "c": 3})Output:falseDATE(year, month, day)- Creates aDatefrom specified year, month, and day:Inputs:Amount: Exactly three inputs.Types: All inputs must beInteger.Outputs:Date.TIME(hour, minute, second, [millisecond])- Creates aTimefrom specified hour, minute, second, and optionally millisecond:Inputs:Amount: Three or four inputs.Types: All inputs must beInteger.Outputs:Time.DATETIME(year, month, day, hour, minute, second, [millisecond])- Creates aDateTimefrom specified year, month, day, hour, minute, second, and optionally millisecond:Inputs:Amount: Six or seven inputs.Types: All inputs must beInteger.Outputs:DateTime.DURATION(days, hours, minutes, seconds, [milliseconds])- Creates aDurationfrom specified days, hours, minutes, seconds, and optionally milliseconds:Inputs:Amount: Four or five inputs.Types: All inputs must beInteger.Outputs:Duration.NOW()- Returns the current UTCDateTime:Outputs:DateTime.TODAY()- Returns the current UTCDate.Outputs:Date.TIME_NOW()- Returns the current UTCTime.Outputs:Time.PARSE_TEMPORAL(string, type, [format])- Converts aStringto the appropriateTemporaltype (DateTime,Date,Time,Duration), optionally using a specified format. The optional format input will be ignored if the specified type isDuration:Inputs:Amount: One or two inputs.Types: FirstString, secondStringone of the Temporal subtypes (case-insensitive), third (optional)String(format pattern).Outputs: The appropriateTemporaltype based on the inputString.Examples:Input:PARSE_TEMPORAL("2023-12-25T15:30:00", "DateTime")Output:DateTime(2023, 12, 25, 15, 30, 0)Input:PARSE_TEMPORAL("2023-12-25", "Date")Output:Date(2023, 12, 25)Input:PARSE_TEMPORAL("15:30:00", "Time")Output:Time(15, 30, 0)Input:PARSE_TEMPORAL("1 02:15:30", "Duration")Output:Duration(1, 2, 15, 30)FORMAT_TEMPORAL(temporal, format)- Formats aTemporal(Date,Time,DateTime,Duration) into aStringusing the specified format:Inputs:Amount: Exactly two inputs.Types: FirstTemporal(Date,Time,DateTime,Duration), secondString(format pattern).Outputs:String.EXTRACT_DATE(datetime)- Extracts theDatecomponent from aDateTime:Inputs:Amount: Exactly one input.Types:DateTime.Outputs:Date.EXTRACT_TIME(datetime)- Extracts theTimecomponent from aDateTime:Inputs:Amount: Exactly one input.Types:DateTime.Outputs: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).Performance MonitoringEnable performance monitoring to track CPU usage time.fromoqsimportoqs_engineresult: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"))Custom FunctionsExtendOQScapabilities by adding custom functions.fromoqsimport(oqs_engine,OQSInterpreter,FunctionNode)fromoqs.errorsimportOQSTypeErrorfromoqs.utils.shortcutsimportget_oqs_typedefcustom_multiply(interpreter:OQSInterpreter,node:FunctionNode)->int|float:ifnot(2<len(node.args)<2):raiseOQSInvalidArgumentQuantityError(function_name=node.name,expected_min=2,expected_max=2,actual=len(node.args))arg_1,arg_2=[interpreter.evaluatate(arg)forarginnode.args]ifisinstance(arg_1,(int,float))andisinstance(arg_2,(int,float)):returnarg_1*arg_2else:raiseOQSTypeError(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)Data TypesOQSsupports 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 as1or1_000.Decimal: Any Integer containing a Decimal Point.such as1.0or.0or1.or1_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"orThis 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 valuestrueandfalse.KVSorKey-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 valuenull.Error TypesOQShas advanced error reporting with the following errors:Invalid Argument Quantity Error: Raised when a function receives fewer or more arguments than expected.Syntax Error: Raised for general syntax mistakes in expressions.Type Error: Raised when an operation is performed on incompatible types.Undefined Variable Error: Raised when an expression refers to a variable that has not been defined.Undefined Function Error: Raised when an expression calls a function that does not exist.Function Evaluation Error: Raised when an error occurs within the execution of a function.Division By Zero Error: Raised when an attempt is made to divide by zero.Unexpected Character Error: Raised when an unexpected character is encountered in the expression.Missing Expected Character Error: Raised when an expected character is missing in the expression.Case Sensitivity in OQSFunction Names: InOQS, function names are not case-sensitive. For example,add,Add, andADDare treated as the same function.Variable Names: Contrary to function names, variable names inOQSare case-sensitive. This meansvar,Var, andVARare considered different variables.Using VariablesVariables inOQScan be used to store data that can be referred to in your expressions. They are defined as key-value pairs and passed to theoqs_engine. Here's an example:fromoqsimportoqs_engine# Using variables in expressionsvariables:dict[str,any]={"x":10,"y":20}result:dict[str,dict[str,any]]=oqs_engine(expression="x * y",variables=variables)print(result)Nested Expressions and EvaluationOQSfully supports nested expressions and evaluations anywhere within an expression, including in function calls. Here's how it works:fromoqsimportoqs_engine# Nested expressionsresult:dict[str,dict[str,any]]=oqs_engine(expression="ADD(1, MULTIPLY(x, y))",variables={"x":2,"y":3})print(result)Unpacking in OQSUnpacking inOQSis 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:fromoqsimportoqs_engine# Unpacking a list into function argumentsvariables:dict[str,any]={"numbers":[1,2,3]}result:dict[str,dict[str,any]]=oqs_engine(expression="SUM(***numbers)",variables=variables)print(result)Translation to Python TypesWhenOQSevaluates an expression, the final types are translated to Python types for seamless integration. Here's howOQStypes map to Python types:Number(bothIntegerandDecimal): Translated to Python'sintorfloat.String: Becomes Python'sstr.List: Translated to Python'slist.Boolean: Becomes Python'sbool.KVS(Key-Value Store): Translated to Python'sdict.Null: Becomes Python'sNone.For example, anOQSlist[1, "hello", true]would be translated to the Python list[1, "hello", True].ContributingContributions to theOQSPython implementation are welcome. Please follow the guidelines in themainOQSrepositoryfor contributing.LicenseThis project is licensed under theCreative Commons Attribution 4.0 International License. More details about that can be found in theMain Repository License.AuthorsTheOQSPython Implementation was built and is being maintained through the support ofInfuzuCore Contributors of theOQSPython Implementation are as follows:Yidi Sprei