ptymer 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ptymer-0.0.1/LICENSE +21 -0
- ptymer-0.0.1/PKG-INFO +143 -0
- ptymer-0.0.1/README.md +101 -0
- ptymer-0.0.1/pyproject.toml +29 -0
- ptymer-0.0.1/setup.cfg +4 -0
- ptymer-0.0.1/src/ptymer/__init__.py +5 -0
- ptymer-0.0.1/src/ptymer/_alarm.py +273 -0
- ptymer-0.0.1/src/ptymer/_hourglass.py +348 -0
- ptymer-0.0.1/src/ptymer/_timer.py +251 -0
- ptymer-0.0.1/src/ptymer.egg-info/PKG-INFO +143 -0
- ptymer-0.0.1/src/ptymer.egg-info/SOURCES.txt +15 -0
- ptymer-0.0.1/src/ptymer.egg-info/dependency_links.txt +1 -0
- ptymer-0.0.1/src/ptymer.egg-info/requires.txt +3 -0
- ptymer-0.0.1/src/ptymer.egg-info/top_level.txt +1 -0
- ptymer-0.0.1/tests/test_alarm.py +84 -0
- ptymer-0.0.1/tests/test_hourglass.py +162 -0
- ptymer-0.0.1/tests/test_timer.py +155 -0
ptymer-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Caio Lima
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ptymer-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ptymer
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Ptymer is a lightweight Python package designed to help developers manage and monitor the execution time of their code. With features like timers and countdowns, it allows you to precisely measure runtime durations and set time-based triggers. Whether you need to benchmark performance or execute actions at specific intervals, Ptymer provides a simple and efficient solution
|
|
5
|
+
Author-email: Caio Lima <caio-augusto@live.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 Caio Lima
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/hyskoniho/ptymer
|
|
29
|
+
Project-URL: Documentation, https://github.com/hyskoniho/ptymer/wiki
|
|
30
|
+
Project-URL: Issues, https://github.com/hyskoniho/ptymer/issues
|
|
31
|
+
Keywords: timer,countdown,hourglass,alarm
|
|
32
|
+
Classifier: Programming Language :: Python
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Operating System :: OS Independent
|
|
36
|
+
Requires-Python: >=3.8
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
License-File: LICENSE
|
|
39
|
+
Requires-Dist: psutil>=6.0.0
|
|
40
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
41
|
+
Requires-Dist: six>=1.16.0
|
|
42
|
+
|
|
43
|
+
<div align="center">
|
|
44
|
+
<h1>PTymer ⏱️</h1>
|
|
45
|
+
<img src="https://img.shields.io/badge/core-python-%2314354C.svg?style=for-the-badge">
|
|
46
|
+
<img src="https://img.shields.io/badge/status-online-green?style=for-the-badge">
|
|
47
|
+
<img src="https://img.shields.io/badge/license-MIT-yellow?style=for-the-badge">
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
**PTymer** is a Python project that provides insights and actions within the execution of code in a time context. This package includes three main classes: `Timer`, `HourGlass`, and `Alarm`, each with specific functionalities for time monitoring and control. You can find the full description in the [Wiki Page](https://github.com/hyskoniho/ptymer/wiki)
|
|
51
|
+
|
|
52
|
+
## Index
|
|
53
|
+
|
|
54
|
+
- [Installation](#installation)
|
|
55
|
+
- [Usage](#usage)
|
|
56
|
+
- [Timer](#timer)
|
|
57
|
+
- [HourGlass](#hourglass)
|
|
58
|
+
- [Alarm](#alarm)
|
|
59
|
+
- [Contribution](#contribution)
|
|
60
|
+
- [License](#license)
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
PTymer is compatible with Python 3.8 or higher.
|
|
65
|
+
To install, use pip:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install ptymer
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
#### Timer
|
|
74
|
+
The Timer class is used to measure the execution time of code snippets. It can be instantiated in several ways:
|
|
75
|
+
##### Normal Instance
|
|
76
|
+
```python
|
|
77
|
+
from ptymer import Timer
|
|
78
|
+
|
|
79
|
+
tm = Timer().start()
|
|
80
|
+
# Your code here
|
|
81
|
+
tm.stop()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
##### Context Manager
|
|
85
|
+
```python
|
|
86
|
+
from ptymer import Timer
|
|
87
|
+
|
|
88
|
+
with Timer() as tm:
|
|
89
|
+
# Your code here
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
##### Decorator
|
|
93
|
+
```python
|
|
94
|
+
from ptymer import Timer
|
|
95
|
+
|
|
96
|
+
@Timer()
|
|
97
|
+
def your_function_here():
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
#### HourGlass
|
|
102
|
+
The HourGlass class is used to create a countdown timer. After the countdown finishes, it executes a user-defined function.
|
|
103
|
+
```python
|
|
104
|
+
from ptymer import HourGlass
|
|
105
|
+
|
|
106
|
+
hg = HourGlass(seconds=5, visibility=True, target=print, args=("Hello World",)).start()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
*Note:* In the arguments tuple, you need to put a comma at the end to identify it as a tuple if there's only one element.
|
|
110
|
+
|
|
111
|
+
#### Alarm
|
|
112
|
+
The Alarm class takes a list of times and a function. When the algorithm identifies that it has reached one of the times, it executes the defined function.
|
|
113
|
+
```python
|
|
114
|
+
from ptymer import Alarm
|
|
115
|
+
|
|
116
|
+
alarm = Alarm(schedules=["10:49:00"], target=target, args=(), visibility=True).start()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
<br></br>
|
|
120
|
+
|
|
121
|
+
###### ⚠️ WARNING!
|
|
122
|
+
Due to multiprocessing, it's highly recommended that you safeguard the execution of the main process, when using `HourGlass` and/or `Alarm` instance, with the following statement before your code:
|
|
123
|
+
```python
|
|
124
|
+
if __name__ == '__main__':
|
|
125
|
+
# your code here
|
|
126
|
+
```
|
|
127
|
+
Some sample usage:
|
|
128
|
+
```python
|
|
129
|
+
def foo():
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
if __name__ == '__main__':
|
|
133
|
+
foo()
|
|
134
|
+
```
|
|
135
|
+
You can find more information about this issue [here](https://github.com/hyskoniho/ptymer/wiki/Handling-Parallelism).
|
|
136
|
+
|
|
137
|
+
## Contribution
|
|
138
|
+
Contributions are welcome!!! Feel free to open issues and pull requests on the GitHub repository.
|
|
139
|
+
Pay attention to the [test files](https://github.com/hyskoniho/ptymer/tree/main/tests) content and don't forget to document every change!
|
|
140
|
+
We use Pytest and there's a [workflow](https://github.com/hyskoniho/ptymer/blob/main/.github/workflows/unit_test.yaml) set up on GitHub Actions that you might want to check out.
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
This project is licensed under the MIT License. See the [LICENSE](https://github.com/hyskoniho/ptymer/blob/main/LICENSE) file for more details.
|
ptymer-0.0.1/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>PTymer ⏱️</h1>
|
|
3
|
+
<img src="https://img.shields.io/badge/core-python-%2314354C.svg?style=for-the-badge">
|
|
4
|
+
<img src="https://img.shields.io/badge/status-online-green?style=for-the-badge">
|
|
5
|
+
<img src="https://img.shields.io/badge/license-MIT-yellow?style=for-the-badge">
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
**PTymer** is a Python project that provides insights and actions within the execution of code in a time context. This package includes three main classes: `Timer`, `HourGlass`, and `Alarm`, each with specific functionalities for time monitoring and control. You can find the full description in the [Wiki Page](https://github.com/hyskoniho/ptymer/wiki)
|
|
9
|
+
|
|
10
|
+
## Index
|
|
11
|
+
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Usage](#usage)
|
|
14
|
+
- [Timer](#timer)
|
|
15
|
+
- [HourGlass](#hourglass)
|
|
16
|
+
- [Alarm](#alarm)
|
|
17
|
+
- [Contribution](#contribution)
|
|
18
|
+
- [License](#license)
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
PTymer is compatible with Python 3.8 or higher.
|
|
23
|
+
To install, use pip:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install ptymer
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
#### Timer
|
|
32
|
+
The Timer class is used to measure the execution time of code snippets. It can be instantiated in several ways:
|
|
33
|
+
##### Normal Instance
|
|
34
|
+
```python
|
|
35
|
+
from ptymer import Timer
|
|
36
|
+
|
|
37
|
+
tm = Timer().start()
|
|
38
|
+
# Your code here
|
|
39
|
+
tm.stop()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
##### Context Manager
|
|
43
|
+
```python
|
|
44
|
+
from ptymer import Timer
|
|
45
|
+
|
|
46
|
+
with Timer() as tm:
|
|
47
|
+
# Your code here
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
##### Decorator
|
|
51
|
+
```python
|
|
52
|
+
from ptymer import Timer
|
|
53
|
+
|
|
54
|
+
@Timer()
|
|
55
|
+
def your_function_here():
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
#### HourGlass
|
|
60
|
+
The HourGlass class is used to create a countdown timer. After the countdown finishes, it executes a user-defined function.
|
|
61
|
+
```python
|
|
62
|
+
from ptymer import HourGlass
|
|
63
|
+
|
|
64
|
+
hg = HourGlass(seconds=5, visibility=True, target=print, args=("Hello World",)).start()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
*Note:* In the arguments tuple, you need to put a comma at the end to identify it as a tuple if there's only one element.
|
|
68
|
+
|
|
69
|
+
#### Alarm
|
|
70
|
+
The Alarm class takes a list of times and a function. When the algorithm identifies that it has reached one of the times, it executes the defined function.
|
|
71
|
+
```python
|
|
72
|
+
from ptymer import Alarm
|
|
73
|
+
|
|
74
|
+
alarm = Alarm(schedules=["10:49:00"], target=target, args=(), visibility=True).start()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
<br></br>
|
|
78
|
+
|
|
79
|
+
###### ⚠️ WARNING!
|
|
80
|
+
Due to multiprocessing, it's highly recommended that you safeguard the execution of the main process, when using `HourGlass` and/or `Alarm` instance, with the following statement before your code:
|
|
81
|
+
```python
|
|
82
|
+
if __name__ == '__main__':
|
|
83
|
+
# your code here
|
|
84
|
+
```
|
|
85
|
+
Some sample usage:
|
|
86
|
+
```python
|
|
87
|
+
def foo():
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
if __name__ == '__main__':
|
|
91
|
+
foo()
|
|
92
|
+
```
|
|
93
|
+
You can find more information about this issue [here](https://github.com/hyskoniho/ptymer/wiki/Handling-Parallelism).
|
|
94
|
+
|
|
95
|
+
## Contribution
|
|
96
|
+
Contributions are welcome!!! Feel free to open issues and pull requests on the GitHub repository.
|
|
97
|
+
Pay attention to the [test files](https://github.com/hyskoniho/ptymer/tree/main/tests) content and don't forget to document every change!
|
|
98
|
+
We use Pytest and there's a [workflow](https://github.com/hyskoniho/ptymer/blob/main/.github/workflows/unit_test.yaml) set up on GitHub Actions that you might want to check out.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
This project is licensed under the MIT License. See the [LICENSE](https://github.com/hyskoniho/ptymer/blob/main/LICENSE) file for more details.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=40.8.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ptymer"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [{ name="Caio Lima", email="caio-augusto@live.com" }]
|
|
9
|
+
license = { file = "LICENSE" }
|
|
10
|
+
description = "Ptymer is a lightweight Python package designed to help developers manage and monitor the execution time of their code. With features like timers and countdowns, it allows you to precisely measure runtime durations and set time-based triggers. Whether you need to benchmark performance or execute actions at specific intervals, Ptymer provides a simple and efficient solution"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.8"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
keywords = ["timer", "countdown", "hourglass", "alarm"]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"psutil>=6.0.0",
|
|
22
|
+
"python-dateutil>=2.9.0.post0",
|
|
23
|
+
"six>=1.16.0"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/hyskoniho/ptymer"
|
|
28
|
+
Documentation = "https://github.com/hyskoniho/ptymer/wiki"
|
|
29
|
+
Issues = "https://github.com/hyskoniho/ptymer/issues"
|
ptymer-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Callable, Any, List, Tuple, Union, Optional
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from dateutil import parser
|
|
5
|
+
from psutil import Process as psProcess, pid_exists
|
|
6
|
+
from multiprocessing import Process, freeze_support
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Alarm():
|
|
10
|
+
schedules: List[Union[datetime, str, Tuple[int, int, int, int, int, int, int]]]
|
|
11
|
+
# list of datetime objects
|
|
12
|
+
target: Optional[Callable] = None
|
|
13
|
+
# function that will be executed when the alarm is triggered
|
|
14
|
+
args: Optional[Tuple[Any]] = None
|
|
15
|
+
# arguments of the function
|
|
16
|
+
visibility: bool = False
|
|
17
|
+
# defines if the alarm will show messages or not
|
|
18
|
+
keep_schedules: bool = False
|
|
19
|
+
# if true, the alarm will not be elimnated after being triggered
|
|
20
|
+
__pid: Optional[int] = None
|
|
21
|
+
# process id of the alarm
|
|
22
|
+
__process: Optional[Process] = None
|
|
23
|
+
# process object of the alarm
|
|
24
|
+
|
|
25
|
+
def __post_init__(self) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Post-initialization method.
|
|
28
|
+
|
|
29
|
+
This method validates the attributes and converts string representations of dates
|
|
30
|
+
to `datetime` objects. It ensures that the schedules, target function, arguments,
|
|
31
|
+
and other attributes are correctly defined and of the proper types.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
TypeError: If `schedules` is not a list, if `target` is not a callable function,
|
|
35
|
+
if `args` is not a tuple, if `visibility` is not a boolean, if `keep_schedules`
|
|
36
|
+
is not a boolean, or if any schedule entry is not a valid date.
|
|
37
|
+
ValueError: If `schedules` is empty, if `target` is not defined, or if `args`
|
|
38
|
+
are defined without a target function.
|
|
39
|
+
|
|
40
|
+
Notes:
|
|
41
|
+
- `schedules` should be a list of dates in `datetime`, `tuple`, or `str` format.
|
|
42
|
+
- `target` should be a callable function.
|
|
43
|
+
- `args` should be a tuple of arguments for the target function.
|
|
44
|
+
- `visibility` and `keep_schedules` should be boolean values.
|
|
45
|
+
- The method converts string dates to `datetime` objects and tuple dates to
|
|
46
|
+
`datetime` objects, truncating microseconds for `datetime` objects.
|
|
47
|
+
"""
|
|
48
|
+
if not isinstance(self.schedules, list):
|
|
49
|
+
raise TypeError("Schedules must be a list!")
|
|
50
|
+
elif not self.schedules:
|
|
51
|
+
raise ValueError("Schedules must be defined!")
|
|
52
|
+
elif self.target and not isinstance(self.target, Callable):
|
|
53
|
+
raise TypeError("Target must be a function!")
|
|
54
|
+
elif self.args and not isinstance(self.args, tuple):
|
|
55
|
+
raise TypeError("Arguments must be a tuple!")
|
|
56
|
+
elif self.args and not self.target:
|
|
57
|
+
raise ValueError(f"Arguments cannot be defined without a target function!")
|
|
58
|
+
elif not isinstance(self.visibility, bool):
|
|
59
|
+
raise TypeError("Visibility must be a boolean!")
|
|
60
|
+
elif not isinstance(self.keep_schedules, bool):
|
|
61
|
+
raise TypeError("keep_schedules must be a boolean!")
|
|
62
|
+
else:
|
|
63
|
+
for idx, date_obj in enumerate(self.schedules):
|
|
64
|
+
try:
|
|
65
|
+
assert (type(date_obj) in [datetime, tuple, str])
|
|
66
|
+
if type(date_obj) == str:
|
|
67
|
+
self.schedules[idx] = parser.parse(date_obj)
|
|
68
|
+
elif type(date_obj) == tuple:
|
|
69
|
+
self.schedules[idx] = datetime(*date_obj)
|
|
70
|
+
# converting tuple(d, h, m, s) to datetime object
|
|
71
|
+
elif type(date_obj) == datetime:
|
|
72
|
+
self.schedules[idx] = date_obj.replace(microsecond=0)
|
|
73
|
+
# truncating microseconds
|
|
74
|
+
except:
|
|
75
|
+
raise TypeError(f"Invalid datetime object: {date_obj}")
|
|
76
|
+
finally:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
def __str__(self) -> str:
|
|
80
|
+
return f"Class Alarm()\nVisibility: {self.visibility}\nSchedules: {self.args}\nKeep_schedules: {self.keep_schedules}\nProcess id: {self.__pid if self.status else None}"
|
|
81
|
+
|
|
82
|
+
def start(self) -> "Alarm":
|
|
83
|
+
"""
|
|
84
|
+
Set up the alarm.
|
|
85
|
+
|
|
86
|
+
This method initializes and starts the alarm process. It checks if an alarm is already set and
|
|
87
|
+
raises an error if so. Otherwise, it starts a new alarm process that runs in the background.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Alarm: The current instance of the `Alarm` class.
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
ValueError: If an alarm is already set.
|
|
94
|
+
|
|
95
|
+
Notes:
|
|
96
|
+
- This method uses `freeze_support()` to ensure compatibility with Windows.
|
|
97
|
+
- The alarm process is started as a daemon process.
|
|
98
|
+
"""
|
|
99
|
+
from os import getpid
|
|
100
|
+
|
|
101
|
+
freeze_support()
|
|
102
|
+
# Freeze support for windows
|
|
103
|
+
|
|
104
|
+
if self.status:
|
|
105
|
+
raise RuntimeError("Alarm already set!")
|
|
106
|
+
else:
|
|
107
|
+
process = Process(target=self._alarm_loop, args=(getpid(),), daemon=True)
|
|
108
|
+
process.start()
|
|
109
|
+
self.__pid = process.pid
|
|
110
|
+
self.__process = process
|
|
111
|
+
|
|
112
|
+
print("Alarm started!") if self.visibility else None
|
|
113
|
+
return self
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def _run_function(target, args, visibility) -> any:
|
|
117
|
+
"""
|
|
118
|
+
Execute the stored function with its arguments.
|
|
119
|
+
|
|
120
|
+
This method attempts to run the function stored in `self.target` with the arguments stored in `self.args`.
|
|
121
|
+
If no arguments are provided, the function is called without arguments.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
any: The return value of the executed function, or `None` if no function is stored. If an exception occurs,
|
|
125
|
+
it returns the exception message as a string.
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
Exception: If an error occurs during the function execution, the exception is caught and its message is printed
|
|
129
|
+
|
|
130
|
+
Notes:
|
|
131
|
+
- If `self.target` is `None`, the method returns `None`.
|
|
132
|
+
- If `self.args` is `None`, the function is called without arguments.
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
if target and args:
|
|
136
|
+
value = target(*args)
|
|
137
|
+
elif target and not args:
|
|
138
|
+
value = target()
|
|
139
|
+
else:
|
|
140
|
+
value = None
|
|
141
|
+
except Exception as e:
|
|
142
|
+
print(f"Error ocurred:\n{e}")
|
|
143
|
+
return str(e)
|
|
144
|
+
else:
|
|
145
|
+
return value
|
|
146
|
+
|
|
147
|
+
def _alarm_loop(self, mainPid: int) -> None:
|
|
148
|
+
"""
|
|
149
|
+
Run the alarm loop.
|
|
150
|
+
|
|
151
|
+
This function monitors the schedules and triggers the alarm at the specified times.
|
|
152
|
+
It suspends and resumes the main process around the execution of the alarm function.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
mainPid (int): The process ID of the main process to be monitored.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
None
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
ValueError: If `mainPid` is not a valid process ID.
|
|
162
|
+
|
|
163
|
+
Notes:
|
|
164
|
+
- The function continuously checks the current time against scheduled alarm times.
|
|
165
|
+
- When the current time matches a scheduled time, the main process is suspended, the alarm function is executed, and then the main process is resumed.
|
|
166
|
+
- If `self.keep_schedules` is `False`, the schedule is removed after the alarm is triggered.
|
|
167
|
+
- The function stops running when there are no more schedules or if the main process no longer exists.
|
|
168
|
+
"""
|
|
169
|
+
process = psProcess(mainPid)
|
|
170
|
+
lastIdx = None
|
|
171
|
+
|
|
172
|
+
while len(self.schedules) > 0:
|
|
173
|
+
now = datetime.now().replace(microsecond=0)
|
|
174
|
+
try:
|
|
175
|
+
idx = self.schedules.index(now)
|
|
176
|
+
except:
|
|
177
|
+
continue # if the current time is not in the schedules, it continues the loop
|
|
178
|
+
else:
|
|
179
|
+
if idx != lastIdx: # this is due the speed of the loop, sometimes it triggers the same alarm twice (or much more)
|
|
180
|
+
print("Alarm triggered!") if self.visibility else None
|
|
181
|
+
|
|
182
|
+
process.suspend()
|
|
183
|
+
self._run_function(self.target, self.args, self.visibility)
|
|
184
|
+
process.resume()
|
|
185
|
+
|
|
186
|
+
lastIdx = idx
|
|
187
|
+
if not self.keep_schedules:
|
|
188
|
+
self.schedules.pop(idx)
|
|
189
|
+
self.__pid = None
|
|
190
|
+
|
|
191
|
+
def stop(self) -> None:
|
|
192
|
+
"""
|
|
193
|
+
Stop the alarm.
|
|
194
|
+
|
|
195
|
+
This method terminates the alarm process if it is currently running. If no alarm is set,
|
|
196
|
+
it raises an error.
|
|
197
|
+
|
|
198
|
+
Raises:
|
|
199
|
+
RuntimeError: If no alarm is currently set.
|
|
200
|
+
|
|
201
|
+
Notes:
|
|
202
|
+
- The method checks if the alarm process exists and terminates it if so.
|
|
203
|
+
- If `self.visibility` is `True`, it prints a message indicating that the alarm has stopped.
|
|
204
|
+
"""
|
|
205
|
+
if self.status:
|
|
206
|
+
process = psProcess(self.__pid)
|
|
207
|
+
process.terminate()
|
|
208
|
+
self.__pid = None
|
|
209
|
+
self.__process = None
|
|
210
|
+
else:
|
|
211
|
+
raise RuntimeError("Alarm not set!")
|
|
212
|
+
|
|
213
|
+
print("Alarm stopped!") if self.visibility else None
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def pid(self) -> int:
|
|
217
|
+
"""
|
|
218
|
+
Get the process ID.
|
|
219
|
+
|
|
220
|
+
This method returns the process ID of the running alarm process. If no alarm process
|
|
221
|
+
is running, it raises an error.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
int: The process ID of the running alarm.
|
|
225
|
+
|
|
226
|
+
Raises:
|
|
227
|
+
AttributeError: If no alarm process is currently running.
|
|
228
|
+
|
|
229
|
+
Notes:
|
|
230
|
+
- The method checks if the alarm process ID is set and if the process exists.
|
|
231
|
+
"""
|
|
232
|
+
if not self.status:
|
|
233
|
+
raise AttributeError(f"There is no alarm running!")
|
|
234
|
+
else:
|
|
235
|
+
return int(self.__pid)
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def status(self) -> bool:
|
|
239
|
+
"""
|
|
240
|
+
Check if the alarm is active.
|
|
241
|
+
|
|
242
|
+
This method checks if the alarm process is currently running.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
bool: `True` if the alarm process is active, `False` otherwise.
|
|
246
|
+
|
|
247
|
+
Notes:
|
|
248
|
+
- The method checks if the process ID is set and if the process exists.
|
|
249
|
+
"""
|
|
250
|
+
if self.__pid and pid_exists(self.__pid):
|
|
251
|
+
return True
|
|
252
|
+
else:
|
|
253
|
+
return False
|
|
254
|
+
|
|
255
|
+
def wait(self) -> None:
|
|
256
|
+
"""
|
|
257
|
+
Wait for the alarm to finish.
|
|
258
|
+
|
|
259
|
+
This method waits for the alarm process to finish before returning.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
None
|
|
263
|
+
|
|
264
|
+
Notes:
|
|
265
|
+
- The method uses the `join()` method of the alarm process.
|
|
266
|
+
"""
|
|
267
|
+
if self.status:
|
|
268
|
+
self.__process.join()
|
|
269
|
+
else:
|
|
270
|
+
raise RuntimeError("Alarm not set!")
|
|
271
|
+
|
|
272
|
+
if __name__ == "__main__":
|
|
273
|
+
pass
|