Copyright 2011-2024, by the California Institute of Technology. ALL RIGHTS RESERVED. United States Government Sponsorship acknowledged. Any commercial use must be negotiated with the Office of Technology Transfer at the California Institute of Technology.
This software may be subject to U.S. export control laws. By accepting this software, the user agrees to comply with all applicable U.S. export laws and regulations. User has the responsibility to obtain export licenses, or other export authority as may be required before exporting such information to foreign countries or providing access to foreign persons.
Contents
- Introduction
- Installation
- User instructions
- Administrator instructions
- Developer information
- Troubleshooting
- pyam hangs on
pyam diff
- MySQL says
Client does not support authentication protocol
- pyam crashes with
Negative size passed to PyString_FromStringAndSize
- Characters like
^M
show up in the pyam progress output - pyam says
Waiting for exclusive use of sandbox
- pyam says
Uncommitted files
- Sort
latest
by date - Put boolean flags in configuration file
- Configure to avoid text-editor prompt for release note message
- pyam hangs on
- Bug reports
pyam is the Python implementation of yam. It is a utility for configuration management and revision control.
pyam can be installed via a standard Python setup.py
script.
External dependencies required by pyam are listed below.
- Python (> =3.6)
- MySQL Connector/Python (>= 1.1)
- pysvn (>= 1.7.6)
- GitPython (>= 3.1.26)
- GNU Make
For example, on Fedora Linux, the following command would install the above dependencies:
$ yum install python mysql-connector-python pysvn make
pyam talks to a repository and a database.
- Subversion (server and repository versions >= 1.5)
- MySQL
pyam has been tested on Unix-like operating systems (including several Linux distributions, OS X, and FreeBSD).
You can use either python 2 or python 3 to install pyam - with python 3 recommended for more recent OS. To install pyam, run (using the appropriate python):
$ python ./setup.py install
If you want to install it to a non-standard location:
$ python ./setup.py install \ --single-version-externally-managed --record=record \ --prefix=<install_directory>
In this non-standard case, you will need to set PYTHONPATH
appropriately.
For example, on Python 2.7, you would need to set PYTHONPATH
to
<install_directory>/lib/python2.7/site-packages
.
This section covers information for users of the pyam utility.
Below is a typical usage example. These are the commands users of pyam would
typically use on a day-to-day basis. The example assumes that someone
previously registered a new packaged with the name FooBarPackage
. It also
assumes that the package contains a previously registered module called
MyModule
.
Check out a sandbox:
$ pyam setup FooBarPkg --directory my_sandbox
Run a program in the sandbox. The srun
utility runs the program in the
sandboxed environment. It does this by finding the Drun
script within
the sandbox and using it to set the environment variables such as PATH
and LD_LIBRARY_PATH
among others:
$ cd my_sandbox $ srun MyProgram
Check out a work module to do some development:
$ pyam checkout MyModule
MyModule
will now be checked out in a unique branch. Do some hacking:
$ cd src/MyModule $ vi foo.py $ svn commit --message='Modify x to add feature y' $ vi bar.py $ svn commit --message='Fix bug z'
Once finished, you can save
the module. If your branch is not on the latest
revision, you may have to sync up at this point. pyam will tell you if this is
necessary when the save
command is issued:
$ pyam sync MyModule
You should also make sure your link modules are up to date before saving:
$ pyam sync --link-modules
Or you could update all link and work modules:
$ pyam sync --all
To save the module so that your changes become available to everyone, do the following:
# Always test changes before saving. $ make all $ make regtest $ cd .. $ pwd my_sandbox/src $ pyam save MyModule
If you get an error message about dangling links or out of date modules, either
fix the problem or ignore them by using the appropriate options. To find these
options use pyam save --help
.
If you have a work module (eg, MyModule) that you wish to discard ("scrap"), use the pyam 'scrap' command:
$ cd <outside the module you wish to scrap> $ pyam scrap MyModule
This will rename the module folder by appending a timestamp and then convert the module to a link module in your sandbox. At this point you can delete the renamed module directory if it is no longer needed. Note that if you wish to completely remove the module from the sandbox, you may use the '--remove' option.
For more control, edit the YAM.config
file directly. It is contained in the
root of the sandbox. There you can specify things like which branch you want or
convert an existing work module into a link module. Once done, you can call
pyam rebuild
.
For example:
$ pyam setup FooBarPkg --directory my_sandbox $ cd my_sandbox
Edit the YAM.config
to your specific needs. The file itself contains some
documentation at the bottom of it:
$ vi YAM.config
Relink the link modules and rebuild the work modules. You can specify the specific module names or you can just pass no arguments to just rebuild everything:
$ pyam rebuild
The DARTS Lab Q&A site has a detailed writeup of maintenance branches and relelases. Please see https://dartslab.jpl.nasa.gov/qa/1530.
To get help with command-line usage, run:
$ pyam -h
For a more succinct output of subcommands only:
$ pyam help
To get usage for a subcommand, run:
$ pyam <subcommand> -h
There are a variety of questions and answers on the DARTS Lab Q&A Site that address how to use pyam. You can search the site for 'pyam' to see the current entries. Here are few of the current pyam entries:
- How does one create a new pyam module?
- How does one use pyam add, modify, delete the modules in a sandbox?
- What does the pyam sync command do?
- How can I remove a module from a sandbox?
- What are maintenance branches and releases?
- Is it possible to make a pyam package release with specific combination of module releases?
- Is there a way to compare the difference in package definitions between a pair of YAM.module files?
- How do I modify the list of modules that define a yam package?
- Is there a command to find out which svn repository an existing module is located?
Don't forget to log into the Q&A site in order to see the answers!
Below is the list of subcommands:
$ pyam help setup set up a sandbox checkout check out a link module as a work module and build it. See Q&A https://dartslab.jpl.nasa.gov/qa/1534/ for examples. rebuild (re)build a work module save release a module history print the history of a module latest print the latest module version latest-package print the latest package version obsolete-builds print the module names whose builds are obsolete dependencies print the build dependencies of the module dependents print the build dependents of the module util miscellaneous helper options diff print the diff, showing the committed changes since a branch was made config do various operations on sandbox configuration ('YAM.config') files. NOTE: In version 1.21.4, the behavior of this function changed; it no longer changes the current sandbox. Its primary purpose now to to create or modify YAM.config files for later use. To checkout a module, use 'pyam checkout'. To convert a module from a work to a link module, use 'pyam scrap'. scrap convert a work module to a link module and apply timestamp to the old work module in the sandbox. Remove the module from the sandbox if requested. See Q&A https://dartslab.jpl.nasa.gov/qa/1534/ for examples. relink recreate symbolic links for the modules sync sync up a work module to the latest revision. See Q&A https://dartslab.jpl.nasa.gov/qa/1536/ for examples. test test access to the repository and database register-new-module register a new module with the repository and database register-new-package register a new package with the repository and database. NOTE: you MUST specify the package_name BEFORE any --modules unregister-module unregister a module from the database (for everyone) unregister-package unregister a package from the database (for everyone) save-package save a package. For example, to save the package with the latest versions of its modules, do 'pyam save- package MyPkg'. To save a specific configuration of packages, create a YAM.config file and do 'pyam save- package MyPkg --config <custom YAM.config file>'. This command no longer requires being run in a sandbox. initialize initialize pyam system status print status of sandbox state dbutil carry out database surgery help show sub-commands
To create a new module, use the register-new-module
command. Once this is
done, users can then check that module out:
$ pyam register-new-module MyModule
Please see Q&A entry https://dartslab.jpl.nasa.gov/qa/1544 for more details, including how to select the subversion repository for the new module.
To create a new package, use the register-new-package
command:
$ pyam register-new-package FooBarPkg
You can then edit <sandbox>/common/YAM.modules
to modify what modules
belong to that package.
A shortcut is to specify the package's modules in the same command using the
--modules
option:
$ pyam register-new-package FooBarPkg --modules MyModule BingBang
By default the SiteDefs
module will always be included automatically. This
is necessary since SiteDefs
contains the build-system files.
pyam's subcommands and options can be tab-completed in bash. This is supported
in pyam, if argcomplete is installed. Activate this by putting the following
in ~/.bashrc
:
eval "$(register-python-argcomplete pyam)"
A pre-save hook can be enabled for each module. This is done by placing an executable in the path:
<module>/.pyam/hooks/pre-save
The hook will be executed with the module path as the working directory. An exit status of anything but 0 will be treated as failure.
- pyam provides user-friendly commands (like
checkout
). This makes it possible to use pyam without having to manually edit theYAM.config
. - pyam does not rely on client's local time. Everything is based on the MySQL server's time.
- pyam calls both
all
andalltgt-all
Make rules so that builds work on non-NFS drives. - By default pyam's output is succinct to avoid confusing the user with irrelevant information.
- Diffs can be shown in the email release notifications.
- Long output text are automatically piped into a pager (
less
). - The
diff
command colorizes the output if called interactively in the shell.
If module A depends on module B, then if A's header files get modified, B would
probably need to be rebuilt. To automate the detection of this situation and
build modules as necessary, use the pyam-build
script:
$ pyam-build
It will detect which modules have obsolete builds and make build releases of them.
This can be run in the background on some server. In this mode, it is useful to run it in a loop like:
$ pyam-block-until-release 'pyam-build'
If you have modules that all modules depend on, but are not detected by pyam, you can specify them explicitly:
$ pyam-block-until-release 'pyam-build --dependencies MyModuleA MyModuleB'
This is useful for cases things like build-related scripts. Scripts are not header files, so they are not automatically detected as dependencies.
This section covers information for the administrator who will be installing and configuring pyam.
Before configuring pyam, you should start a MySQL server and an Subversion repository. The location of these items will be used in configuring pyam.
pyam expects a database to exist on the MySQL server. Once you have a MySQL
server up, all you need to do is create an empty database. The
database_name
, username
, and password
you use will be referenced in
the pyam configuration. For example, a MySQL command you could use to create a
database named database_name
with username
granted access is:
mysql> create database database_name; mysql> create user 'username'@'%' identified by 'password'; mysql> grant all privileges on database_name.* to 'username'@'%' identified by 'password'; mysql> grant all privileges on database_name.* to 'username'@'hostname' identified by 'password'; mysql> flush privileges;
Note that the second grant
is for running pyam
from the host itself.
pyam works with any Subversion protocol (svn://
, https://
,
file://
). The repository URL will be referenced in the pyam configuration
file. The simplest one, which doesn't require a server, is the file://
protocol. For example, you could simply create a repository with:
$ svnadmin create my_repository_path $ svn ls file://my_repository_path
The top-level command-line options for pyam can be put in a configuration file.
For example, in the lines below, we set the defaults for command-line
options including --database-connection
and --default-repository-url
:
[pyam] database-connection=username:[email protected]:3306/database_name default-repository-url=file://my_repository_path release-directory=/home/blah/release_directory email-server=smtp.fakeurl.gov:25 [email protected]
The above contents can be put in a file. That file will be found by pyam via
the environment variables YAM_PROJECT_CONFIG_DIR
, and YAM_PROJECT
. This
behavior is inherited from old yam for backward compatibility. pyam will look
for the configuration file at:
$YAM_PROJECT_CONFIG_DIR/$YAM_PROJECT.pyamrc
For example, if YAM_PROJECT_CONFIG_DIR
is defined as /home/blah
and
YAM_PROJECT
is defined as my_project
, pyam will look for:
/home/blah/my_project.pyamrc
To discover all the options or to see if your configuration is properly read in, run:
$ pyam --help
which should print your configuration in the (default: *)
lines.
Subversion repositories prior to version 1.5 do not have support for merge
tracking. pyam normally makes use of Subversion's merge tracking to provide
better output for svn log
and to do merging more cleanly. But pyam can also
run in fallback mode, where it does not rely on merge tracking. To enable this,
specify your repository version in your pyam configuration file. Any version
prior to 1.5 will trigger the fallback behavior. For example:
repository-version=1.2.1
Note that, since older Subversion repositories don't support merge tracking,
when you do a pyam sync
, Subversion won't carry over your old commit
messages. So you will have to enter your commit messages again (after the sync)
for them to show up in the ChangeLog
file. (On repositories with merge
tracking, Subversion will automatically carry over the commit messages from the
old branch into the new merged branch.)
Once configured, we can test whether pyam can access the database and the default repository by running:
$ pyam test
If successful, pyam will print out something like this:
Database access: succeeded Default repository access: succeeded
Some configuration must be done via environment variables. This is because
the Make build system needs access to the variables (when called manually
by the user as in make all
). These variables are:
YAM_SITE - A name given to the site (e.g., "telerobotics") YAM_NATIVE - A string that represents the operating system name (e.g., "x86_64-fedora15-linux") YAM_TARGET - Typically same as YAM_NATIVE (unless cross compiling)
After configuration is complete, and the repository and database servers are running, we can initialize them for pyam. This need only be done once to set up the database tables and repository directories. This is something the administrator would typically do rather than everyday users. To do the initialization, call:
$ pyam initialize
Calling it twice will have no effect the second time. Once this is done, users can start using pyam.
The build system is contained in the SiteDefs module. Create a sandbox and check out SiteDefs to customize the build system:
$ pyam setup --directory my_sandbox --modules SiteDefs $ cd my_sandbox $ pyam checkout SiteDefs
The makefiles need to be customized to support building using GCC. These files are where you can specify third-party library paths, build flags, and other things. This is done by setting the variables in several makefiles:
SiteDefs/mkHome/auto/site.env SiteDefs/mkHome/auto/<site>-site-supported.mk SiteDefs/sites/shared.mk SiteDefs/sites/<site>/site-config-<target_os> SiteDefs/sites/<site>/site.local SiteDefs/targets/<target_os>.mk
After customization is done and files are committed, save the SiteDefs module:
$ pyam save SiteDefs
This will make the changes available to all users.
This section covers information for the developers of the pyam tool itself.
Classes were designed with testability in mind. Most classes don't talk to concrete classes directly, but instead they talk to interfaces. Concrete objects are passed in their constructors. We don't use inheritance unless we plan to implement an abstract method. We never use static methods. We use (non-member) functions instead.
Most of the interfaces talk to external entities like databases and file systems. These interfaces are listed below.
- BuildSystem
- ConfigurationReader
- ConfigurationWriter
- DatabaseReader
- DatabaseWriter
- FileSystem
- RevisionControlSystem
The concrete implementations are listed below.
- MakeBuildSystem
- ConcreteConfigurationReader
- ConcreteConfigurationWriter
- SQLDatabaseReader
- SQLDatabaseWriter
- LocalFileSystem
- SVNRevisionControlSystem
None of these concrete classes talk directly to each other. Other concrete classes are passed in via their constructors. This avoids any dependency between concrete classes and thus modularity.
Building on top of the interfaces are the yam module and yam package classes. These are listed below.
- Module
- BranchedWorkModule
- MainWorkModule
- TaggedWorkModule
- WorkModule
- Sandbox
- LooseSandbox
- MainPackageSandbox
- PackageSandbox
- TaggedPackageSandbox
In their constructors they take some subset of the previously mentioned interfaces. None of these module and package classes talk directly to the file system, database, or repository. They talk only to the interfaces. This loose coupling makes them easy to unit test. We can verify their behavior without having to look at files or query databases.
The naming convention of the code follows PEP 8.
The automated test suite is contained in the test
directory.
Each module has its own set of unit tests. Each class/function is tested in isolation from all/most other classes/functions.
We also have system tests for pyam. These tests are done against a temporary
MySQL server and SVN repository. Before each system test, we launch a script
that creates and launches the temporary server and repository. Each pyam
subcommand (setup
, rebuild
, latest
, etc.) is tested in these system
tests.
There are also tests that do static analysis. We run the usual pep8, pyflakes, and pylint. In addition to these, we run custom static analysis to enforce higher-level design decisions.
Exceptions that are meant to bubble back up to the user calling pyam
are
derived from YamException. Exceptions that should never bubble up and should
always be handled at the lower levels are prefixed with the word Internal
and are not derived from YamException.
YAM_PROJECT_CONFIG_DIR
environment variable.- Naming of revision tags with things like
R1-00a
instead of just using a number. - Using of non-standard Subversion repository structure.
(
featureBranches
,releases
,trunk
) should really be (branches
,tags
,trunk
).
Below are various troubleshooting hints.
This can happen if you modify your global Subversion configuration to use a
non-standard diff-cmd
. Don't do this as it even affects programs other
than the svn
command-line utility that merely use Subversion bindings.
See https://dev.mysql.com/doc/refman/4.1/en/old-client.html.
If you are getting this during a save
or diff
, you probably added a
file to the repository so large that Python/pysvn is running out of memory or
overflowing some buffer. We've seen this once on a low memory machine when
someone added a multiple gigabyte file to the repository and tried to save that
module.
You are probably running in some non-standard terminal (like emacs). Set the
TERM
environment variable to dumb
to disable the spinning activity in
the --->
progress messages.
The lock file indicates that there is some other pyam process running and
modifying the sandbox. If not, then probably a previous pyam got killed with
kill -9
. In the latter case, you'll need to manually remove the lock file.
Commit your files with svn commit
. If you get the following, then it means
that you need to commit the Subversion metadata:
$ svn status M .
You commit it by running svn commit .
.
$ pyam latest | sort -k5
For things like --require-bug-id
, to enable it in the configuration file,
add the following:
[pyam] require-bug-id=x
To disable, just remove the line.
To invoke flags like --no-release-notes
in the configuration file:
[pyam] release-notes=
Set the text editor to be the POSIX false
program:
[pyam] text-editor=false
Please report bugs (in pyam or in this document) to Abhi Jain <[email protected]>.