Code Style - armory3d/armory GitHub Wiki

A project like Armory with multiple people involved needs a maintainable codebase. To prevent chaos, there exist some simple rules you should follow to make your code more readable and thus maintable. You will be thankful for others if you can understand their code while working on it.

These guidelines are not completely strict. You should always find solutions that fit to the needs of your code. The important thing is that you should keep everything readable and fast!

The current codebase does not fully comply to these rules yet, but you can help to improve it!

General

  • Comment your code!

    Don't overdo it, but make sure that others can follow your thoughts. If there is some background information, for example why you've chosen a certain approach to a problem or where some values are taken from, write it down.

  • Give variables descriptive (but not too long) names!

    stepsPerSecond is much clearer than just s. If you choose good descriptive variable names, the code mostly "comments itself".

  • Always consider the performance of your code!

    Extra points for you if you write both fast and clear code ;)

  • Don't mix tabs and spaces.

    For Python we use 4 spaces for indentation, for Haxe we use tabs. Please setup your code editor accordingly. Also, make sure that there is no trailing whitespace at the end of lines (code editors can also do that for you).

  • Give your code a meaningful structure.

    Keep things organized and modular, but don't over-generalize where you don't need to.

Python

Armory follows the PEP 8 style guide which is the de facto standard code style for Python. There are linter plug-ins available for most code editors to help you stick to these rules.

Also have a look at The Zen of Python.

  • Use snake_case for variable and function names and UpperCamelCase for class names.

  • Use immutable tuples () instead of mutable lists [] if the data is read-only.

  • Check if a value is None with is or is not, do not use == or !=.

    # Good
    if val is not None:
        do_something()
    
    # Bad
    if val != None:
        do_something()
    
  • Use type hints where it makes sense. Type hints have no effect during runtime but they make code much more readable and enable auto-completion in your IDE of choice.

    # Good
    def foo(name: str, mesh: bpy.types.Mesh) -> int:
        return 42
    
    # If the function has no return value, `-> None` is not required.
    def foo(name: str, mesh: bpy.types.Mesh):
        return
    
    # Bad
    def foo(name, mesh):
        return 3.14
    
  • Use f-strings if they improve readability.

    # Good
    my_string = f'Values: {a}, {a + b}, {foo()}.'
    
    # Bad
    my_string = 'Values: ' + str(a) + ', ' + str(a + b) + ', ' + foo() + '.'
    

    Keep in mind that f-strings don't support escape sequences like \n.

  • Group imports in the following categories (blank lines in between, see PEP 8: Imports) and sort them alphabetically:

    1. Python standard library
    2. Third-party modules (numpy for example) and Blender (bpy etc.)
    3. Armory-related modules (arm).
  • Use two blank lines between module-level functions and classes.

  • Add docstrings to functions if the functions itself are not descriptive enough. Module docstrings are a good idea as well so that other people know what a module is used for.

    """This is a module docstring at the top of the file.
    It explains what functionality the module contains.
    """
    
    def foo():
        """This docstring explains what foo() does.
        If it spans multiple lines, put the closing quotes on a new line.
        """
    
    
    

Haxe

For Haxe the code style is specified in checkstyle.json. The file format is explained in detail here.

  • Use lowerCamelCase for variable and function names.

  • Use the following modifier order:

    macro override public static inline function foo() {}
    
  • Add docstrings to public functions and classes as they become visible in the API Documentation. Docstrings for private functions can make sense as well if the function needs additional description.

    /**
    	This docstring explains what foo() does.
    	You can also use *Markdown* syntax here.
    
    	@param value Description of the `value` parameter 
    	@return Description of the return value
    **/
    public function foo(value: Int): Int {}