Skip to content

Usage

Once the template is applied, the base code directory will have the next structure following the packaging.python.org rules.

Base code generated from the template
my-python-project               <- Custom template project directory
├── src                         <- Folder with the source files and packages.
│   └── my_python_project       <- Folder with the package code files.
│       ├── __init__.py         <- Constructor file.
│       ├── main.py             <- File with the source code.
│       └── helpers.py          <- Example class with helpers methods.
├── data                        <- Data used for the project.
│   ├── config.yaml             <- Configuration file.
│   └── logs                    <- Logs folder.
│       └── file.log            <- Logs file from the project.
├ requirements.txt              <- Requirements file.
...

Running the base code

Before starting to develop your own code you can run the base code by following the next steps.

  • Step 1: Create your virtual environment(optional)

    This step is optional but recommended, create your virtual environment for this project by the name you typed when generating the custom template. By default is my-python-project (env_name).

  • Step 2: Install the requirements

    Install the requirements to run the project in the virtual environment by the following command. The only library preconfigured in the requirements is PyYAML, required to read the configuration file.

    pip install -r requirements.txt
    
  • Step 3: run the main.py file

    Let's start by running the main.py file in debug mode.

    cd my-python-project
    python src/my_python_project/main.py -l debug 
    
    Running the code with arguments

    When running the code you have the following flags to modify the behaviour of the program.

    python src/{{ cookiecutter.package_name }}/main.py [flags]
    

    Options:

    -c <config file path>, --config <config file path> Add the config file path after this flag

    -l ['debug', 'info', 'warning'], --log ['debug', 'info', 'warning'] Set up de level of the logs promted. By default info

    After running the command you should see an output showing the initial arguments and the execution of the public and private methods from the Helpers class.

    output
    2022-12-11 23:49:13 [DEBUG] main.py - __init__ (L50): 
    2022-12-11 23:49:13 [DEBUG] main.py - __init__ (L51): Initial args: 
    2022-12-11 23:49:13 [DEBUG] main.py - __init__ (L53): >> config: data/config.yaml
    2022-12-11 23:49:13 [DEBUG] main.py - __init__ (L53): >> log: ['debug']
    2022-12-11 23:49:13 [DEBUG] main.py - __init__ (L53): >> test: False
    2022-12-11 23:49:13 [DEBUG] main.py - __init__ (L54): 
    
    2022-12-11 23:49:13 [INFO] main.py - run (L101): [Initializing my-python-project]
    2022-12-11 23:49:13 [INFO] helpers.py - public_method (L40): This "public_method" is a method from Helpers.
    2022-12-11 23:49:13 [INFO] helpers.py - _private_method (L33): This "_private_method" is a method from Helpers.
    2022-12-11 23:49:13 [INFO] helpers.py - public_method (L42): This "Hello World" is a global variable.
    2022-12-11 23:49:13 [INFO] main.py - run (L110): [Exiting my-python-project app.Total elapsed time: 00:00:00.]
    

Understanding the base code

To get a better understanding of the base code from the template, lets review some important files from the project generated.

1. The main.py file

It is the class acting as an orchestrator of the whole code.

The main.py file is made of one class called App and, the following three methods.

Methods Description
\__init__ Constructor method to read the configuration parameters, generate the instances from modules and declare the global variables.
_get_logger Method to generate the logger used in the project.
run Main method to run the whole app and manage all calls.

1.1 The __init__ method

The __init__ method is in charge of declaring the configuration values, define the global variables and, generate the instances from other modules like the helpers class.

main.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def __init__(self, args: argparse.Namespace):
    """
    Constructor method to read the configuration parameters, generate the instances from modules and declare the global variables
    :param args: arguments from the command input flags
    """
    # Argument variables
    dir_config = args.config
    test = args.test
    arg_level = args.log[0]

    # Reading the config json file
    yaml_file = open(dir_config, 'r')
    config = yaml.safe_load(yaml_file)

    # Getting logger
    logger = self._get_logger(level=arg_level)

    # Logging argument variables
    logger.debug('')
    logger.debug("Initial args: ")
    for k, v in vars(args).items():
        logger.debug(f">> {k}: {v}")
    logger.debug("\n")

    # Global variables
    self.config = config
    self.log = logger

    # Global instances
    self.helpers = Helpers(logger=logger, config=config)

