Modules
SmartPy code is structured as one or more modules.
You can create modules in two different ways:
- In a SmartPy
.spyfile - Inlined in a Python
.pyfile 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.
To use a module in another module it must be imported first.
Module imports
SmartPy modules can import other SmartPy modules to use the functions or classes defined within.
import smartpy as sp
# an inlined SmartPy module
@sp.module
def example():
def foo():
pass
# another inlined SmartPy module that uses the previous module
@sp.module
def my_module():
# Since v0.20.0: to use the `example` module you must import it
import example
def bar():
example.foo()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.
@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 * yTo use this module in another inline module in the same Python file you can import it directly:
@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 + yTo use this module in another inline module in a different Python file you can use Python importing first and then import it directly:
# 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 + yFor 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.
You can also write pure SmartPy code in a file with the extension .spy and import that file.
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.
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 * yOther SmartPy files can import this module with a standard import statement:
import calculator_main as cmTest scenarios can import and use this module with the add_module statement:
@sp.add_test()
def test():
sc = sp.test_scenario("A Test")
m = sc.add_module("calculator_main.spy")
c = m.Calculator()
sc += cFilepath 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:
- A local inline module called
calculator_main - The module in a SmartPy file
calculator_main.spyrelative to the current working directory - The module in a SmartPy file
calculator_main.spyrelative to the directories in thePYTHONPATHenvironment variable - The module in a SmartPy file
calculator_main.spyrelative 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
import utils.calculator_main as cmWARNING
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.