Scripting Basics
Scripting in HEC-DSSVue is accomplished using Jython, an implementation of the Python programming language designed specifically for integration with the Java programming language. More information about Jython can be found at the official Jython website – www.jython.org.
Python (of which Jython is an implementation) is an interpreted language with simple syntax and high-level data types. This section is not a comprehensive Python tutorial, but rather a simple primer to allow the creation of simple scripts. This primer does not cover defining classes in Python.
The official Python website - www.python.org - has links to online Python tutorials as well as programming books.
Outputting Text
Text information can be displayed in the console window using the print statement which has the syntax:
print [item[, item…]]
The items are comma-separated and do not need to be of the same type. The print statement will insert a space between every specified item in the output.
Example 1: Outputting Text
i=123
x=12.3
print "Testing myFunction, i =", i, ", x =", x
Data Types and Variables
Python has Boolean, integer, long integer, floating-point, imaginary number, and sequence and dictionary data types. Sequences are divided into mutable (or changeable) sequences called lists, immutable sequences called tuples. Tuples may be enclosed in parentheses or may be "naked". Strings are special tuples of characters that have their own syntax. Dictionaries are like sequences but are indexed by non-numeric values. In addition, Python also has a special type called None, which is used to indicate the absence of any value. The only valid values for the Boolean type are True and False. In certain circumstances these values also evaluate to the integers 1 and 0, respectively.
Python variable names consist of an upper- or lower-case letter or the "_" (underscore) character followed by an unlimited number of upper- or lower-case characters, digits or underscore characters.
Like many languages, Python uses the single equals sign, "=", to assign values to variables. However, Python allows assignment to multiple variables from a single sequence, as long as the number of variables on the left of the equal sign is the same as the length of the sequence on the right side. If the sequence on the right side is a dictionary, only the dictionary keys are assigned to the variables on the left.
There are situations regarding the HEC-DSSVue API where it is necessary or desirable to set a time-series value to "missing" or to test whether a time-series value is missing. The hec.script module supplies a constant to use in these situations.
The currently-defined constants in the hec.script module are:
Constant | Type | Represents |
Constants.TRUE | integer | True |
Constants.FALSE | integer | False |
Constants.UNDEFINED | floating-point | missing data value |
Example 2: Variable Types
from hec.script import Constants
# set some integer values
i = 0
j = 1
k = -10998
m = True
# set a long integer
n = 79228162514264337593543950336L
# set some floating-point values
x = 9.375
y = 6.023e23
z = -7.2e-3
t = Constants.UNDEFINED
# set some strings
string_1 = "abc"
string_2 = 'xyz'
string_3 = "he said \"I won't!\""
print (string_3)
string_4 = 'he said "I will not!"'
string_5 = """this is a
multi-line string"""
# set a tuple - tuples are contained within ()
tuple_1 = (1, 2, "abc", x, None)
# set a list - lists are contained within []
list_1 = [1, 2, "abc", x, tuple_1]
# set a dictionary, using key : value syntax
# dictionaries are contained within {}
dict_1 = {"color" : "red", "size" : 10, "list" : [1, 5, 8]}
# multiple assignment
a, b, c = string_1
a, b, c = "abc"
a, b, c, d, e = tuple_1
a, b, c, d, e = (1, 2, "abc", x, None)
a, b, c, d, e = 1, 2, "abc", x, None # "Naked" tuple assignment
a, b, c, d, e = list_1
a, b, c, d, e = [1, 2, "abc", x, tuple_1]
a, b, c = dict_1
a, b, c = {"color" : "red", "size" : 10, "list" : [1, 5, 8]}
Indexing into sequence types is done using [i] where i starts at 0 for the first element. Subsets of sequence types (called slices) are accessed using [i:j:k] where i is the first element in the subset, j is the element after the last element in the subset, and k is the step size. If negative numbers are used to specify and index or slice, the index is applied to the end of the sequence, where [-1] specifies the last element, [-2] the next-to last and so on. If k is omitted in slice syntax it defaults to 1. If i is omitted in slice syntax it defaults to 0. If j is omitted in slice syntax it defaults to the length of the sequence, so list_1 [0:len(list_1)] is the same as list_1[:]. Indexing into dictionaries is done using [x] where x is the key.
The number of elements in a sequence type or dictionary is returned by the len() function.
Example 3: Sequence Indexing and Slicing
x = 9.375
string_4 = 'he said "I will not!"'
tuple_1 = (1, 2, "abc", x, None)
list_1 = [1, 2, "abc", x, tuple_1]
dict_1 = {"color" : "red", "size" : 10, "list" : [1, 5, 8]}
print(string_4[3]) # 4th element
print(string_4[3:5]) # 4th & 5th elements
print(list_1[0::2] ) # even elements
print(list_1[1::2] ) # odd elements
print(list_1[-1]) # last element
print(list_1[2:-1])# 3rd through next-to-last element
print(list_1[2:len(list_1)]) # 3rd through last element (also list_1[2:])
print(dict_1["size"]) # value associated with "size" key
i = len(list_1) # length of list_1
Operators
Each of the following operators can be used in the form a = b operator c. Each can also be used as an assignment operator in the form a operator= b (e.g., a += 1, x **= 2).
+ arithmetic addition
- negation or arithmetic subtraction
* arithmetic multiplication
/ arithmetic division
// integer arithmetic division
** arithmetic power
% arithmetic modulo
& bit-wise and
| bit-wise or
~ bit-wise not
^ bit-wise xor (exclusive or)
<< bit-wise left shift
>> bit-wise right shift
Each of the following operators returns True or False and can be used in conditional expressions as discussed in Section 8.3.6.
> greater than
< less than
>= greater than or equal to
<= less than or equal to
!= not equal to
== equal to
Comments
Python uses the "#" (hash) character to indicate comments. Everything from the "#" character to the end of the line is ignored by the interpreter. Comments may not be placed after a program line continuation ("\") character on the same input line.
Program Lines
Unless otherwise indicated, every input line corresponds to one logical program statement. Two or more statements can be combined on line input line by inserting the ";" (semicolon) character between adjacent statements. A single statement may be continued across multiple input lines by ending each line with the "\" (back slash) character. Comments may not be placed after a program line continuation ("\") character on the same input line
Example 4: Input vs. Program Lines
# multiple statements per line
r = 1; pi = 3.1415927; a = pi * r ** 2
# multiple lines per statement
a = \
pi * \
r ** 2
Input lines are grouped according to their function. Input lines forming the body of a conditional, loop, exception handler, or function or class definition must be grouped together. Input lines not in any of the construct comprise their own group. In Python, grouping of input lines is indicated by indentation. All lines of a group must be indented the same number of spaces. A horizontal tab character counts as eight spaces on most systems. In some Python documentation, a group of input lines is called a suite.
Example 5: Input Line Grouping
# this is the main script group
x1=10
x2=5
dist = x2 - x1
if dist > 100.:
# this is the "if" conditional group
y = dist / 2.
z = y ** 2.
else :
# this is the "else" conditional group
y = dist
z = y ** 2. / 1.5
# back to main script group
q = y * z
print(q)
Conditional Expressions
Conditional expressions have the form:
if [not] condition :
if-group
[elif [not] condition :
elif-group]
[else :
else-group]
The ":" (colon) character must be placed after each condition. The condition in each test is an expression built from one or more simple conditions using the form:
simple-condition ( and | or ) [not] simple-condition
Parentheses can be used to group conditions. The simple-condition in each expression is either an expression using one of the conditional operators mentioned before or is of the form:
item [not] in sequence
If the statement group to be processed upon a condition is a single statement, that statement may follow the condition on the same line (after the colon character).
Example 6: Conditional Expressions
if (x < y or y >= z) and string_1.index(“debug”) != -1 :
# do something
…
elif z not in value_list or (x < z * 2.5) :
# do something different
…
else :
# do something else
Example 7: Simple Conditional Expressions
if x1 < x2 : xMax = x2
else : xMax = x1
Looping
Python supports conditional looping and iterative looping. For each type, the body of the loop (the loop-group) can contain break and/or continue statements.
The break statement immediately halts execution of the loop-group and transfers control to the statement immediately following the loop.
The continue statement skips the remainder of the current iteration of the loop-group and continues with the next iteration of the loop-group
Conditional Looping
Python supports conditional looping with the while statement, which has the form:
while condition :
loop-group
Conditional looping executes the body of the loop (the loop-group) as long as the condition evaluates to true.
Example 8: Conditional Looping
# print the first 10 characters
string_1 = "this is a test string"
i = 0
while i < 10 :
print string_1[i]
i += 1
Iterative Looping
Python supports iterative looping with the for statement, which has the form:
for item in sequence :
loop-group
[else :
else-group]
Iterative looping executes the body of the loop (the loop-group) once for each element in sequence, first setting item to be that element. If the iteration proceeds to completion without being interrupted by a break statement the else-group will be executed, if specified.
Example 9: Iterative Looping
# print the first 10 characters
string_1 = "this is a test string"
for i in range(10) :
print string_1[i]
# print all the characters
string_1 = "this is a test string"
for i in range(len(string_1)) :
print string_1[i]
# print all the characters (more Python-y)
string_1 = "this is a test string"
for c in string_1 :
print c
The range([start,] stop[, increment]) helper function generates a sequence of numbers from start (default = 0) to stop, incrementing by increment (default = 1). Thus range(4) generates the sequence (0, 1, 2, 3).
Defining and Using Functions
In Python, functions are defined with the syntax:
def functionName([arguments]) :
function-body
Function names follow the same naming convention as variable names specified in Section 8.3.2. The arguments are specified as a comma-delimited list of variable names that will be used within the function-body. These variables will be positionally assigned the values used in the function call. More complex methods of specifying function arguments are specified in Python tutorials and references listed at the official Python website (www.python.org).
A function must be defined in a Python program before it can be called. Therefore, function definitions must occur earlier in the program than calls to those functions.
A function may optionally return a value of any type or a naked tuple of values.
Example 10: Defining and Using Functions
def printString(stringToPrint) :
"Prints a tag plus the supplied string"
tag = "function printString : "
print tag + stringToPrint
def addString(string_1, string_2) :
"Concatenates 2 strings and returns the result"
concatenatedString = string_1 + string_2
return concatenatedString
testString = "this is a test"
printString(testString)
wholeString = addString("part1:", "part2")
printString(wholeString)
printString(addString("this is ", "another test"))
Modules, Functions and Methods
A function is a procedure which takes zero or more parameters, performs some action, optionally modifies one or more of the parameters and optionally returns a value or sequence of values.
A class is the definition of a type of object. All objects of that type (class) have the same definition and thus have the same attributes and behavior. Classes may define functions that apply to individual objects of that type. These functions are called methods.
An object is an instance of a class, which behaves in the way defined by the class, and has any methods defined by the class.
Python provides many functions and classes by default. In our examples, we have used functions len() and range() which Python provides by default. We have also used the classes list and string, which Python also provides by default. We didn't use any methods of the class list, but we used the string method index() in Example 7 (string_1.index("debug") != -1). It is important to note that the object method index() doesn't apply to the string class in general, but to the specific string object string_1.
There are other functions and classes which Python does not provide by default. These functions and classes are grouped into modules according to their common purpose. Examples of modules are "os" for operating system functions and "socket" for socket-based network functions. Before any of the functions or classes in a module can be accessed, the module must be imported with the import statement, which has the syntax:
from module import *
Other methods of using the import statement are specified in Python tutorials and references listed at the official Python website (www.python.org). In the Jython implementation, Java packages can be imported as if they were Python modules, and the Java package java.lang is imported by default.
Example 11: Using a Function from an Imported python Module
# use the getcwd() function in the os module to get
# the current working directory
from os import *
cwd = getcwd()
A module does not have to be imported in order to work with objects of a class defined in that module if that object was returned by a function or method already accessible. For example, the Python module "string" does not have to be imported to call methods of string objects, but does have to be imported to access string functions.
Handling Exceptions
Certain errors within a Python program can cause Python to raise an exception. An exception that is not handled by the program will cause the program to display error information in the console window and halt the program.
Python provides structured exception handling with the following constructs:
try :
try-group
except :
except-group
[else :
else-group]
try :
try-group
finally :
finally-group
In the try-except-else construct, if an exception is raised during execution of the try-group, the control immediately transfers to the first line of the except-group. If no exception is raised during execution of the try-group, the control transfers to the first line of the else-group, if present. If there is no exception raised and no else-group is specified, the control transfers to the first line after the except-group.
The two constructs cannot be combined into a try-except-finally construct, but the same effect can be obtained by making a try-except-else construct the try-group of a try-finally construct.
Example 12: Exception Handling
string_1 = 'str1 '
substring = 'tr'
try :
try :
string_1.find(substring) # may raise an exception
except :
print substring + " is not in " + string_1
# do some stuff that might raise another exception
#
else :
print substring + " is in " + string_1
# do some stuff that might raise another exception
#
finally :
print "No matter what, we get here!"
More exception handling information, including filtering on specific types of exceptions, exception handler chains, and raising exceptions, is provided in Python tutorials and references listed at the official Python website (www.python.org). In the Jython implementation, instances or subclasses of java.lang.Throwable can also be handled as exceptions. Detailed information can be found at the Java API website (http://java.sun.com/javase/6/docs/api/).