Modules

In DolphinDB, you can save user-defined functions as reusable modules and organize the modules into hierarchical namespaces. Modules can be loaded during the system startup, or imported into a session when needed.

1. What are Modules

A module in DolphinDB is a script file that only contains function definitions. Modules have the following features:

  • Module files are located in the [home]/modules directory by default.
  • Module files have the extension .dos (short for "DolphinScript") or .dom (short for "DolphinModule").
  • The first line of a module file must be the module declaration, module [module_name].
  • Apart from the first line, a module file can only contain module import statements and function definitions.

2. Defining Modules

This section describes the steps to define modules.

2.1. Defining Directory for Module Files

Module files are located at [home]/modules by default.

  • The [home] directory is specified by the configuration parameter home, which you can check with getHomeDir.
  • The directory for the module files is specified by the configuration parameter moduleDir. The default value is the relative path "modules". The system searches "modules" in the following order: home directory of the node, the working directory of the node, and the directory with the DolphinDB executable. Please note that in the single node mode, these 3 directories are the same by default.

2.2. Creating a Module File

Create a file [moduleName].dos in the "modules" folder (e.g., fileLog.dos). The first line must be the module declaration:

module fileLog

In this example, "fileLog" is the module name, which must be the same as the name of the module file.

The rest of the file should only contain function definitions and, if necessary, module import statements. Any code outside of function definitions, module declarations, or import statements will be ignored. In this example, the "fileLog" module contains only one function, appendLog, which writes log entries to a user-specified file.

module fileLog

def appendLog(filePath, logText){
	f = file(filePath,"a+")
	f.writeLine(string(now()) + " : " + logText)
	f.close()
}

To organize modules, you can create subdirectories as namespaces under the "modules" directory. For details, see section 4.1. Declaring Namespace for Modules.

2.3. Serializing a Module File

Use saveModule to serialize a .dos module file to a binary file with the extension .dom. This enhances the security of the code. In this example, we serialize the module file from the previous section:

saveModule("fileLog")

The generated .dom file is saved to the same directory as the original .dos file.

Note:
  • If the .dos file is changed, execute saveModule again to generate a new .dom file. Set the "overwrite" parameter to "true" to replace the old .domfile:
    saveModule(name="fileLog", overwrite=true)
  • When a module references another module, the serialization process only includes the name of the referenced module, not its definition. As a result, when loading or moving a .dom file, ensure that you also load or move any referenced modules.

3. Importing Modules

This section describes two ways to import modules, importing with the use keyword, or loading modules as built-in functions.

3.1. Importing Modules with use

You can import a module with the use keyword. If the imported module uses other modules, these modules are also loaded automatically.

Note:
  • The imported modules are only available in the current session.
  • For version 2.00.11 and earlier, only module files with the .dos extension can be imported with use.Starting from version 2.00.12, use can be used to load .dos and .dom files. If files with the same name but different extensions (.dos and .dom) exist in the same location, the system will import the .dos file.

After importing the module, you can call the functions defined in the module in the following ways:

  • Refer to the module function:
    use fileLog 
    appendLog("mylog.txt", "test my log")
  • Specify the namespace of the function (i.e., the full path of the function under the modules directory)

    Use this option if there are functions with the same name in other imported modules.

    use fileLog fileLog::appendLog("mylog.txt", "test my log")

3.2. Loading Module Functions as Built-in Functions

As mentioned in the preceding section, the use keyword only imports a module to the current session. To make the functions defined in a module available to all sessions, we need to add them as system built-in functions with the function loadModule or the configuration parameter preloadModules. This also makes the code more concise without the use statement, and more convenient for module function references through API calls.

Note the following about module functions that are added as built-in functions:

  • The functions cannot be overridden.
  • When the function is referenced by remoteRun or rpc, its definition will not be serialized and sent to the remote node. Therefore, make sure the relevant module has been loaded on the remote node before the remote call or an exception will be thrown.
  • The functions have only one copy in the memory and it is visible to all sessions. This saves not only the memory usage but also the time it takes for each session to import the module.

Both .dos files and .dom files can be used to load modules. If the "modules" directory contains a .dos file and a .dom file with the same name, the system will only load the latter. For a module function imported from a .dom file, its definition is not visible to the users.

3.2.1. Using the Function loadModule

loadModule can only be used in the initialization script (dolphindb.dos as the default file) of the system, and not in the CLI or GUI. For example, to import the module file created in 2.2. Creating a Module File section, append this line to the initialization script:

loadModule("fileLog")

When calling a function from the loaded module, specify the namespace of the function (i.e., the full path of the function under the modules directory):

fileLog::appendLog("mylog.txt", "test my log")

3.2.2. Using the Configuration Parameter preloadModules

For standalone deployment, specify the modules to be loaded by configuring the parameter preloadModules in the configuration file dolphindb.cfg; for cluster deployment, configure preloadModules in both controller.cfg and cluster.cfg as the module must be imported to both the controller and the relevant data nodes.

For example:

preloadModules=fileLog

Use commas to separate multiple modules.

3.2.3. Functions Loaded from Modules vs. Function Views

