10. Developer’s Guide¶
The purpose of this page is to provide guidelines for contributors to the TuLiP project. Also consult the Developers’ Wiki and the tulip-control-discuss mailing list (members only).
10.1. Organization and Rules¶
We begin with important organizational notes and rules that should be followed:
PEP 8. Especially, you should
use 4-space indentation;
keep lines as short as possible, and at most 79 characters;
name classes like
OurExample
, and methods likegenerate_names
; notice that the former name is an object, whereas the latter is a command.
Avoid trailing whitespace.
PEP 257. In summary, you should write a docstring for any public object (e.g., classes, methods, modules), and it should begin with a single-line summary. This summary should be an imperative statement (e.g., “Compute the volume of given polytope.”). Any additional documentation beyond the single-line summary can be included after leaving a blank line.
Be careful what you export, i.e., make sure that what is obtained when someone uses “from tulip.foo import *” is what you intend. Otherwise, hide names using the “_” prefix.
API documentation is built using Epydoc. Accordingly, docstrings should be marked up with Epytext.
The User’s and Developer’s Guides are built using Sphinx. It uses a small extension of reStructuredText. Consult the reST quick reference.
Besides the previous two sources, documentation can appear in plaintext files, notably in README files. These should have line widths of at most 80 characters. E.g., this can be achieved at the command-line using
fold -s -w 80
or in Emacs byC-u 80 M-x set-fill-column
.When committing to the repository, you should write a summary line, at most 60 characters in length, and if elaboration is necessary, then first skip a line (i.e., leave one blank) before beginning with details.
A copyright notice and pointer to the
LICENSE
file oftulip
shall be placed as comments at the top of each source file (unless no copyright applies).When referring to publications, check for a corresponding entry in
doc/bib.txt
and create one if needed. The syntax is described ingenbib.py
. References in the Sphinx-built documentation are achieved by including a link, e.g., inline like`[WTOXM11] <bibliography.html#wtoxm11>`_
which renders as [WTOXM11]. References in docstrings (in the code) should be to the URL of the corresponding entry on the TuLiP website, using Markdown syntax, e.g.,
[WTOXM11]( https://tulip-control.sourceforge.io/doc/bibliography.html#wtoxm11)
10.2. Testing¶
A script for running tests is run_test.py
in the root of the source
tree. Without the -f
or --testfiles
switch, run_tests.py
expects the
user to request a family of tests to perform. The default is “base”, which
corresponds to tests that should pass when the required dependencies of TuLiP
are satisfied. The other extreme is “full”, which performs all tests. In
between, other families are defined, e.g., “hybrid”, which involves all “base”
tests and any tests that should pass given a successful pip install tulip[hybrid]
,
namely, when the optional packages cvxopt
and polytope
are present.
Provided the -f
or --testfiles
switch, it searches under the directory
tests/
for files with names ending in “_test.py”, and passes these to
pytest. Use the flag “-h” to
see driver script options. Extra details about options:
The flag “–cover” to generate a coverage report, which will likely be placed under
tests/cover/
. It uses Ned Batchelder’s coverage module.The flag “–outofsource” will cause
tulip
to be imported from outside the current directory. This is useful for testing against the installed form of TuLiP.
10.3. Version naming¶
For version numbers, the style is N.m.x where
N = major version; everything that worked before could break
m = revision; most functions should work, but might need (minor) modifications
x = minor revision; code that ran should continue running
So, if you go from version 1.2.2 to 1.2.3, then no interfaces should change, code should continue to run, etc. If you go from 1.2.3 to 1.3.0, then there might be changes in some arguments lists or other small things, but previous functionality still in place (somewhow). If you go from 1.3.0 to 2.0.0, then we can make whatever changes we want.
None of these version numbers go in individual files, but the version number is a label for the entire package.
10.4. Making releases¶
Collect list of major changes.
Update the changelog.
Tag with message of the form “REL: version 1.2.0”.
Create source release,
python setup.py sdist
.Post it to PyPI and SourceForge.net.
Build and post User’s Guide and API manual. Under the directory doc/, run
./rsync-web.sh USERNAME ./rsync-docs.sh USERNAME
where
USERNAME
is your SourceForge.net handle.Make announcement on tulip-control-announce mailing list, providing major website links and the summary of changes.
Bump version in the repository, in preparation for next release.
10.5. Contributions¶
Pull requests are expected to satisfy the following criteria:
add tests for new code
ensure all tests are passing
document any additions
adhere to PEP8 style conventions
use informative commit messages
tidy commit history
10.6. Advice¶
The following are software engineering best practices that you should try to follow. We mention them here for convenience of reference and to aid new committers. Unlike Organization and Rules, this section can be entirely ignored.
- Keep function length to a minimum.
As mentioned at this talk, MSL included the rule that no function should be longer than 75 lines of code. The Linux coding style guide is succinct
- “The answer to that is that if you need more than 3 levels of indentation,
you’re screwed anyway, and should fix your program.”
For example, within any iteration, usually the iterated code block deserves its own function (or method). This changes context, helping to focus at each level individually. Things can also be named better, reusing names within the iteration w/o conflicts. Incidentally it also saves from long lines. Besides these, short functions are viewable w/o vertical scrolling. When debugging after months, the shorter the function, the faster it is loaded to working memory.
- Avoid complicated conditions for if statements and other expressions.
Break them down into simpler ones. When possible write them in sequence (not nested), so that they are checked in an obvious order. This way a function returns when a condition is False, so the conjunction is implicit and easier to follow, one check at a time.
- Name things to minimize comments.
Comments are useless if they attempt to explain what the code evidently does and can be harmful if they fail to do so and instead describe what it was intended to do, giving a false impression of correctness.
Modules shouldn’t become God objects. Keep them short (at most a few thousand lines) and well-organized.
Commit changes before you go to sleep. You can always rebase later multiple times, until you are happy with the history. This ensures that history won’t have been forgotten by the time you return to that workspace.
Prefix commits to classify the changes. The NumPy development workflow contains a summary of common abbreviations. Suggested abbreviations:
API: backward incompatible change
BIB: biliography (for BibTeX files)
BIN: for generated files (usually those are binaries)
BLD: related to building
BUG: error correction
CI: related to continuous integration tests (usually
.travis.yml
)CHG: change the code
DEP: deprecate something, or remove a deprecated object
DEV: development utility
DOC: documentation (docstrings too)
DRAFT: to be rewritten / fixed up (to be rebased, never in master)
ENH: enhancement
EXP: experimental (to be rebased, never in master)
GIT: related to
git
configuration, for example changes to the files.gitignore
and.gitattributes
IMG: changes to sources of images (for example, SVG files)
MAI: maintenance
MNT: same as “MAI”
MV: move file(s)
PEP8: style convention
PEP*: change related to PEP*
REF: refactoring
REL: release-related
REV: revert an earlier commit
STY: style correction
TST: testing
TYP: type hints
UI: user interface, e.g., command-line options, printing messages, logging, and similar changes
WEB: changes to website; mostly relevant to branch gh-pages
Deciding which prefix from the above to use is not always straightforward, but doing so is a good exercise. Choose the more severe prefix applicable (usually API instead of MAI). For example, what distinguishes REF from MAI? REF should be a refactoring that produces code that is (for most practical purposes) equivalent, with the equivalence being clearly evident.
Further reading, of general interest:
“On commit messages” by Peter Hutterer (28 Dec 2009).
Sections 1, 2, 4, 6, 8 of the Linux kernel coding style guide
Chapter 1: “Style”, The Practice of Programming