Developer's Guide ================= The purpose of this page is to provide guidelines for contributors to the TuLiP project. Also consult the `Developers' Wiki `_. .. _sec:code-style-guidelines: Organization and Rules ---------------------- We begin with important organizational notes and **rules** that should be followed: - `PEP 20 `_. - `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 like ``generate_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 `_ by ``C-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 of ``tulip`` 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 in ``genbib.py``. References in the Sphinx-built documentation are achieved by including a link, e.g., inline like :: `[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) 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. 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 argument lists or other small things, but previous functionality still in place. If you go from 1.3.0 to 2.0.0, then we can make extensive backward-incompatible changes. None of these version numbers go in individual files, but the version number is a label for the entire package. 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 -m build`` #. 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. Contributions ------------- Pull requests are expected to satisfy the following criteria: 1. add tests for new code 2. ensure all tests are passing 3. document any additions 4. adhere to PEP8 style conventions 5. use informative commit messages 6. tidy commit history 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 :ref:`sec:code-style-guidelines`, this section can be entirely ignored. - Keep function length to a minimum. As mentioned `in this talk `_, `MSL `_ included the rule that no function should be longer than 75 lines of code. For example, the code block inside an iteration statement can be more readable if written as a separate function. This helps focus at each level of logic. In addition, less nesting of statements results in shorter lines. Shorter functions fit on a single screen, making reading them easier. When debugging after months, the shorter the function, the faster it is loaded into working memory. - One way to make larger predicates of `if` statements more readable is to spread them over multiple lines, for example:: is_positive_int = ( x.is_integer() and x > 0) if is_positive_int: ... - Choose identifiers to minimize comments. Comments become verbose if they duplicate what the code does, and can confuse if they are not in sync with the code they describe. - Add type hints to functions and use static type checkers, for example `Mypy `_. .. advice for emacs users ? - Modules should not become too large. This is not necessarily about the number of lines in a module, but rather its scope. - Commit changes before you go to sleep. You can always `rebase `_ later, until you are happy with ``git`` history. This ensures that when you commit, you remember the context needed for writing the commit messages and organizing the changes into commits. - Prefix commits to classify the changes. The `NumPy development workflow `_ contains a summary of common abbreviations. Suggested abbreviations: - API: backward incompatible change to the interface - _API: backward incompatible change to the internal interface (the prefix is based on `PEP 8 conventions `_). In Python an element is internal if its identifier starts with an underscore. - 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 - CLI: command-line interface - 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`` - GUI: graphical user-interface - 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 ``MNT``). For example, what distinguishes ``REF`` from ``MNT``? ``REF`` should be a refactoring that produces code that is equivalent, with the equivalence being clearly evident. Further reading, of general interest: - "`On commit messages `_" by Peter Hutterer (28 Dec 2009). - `Google Python Style Guide `_ - Sections 2, 4, 6, 8 of the `Linux kernel coding style guide `_ - `The Power of 10: Rules for Developing Safety-Critical Code `_ - Chapter 1: "Style", `The Practice of Programming `_ - `NumPy development documentation `_