Skip to content

Developer's Guide

The BLISS project is hosted on the ESRF Gitlab.

Cloning BLISS

To clone bliss:

git clone https://gitlab.esrf.fr/bliss/bliss.git

The first thing to do after cloning bliss is to set up the pre-commit hook, as explained in the code formatting section. This way, black will run before any commit, ensuring a consistent code style in the project.

Bliss has some dependencies on third-party software. The complete list of dependencies can be obtained from the setup.py script:

python setup.py egg_info

(see bliss.egg_info/requirements.txt).

Your work environment is a matter of taste. If you want to isolate your bliss development it is a good idea to use virtualenv or Conda.

Workflow

Bliss project promotes a development based on Feature Branch Workflow:

The core idea behind the Feature Branch Workflow is that all feature development should take place in a dedicated branch instead of the master branch. This encapsulation makes it easy for multiple developers to work on a particular feature without disturbing the main codebase. It also means the master branch will never contain broken code, which is a huge advantage for continuous integration environments.

Encapsulating feature development also makes it possible to leverage pull requests, which are a way to initiate discussions around a branch. They give other developers the opportunity to sign off on a feature before it gets integrated into the official project. Or, if you get stuck in the middle of a feature, you can open a pull request asking for suggestions from your colleagues. The point is, pull requests make it incredibly easy for your team to comment on each other’s work.

Use case story 1: Adding a new controller

John has been asked by beamline ID00 to integrate a new pressure meter from Alibaba Inc, in their experiments.

  • First, he checks the bliss.controllers repository to see if the device is already implemented
  • If not, he creates a new issue on gitlab. He assigns it to himself and adds labels new feature and plugin. He is very happy to receive a comment by his colleague Maria that happened to receive a similar request from ID99. They quickly agree on a shared development
  • They both agree to work on a new branch called alibaba_pressure_meter. Since John is going on vacation, it is up to poor Maria to start developing
  • She clones the Bliss repository and creates a new branch called alibaba_pressure_meter:
    git checkout -b alibaba_pressure_meter
    
  • She is a fan of TDD, so she starts thinking how she would like to control the device and then she starts writing simple unit tests. They will all fail in the beginning but that doesn't scare her at all because now she knows exactly what to do to make them work
  • After some development, Maria is happy with the result so she pushes her work to gitlab. She can immediately see on the Bliss ESRF Gitlab project page a new log entry with the work she just pushed. Gitlab even offers to create a merge request so she just clicks on it, fills in the missing data in the form and assigns her colleague Marco to integrate her merge request. Maria is quite confident because she knows that an extra pair of eyes will help catch any issue with her proposition
  • Marco makes some comments on Maria's code directly on the gitlab merge request web page. Maria realizes that she forgot to document one of the most important methods so she fixes the commit. Marco can now accept the merge request
  • John comes back from vacation and he is suprised to see the code for his device is already available in the master branch. Since his use case is a little different that Maria's, he realizes that he needs to add a couple more functions so he makes a new issue on gitlab and the process repeats again

Contributing

You can contribute to Bliss in many ways: from simple (or hard) bug fixes to writting new controller extensions, introduce new features or writing documentation. No matter how you are contributing, the following principles apply:

  • Try to use the same code style as used in the rest of the project. See the bliss-style-guide below for more information
  • New features should be documented. Include examples and use cases where appropriate
  • Add appropriate unit tests

Usual process

  • Create an issue in gitlab
  • Create a merge request and corresponding branch in gitlab
  • Fetch the new branch in your repo
  • Make your changes, black them
  • Create the scriv “changelog fragment”
  • Make sur your branch is on top of master or rebase it
  • Push the branch

scriv

