|
4 | 4 | [](https://github.com/2xB/faultguard) |
5 | 5 | [](https://pypi.org/project/faultguard/) |
6 | 6 |
|
7 | | -Let users save important data after a crash of your Python3 application. |
| 7 | +Preventing data loss of your Python3 application. Keeps important data both duplicated in memory and on disk. |
8 | 8 |
|
9 | | -## Introduction |
| 9 | +## Overview |
10 | 10 |
|
11 | | -If a process experiences e.g. a segmentation fault, it cannot execute further operations to recover. Also, memory of a process is considered inconsistent after a segmentation fault. However, as soon as a project depends on third party libraries, the appearence of such faults is out of hand. |
| 11 | +Usually, after a crash through e.g. a segmentation fault or a power outage, data of running applications is lost. In environments where this is problematic – e.g. when users should not loose their work or important data is collected –, `faultguard` prevents data loss in two ways: |
12 | 12 |
|
13 | | -This module provides an approach to implement a crash rescue handler that can access important data even after segmentation faults: While the guarded application runs (see `launch` function in the example below), it has access to a special Python dictionary (`faultguard_data` in example) in which it stores a copy of important user data. Data stored in this dictionary remains accessible when the guarded application abruptly terminates, at which point a rescue handler can access the dictionary and rescue data from there (`rescue` function in example). |
| 13 | + 1. `faultguard` keeps the selected data automatically backed up in a second process. This way, if your Python application crashes - even with a segmentation fault caused e.g. by an external library -, in most cases the backup process is still running and immediately provides its data to a rescue handling function that you can define. This even allows you to e.g. provide users with a custom graphical dialog informing about the crash and providing options for the recovered data. |
| 14 | + |
| 15 | + 2. `faultguard` can save the selected data automatically in customizable time intervals to a file from which it can be recovered on the next application launch. |
14 | 16 |
|
15 | | -Starting the application with a given rescue handler is just one line of code when using `faultguard`, shown in the `main` function in the example below. |
| 17 | +An example using all features of `faultguard` can be found in `example.py`. |
16 | 18 |
|
17 | | -On the technical side, this is realized through Python modules `pickle`, `multiprocessing` and `collections`, which are used to serialize and deserialize various types of data and provide the dictionary-like data type that is available in both the guarded application and the rescue handler process. |
18 | | -The Python module 'signal' is used to ensure signals like keyboard interrupts are handled correctly and received by the guarded process. |
| 19 | +To secure an application data using `faultguard`, you define a `launch` function that `faultguard` provides with a custom data dictionary. This dictionary, although working like a usual dictionary and accepting all content that can be serialized using `pickle`, is automatically backed up as described above. If the guarded application crashes, the backup process launches a crash handler in form of a `rescue` function also defined by you and provides it with the backed up dictionary. Additionally, if you provide `faultguard` with a time interval and a path for autosaves, it stores the data on disk and you can call the `recover` method to recover the file content and call your `rescue` function. |
19 | 20 |
|
20 | | -This module is really simple, although its functionality is very reuseable. Feel encouraged to look into the source code and to contribute through (well documented :D ) pull requests! |
| 21 | +The `faultguard` interface is very simple - you just provide it with a `launch` and a `rescue` function and everything else works automatically. If you use autosaving, on application launch you should additionally test if a backup file exists, which would show that `faultguard` did previously not exit properly. If a backup file exists, you should let `faultguard` recover it and then delete it to make place for a new one. |
| 22 | + |
| 23 | +On the technical side, the in-memory backup is realized through Python modules `pickle`, `multiprocessing` and `collections`, which are used to serialize and deserialize various types of data and provide the dictionary-like data type that is available in both the guarded application and the rescue handler process. The autosave functionality uses the Python module `lzma` for efficient compression of autosave files and `os` for file handling. |
| 24 | +The Python module `signal` is used to ensure signals like keyboard interrupts are handled correctly and received by the guarded process. |
| 25 | + |
| 26 | +Feel encouraged to look into the source code and to contribute through (well documented :D ) pull requests! |
| 27 | + |
| 28 | +Faultguard is tested on Linux and Windows. |
21 | 29 |
|
22 | 30 | ## Installation |
23 | 31 |
|
24 | | -This module is available through pip or can be installed manually via setup.py. |
| 32 | +This module is available via `pip install faultguard` or can be installed manually via `setup.py`, e.g. downloading the source code and running `python setup.py install`. |
25 | 33 |
|
26 | | -## Disclamer |
| 34 | +## Disclaimer |
27 | 35 |
|
28 | 36 | If a crash is observed frequently or reproducibly, it should be diagnosed – e.g. with `faulthandler` (another Python module) and `gdb`. If you somehow manage to generate a segmentation fault in the `faultguard` data dictionary, and therefore destroy the guard process, the rescue will of course not work. Preventing faults from happening in the first place is always the most important, so don't rely solely on this module, just use it as an additional safety net! |
29 | 37 |
|
30 | | -## Example |
31 | | - |
32 | | -It follows a minimal working example for this module: |
33 | | - |
34 | | -```python |
35 | | -import faultguard |
36 | | -import numpy as np |
37 | | - |
38 | | -def launch(faultguard_data, args): |
39 | | - """ |
40 | | - Demo software main method |
41 | | - |
42 | | - :param faultguard_data: Faultguard data dictionary |
43 | | - :param args: Data passed from faultguard.start. |
44 | | - """ |
45 | | - print("Launching demo") |
46 | | - |
47 | | - # Some important data |
48 | | - important_data_1 = np.array([1,2,3]) |
49 | | - important_data_2 = args[0] + " " + args[1] |
50 | | - |
51 | | - # Some dummy important data manipulation |
52 | | - for i in range(10): |
53 | | - important_data_1[i%3] = i |
54 | | - important_data_2 += str(i) |
55 | | - print("important_data_1:", important_data_1) |
56 | | - print("important_data_2:", important_data_2) |
57 | | - |
58 | | - # Sending important data to faultguard process |
59 | | - faultguard_data["important_data_1"] = important_data_1 |
60 | | - faultguard_data["important_data_2"] = important_data_2 |
61 | | - |
62 | | - # Generate segfault |
63 | | - if i == 7: |
64 | | - import ctypes |
65 | | - ctypes.string_at(0) |
66 | | - |
67 | | -def rescue(faultguard_data, exit_code, args): |
68 | | - """ |
69 | | - Demo rescue handler |
70 | | - |
71 | | - :param faultguard_data: Faultguard data dictionary |
72 | | - :param exit_code: Exit code of occured fault. |
73 | | - :param args: Data passed from faultguard.start. |
74 | | - """ |
75 | | - print("Fault occured. Exit code: {}. Rescued data:".format(exit_code)) |
76 | | - |
77 | | - # Check if fault occurs before data was initialized |
78 | | - if "important_data_1" not in faultguard_data or "important_data_2" not in faultguard_data: |
79 | | - return |
80 | | - |
81 | | - # Restore data |
82 | | - important_data_1 = faultguard_data["important_data_1"] |
83 | | - important_data_2 = faultguard_data["important_data_2"] |
84 | | - |
85 | | - # You might need to assign the class here by important_data_1.__class__ = ... |
86 | | - print("important_data_1:", important_data_1) |
87 | | - print("important_data_2:", important_data_2) |
88 | | - |
89 | | -def main(): |
90 | | - faultguard.start(launch, rescue, args=("Hello", "World")) |
91 | | - |
92 | | -if __name__ == "__main__": |
93 | | - main() |
94 | | -``` |
95 | | - |
96 | 38 | ## Credit |
97 | 39 |
|
98 | 40 | This project was initially developed for a hardware project at the University of Münster. |
0 commit comments