Reference
This section covers high-level aspects of Avalon in an information-oriented fashion.
Looking for API reference?
See the auto-generated documentation for the Avalon API
Content Life Cycle
Data in Avalon is either persistent or in transit. Persistent data resides in either a file-system or database, whereas data in transit is in one of three states.
Note
For each state, there is an API for developers and at least one GUI for users.
Create
Creation is the process of introducing new data into a project and is divided into two parts; asset and subset creation.
Assets are abstract representations of the data used throughout a project - such as sequences, shots, characters and props - whereas Subsets represents data per asset - such as geometry, textures or rigs.
Asset and subset creation is governed by the Project Inventory API and Creator API respectively via one or more plug-ins associated to named "families" of data, such as model
, look
or render
.
Assets are created via the Project Inventory API and subsets are generally created via the use of a Digital Content Creation package, such as Autodesk Maya or The Foundry Nuke.
API Example
from avalon import api class CreateModel(api.Creator): """Polygonal geometry for animation""" label = "Create Avalon Model" name = "modelDefault" family = "avalon.model"
More information
Import
Import is the process of parsing persistent data from disk and into the memory of a running application.
Due to data being either localised or referenced, import is referred to as loading, a process governed by the Loading API through one or more plug-ins associated to named families of data.
API Example
from avalon import api class LoadModel(api.Loader): """Load data of family avalon.model""" label = "Load Avalon Model" families = ["avalon.model"] representations = ["ma"] def process(self, name, namespace, context): from maya import cmds from avalon import maya with maya.maintained_selection(): nodes = cmds.file(self.fname) self[:] = nodes
More information
Export
Export is the process of transforming in-memory data native to an application into something that can persist on disk. During export, data is funneled through a validation mechanism that check for consistency. Because of this additional mechanism, export is referred to as publishing.
The manner in which data is validated and written is governed by a series of plug-ins, orchestrated by the Publishing API and associated to families of data.
API Example
from pyblish import api class ExtractAvalonModel(api.InstancePlugin): """Produce a stripped down Maya file from instance""" label = "Extract Avalon Model" order = api.ExtractorOrder hosts = ["maya"] families = ["avalon.model"] def process(self, instance): from maya import cmds from avalon import maya with maya.maintained_selection(), maya.without_extension(): cmds.select(instance, noExpand=True) cmds.file(path, typ="mayaAscii", exportSelected=True)
More information
Persist
Once exported, data resides in one or two locations - as files in a file-system, or as documents in a database. The exact location within the file-system is governed by the Project Configuration API via path "templates" - a string encoded with placeholder variables associated to the various objects in the object model, customisable per-project.
Example
{ "work": "{root}/{project}/{asset}/work/{task}/{user}/{app}", "publish": "{root}/{project}/{asset}/publish/{subset}/v{version:0>3}/{subset}.{representation}" }
Object Model
Wherever data is stored, it is stored as a hierarchy of increasingly granular objects, representative of the division of labour in each project created through Avalon.
Assets represents the most course grained division of labour and is typically used for shots and builds, e.g. the 6th shot and the hero character.
Each asset contain one or more Subsets which are typically used for individual models and animation caches for a build or shot.
Each subset contain one or more Versions which are immutable sources of data containing the final element of the object model; the Representation.
Representations are the storage method of a version, such as a .png thumbnail, an .obj geometry file or .mp4 turntable of a hero model. Both of which represents the same set of data in three different ways.
Each object containing a series of members defined by an explicit schema, enforced via jsonschema and organised hierarchically with the former containing the latter.
Read more
- Read about schemas in the Database section below.
Metadata
Every object in the model contains a dictionary member called .data
.
The conceptual difference between top-level members and members of .data
is that top-level members can be assumed to exist in every application, whereas members of .data
are optional.
Optional members facilitate flexible code at the cost of having more of it and therefore more to maintain.
Database
Avalon stores data in two separate locations, on disk and in a database. The separation is made due to performance and search capabilities offered by databases.
MongoDB was chosen due to the inherent simplicity and similarity to Python's built-in dictionary type, and performance great enough to enable graphical user interfaces to be built without asynchronousity in mind.
Inside of MongoDB, data is stored as Collections containing many Documents. In Avalon, each Collection represents a project and documents make up the Object Model.
- Asset
- Subset
- Version
- Representation
These form a hierarchy, where each contain the latter. Assets make up the top-level object within a project, and can represent anything from characters, shots to levels and more.
Asset | Description |
---|---|
Hulk | A bulky fellow |
Bruce | The hero of the film |
1000 | First shot |
1200 | Second shot |
Subsets is the asset broken down into smaller sets of information, such as a rig or a model.
Subset | Description |
---|---|
model | Hulk's model |
rig | Hulk's rig |
lookdev | Hulk's look |
animation | Hulk's point cached geometry |
A subset must have a least one Version, which is typically immutable.
Version | Comment |
---|---|
v001 | Initial version |
v002 | Fixed whole in mesh |
v003 | Increased the size of pecs |
Finally, in each version there is at least one Representation; typically a file or sequence of files.
Representation | Description |
---|---|
ma | Maya rig |
mov | Turntable |
abc | Still frame of mesh used in rig |
Read more about the kinds of objects in Schemas below.
Schemas
All data within the database and on disk follow a strict layout, known as a "schema".
Project
A project is a top-level object that cannot be contained elsewhere, but contains everything else.
{ "config": "Document metadata", "data": "Document metadata", "name": "Name of directory", "parent": "Unique identifier to parent document", "schema": "Schema identifier for payload", "type": "The type of document" }
Example
{ "config": { "apps": [ { "label": "Autodesk Maya 2016", "name": "maya2016" }, { "label": "The Foundry Nuke 10.0", "name": "nuke10" } ], "schema": "avalon-core:config-1.0", "tasks": [ { "name": "model" }, { "name": "render" }, { "name": "animate" }, { "name": "rig" }, { "name": "lookdev" }, { "name": "layout" } ], "template": { "publish": "{root}/{project}/{silo}/{asset}/publish/{subset}/v{version:0>3}/{subset}.{representation}", "work": "{root}/{project}/{silo}/{asset}/work/{task}/{app}" } }, "data": { "fps": 24, "height": 1080, "width": 1920 }, "name": "hulk", "parent": "592c33475f8c1b064c4d1696", "schema": "avalon-core:project-2.0", "type": "project" }
Asset
A part of a project, such as a Character or Shot.
{ "data": "Document metadata", "name": "Name of asset", "parent": "Unique identifier to parent document", "schema": "Schema identifier for payload", "silo": "Group or container of asset", "type": "The type of document" }
Example
{ "data": { "key": "value" }, "name": "Bruce", "parent": "592c33475f8c1b064c4d1696", "schema": "avalon-core:asset-2.0", "silo": "assets", "type": "asset" }
Subset
A part of an Asset, such as a model or a rig.
{ "data": "Document metadata", "name": "Name of directory", "parent": "Unique identifier to parent document", "schema": "The schema associated with this document", "type": "The type of document" }
Example
{ "data": { "frameEnd": 1201, "frameStart": 1000 }, "name": "shot01", "parent": "592c33475f8c1b064c4d1696", "schema": "avalon-core:subset-2.0", "type": "subset" }
Version
An immutable iteration of a Subset.
Versions are immutable, in that they never change once made. This is in stark contrast to mutable versions which is when one version may be "updated" such that the same file now contains new information.
{ "data": "Document metadata", "locations": "Where on the planet this version can be found.", "name": "Number of version", "parent": "Unique identifier to parent document", "schema": "The schema associated with this document", "type": "The type of document" }
Example
{ "data": { "author": "marcus", "families": [ "avalon.model" ], "source": "{root}/f02_prod/assets/BubbleWitch/work/modeling/marcus/maya/scenes/model_v001.ma", "time": "20170510T090203Z" }, "locations": [ "data.avalon.com" ], "name": 12, "parent": "592c33475f8c1b064c4d1696", "schema": "avalon-core:version-2.0", "type": "version" }
Representation
One of many representations of a Version.
Think of a representation as one way of storing the same set of data on disk. For example, an image may be stored as both PNG and JPEG. Different files, same data. It could also be stored as a description. "A picture of my computer." Much less information is ultimately stored, but it is nonetheless the exact same original data in a different (albeit lossy) representation. The image could also be represented by a feeling (warm, mystical) or a spoken word (muah!).
Representations are very powerful and lie at the heart of assets that are more than just a single file.
As a practical example, a Look is stored as both an MA scene file and a JSON. The JSON stores the shader relationships, whereas the MA file stores the actual shaders. Same data, different representations.
{ "context": "Summary of the context to which this representation belong.", "data": "Document metadata", "dependencies": "Other representation that this representation depends on", "name": "Name of representation", "parent": "Unique identifier to parent document", "schema": "Schema identifier for payload", "type": "The type of document" }
Example
{ "context": { "asset": "Bruce", "project": "hulk", "representation": "ma", "silo": "assets", "subset": "rigDefault", "version": 12 }, "data": { "label": "Alembic" }, "dependencies": [ "592d547a5f8c1b388093c145" ], "name": "abc", "parent": "592c33475f8c1b064c4d1696", "schema": "avalon-core:representation-2.0", "type": "representation" }
Container
An imported Version, as yielded from api.registered_host().ls()
.
{ "id": "Identifier for finding object in host", "loader": "Name of loader plug-in used to produce this container", "name": "Internal object name of container in application", "namespace": "Internal namespace of container in application", "objectName": "Name of internal object, such as the objectSet in Maya.", "representation": "Unique id of representation in database", "schema": "Schema identifier for payload" }
Example
{ "id": "pyblish.avalon.container", "loader": "ModelLoader", "name": "modelDefault_01", "namespace": "Bruce_", "objectName": "Bruce_:rigDefault_CON", "representation": "59523f355f8c1b5f6c5e8348", "schema": "avalon-core:container-2.0" }
Software
Avalon assumes content is created within an application of some kind and manages the execution of each application via Launcher.
Apps
Launcher is responsible for launching "apps", such as Maya. "App" is the term used for a pre-configured application in Avalon.
Problem
It could call on c:\Program Files\Autodesk\Maya2017\bin\maya.exe
directly, but doing so is problematic because..
- It assumes a particular operating system
- It assumes a particular installation directory
- It assumes a particular app is what you want for your project(s)
- It assumes no customisation of environment prior to launch
Solution
The Project Executable API addresses this by splitting the problem it into three independently configurable parts.
- Apps are assumed to be available on your
PATH
, e.g.maya.sh
ormaya.bat
- Configuration is performed per application in an individual configuration file, e.g.
maya.toml
- Apps are associated per project, e.g. Hulk uses Maya and Nuke.
Library API
Public members of avalon.api
. See API Documentation for full details.
Member | Description |
---|---|
install |
Install host into the running Python session. |
uninstall |
Undo all of what install() did |
schema |
Wrapper around :mod:jsonschema |
Loader |
Load representation into host application |
Creator |
Determine how assets are created |
Action |
A custom action available |
InventoryAction |
A custom action for the scene inventory tool |
Application |
Default application launcher |
discover |
Find and return subclasses of superclass |
Session |
dict() -> new empty dictionary |
session |
dict() -> new empty dictionary |
on |
Call callback on event |
after |
Convenience to on() for after-events |
before |
Convenience to on() for before-events |
emit |
Trigger an event |
publish |
Shorthand to publish from within host |
create |
Create a new instance |
load |
Use Loader to load a representation. |
update |
Update a container |
switch |
Switch a container to representation |
remove |
Remove a container |
data |
dict() -> new empty dictionary |
update_current_task |
Update active Session to a new task work area. |
get_representation_path |
Get filename from representation document |
loaders_from_representation |
Return all compatible loaders for a representation. |
register_host |
Register a new host for the current process |
register_plugin_path |
Register a directory of one or more plug-ins |
register_plugin |
Register an individual obj of type superclass |
register_root |
Register currently active root |
registered_root |
Return currently registered root |
registered_plugin_paths |
Return all currently registered plug-in paths |
registered_host |
Return currently registered host |
registered_config |
Return currently registered config |
deregister_plugin |
Oppsite of register_plugin() |
deregister_plugin_path |
Oppsite of register_plugin_path() |
logger |
|
time |
Return file-system safe string of current date and time |
Host API
A host must implement the following members.
Member | Returns | Description |
---|---|---|
ls |
generator |
List loaded assets |
create |
str |
Build fixture for outgoing data (see instance), returns instance. |
load |
None |
Import external data into container |
update |
None |
Update an existing container |
remove |
None |
Remove an existing container |
Information hierarchy
Imported data is stored in a container
. A container hosts a loaded asset along with metadata used to associate assets that use other assets, such as a Wheel asset used in a Car asset.
Id
Internally, Pyblish instances and containers are distinguished from native content via an "id". For example, in Maya, the id
is a user-defined attribute.
Name | Description | Example |
---|---|---|
pyblish.avalon.container |
Unit of incoming data | ...:model_GRP , ...:rig_GRP |
pyblish.avalon.instance |
Unit of outgoing data | Strange_model_default |
Project Inventory API
The inventory contains all ASSETs of a project, including metadata.
.inventory.toml
# Mandatory, do not touch schema = "avalon-core:inventory-1.0" # Project metadata label = "The Hulk" fps = 24 resolution_width = 1920 resolution_height = 1080 # Available assets [[assets]] name = "Batman" [[assets]] name = "Bruce" label = "Bruce Wayne" # (Optional) Nicer name group = "Character" # (Optional) Visual grouping icon = "gear" # (Optional) Icon from FontAwesome [[assets]] name = "Camera" # Available shots [[film]] name = "1000" edit_in = 1000 edit_out = 1202 [[film]] name = "1200" edit_in = 1000 # Optional metadata per shot edit_out = 1143
The above is an example of an "inventory". A complete snapshot of all available assts within a given project, along with optional metadata.
Project Configuration API
The project configuration contains the applications and tasks available within a given project, along with the template used to create directories.
.config.toml
# Mandatory, do not touch schema = "avalon-core:config-1.0" # Available tasks to choose from. [[tasks]] name = "modeling" label = "Character Modeling" icon = "video-camera" [[tasks]] name = "animation" # Available applications to choose from, the name references # the executable API (see below) [[apps]] name = "maya2016" label = "Autodesk Maya 2016" [[apps]] name = "python" label = "Python 3.6" args = ["-u", "-c", "print('Something nice')"] # Directory layouts for this project. [template] work = "{root}/{project}/{silo}/{asset}/work/{task}/{user}/{app}" publish = "{root}/{project}/{silo}/{asset}/publish/{subset}/v{version:0>3}/{subset}.{representation}"
The directory layout have the following members available.
Member | Type | Description |
---|---|---|
{app} |
str |
The current application directory name, defined in Executable API |
{task} |
str |
Name of the current task |
{user} |
str |
Currently logged on user (provided by getpass.getuser() ) |
{root} |
str |
Absolute path to root directory, e.g. m:\f01_project |
{project} |
str |
Name of current project |
{silo} |
str |
Name of silo, e.g. assets |
{asset} |
str |
Name of asset, e.g. Bruce |
{subset} |
str |
Name of subset, e.g. modelDefault |
{version} |
int |
Number of version, e.g. 1 |
{representation} |
str |
Name of representation, e.g. ma |
Project Executable API
Every executable must have an associated Application Definition file which looks like this.
maya2016.toml
# Required header, do not touch. schema = "avalon-core:application-1.0" # Name of the created directory, available in the # `template` of the Configuration API application_dir = "maya" # These directories will be created under the # given application directory default_dirs = [ "scenes", "data", "renderData/shaders", "images" ] # Name displayed in GUIs label = "Autodesk Maya 2016x64" # Arguments passed to the executable on launch arguments = [ "-proj", "{AVALON_WORKDIR}",] # Name of the executable on the local computer. # This name must be available via the users `PATH`. # That is, the user must be able to type this into # the terminal to launch said application. executable = "maya2016" description = "" # Files copied into the application directory on launch [copy] "{AVALON_CORE}/res/workspace.mel" = "workspace.mel" # The environment variables overrides any previously set # variables from the parent process. [environment] MAYA_DISABLE_CLIC_IPM = "Yes" # Disable the AdSSO process MAYA_DISABLE_CIP = "Yes" # Shorten time to boot MAYA_DISABLE_CER = "Yes" PYTHONPATH = [ "{PYBLISH_MAYA}/pyblish_maya/pythonpath", "{AVALON_CORE}/avalon/maya/pythonpath", "{PYTHONPATH}" ]
Project Template API
The vast majority of data managed by Avalon is stored as files on disk, and every file has a path. The path is an integral part of any production and requires fine-grained control.
Avalon provides this control in the form of templates
.
A template is a string of {placeholders}
, each placeholder representing some data.
template = "{project}/{asset}/myfile_v{version:0>3}.ma"
The keywords within the {}
are known as placeholders
and may be dynamically replaced at run-time by your code.
Example
On saving a file, this template could be used to construct a path.
template = "{project}/{asset}/myfile_{version:0>3}.ma" fname = template.format( project="hulk", asset="bruce", version=13 ) print(fname)
hulk/bruce/myfile_013.ma
|
Provided that pass the same data, the template itself can vary, whilst leaving your code intact.
template = "c:/assets/{asset}/{project}/myfile_version{version:0>2}.mb"
Viola, a drastically different directory layout, with identical data and code to process it. Notice how this templates includes reference to c:\
which not only asserts a particular operating system (Windows) but also leaves no room for an alternative "root". See Root Template below for an example of how to overcome this limitation.
This is how Avalon works.
In this way, you can establish convention at a high-level whilst still remaning specific in places where you need to either read from or write to disk. More importantly, this is the aspect which allows Avalon to not make assumptions about your directory layout; it is up to you to write this template.
Having said that, though Avalon doesn't make assumptions about where data is written, it does make assumptions on the types of data written - referred to as the Avalon Object Model - and your directory layout must accommodate for these types at minimum.
- See Object Model for a high-level overview of these types
For any project, Avalon assumes a work
template to be available in your Project Configuration. This template is used to generate the initial directory layout for your work-in-progress files.
The layout and ordering of placeholders in this template is within your control, and these are the ones available to you.
{ "root": "Currently registered root, e.g. 'c:/projects'", "project": "Current project, e.g. 'hulk'", "silo": "Current silo, e.g. 'assets', 'film', 'episodes'", "asset": "Current asset, e.g. 'Bruce'", "task": "Current task", "app": "Current app, e.g. 'maya2019'", "user": "Current user, e.g. 'rick'" }
Here's an example of a work
template.
template = "{root}/{project}/{silo}/{asset}/work/{task}/{user}/{app}"
- See Project Configuration API for more.
Template Access
Templates are generally stored in your project configuration.
# Untested from avalon import io project = io.find_one({"type": "project"}) template = project["config"]["template"]["work"]
Root Template
Every path has a "root"; the first part of a path.
c:\ /root /projects/
When a template includes a root explicitly, like any of the above, then a template is limited to (1) a particular platform and (2) a particular mount on that platform.
For example, if your studio is exclusively on Windows and all share the drive z:\
, then it is safe to embed this into your template.
template = "z:/projects/{project}/{asset}/..."
However, when there are multiple operating systems and/or multiple mount points - e.g. z:\
on some machines and and x:\
on others - then the above template no longer works.
To work around this, Avalon provides the idea of a "root" directory.
# Windows box, from our London branch from avalon import api api.register_root(r"c:\projects")
At run-time, a root directory is registered relative your platform and mount point, such that your template can look like..
template = "{root}/{project}{asset}..."
Such that, on another platform or machine, the root registration can change to account for a differing root.
# Linux box, in LA from avalon import api api.register_root("/mnt/projects")
As a result, your folder creation and publishing code can remain as-is, whilst assets are being read and written all over the place.
Environment Variables
Avalon uses environment variables extensively. For example, simply running an application involves two layers of information exchange.
- Your shell > Launcher
- Launcher > Application
In this documentation, environment variables are UPPERCASE and setting one is similar across all platforms.
set MY_VARIABLE=value
export MY_VARIABLE=value
Variables may also be set from within Python, by altering the os.environ
dictionary.
import os os.environ["MY_VARIABLE"] = "value"
Avalon Environment
These are all environment variables relevant in some way to Avalon.
{ "AVALON_APP": "Name of application", "AVALON_ASSET": "Name of asset", "AVALON_CONFIG": "Name of Avalon configuration", "AVALON_CONTAINER_ID": "Unique identifier for a loaded representation in a working file", "AVALON_DB": "Name of database", "AVALON_DEADLINE": "Address to Deadline", "AVALON_DEBUG": "Enable debugging mode. Some applications may use this for e.g. extended verbosity or mock plug-ins.", "AVALON_INSTANCE_ID": "Unique identifier for instances in a working file", "AVALON_LABEL": "Nice name of Avalon, used in e.g. graphical user interfaces", "AVALON_MONGO": "Address to the asset database", "AVALON_PASSWORD": "Generic password", "AVALON_PROJECT": "Name of project", "AVALON_PROJECTS": "Absolute path to root of project directories", "AVALON_SENTRY": "Address to Sentry", "AVALON_SILO": "Name of asset group or container", "AVALON_TASK": "Name of task", "AVALON_TIMEOUT": "Wherever there is a need for a timeout, this is the default value.", "AVALON_UPLOAD": "Boolean of whether to upload published material to central asset repository", "AVALON_USERNAME": "Generic username", "AVALON_WORKDIR": "Current working directory of a host, such as Maya's location of workspace.mel" }
Example
{ "AVALON_APP": "maya2016", "AVALON_ASSET": "Bruce", "AVALON_CONFIG": "polly", "AVALON_CONTAINER_ID": "avalon.container", "AVALON_DB": "avalon", "AVALON_DEADLINE": "http://192.168.99.101", "AVALON_DEBUG": "True", "AVALON_INSTANCE_ID": "avalon.instance", "AVALON_LABEL": "Mindbender", "AVALON_MONGO": "mongodb://localhost:27017", "AVALON_PASSWORD": "abc123", "AVALON_PROJECT": "Hulk", "AVALON_PROJECTS": "/nas/projects", "AVALON_SENTRY": "https://5b872b280de742919b115bdc8da076a5:8d278266fe764361b8fa6024af004a9c@logs.mindbender.com/2", "AVALON_SILO": "assets", "AVALON_TASK": "modeling", "AVALON_TIMEOUT": "1000", "AVALON_UPLOAD": "True", "AVALON_USERNAME": "myself", "AVALON_WORKDIR": "/mnt/projects/alita/assets/vector/maya" }
Inheritance and Persistence
Setting a variable as above only affects the current instance of the process, such as cmd.exe
and python
. That's because variables are inherited.
Inheritance in this context means that any process launched from a parent process will contain a duplicate of the parent environment, and no change you make to the environment of this child process will affect the parent process.
set MY_VARIABLE=value cmd echo %MY_VARIABLE% :: value set CHILD_VARIABLE=1 echo %CHILD_VARIABLE% :: 1 exit echo %CHILD_VARIABLE% :: %CHILD_VARIABLE%
export MY_VARIABLE=value cmd echo $MY_VARIABLE # value export CHILD_VARIABLE=1 echo $CHILD_VARIALBE # 1 exit echo $CHILD_VARIABLE #
Notice how MY_VARIABLE
is available in the child process, but the variable created within the child process it not accessible from the parent process.
This concept is incredibly powerful and is how Avalon manages the individual environment variables for your projects and applications.
PATH
Some environment variables have special meaning to your operating system, PATH
is one of them. It contains absolute paths from which executables are accessed.
That is, when you type git
on the command-line, your operating system performs a search in each of the paths listed in PATH
until it finds it.
If you wanted to expose an executable of your own, you can add the directory containing the executable to the PATH
.
PYTHONPATH
Like PATH
, PYTHONPATH
is where Python looks for files during import
.
Events
Attach callbacks to critical events throughout the use of Avalon.
from avalon import api def on_event(): print("An event happened") api.on("event", on_event) api.emit("event")
An event happened
|
Some events are called by Avalon.
init
Called as early as possible during the initialisation of an application.new
Called upon the creation of a new document/scenesave
Called upon saving a document/scene
Custom Events
Aside from the built-in events, you can emit your own events too.
from avalon import api def create_alembic(): # Creating alembic.. api.emit("alembic_created")