Scriv (https://scriv.readthedocs.io/) is a changelog fragments aggregator. It takes files in changelog.d/ directory to compose CHANGELOG.md when a new BLISS release is made.

Continuous integration

A new Gitlab CI task to verify if a MR contains changelog fragments have been added, it displays in orange in if no changelog is present. Of course, it is not mandatory to have a changelog ; this is left at the appreciation of the merge request integrator.

So, when submitting a merge request:

  • Create the fragment file with scriv create
    [~/PROJECTS/bliss] 18:45 (3740-scriv-doc[])
    [(bliss) guilloud@epilobe]$ scriv create
    Creating changelog.d/20230405_185150_cyril.guilloud_3740_scriv_doc.md
    
  • edit the fragment file:
    emacs changelog.d/20230405_185150_cyril.guilloud_3740_scriv_doc.md
    
  • commit the fragment in the same merge request.
  • example: Here, “Changed” section has been uncommented to add “Doc entry for scriv usage.” changelog
<!--
A new scriv changelog fragment.

Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Removed

- A bullet item for the Removed category.

-->
<!--
### Added

- A bullet item for the Added category.

-->

### Changed

- Doc entry for scriv usage.


<!--
### Deprecated

- A bullet item for the Deprecated category.

-->
<!--
### Fixed

- A bullet item for the Fixed category.

-->
<!--
### Security

- A bullet item for the Security category.

-->

Bliss Style Guide

The Bliss style guide summarizes the Bliss coding guidelines. When adding code to Bliss (new feature, new extension or simply a patch) make sure you follow these guide lines.

In general the Bliss Style Guide closely follows PEP8 with some small differences and extensions.

Code formatting with black

Code formatting is automatically managed by black.

The project use a specific version of black, which is part of the development requirements: conda install --file requirements-dev.txt.

There is 3 complementary ways to work with black:

 pip3 install black
 [...]
 black .
 All done! ✨ 🍰 ✨
 466 files left unchanged.
  • Let the pre-commit hook format your changes. Make sure it is properly set up by running:
    # requirements-dev.txt should be installed first
    pre-commit install
    

Note

If black changed any of the staged code during the pre-commit phase, the commit will abort. This lets you check the changes black made before re-applying the commit:

git commit demo.py -m "Some message"
black..........................................................Failed
Files were modified by this hook. Additional output:
reformatted doc/demo.py
All done!  🍰 1 file reformatted.
[WARNING] Stashed changes conflicted with hook auto-fixes...
    Rolling back fixes...

git commit demo.py -m "Some message"
black..........................................................Passed
[branch 89b740f2] Some message
1 file changed, 1 insertion(+)

Reformating existing commits in a branch

git rebase -X theirs  $(git merge-base HEAD origin/master) --exec 'python -m black --fast $(git diff --name-only HEAD^ | grep \\.py) && git commit -a --amend --no-edit'

Naming Conventions

  • Module names: lowercase_with_underscores
  • Class names: CamelCase, with acronyms kept uppercase (HTTPWriter and not HttpWriter)
  • Variable names: lowercase_with_underscores
  • Method and function names: lowercase_with_underscores
  • Constants: UPPERCASE_WITH_UNDERSCORES
  • precompiled regular expressions: name_re

Protected members are prefixed with a single underscore. Double underscores are reserved for mixin classes.

On classes with keywords, trailing underscores are appended. Clashes with builtins are allowed and must not be resolved by appending an underline to the variable name. If the function needs to access a shadowed builtin, rebind the builtin to a different name instead.

Function and method arguments:

  • class methods: cls as first parameter
  • instance methods: self as first parameter
  • lambdas for properties might have the first parameter replaced with x like in display_name = property(lambda x: x.real_name or x.username)

Docstrings convention

All docstrings are formatted with reStructuredText as understood by Sphinx. Depending on the number of lines in the docstring, they are laid out differently. If it's just one line, the closing triple quote is on the same line as the opening, otherwise the text is on the same line as the opening quote and the triple quote that closes the string on its own line:

    def foo():
        """This is a simple docstring"""


    def bar():
        """
        This is a longer docstring with so much information in there
        that it spans three lines.  In this case the closing triple quote
        is on its own line.
        """

Bliss supports napoleon sphinx extension. The recommended way to document API is to follow the Google Python Style Guide:

    def move(axis, position, wait=False):
        """
        Move the given axis to the given absolute position

        Note:
            using `wait=True` will block the current :class:`~gevent.Greenlet`.

        See Also:
            :func:`rmove`

        code example: ``register(self, children_list=[self.comm])  # comm layer``

        `This will be printed in italic`

        Args:
            axis (Axis): instance of bliss :class:`bliss.common.axis.Axis`
            position (float): position (axis units)
            wait (bool): wait or not for motion to end [default: False]

        Returns:
            float: actual position where motor is (axis units)

        Raises:
            RuntimeError
        """
        pass

Note

Move takes no final s to follow PEP257 coding convention Raises, Returns take a final s as they are sphinx keywords. (Returns has Return as alias so… this is just a quesiton or convention)

Comments

Rules for comments are similar to docstrings. Both are formatted with reStructuredText. If a comment is used to document an attribute, put a colon after the opening pound sign (#):

class User(object):

    #: the name of the user as unicode string
    name = Column(String)

    #: the sha1 hash of the password + inline salt
    pw_hash = Column(String)

Bliss Module Template

Here is a template that can be used to start writing a new bliss module:

# -*- coding: utf-8 -*-
#
# This file is part of the bliss project
#
# Copyright (c) Beamline Control Unit, ESRF
# Distributed under the GNU LGPLv3. See LICENSE for more info.

"""A brief description goes here.

Long description here with examples if possible
"""

__all__ = [] # list of members to export

# standard module imports

# third-party module imports

# local bliss imports

Releasing

for ESRF, see: http://wikiserv.esrf.fr/bliss/index.php/BLISS_Releasing

Repository

.
├─ bin/
├─ bliss/
├─ doc/
├─ examples/
├─ extensions/
├─ scripts/
├─ spec/
├─ tests/
├─ LICENSE                  LGPLv3 license description
├─ README.md                Documentation entry-point
├─ .gitignore               Files to be ignored by git
├─ .gitlab-ci.yml           Configuration of the continuous integration workflow
├─ .pre-commit-config.yaml  Config to get "black" module for git commit hook
├─ requirements.txt         Modules to Conda-install to run BLISS
├─ requirements-dev.txt     Modules to Conda-install to develop BLISS
├─ requirements-doc.txt     Modules to Conda-install to build documentation
├─ requirements-test.txt    Modules to Conda-install to run tests
├─ setup.cfg                Options to use for test; aliases;
└─ setup.py                 BLISS configuration file