1.2 The run method

The run method acts as the runner of the whole script here is where everything related with the app happens. The run method is set to start developing your code in the lines 94 and 95.

main.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def run(self):
    """
    Main method to run the whole app and manage all calls.
    :return: None
    """
    # Initializing the app
    start_app = time.time()
    self.log.info(f"\033[1m[Initializing {self.config['project_name']}]\033[0m")

    # >>> Start your code here <<<
    self.helpers.public_method()

    # Exiting the app
    end_app = time.time()
    elapsed_time = end_app - start_app
    str_elapsed_time = time.strftime('%H:%M:%S.', time.gmtime(elapsed_time))
    self.log.info(f"\033[1m[Exiting {self.config['project_name']} app."
                    f"Total elapsed time: {str_elapsed_time}]\033[0m")
    sys.exit(0)

2. The helpers.py file

The helpers.py file located at src/package_name directory contains an example class template as a guide to call this class and others from the main.py file.

When instantiating a class in main.py (class App) we should alway pass the following input parameters:

  • logger: logger object defined in the main file to promt output messages.
  • config: configuration parameters from the configuration file config.yaml.
main.py (instanced class from helpers.py)
60
61
# Global instances
self.example_class = Helpers(logger=logger, config=config)

2.1 The __init__ method

The __init__ method is the constructor method from the class Helpers. This method creates an instance from its class and, declare initial parameters and global variables.

helpers.py
17
18
19
20
21
22
23
24
25
26
27
    def __init__(self, logger: logging.Logger, config: dict):
        """
        Constructor method to create an instance from the class with initial arguments and
        global variables
        :param logger: logger defined in the main file
        :param config: configuration parameters
        """
        # Global variables
        self.logger = logger
        self.config = config
        self.example_global_variable = "Hello World"

3. The configuration file

The configuration file is located in /data/config.yaml this file should contain all the configuration parameters for your code.

config.yaml
# Configuration parameters
project_name: "{{cookiecutter.repository_name}}"
path_logs: data/logs/file.log

# >>> Add your configuration parameters here <<<
Mockup configuration parameters

There are some mock up configuration parameters as a type reminder. It is recommended to delete these parameters before releasing the code. These parameters don't affect the behaviour of the code.

config.yaml
# ⌄⌄⌄ Example. Not actual parameters, remove before code. ⌄⌄⌄
example_string: This is an string
example_integer: 2077
example_float: 2.077
example_boolean: True
example_none: None
example_list:
- A
- B
- C
example_dictionary:
key_a: value_a
key_b: value_b
example_set: !!set
? a
? b
? 1
? 2
example_anchor: &my_anchor data_to_duplicate
example_run_anchor: *my_anchor
# ⌃⌃⌃ Example. Not actual parameters, remove before code. ⌃⌃⌃

4. The logs file

The logs file is an empty located at '/data/logs/file.log'. This file acts as a history of all the logs generated from the executions. You can change the behaviour of these logs in the main.py file at the _get_logger method.

main.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def _get_logger(self, level: str) -> logging.Logger:
    """
    Method to generate the logger used in the project
    :param level: the level of the logs to output
    :return: the custom logger
    """
    # Setting up the output level
    levels = {'debug': logging.DEBUG,
                'info': logging.INFO,
                'warning': logging.WARNING}
    set_level = levels[level]

    # Setting up the logger
    set_log_format = '%(asctime)s [%(levelname)s] %(filename)s - %(funcName)s (L%(lineno)s): %(message)s'
    set_date_format = '%Y-%m-%d %H:%M:%S'
    logging.basicConfig(level=set_level,
                        format=set_log_format,
                        datefmt=set_date_format)
    my_logger = logging.getLogger(__name__)

    # Create a file log handler
    file_handler = logging.FileHandler(self.config['path_logs'])
    file_handler.setLevel(logging.DEBUG)
    f_format = logging.Formatter(set_log_format)
    file_handler.setFormatter(f_format)

    # Add handlers to the logger
    my_logger.addHandler(file_handler)
    return my_logger