Skip to content
On this page

Modules

SmartPy code is structured as one or more modules. You can create modules in two different ways:

  • In a SmartPy .spy file
  • Inlined in a Python .py file with the annotation @sp.module

INFO

All code within a module is processed as SmartPy syntax, not as Python. Code outside a module, including code in Test scenarios, is processed as standard Python.

The top-level functions in inlined SmartPy modules are not executed directly as normal Python functions. These functions are merely a way of structuring the code. To run the code inside them, you must add them to a test scenario.

SmartPy files

SmartPy files are files that have the .spy file extension. These files contain only SmartPy code and can import other .spy files. The code in the SmartPy file represents only one SmartPy module but multiple classes / type definitions / functions can be defined in that module.

For example, this SmartPy file creates a simple smart contract named Calculator in a file named calculator_main.spy. This contract uses three imported libraries: the standard SmartPy library and its math module and utils module.

smartpy
import smartpy as sp
import smartpy.math as m
import smartpy.utils as utils

class Calculator(sp.Contract):
    def __init__(self):
        self.data.result = m.pow((10, 0)) - 1
        self.data.i = utils.mutez_to_nat(sp.mutez(10))

    @sp.entrypoint
    def multiply(self, x, y):
        self.data.result = x * y

Other SmartPy files can import this module with a standard import statement:

smartpy
import calculator_main as cm

Test scenarios can import and use this module with the add_module statement:

python
@sp.add_test()
def test():
    sc = sp.test_scenario("A Test")
    m = sc.add_module("calculator_main.spy")
    c = m.Calculator()
    sc += c

Filepath resolution

SmartPy searches for files based on the file path in the import statement. For example, the statement import calculator_main as cm checks for modules in this order:

  1. A local inline module called calculator_main
  2. The module in a SmartPy file calculator_main.spy relative to the current working directory
  3. The module in a SmartPy file calculator_main.spy relative to the directories in the PYTHONPATH environment variable
  4. The module in a SmartPy file calculator_main.spy relative to the user site-packages and standard site-packages directories

To import a SmartPy file located in a sub-directory use the standard Python dotted syntax for the import.

For example if calculator_main.spy was in the utils directory then use

smartpy
import utils.calculator_main as cm

WARNING

SmartPy import statements that are more than one level deep must include the as statement to give the imported module a single, local name. They must follow the import a.b.c as d format, not from a.b.c import d.

The filepath resolution process then proceeds with the file utils/calculator_main.spy.

The same filepath resolution approach is used for resolving the filepath when adding modules to a test scenario add_module statement.

TIP

Try adjusting the PYTHONPATH environment variable if importing is not resolving your SmartPy files.

Inlined modules

You can put SmartPy code in Python .py files by using the @sp.module annotation.

For example, this code defines a SmartPy module named calc with a single smart contract named Calculator. You can name modules whatever you want and a Python file can define multiple modules. As with SmartPy files multiple classes / type definitions / functions can be defined in each module.

smartpy
@sp.module
def calc():
    class Calculator(sp.Contract):
        def __init__(self):
            self.data.result = 0

        @sp.entrypoint
        def multiply(self, x, y):
            self.data.result = x * y

To use this module in another inline module in the same Python file you can import it directly:

smartpy
@sp.module
def main():
    import calc
    class NewCalculator(calc.Calculator):
        def __init__(self):
            calc.Calculator.__init__(self)

        @sp.entrypoint
        def add(self, x, y):
            self.data.result = x + y

To use this module in another inline module in a different Python file you can use Python importing first and then import it directly:

smartpy
# NOTE this is a normal Python import
# we assume that `calc` is defined in a file `my_lib.py`
from my_lib import calc

@sp.module
def main():

    # NOTE this is a SmartPy import
    import calc

    class NewCalculator(calc.Calculator):
        def __init__(self):
            calc.Calculator.__init__(self)

        @sp.entrypoint
        def add(self, x, y):
            self.data.result = x + y

For inlined SmartPy code blocks and .spy files, importing modules replaces the need to add modules to the modules list in the test scenario.

The importing mechanism also figures out which other dependent modules to import, so you only have to import the modules that you need in SmartPy code.

TIP

To use a module in a test scenario you will still have to add it to your scenario.