Great question. This bit isn’t specific to Touch, but is rather a Python behavior / mechanic. The import
keyword is used to invoke the python import system. Broadly speaking, in many languages code is written in smaller modules or libraries that are then included in larger projects. This organizes code into smaller chunks that are both reusable and more easily maintained (developing smaller libraries that allow parallel development or management is sometimes referred to as “orthogonal” ).
What is import
import
tells Python that there’s a set of functions outside of the default python tool set that are going to be accessed in any given module or library. As a best practice, import statements typically happen at the top of a module, and are outside of any functions so that their scope (your ability to use them) allows them to be used by any function in that module.
For example - this syntax is also completely functional:
def UpdateDateTimeBuffer( datetime_buffer ):
import datetime
datetime_buffer.clear()
datetime_object = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
datetime_buffer.write(datetime_object)
return
def FetchDateAndTime():
import datetime
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
The above is just redundant - if we only import dateteime
in the local scope of function, no other function can use it, and we subsequently need to re-import in every function. This is why you typically see this instead:
import datetime
def UpdateDateTimeBuffer( datetime_buffer ):
datetime_buffer.clear()
datetime_object = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
datetime_buffer.write(datetime_object)
return
def FetchDateAndTime():
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
The datetime
library is available to all functions, which is exactly what we want. As a final note, you can technically do this:
def UpdateDateTimeBuffer( datetime_buffer ):
datetime_buffer.clear()
datetime_object = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
datetime_buffer.write(datetime_object)
return
import datetime
def FetchDateAndTime():
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
This is just considered bad form since it would be difficult for another developer to find that hidden import statement.
Where does it come from?
To python, the import
statement typically refers to a .py
file or folder on your computer. datetime
, for example can be found in your TouchDesigner installer folder, on my machine thats: C:\Program Files\Derivative\TouchDesigner.2021.15240\bin\Lib
Anytime you use the import
statement in python you’re actually issuing an instruction to search the Lib
directory for a file or folder that matches the name after import
- so in the case of import datetime
python is looking for a file called datetime.py
in the directory Lib
and added the functions in that library to what’s available for Python to use.
TouchDesigner Magic
TouchDesigner extends that same behavior to also include DATs - to really answer your question we’re going to use this trick so we can see a few other pieces at work.
When does it run?!
To see how / when a module runs we’re going to first setup a small test bed:
Here a DAT which contains some helper functions is called myMOD
- let’s think of this DAT as if it were datetime
. Next we have another DAT that would be where you might author your functions, in this case called exampleMOD
.
We’ll use the import
statement in exampleMOD
to import all of the code from myMOD
, which is the same thing that’s happening (nearly) to when we import datetime
.
In this example, text2
uses our exampleMOD
module. If we run that DAT we’ll see in the text port a cascade of debug statements that will let us trace back to when each piece element loaded and run:
python >>>
2021-11-12 16:50:07 myMod Init (Debug - DAT:/project1/myMOD fn:<module> line:11)
2021-11-12 16:50:07 hello world (Debug - DAT:/project1/myMOD fn:superFunc line:5)
2021-11-12 16:50:07 hello world
(Debug - DAT:/project1/exampleMOD fn:TestFunc line:7)
When we run that module for the first time, that’s when we first load all of the functions from myMOD
, next we see that the function superFunc
ran, and finally we see that our function in exampleMOD
ran. If we run that text2
dat again:
python >>>
2021-11-12 16:50:07 myMod Init (Debug - DAT:/project1/myMOD fn:<module> line:11)
2021-11-12 16:50:07 hello world (Debug - DAT:/project1/myMOD fn:superFunc line:5)
2021-11-12 16:50:07 hello world
(Debug - DAT:/project1/exampleMOD fn:TestFunc line:7)
python >>>
2021-11-12 16:52:04 hello world (Debug - DAT:/project1/myMOD fn:superFunc line:5)
2021-11-12 16:52:04 hello world
(Debug - DAT:/project1/exampleMOD fn:TestFunc line:7)
python >>>
We only see the function calls, and not the import - now that our module has been imported it will be persistent through this session with TouchDesigner. At this point all of the functions in our myMOD
module are now available without reimporting them, even if we add another text DAT, and run the function there:
TL;DR
The quick answer is that import
is a keyword in a module, and Python will automatically run a helper function to load externals when it sees this keyword the first time a function in that module is executed.
import-example.toe (3.7 KB)