Functional API
The Functional API provides fine-grained control over remote Python execution using decorators. Unlike the Client API which wraps entire classes, the Functional API lets you decorate individual functions and explicitly manage payloads and sessions.
When to Use Functional API
- Fine-grained control over execution
- Mixing decorated functions with other code
- Reusable functions that call each other (with
@d3function) - Simple one-off scripts (with
@d3pythonscript)
Supported Decorators
The Functional API offers two decorators:
@d3pythonscript@d3function
When to Use @d3pythonscript
- Simple, self-contained scripts
- Scripts that won’t be called frequently
- Quick prototyping
When to Use @d3function
- Reusable utility functions
- Functions that call other functions
- Performance-sensitive code (register once, call many times)
- Organized module structure
| Feature | @d3pythonscript | @d3function |
|---|---|---|
| Registration | Not needed | Required before execution |
| Payload content | Inlined function body | Function call |
| Function chaining | Not supported | Functions can call each other |
| Performance | Sends full code each time | Code registered once, calls are small |
| Best for | One-off scripts | Reusable utility functions |
Examples
Simple Execution with @d3pythonscript
Use @d3pythonscript for one-off scripts that don’t need registration:
from designer_plugin.d3sdk import d3pythonscript, D3Session
from designer_plugin.pystub import *
@d3pythonscript
def rename_surface(surface_name: str, new_name: str):
"""Rename a surface in Designer."""
surface: Screen2 = resourceManager.load(
Path(f'objects/screen2/{surface_name}.apx'),
Screen2)
surface.rename(surface.path.replaceFilename(new_name))
# Execute with a session
with D3Session('localhost') as session:
session.rpc(rename_surface.payload("surface 1", "new_name"))
Retrieving Data from Designer with @d3pythonscript
from designer_plugin.d3sdk import d3pythonscript, D3Session
from designer_plugin.pystub import *
@d3pythonscript
def get_surface_uid(surface_name: str) -> str:
"""Get the UID of a surface by name."""
surface: Screen2 = resourceManager.load(
Path(f'objects/screen2/{surface_name}.apx'),
Screen2)
return str(surface.uid)
# Execute methods remotely within a session
with D3Session('localhost') as session:
uid = session.rpc(get_surface_uid.payload("surface 1"))
print(f"Surface UID: {uid}")
You cannot retrieve the object from designer_plugin.pystub. See Limitation.
Adding a Layer with @d3pythonscript
from designer_plugin.d3sdk import d3pythonscript, D3Session
from designer_plugin.pystub import *
@d3pythonscript
def hello_world(text: str):
local_state: LocalState = state.localOrDirectorState()
layer = local_state.track.addNewLayer(
TextModule, 0, 60, f"Text - {text}"
)
layer.findSequence("text").sequence.setString(0, text)
arial_font: TextFont = resourceManager.load(
Path("objects/text/arial.apx"), TextFont
)
layer.findSequence('font').sequence.setResource(0, arial_font)
# Execute methods remotely within a session
with D3Session('localhost') as session:
session.rpc(hello_world.payload("Hello world"))
Function Chaining with @d3function
Use @d3function for reusable functions that can call each other:
from designer_plugin.d3sdk import d3function, D3Session
from designer_plugin.pystub import *
@d3function("mymodule")
def get_time() -> str:
import datetime
return str(datetime.datetime.now())
@d3function("mymodule")
def get_surface_with_time(surface_name: str) -> dict:
"""Get surface info with timestamp."""
surface: Screen2 = resourceManager.load(
Path(f'objects/screen2/{surface_name}.apx'),
Screen2)
return {
"uid": str(surface.uid),
"time": get_time() # Call another function in the same module
}
# Execute with a session (module auto-registered)
with D3Session('localhost') as session:
result = session.rpc(get_surface_with_time.payload("surface 1"))
print(result)
Async Execution with D3AsyncSession
import asyncio
from designer_plugin.d3sdk import D3AsyncSession
async def main():
async with D3AsyncSession('localhost') as session:
result = await session.rpc(get_surface_with_time.payload("surface 1"))
print(result)
asyncio.run(main())
Execution Flow
- Code Preparation: Your Python 3 code is converted to Python 2.7 (f-strings, type hints removed)
- Payload Generation: Arguments are serialised into a JSON payload
- Module Registration (for
@d3function): Function code is lazily registered on Designer on the firstrpc()call - Remote Execution: Payload is sent to Designer via HTTP
- Response Handling: Return value is parsed and returned to your code
Session Methods
Both D3Session and D3AsyncSession provide rpc() and execute() methods.
| Method | Returns | Use Case |
|---|---|---|
rpc(payload) | Return value only | Simple execution |
execute(payload) | Full PluginResponse | Access logs, status, debugging |
Details
Module Registration
Modules are registered lazily. The session auto-registers a module the first time you execute a function from it. No manual setup is required in most cases.
For eager registration (e.g. to catch registration errors upfront), use register_module or register_all_modules explicitly:
with D3Session('localhost') as session:
session.register_module("mymodule") # Register eagerly
Import Handling
For @d3pythonscript, imports must be inside the function body:
@d3pythonscript
def use_numpy():
import numpy as np # Import inside function
return np.zeros(10).tolist()
Code Transformation
Both decorators automatically convert Python 3 code to Python 2.7:
- F-strings →
.format()calls - Type hints → Removed
async/await→ Converted to sync
The same conversion rules apply to the Client API.
Helper Functions
get_register_payload()
Get the registration payload for a specific module.
Signature:
def get_register_payload(module_name: str) -> RegisterPayload | None
Returns: RegisterPayload for the module, or None if not found.
get_all_d3functions()
List all registered d3functions.
Signature:
def get_all_d3functions() -> list[tuple[str, str]]
Returns: List of (module_name, function_name) tuples.
Example:
from designer_plugin.d3sdk import get_all_d3functions
functions = get_all_d3functions()
# [("mymodule", "get_time"), ("mymodule", "get_surface")]
get_all_modules()
List all module names with registered d3functions.
Signature:
def get_all_modules() -> list[str]
Returns: List of module names.
Example:
from designer_plugin.d3sdk import get_all_modules
modules = get_all_modules()
# ["mymodule", "utilities"]
Next Steps
- Client API Reference - Class-based remote execution
- API Reference - Data models and low-level API
- Overview - designer-plugin overview