Note the differences between the built-in functions loaded with loadModule or preloadModules and the DolphinDB function views:

  • The definition of a function view can be viewed by its creator, administrators and users with the required privileges. A module function loaded from a .dom file is more secure as no user can view its definition.
  • Access control can be applied to function views but not loaded module functions.
  • When the system serializes a module which references a function in another module, the referenced function is only serialized by its name and its definition is not serialized. A serialized function view, on the other hand, is self-contained with all the referenced objects serialized as well.

Function views and modules are designed for different scenarios. Function views are used in database queries whereas modules usually contain common algorithms or processing logic which can be referenced in different scripts. A function view may reference a function in a module but a module seldom references a function view.

4. Organizing Modules

This section describes how to organize modules using namespaces.

4.1. Declaring Namespace for Modules

To organize modules more efficiently, create subdirectories under the modules directory to create a hierarchy of namespaces for the modules. For example:

  • For a module at modules/system/file/fileUtil.dos, use the declaration: module system::file::fileUtil
  • For a module at modules/system/temporal/dateUtil.dos, use the declaration: module system::temporal::dateUtil

4.2. Calling Modules with Namespaces

For a module located at the sub directories under modules, specify the full namespace of the module when:

  • Calling the functions saveModule and loadModule
  • Configuring the preloadModules parameter
  • Importing it with the use statement

This example imports the "fileLog" module from the previous section:

use system::log::fileLog

After the module is imported, you can directly call the function in the module:

appendLog("mylog.txt", "test my log")

or call the module function with its full namespace:

system::log::fileLog::appendLog("mylog.txt", "test my log")
Note: If there are functions with the same name in other imported modules, call the module function with its full namespace (see 6.1. Calling Module Functions with the Same Name).

5. Debugging Modules Remotely in GUI and VS Code

When DolphinDB clients GUI or VS Code (through the DolphinDB extension) is connected to a remote DolphinDB server, the module edited locally must be uploaded to the [home]/modules directory on the remote server before it is called by the use statement.

5.1. DolphinDB GUI

To synchronize modules to a remote server in the DolphinDB GUI by following the steps below:

  1. Specify a remote server:
    • Click Server → Add Server, and specify the Remote Directory.
      Figure 1. Add a Server
    • Alternatively, click Server → Edit Server to add the remote directory.
      Figure 2. Edit a Server
  2. Select the local "modules" directory to launch its menu. Click Synchronize module to server to synchronize all files and sub directories under the local "modules" directory to the previously specified remote directory.
    Figure 3. Synchronize module to server

    For example, the Remote Directory is specified as '[home]/modules', and the file to synchronize is C:/users/usr1/Project/scripts/test.dos. During the synchronization, the system will generate the directory and the file '[home]/modules/Project/scripts/test.dos' on the remote server.

Once the synchronization is complete, you can import modules with the use statement on the remote server. For more information about setting modules directory, see 2.1. Defining Directory for Module Files.

5.2. VS Code

The DolphinDB extension for Visual Studio Code provides a convenient way to upload modules to the server. Here's how to use this feature:

  1. Edit your module file in VS Code.
  2. Once you're ready to upload, right-click on the module file.
  3. From the context menu, select DolphinDB: Upload Module.

  4. A prompt will appear asking if you want to encrypt the module as a .dom file. Choose your preferred option.

6. Usage Rules

6.1. Calling Module Functions with the Same Name

Since different modules may contain functions with the same name, it is recommended to call a module function with its namespace.

For function calls that do not specify the function namespace, DolphinDB disambiguates functions with the same names according to the following rules:

  • If one and only one imported module contains the function, it refers to the function defined in that module.
  • If two or more imported modules contain the function, throw an exception.
    Modules [Module1] and [Module2] contain function [functionName]. Please use module name to qualify the function.
  • If no imported module contains the function, search for system built-in functions; If there is no match, throw an exception.
  • If a function in an imported module has the same name as a user-defined function, use the function in the module. If you want to call the user-defined function, specify its namespace.
    Note: The default namespace for all user-defined functions and built-in functions is the root directory, indicated by two colons ::.

In this example,

  1. First, create a user-defined function myfunc:
    login("admin","123456")
    def myfunc(){
     return 1
    }
    addFunctionView(myfunc)
  2. Next, create a module "sys" that contains a function also named myfunc:
    module sys
    def myfunc(){
     return 3
    }

Now, if you want to call the function in the module "sys" in your current session,

  1. First, import the module with use:use sys
  2. Then call the function with its namespace:sys::myfunc()

    or simply:

    myfunc()

    To call the user-defined function, use:

    ::myfunc()

6.2. Reloading a Module

Each module is only imported once per session. If you want to repeatedly change a module to test interactively, there are two options:

  • Execute the entire module after updating the module. This method is only effective for the current session.
  • Call clearCachedModules after updating the module. Then when you import the module with the use statement again, the system reloads it from the module file instead of using the cached data, and there is no need to restart the node.
    Note: This option is available to administrators only.

6.3. Cross-module Calls

A module can be imported into another module. For example, you can import module A into module B, then import module B into module C.

However, circular imports are not supported. For example, if module A references functions defined in module B, then module B cannot reference the functions in module A.