---
# System prepended metadata

title: Some python and git standards and best practices
tags: [python, it]

---

# Standars and best practices

[TOC]

## git

### General recommendations

* **Good branch names**
    * **wip**: Works in progress; stuff I know won't be finished soon
    * **feat**: Feature I'm adding or expanding
    * **bug**: Bug fix or experiment
    * **junk**: Throwaway branch created to experiment
    * **new**/frabnotz
    * **new**/foo
    * **new**/bar
    * **test**/foo
    * **test**/frabnotz

```sh 
$ git branch --list "test/*"
test/foo
test/frabnotz

$ git branch --list "*/foo"
new/foo
test/foo
```

![](https://i.imgur.com/OJcexEq.png)


* **Good commits messages**
    * A good commit should contain a set of homogeneus and relevant changes.
    * **Subject**
        * Simple but explanatory
        * Capitalized, without period at the end of line.
        * Max of 50 characters!!
        * Imperative mode
            * Clean your room
            * Close the door
            * Take out the trash
    * **Body**
        * Just if it is necessary
        * Max of 72 characters!!
        * Explain what and why vs. how

> Fix typo in introduction to user guide
> Accelerate to 88 miles per hour
> Open the pod bay doors


> Derezz the master control program
> MCP turned out to be evil and had become intent on world domination.
This commit throws Tron's disc into MCP (causing its deresolution)
and turns it back into a chess game.

[Más info: Pro git](https://git-scm.com/book/en/v2)

## Repo structure

```
name-of-repo
├── .env.example
├── .gitignore  
├── docs
├── dev
│   └── Dockerfile
├── prod
│   └── Dockerfile
├── docker-compose.yml
├── name-of-repo
│   └── __init__.py
│   └── file_1.py
│   └── file_2.py
│   └── utils.py
│   └── module_3
│       └── __init__.py
├── README.md
├── requirements.txt
└── tests
    ├── __init__.py
    └── test_name-of-repo.py
└── config.py
└── main_file.py
```

### README file

Template

``` markdown
# Name

Description

# Table of contents
[](#)

[TOC]

## Features

* feature 1
* feature 2

## Installation

(step by step)

\`\`\`

\`\`\`

## Python version

## Usage
\`\`\`

\`\`\`

## Examples

## Test

## Deployment scenario

## TODO

## License
[MIT](https://choosealicense.com/licenses/mit/)
```

### Plus: budges!

[A collection of budges](https://gist.github.com/tterb/982ae14a9307b80117dbf49f624ce0e8)

[![NPM Version](https://img.shields.io/npm/v/npm.svg?style=flat)]()
`[![NPM Version](https://img.shields.io/npm/v/npm.svg?style=flat)]()`

[![PyPi Python Versions](https://img.shields.io/pypi/pyversions/yt2mp3.svg)](https://pypi.python.org/pypi/yt2mp3/)
`[![PyPi Python Versions](https://img.shields.io/pypi/pyversions/yt2mp3.svg)](https://pypi.python.org/pypi/yt2mp3/)`

### .env

**IT MUST NOT** be added!
Add and example.

Environmental variables text file with key, value pairs:

```
key_1 = value_1
key_2 = value_2
```

They could be acceded by:

```python
import os

value_1 = os.getenv('key_1')
```

### .gitignore

Default python .gitignore

```gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# vscode
.idea/
.vscode/

# C extensions
*.so

# Distribution / packaging
.Python
.env
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/
```

#### Generate a repo from scratch

*with poetry*

```python
pip install poetry
poetry new <name>
```

## Python code

### Great names, code doc and comments

* Function: lowercase words separated by underscores, ex: `my_function`.
    * Choose a self explanatory name with infinitive verbs, ex: 
```python
def multiply_by_two(x):
    return x * 2
```
* Variable: lowercase, single words separated by underscore, ex: `my_variable`
    * Choose good names! Avoid n, x, y, a.
* Class: capitalize words, ex: `MyClass`.
* Method: lowercase words separated by underscores, ex: `class_method`.
* Constant: Uppercase single words separated by underscores, ex: `MY_CONSTANT`.
* Module: Short, lowercase words separated by underscores: `my_module.py`
* Package: Short, lowercase words separated: `mypackage`
* Comments: Capitalize, double space after a line.

#### Docstring

reST style

##### functions

```python
def my_function(param_1, param_2):
    """
    Return / Description
    :param param_1: (type) description
    :param param_2: (type) description
    :return: (type) description
    """
```

###### Example

```python
def def multiply_by_two(x):
    """
    Return the result of multiplying by two the x param
    :param x: (float) number to multiply
    :return: (float) param x multiplied by two
    """
    return x * 2
```

##### classes

```python
class Keeper(Storer):

    """
    Keep data fresher longer.

    Extend `Storer`.  Class attribute `instances` keeps track
    of the number of `Keeper` objects instantiated.
    """

    instances = 0
    """How many `Keeper` objects are there?"""

    def __init__(self):
        """
        Extend `Storer.__init__()` to keep track of
        instances.  Keep count in `self.instances` and data
        in `self.data`.
        """
        Storer.__init__(self)
        self.instances += 1

        self.data = []
        """Store data in a list, most recent last."""

    def storedata(self, data):
        """
        Extend `Storer.storedata()`; append new `data` to a
        list (in `self.data`).
        """
        self.data = data

```

###### example

```python
class MyClass(BaseClass):

    """
    Short class description.

    Extend `BaseClass`. Important notes about attributes/methods/instances.
    """

    def __init__(self):
        """
        Extend `BaseClass.__init__()`.
        """
        BaseClass.__init__(self)
        self.attr_1 += 1

        self.attr_2 = []

    def method_1(self, param):
        """
        Extend `BaseClass.method_1()`; short method description
        """
        self.attr_2 = param
```

#### Inline comments

They must add valuable information

* In the same line of code that it is refering to
* Capitalize
* 2 spaces and # before the comment

```python
x = 5  # This is an inline comment
```

### Whitespaces

* Operators

```python
x = 5
y = x * 2

if z >= x:
    # ...
if z is not None:
    # ...
```

* Exceptions


```python

y = x**2 + 5
z = (x+y) * (x-y)

if x>5 and x%2==0:
    # ...

list[x+1 : x+2]

def function(default_parameter=5):
    # ...
```

When DO NOT add whitespaces:

```python
print(x, y)

double(3)

list[3]

tuple = (1,)
```

### Blank lines

* **2** before a class/function definition.
```python
class MyFirstClass:
    pass


class MySecondClass:
    pass
    
    
def top_level_function():
    return None
```
* **1** between class method definitions
```python
class MyClass:
    def first_method(self):
        return None

    def second_method(self):
        return None
```

* **1** between group of streps inside functions

```python
def calculate_variance(number_list):
    sum_list = 0
    for number in number_list:
        sum_list = sum_list + number
    mean = sum_list / len(number_list)

    sum_squares = 0
    for number in number_list:
        sum_squares = sum_squares + number**2
    mean_squares = sum_squares / len(number_list)

    return mean_squares - mean**2
```

### Lenght of line: 79!!!

Ways to guarantee max length of lines:

```python
def function(arg_one, arg_two,
             arg_three, arg_four):
    return arg_one
    
from mypkg import example1, \
    example2, example3
    
total = (first_variable
         + second_variable
         - third_variable)
```

### Identations

```python
def function(arg_one, arg_two,
             arg_three, arg_four):
    return arg_one
    
x = 5
if (x > 3 and
    x < 10):
    print(x)
    
list_of_numbers = [
    1, 2, 3,
    4, 5, 6,
    7, 8, 9
]
```

### imports

1. standard library imports
2. related third party imports
3. local application/library specific imports

* linter: isort

### Recommendations

* Don’t compare Boolean values to True or False using the equivalence operator.

```python
# if my_bool == True:
if my_bool:
    return '6 is bigger than 5'
```

* Empty list should be interpreted as a False statement:

```python
my_list = []
# if not len(my_list):
if not my_list:
    print('List is empty!')
```

* Use is not instead of not None

```python
# if not x is None:
if x is not None:
    return 'x exists!'
```

### List of PEP
* [PEP8](https://www.python.org/dev/peps/pep-0008/)
* [PEP257](https://www.python.org/dev/peps/pep-0257/)

### Linters

* isort
```bash
$ pip install isort
$ isort code.py
```

* black
```bash
$ pip install black
$ black code.py
```

* flake8
```bash
$ pip install flake8
$ flake8 code.py
```

* pycodestyle
```bash
$ pip install pycodestyle
$ pycodestyle code.py
```

#### Extra python code recommendations

##### Check edge cases

##### Arg parser

``` python 
import argparse

parser = argparse.ArgumentParser(description='Description.')
parser.add_argument('--foo', type=int, default=42, help='FOO!')
args = parser.parse_args()

```

##### unittest

```python
import unittest

class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget('The widget')

    def test_default_widget_size(self):
        self.assertEqual(self.widget.size(), (50,50),
                         'incorrect default size')

    def test_widget_resize(self):
        self.widget.resize(100,150)
        self.assertEqual(self.widget.size(), (100,150),
                         'wrong size after resize')

```

`python -m unittest tests/test_something.py`


##### typehints

```python
def greeting(name: str) -> str:
    return 'Hello ' + name
    
Vector = list[float]

def scale(scalar: float, vector: Vector) -> Vector:

ConnectionOptions = dict[str, str]
Address = tuple[str, int]
Server = tuple[Address, ConnectionOptions]

def broadcast_message(message: str, servers: Sequence[Server])
```

##### dataclasses

```python
from dataclasses import dataclass

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

# Will add, among other things, a __init__() that looks like:

def __init__(self, name: str, unit_price: float, quantity_on_hand: int=0):
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand
```

## Documentation 

