Bundle Example: Hello World¶
This example describes how to create a ChimeraX bundle
that defines a new command, hello
. The steps in
implementing the bundle are:
Create a
bundle_info.xml
containing information about the bundle,Create a Python package that interfaces with ChimeraX and implements the command functionality, and
Install and test the bundle in ChimeraX.
The final step builds a Python wheel that ChimeraX uses to install the bundle. So if the bundle passes testing, it is immediately available for sharing with other users.
Source Code Organization¶
The source code for this example may be downloaded as a zip-format file containing a folder named hello_world. Alternatively, one can start with an empty folder and create source files based on the samples below. The source folder may be arbitrarily named, as it is only used during installation; however, avoiding whitespace characters in the folder name bypasses the need to type quote characters in some steps.
Sample Files¶
The files in the source code folder are:
hello_world
- bundle folderbundle_info.xml
- bundle information read by ChimeraXsrc
- source code to Python package for bundle__init__.py
- package initializer and interface to ChimeraXcmd.py
- source code to implementhello
command
The file contents are shown below.
bundle_info.xml
¶
bundle_info.xml
is an eXtensible Markup Language
format file whose tags are listed in Bundle Information XML Tags.
While there are many tags defined, only a few are needed
for bundles written completely in Python.
1<!--
2ChimeraX bundle names must start with "ChimeraX-"
3to avoid clashes with package names in pypi.python.org.
4When uploaded to the ChimeraX toolshed, the bundle
5will be displayed without the ChimeraX- prefix.
6-->
7
8<BundleInfo name="ChimeraX-HelloWorld"
9 version="0.1" package="chimerax.hello_world"
10 minSessionVersion="1" maxSessionVersion="1">
11
12 <!-- Additional information about bundle source -->
13 <Author>UCSF RBVI</Author>
14 <Email>chimerax@cgl.ucsf.edu</Email>
15 <URL>https://www.rbvi.ucsf.edu/chimerax/</URL>
16
17 <!-- Synopsis is a one-line description
18 Description is a full multi-line description -->
19 <Synopsis>Basic example for adding a command</Synopsis>
20 <Description>Basic example code for implementing ChimeraX bundle.
21
22Implements command "hello" to print the string "hello world" in the log.
23 </Description>
24
25 <!-- Categories is a list where this bundle should appear -->
26 <Categories>
27 <Category name="General"/>
28 </Categories>
29
30 <!-- Dependencies on other ChimeraX/Python packages -->
31 <Dependencies>
32 <Dependency name="ChimeraX-Core" version="~=1.1"/>
33 </Dependencies>
34
35 <!-- Python and ChimeraX-specific classifiers
36 From https://pypi.python.org/pypi?%3Aaction=list_classifiers
37 Some Python classifiers are always inserted by the build process.
38 These include the Environment and Operating System classifiers
39 as well as:
40 Framework :: ChimeraX
41 Intended Audience :: Science/Research
42 Programming Language :: Python :: 3
43 Topic :: Scientific/Engineering :: Visualization
44 Topic :: Scientific/Engineering :: Chemistry
45 Topic :: Scientific/Engineering :: Bio-Informatics
46 The "ChimeraX :: Bundle" classifier is also supplied automatically. -->
47 <Classifiers>
48 <!-- Development Status should be compatible with bundle version number -->
49 <PythonClassifier>Development Status :: 3 - Alpha</PythonClassifier>
50 <PythonClassifier>License :: Freeware</PythonClassifier>
51 <!-- ChimeraX classifiers describe supplied functionality -->
52 <ChimeraXClassifier>ChimeraX :: Command :: hello :: General ::
53 Print "hello world" in the log</ChimeraXClassifier>
54 </Classifiers>
55
56</BundleInfo>
The document tag (which contains all other tags)
is named BundleInfo
, whose required
attributes are:
name
: the name of the bundle,version
: version of the bundles, usually in the form of major.minor.patch,package
: the name of the Python package where ChimeraX can find the code for this bundle, andminSessionVersion
andmaxSessionVersion
: the minimum and maximum session file versions that the bundle supports.
The next few tags supply information about who wrote the bundle, where to find more information on the web, as well as short and long descriptions of what functionality the bundle provides.
The Category
tags list the categories to which the
bundle belong. These Category
values are used by the
ChimeraX Toolshed when the bundle is contributed to the
repository. (Note that these values are completely distinct
from the category values described below in
ChimeraXClassifier
.)
The Dependency
tags list the bundles that must be installed
for this bundle to work. The ChimeraX-Core
bundle is a
pre-installed bundle that provides much of ChimeraX functionality.
For alpha and beta releases, the version number will start from
“0.1” and slowly approach “1.0”. Because ChimeraX Python API
follows semantic versioning rules (newer versions of ChimeraX
are compatible with older ones with the same major version number),
bundles written for earlier versions of ChimeraX will typically
work in later versions as well. This is indicated by the ~=
in the version
attribute of the Dependency
tag for
ChimeraX-Core
. A Dependency
tag should be present for each
additional bundle that must be installed. During installation
for this bundle, if any of the bundles listed in Dependency
tags are missing, they are automatically installed as well.
A Dependency
tag can also list non-ChimeraX PyPi packages
needed by the bundle, such as pandas or pytorch, along with
required version numbers, and such packages will be installed
with the bundle.
Finally, there are Classifier
tags, of which there are two
flavors: Python and ChimeraX. Values for Python classifiers
are the same as those found in standard Python package setup
scripts. Values for ChimeraXClassifier
tags classifiers
follow the same form as Python classifiers, using ::
as
separators among data fields.
The first data field must be the string ChimeraX
.
The second field specifies the type of functionality supplied,
in this case, a command.
For command classifiers, the third field is the name of the
command, in this case, hello
.
The fourth field for command classifiers is its category,
in this case, General
. (The category for a command is
reserved for future use but does not currently affect ChimeraX
behavior.)
The final data field for command classifiers is a synopsis
of what the command does, and is shown as help text in the
ChimeraX interface.
Commands may be a single word or multiple words.
The latter is useful for grouping multiple commands by
sharing the same first word. ChimeraX also automatically
support unambiguous prefixes as abbreviations. For example,
the user can use hel
as an abbreviation for hello
if no other command begins with hel
; however, h
is not an abbreviation because the hide
command also
starts with h
.
All bundle functionality must be listed in in ChimeraX classifiers in order for ChimeraX to integrate them into its user interface. In this example, the bundle only provides a single new command-line interface command. Reference documentation for bundle information tags, and specifically ChimeraX classifiers, is in Bundle Information XML Tags.
src
¶
src
is the folder containing the source code for the
Python package that implements the bundle functionality.
The ChimeraX devel
command, used for building and
installing bundles, automatically includes all .py
files in src
as part of the bundle. (Additional
files may also be included using bundle information tags
such as DataFiles
as shown in Bundle Example: Add a Tool.)
The only required file in src
is __init__.py
.
Other .py
files are typically arranged to implement
different types of functionality. For example, cmd.py
is used for command-line commands; tool.py
or gui.py
for graphical interfaces; io.py
for reading and saving
files, etc.
src/__init__.py
¶
1# vim: set expandtab shiftwidth=4 softtabstop=4:
2
3from chimerax.core.toolshed import BundleAPI
4
5
6# Subclass from chimerax.core.toolshed.BundleAPI and
7# override the method for registering commands,
8# inheriting all other methods from the base class.
9class _MyAPI(BundleAPI):
10
11 api_version = 1 # register_command called with BundleInfo and
12 # CommandInfo instance instead of command name
13 # (when api_version==0)
14
15 # Override method
16 @staticmethod
17 def register_command(bi, ci, logger):
18 # bi is an instance of chimerax.core.toolshed.BundleInfo
19 # ci is an instance of chimerax.core.toolshed.CommandInfo
20 # logger is an instance of chimerax.core.logger.Logger
21
22 # This method is called once for each command listed
23 # in bundle_info.xml. Since we only listed one command,
24 # we expect only a single call to this method.
25
26 # We import the function to call and its argument
27 # description from the ``cmd`` module, adding a
28 # synopsis from bundle_info.xml if none is supplied
29 # by the code.
30 from . import cmd
31 desc = cmd.hello_world_desc
32 if desc.synopsis is None:
33 desc.synopsis = ci.synopsis
34
35 # We then register the function as the command callback
36 # with the chimerax.core.commands module.
37 # Note that the command name registered is not hardwired,
38 # but actually comes from bundle_info.xml. In this example,
39 # the command name is "hello", not "hello world".
40 from chimerax.core.commands import register
41 register(ci.name, desc, cmd.hello_world)
42
43
44# Create the ``bundle_api`` object that ChimeraX expects.
45bundle_api = _MyAPI()
__init__.py
contains the initialization code that defines
the bundle_api
object that ChimeraX needs in order to
invoke bundle functionality. ChimeraX expects bundle_api
class to be derived from chimerax.core.toolshed.BundleAPI
,
which has one public attribute, api_version
, and these methods:
initialize
- invoked when ChimeraX starts up and the bundle needs custom initializationfinish
- invoked when ChimeraX exits and the bundle needs custom clean upregister_command
- invoked the first time a bundle command is usedregister_selector
- invoked the first time a bundle chemical subgroup selector is usedstart_tool
- invoked to display a bundle graphical interfaceopen_file
- invoked when a file of a bundle-supported format is openedsave_file
- invoked when a file of a bundle-supported format is savedfetch_from_database
- invoked when an entry is fetched from a network databaseget_class
- invoked when a session is saved and a bundle object needs to be serialized
The api_version
attribute should be set to 1
. The default
value for api_version
is 0
and is supported for older bundles.
New bundles should always use the latest supported API version.
This example only provides a single command, so the only method that
needs to be overridden is register_command
. The other methods
should never be called because there are no ChimeraXClassifier
tags in bundle_info.xml
that mention other types of functionality.
register_command
is called once for each command listed in a
ChimeraXClassifier
tag. When ChimeraX starts up, it registers
a placeholder for each command in all bundles, but normally does
import the bundles. When a command is used and ChimeraX detects
that it is actually a placeholder, it asks the bundle to register
the run-time information regarding what arguments are expected and
which function should be called to process the command, after which
the command line is parsed and the registered function is called.
Once a command is registered, ChimeraX will not call
register_command
for it again.
In BundleAPI
version 1, the register_command
method is called
with three arguments:
bi
- instance ofchimerax.core.toolshed.BundleInfo
ci
- instance ofchimerax.core.toolshed.CommandInfo
logger
- instance ofchimerax.core.logger.Logger
bi
provides access to bundle information such as its name, version,
and description. For this example, no bundle information is required
and bi
is unused. ci
provides access to command information,
and the two attributes used are synopsis
(for setting help text
if none is provided in code) and name
(for notifying ChimeraX of
what function to use to process the command). logger
may be used
to notify users of warnings and errors; in this example, errors will
be handled by the normal Python exception machinery.
The most important line of code in register_command
is the call
to chimerax.core.commands.register()
, whose arguments are:
name
- a Python string for the command name,cmd_desc
- an instance ofchimerax.core.commands.CmdDesc
which describes what command line arguments are expected, andfunction
- a Python function to process the command.
In this example, the command name comes from the command information
instance, ci.name
. Both the argument description and the Python
function are defined in another package module: cmd.py
.
The argument description comes from cmd.hello_world_desc
, possibly
augmented with help text from ci.synopsis
. The command-processing
function also comes from the same module, cmd.hello_world
.
The arguments that cmd.hello_world
will be called with are
determined by the attributes of cmd.hello_world_desc
and is
described below.
Note that register_command
and other BundleAPI
methods are static
methods and are not associated with the bundle_api
instance.
The intent is that these methods remain simple and should not need
other data. If necessary, data can be stored as attributes of bundle_api
and the static methods can refer to the instance explicitly.
src/cmd.py
¶
1# vim: set expandtab shiftwidth=4 softtabstop=4:
2
3from chimerax.core.commands import CmdDesc
4
5
6def hello_world(session):
7 # All command functions are invoked with ``session`` as its
8 # first argument. Useful session attributes include:
9 # logger: chimerax.core.logger.Logger instance
10 # models: chimerax.core.models.Models instance
11 session.logger.info("Hello world!")
12
13# CmdDesc contains the command description. For the
14# "hello" command, we expect no arguments.
15hello_world_desc = CmdDesc()
To implement the hello
command, two components are needed:
a function that prints Hello world!
to the ChimeraX log,
and a description to register so that ChimeraX knows how to
parse the typed command text and call the function with the
appropriate arguments.
In this simple example, hello_world
is the name of the function
and hello_world_desc
is the description for the command.
(Note that the function and description names need not match
the command name.)
hello_world_desc
, the command description, is an
instance of chimerax.core.commands.CmdDesc
. No
arguments are passed to the constructor, meaning the
user should not type anything after the command name.
If additional text is entered after the command, ChimeraX will flag
that as an error and display an error message without invoking
the hello_world
function.
If the command is entered correctly, ChimeraX calls the
hello_world
function with a single argument, session
,
which provides access to session data such as the open models
and current selection. For this example, hello_world
uses
the session logger, an instance of chimerax.core.logger.Logger
,
to display the informational message “Hello world!” The message
is displayed in the log window when the ChimeraX graphical
interface is displayed; otherwise, it is printed to the console.
Later tutorials will discuss how to use the command description to inform ChimeraX how to convert input text to Python values and map them to arguments when calling the command-processing function.
Building and Testing Bundles¶
To build a bundle, start ChimeraX and execute the command:
devel build PATH_TO_SOURCE_CODE_FOLDER
Python source code and other resource files are copied
into a build
sub-folder below the source code
folder. C/C++ source files, if any, are compiled and
also copied into the build
folder.
The files in build
are then assembled into a
Python wheel in the dist
sub-folder.
The file with the .whl
extension in the dist
folder is the ChimeraX bundle.
To test the bundle, execute the ChimeraX command:
devel install PATH_TO_SOURCE_CODE_FOLDER
This will build the bundle, if necessary, and install the bundle in ChimeraX. Bundle functionality should be available immediately.
To remove temporary files created while building the bundle, execute the ChimeraX command:
devel clean PATH_TO_SOURCE_CODE_FOLDER
Some files, such as the bundle itself, may still remain and need to be removed manually.
Building bundles as part of a batch process is straightforward, as these ChimeraX commands may be invoked directly by using commands such as:
ChimeraX --nogui --exit --cmd 'devel install PATH_TO_SOURCE_CODE_FOLDER exit true'
This example executes the devel install
command without
displaying a graphics window (--nogui
) and exits immediately
after installation (exit true
). The initial --exit
flag guarantees that ChimeraX will exit even if installation
fails for some reason.
Distributing Bundles¶
With ChimeraX bundles being packaged as standard Python
wheel-format files, they can be distributed as plain files
and installed using the ChimeraX toolshed install
command. Thus, electronic mail, web sites and file
sharing services can all be used to distribute ChimeraX
bundles.
Private distributions are most useful during bundle development, when circulation may be limited to testers. When bundles are ready for public release, they can be published on the ChimeraX Toolshed, which is designed to help developers by eliminating the need for custom distribution channels, and to aid users by providing a central repository where bundles with a variety of different functionality may be found.
Customizable information for each bundle on the toolshed includes its description, screen captures, authors, citation instructions and license terms. Automatically maintained information includes release history and download statistics.
To submit a bundle for publication on the toolshed,
you must first sign in. Currently, only Google
sign in is supported. Once signed in, use the
Submit a Bundle
link at the top of the page
to initiate submission, and follow the instructions.
The first time a bundle is submitted to the toolshed,
it is held for inspection by the ChimeraX team, which
may contact the authors for more information.
Once approved, all subsequent submissions of new
versions of the bundle are posted immediately on the site.
What’s Next¶
Bundle Example: Hello World (current topic)
Bundle Example: Add a Command (next topic)