Software Development Standards
We have documented the programming standards used in this project in the following segments:
VantTec Python Standard
The VantTec Python standard is based on the “PEP 8 – Style Guide for Python Code” and the “The Hitchhiker”s guide to Python”
Also, look at this script for references on how to write good Python code: https://github.com/hblanks/zen-of-python-by-example/blob/master/pep20_by_example.py.
For recommendations and modifications, please refer to Guillermo Cepeda or Sebastian Martinez.
Programming recommendations
Use ‘is not’ operator rather than ‘not … is’. While both expressions are functionally identical, the former is more readable and preferred:
# Correct:
if foo is not None:
# Wrong:
if not foo is None:
Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier:
# Correct:
def f(x): return 2*x
# Wrong:
f = lambda x: 2*x
When catching exceptions, mention specific exceptions whenever possible instead of using a bare except: clause:
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
Correct:
def foo(x):
if x >= 0:
return math.sqrt(x)
else:
return None
def bar(x):
if x < 0:
return None
return math.sqrt(x)
Code Layout
Identation
Do not use tab, use four spaces instead per indentation level.
As you can see:
# Correct:
# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
var_three, var_four)
# Add 4 spaces (an extra level of indentation) to distinguish arguments from the rest.
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
# Hanging indents should add a level.
foo = long_function_name(
var_one, var_two,
var_three, var_four)
# Wrong:
# Arguments on first line forbidden when not using vertical alignment.
foo = long_function_name(var_one, var_two,
var_three, var_four)
# Further indentation required as indentation is not distinguishable.
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
The closing brace/bracket/parenthesis on multiline constructs should be like this:
my_list = [1, 2, 3,
4, 5, 6]
result = some_function_that_takes_arguments('a', 'b', 'c',
'd', 'e', 'f')
Maximum Line Length
Limit all lines to a maximum of 80 characters.
If the length of a line is larger than 80 characters, try to use a “space + backslash”. With this, the editor will detect it is a line continuation marker
- Example::
- with open(‘/path/to/some/file/you/want/to/read’) as file_1,
open(‘/path/to/some/file/being/written’, ‘w’) as file_2:
file_2.write(file_1.read())
Pro Tip
If you use Visual Studio Code as your code editor, you can add a vertical line into your screen, as an 80 characters visual reference.
- Just go to File >> Preferences >> Settings >> search for Editor:Rulers and in the json file just paste this::
“editor.rulers”: [120]
Line Break Before Binary Operations
Using line breaks before binary operations helps readability:
# easy to match operators with operands
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
Blank Line
Surround top-level function and class definitions with two blank lines. (IMPORTANT!)
Method definitions inside a class are surrounded by a single blank line.
Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).
Use blank lines in functions, to indicate logical sections.
Source File Encoding and Interpreter
At the beginning of every script you should add these lines:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
First line:
The program loader takes the presence of “#!” as an indication that the file is a script, and tries to execute that script using the interpreter specified by the rest of the line.
Second line:
Imports
Imports should usually be on separate lines:
# Correct:
import os
import sys
This is also okay:
from subprocess import Popen, PIPE
Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.
Never use: from <library> import *
- Imports should be grouped in the following order
Standard imports
Related third party imports
Local application / library specific imports
When importing a class from a class
Like this:
from Class import MyClass
String Quotes
In Python, double-quoted strings and single-quoted strings are the same, however, double quotes will only be used when dealing with paths and topics (ROS).
Whitespace in Expressions and Statements
Avoid extraneous whitespace in the following situations:
Immediately inside parentheses, brackets, and braces:
yes: spam(ham[1], {eggs: 2}) no: spam( ham[1], {eggs: 2} )
Between a trailing comma anda a following close parenthesis:
yes: foo = (0,) no: foo = (0, )
Immediately before a comma, semicolon, or colon
yes: if x == 4: print x, y; x, y = y, x no: if x == 4: print x, y ; x, y = y, x
Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc.), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans (and, or, not):
# Correct:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
# Wrong:
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
Don’t use spaces around the = sign when used to indicate a keyword argument, or when used to indicate a default value for an unannotated function parameter:
# Correct:
def complex(real, imag=0.0):
return magic(r=real, i=imag)
# Wrong:
def complex(real, imag = 0.0):
return magic(r = real, i = imag)
Naming Conventions
As a general rule, use short and descriptive names!
Classes
With CapWords:
class MyClass
Objects
With camelCase:
autoNav = AutoNav()
Global Variables
Let’s try to avoid them
Functions and Variable Names
- For functions and variables: with lowercase_and_underscore
Variable names follow the same convention as function names. Never use names such as I (i), l (L), O or o, as some can’t be differentiated from one another
Use one leading underscore only for non-public methods and instance variables of a class.
camelCase is allowed only in contexts where that’s already the prevailing style (e.g. threading.py), to retain backwards compatibility.
Function and Method Arguments
Always use self for the first argument to instance methods.
Always use cls for the first argument to class methods.
- Use one leading underscore only for non-public methods and instance variables.
More info about this here: https://realpython.com/instance-class-and-static-methods-demystified/
When writing class attributes or composition, do it like this: myClass.myObject_, myClass.my_attribute_
Method Names and Instance Variables
Use lowercase_and_underscores
Use one leading underscore only for non-public methods and instance variables.
To avoid name clashes with subclasses, use two leading underscores to invoke Python’s name mangling rules. Python mangles these names with the class name: if class Foo has an attribute named __a, it cannot be accessed by Foo.__a. (An insistent user could still gain access by calling Foo._Foo__a.) Generally, double leading underscores should be used only to avoid name conflicts with attributes in classes designed to be subclassed.
Constants
Use CAPITAL_LETTERS_AND_UNDERSCORES
Comments
Comments at the beginning of files
“”” @file : file.py @date: Thu Dec 26, 2019 @date_modif: Thu Dec 26, 2019 @author: name @e-mail: @author: (If multiple co-authors, write the name and e-mail of each one) @e-mail: @brief: @version: “””
Class Comments
Comment before class only if it’s not descriptive
Functions Comments
“”” @name: @brief: @param a[in]: describe
b[out]: describe
@return “””
More Tips
One statement per line
It is bad practice to have two disjointed statements on the same line of code. Wrong:
print 'one'; print 'two'
if x == 1: print 'one'
if <complex comparasion > and <other complex comparasion>:
# do something
Correct:
print 'one'
print 'two'
if x == 1:
print 'one'
cond1 = <complex comparasion>
cond2 = <other complex comparasion>
if cond1 and cond2:
# do something
Use sets or dictionaries instead of lists in cases where:
The collection will contain a large number of items
You will be repeatedly searching for items in the collection
You do not have duplicate items.
For small collections, or collections which you will not frequently be searching through, the additional time and memory required to set up the hashtable will often be greater than the time saved by the improved search speed.
Access a Dictionary Element
Dont use the dict.has_key() method. Instead, use x in d syntax, or pass a default argument to dict.get().
- Bad::
d = {‘hello’:’world’} if d.has_key(‘hello’):
print d[‘hello’]
- else:
print ‘default value’
Good:
d = {'hello':'world'}
print d.get('hello', 'default value') # prints 'world'
print d.get('foo', 'default value') # prints 'default value'
#or
if 'hello' in d:
print d['hello']
else:
print 'default value'
VantTec C++ Standard
To create this code standard, we took in consideration the ‘Google C++ style guide <https://google.github.io/styleguide/cppguide.html>’. For recommendations and modifications, please refer to Guillermo Cepeda or Sebastian Martinez.
The #define Guard
All header files should have #define guards to prevent multiple inclusion. Always use the next format: <PROJECT>_<PATH>_<FILE>_H_.
For Example, the file foo/src/bar/baz.h in project foo should have the following guard:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
Names and Order of Includes
Include headers in the following order:
C System headers (std)
C++ Standard Library headers
Other libraries headers (third-party)
Your project’s headers.
Separate each non-empty group with one blank line and sort them in alphabetical order.
Namespaces
Avoid the use of namespaces
Variables
Use of local Variables
Always initialize variables before using them.
If variables are used on loops, then variables can be initialized on loops statements.
for(char *p = foo(); …)
Otherwise on nested loops variables must be declare before the loop
int temp_1=0; int temp_2 =0;
- while (temp_1 < range ){
- while(temp_2 < range2){
temp_2++; }
temp_1++;
}
Initialize objects as variables, always before and close to is use.
Naming
In general, use short and descriptive names
Variable names should be short and descriptive
Example:
int speed_challenge_state = ..
usv_perception.cpp
Avoid the use of abbreviations and incomplete words
Example:
# Correct:
int speed_challenge_counter= ..
# Wrong:
int speedch_Cnt = ...
File Naming
Use lowercase_and_underscores:
sliding_mode_controller.cpp
Typedef naming
Use CapWords:
typedef hash_map<referenceFrames*, std::string> ReferenceFrame;
Class and Struct Naming
Use CapWords:
class SpeedChallenge {};
Function naming
Use camelCase:
void decodificarXbee();
Variable Naming
Use lowercase_and_underscores:
int bouy_red
For attributes of a class, end with an underscore:
int bouy_
Constant Naming
Use ALL_CAPITALS:
const int STATES_NUMBER= 9;
Macros
Avoid the use macros !
Use instead:
constants
inline functions
enum
Other conveniences and notes
Number of characters per line : 80
Suggestions
If you use Visual Studio Code as your code editor, you can add a vertical line into your screen, so you can see where your line should end. Just go to File >> Preferences >> Settings >> search for Editor:Rulers and in the json file just paste this:
Now you have a nice vertical line
Class vs Structs:
Use a struct only for passive objects that carry data; everything else is a class.
VantTec ROS Standard
The information and standard that VantTec uses is gatered from the official ROS wiki. https://wiki.ros.org/ROS/Patterns/Conventions#Naming_ROS_Resources
Standard Units of Measure and Coordinate Systems
Standard units and coordinate conventions for use in ROS have been formalized in: http://www.ros.org/reps/rep-0103.html
Packages
The ROS packages occupy a flat namespace, so naming should be done carefully and consistently. There is a standard for package naming in REP-144
Package names should follow common C variable naming conventions: lower case, start with a letter, use underscore separators, e.g. laser_viewer
Package names should be specific enough to identify what the package does. For example, a motion planner is not called planner. If it implements the wavefront propagation algorithm, it might be called wavefront_planner. There’s obviously tension between making a name specific and keeping it from becoming overly verbose.
A dedicated should be created for custom messages, services and actions.
Topics / services
Topic and service names live in a hierarchical namespace, and client libraries provide mechanisms for remapping them at runtime, so there is more flexibility than with packages. However, it’s best to minimize the need for namespacing and name remapping.
Topic and service names should follow common C variable naming conventions: lower case, with underscore separators, e.g. laser_scan
Topic and service names should be reasonably descriptive. If a planner node publishes a message containing its current state, the associated topic should be called planner_state, not just state.
Messages
Message files are used to determine the class name of the autogenerated code. As such, they must be CamelCased. e.g. LaserScan.msg
NOTE: This is an exception to the convention that all filenames are lower case and underscore separated. Using CamelCase message names will prevent issues from arising due to inconsistent support for filename case sensitivity across various operating systems.
Message fields should be lowercase with underscore separation. e.g. range_min
Nodes
Nodes have both a type and name. The type is the name of the executable to launch the node. The name is what is passed to other ROS nodes when it starts up. We separate these two concepts because names must be unique, whereas you may have multiple nodes of the same type.
When possible, the default name of a node should follow from the name of the executable used to launch the node. This default name can be remapped at startup to something unique
Node type names:
- In general, we encourage the node type names to be short because they are scoped by the package name. For example, if your laser_scan package has a viewer for laser scans, simply call it view (instead of laser_scan_viewer). Thus, when you run it with rosrun, you would type::
rosrun laser_scan view
TF frame_ids
See https://wiki.ros.org/geometry/CoordinateFrameConventions#Naming
Comments
Comments at the beginning of files:
Class Comments
Comment before class only if it not descriptive
Functions Comments: