utilitz 0.3.2__tar.gz → 0.4.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: utilitz
3
- Version: 0.3.2
3
+ Version: 0.4.1
4
4
  Summary: Simple Python utility functions to save and load data
5
5
  Project-URL: Homepage, https://github.com/artitzco/utilitz
6
6
  Project-URL: Issues, https://github.com/artitzco/utilitz/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "utilitz"
7
- version = "0.3.2"
7
+ version = "0.4.1"
8
8
  authors = [
9
9
  { name = "Artitzco", email = "artitzco@proton.me" },
10
10
  ]
@@ -1,8 +1,10 @@
1
1
  from pathlib import Path
2
2
  import os
3
3
 
4
+ import sys
4
5
 
5
- def locate_project(level=None, forced=False):
6
+
7
+ def locate_project(level=None, forced=False, source=None):
6
8
  """
7
9
  Change the current working directory to a project root based on a search pattern or parent level.
8
10
 
@@ -19,7 +21,7 @@ def locate_project(level=None, forced=False):
19
21
  - Raises FileNotFoundError if the search fails.
20
22
  """
21
23
  global_vars = globals()
22
- varname = '__locate__project__'
24
+ varname = 'utilitz__locate__project__'
23
25
  cwd = Path.cwd()
24
26
  if varname in global_vars:
25
27
  if level is None or forced:
@@ -28,6 +30,7 @@ def locate_project(level=None, forced=False):
28
30
  del global_vars[varname]
29
31
  print(f'[INFO] Working directory restored: {cwd}')
30
32
  if not forced:
33
+ set_soruce(source)
31
34
  return
32
35
 
33
36
  if level is not None:
@@ -52,3 +55,32 @@ def locate_project(level=None, forced=False):
52
55
  raise FileNotFoundError(
53
56
  f"Could not find a directory matching level '{level}' from {cwd}"
54
57
  )
58
+ set_soruce(source)
59
+
60
+
61
+ def set_soruce(source):
62
+ if source is None:
63
+ source = []
64
+ if isinstance(source, str):
65
+ source = [source]
66
+
67
+ varname = 'utilitz__set_source__'
68
+ path_list = globals().get(varname, [])
69
+ new_path_list = [str(Path().resolve() / x) for x in source]
70
+
71
+ # agregar nuevos paths
72
+ for x in new_path_list:
73
+ if x not in path_list:
74
+ sys.path.append(x)
75
+ print(f"[INFO] Path added to sys.path: {x}")
76
+
77
+ # quitar paths que ya no están
78
+ for x in path_list:
79
+ if x not in new_path_list:
80
+ if x in sys.path:
81
+ sys.path.remove(x)
82
+ print(f"[INFO] Path removed from sys.path: {x}")
83
+
84
+ # actualizar globals si hubo cambios
85
+ if new_path_list != path_list:
86
+ globals()[varname] = new_path_list
@@ -0,0 +1,66 @@
1
+ from pynput import keyboard, mouse
2
+ from datetime import datetime
3
+ import pyautogui
4
+ import time
5
+
6
+
7
+ class MonitorActivity:
8
+
9
+ def __init__(self):
10
+ self.last_activity_time = time.monotonic()
11
+ self._keyboard_listener = keyboard.Listener(
12
+ on_press=self._on_keyboard_event,
13
+ on_release=self._on_keyboard_event,
14
+ )
15
+ self._mouse_listener = mouse.Listener(
16
+ on_move=self._on_mouse_event,
17
+ on_click=self._on_mouse_event,
18
+ on_scroll=self._on_mouse_event,
19
+ )
20
+
21
+ def _on_keyboard_event(self, *args, **kwargs):
22
+ self.last_activity_time = time.monotonic()
23
+
24
+ def _on_mouse_event(self, *args, **kwargs):
25
+ self.last_activity_time = time.monotonic()
26
+
27
+ def start(self):
28
+ self._keyboard_listener.start()
29
+ self._mouse_listener.start()
30
+
31
+ def stop(self):
32
+ self._keyboard_listener.stop()
33
+ self._mouse_listener.stop()
34
+
35
+ def monitor_keep_alive(seconds, key='ctrl', verbose=1, tolerance = 0.1):
36
+ def time_format(ts):
37
+ return datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S")
38
+
39
+ monitor = MonitorActivity()
40
+ monitor.start()
41
+ status = '==START=='
42
+ if verbose:
43
+ print(time_format(time.time()), status)
44
+ time.sleep(seconds)
45
+ while True:
46
+ sleep_end_time = time.monotonic()
47
+ last_activity_time = monitor.last_activity_time
48
+
49
+ inactive_time = sleep_end_time - last_activity_time
50
+ is_active = inactive_time < seconds
51
+
52
+ if is_active:
53
+ last_status = status
54
+ status = '==ACTIVE=='
55
+ sleep_time = max(tolerance, seconds - inactive_time - tolerance)
56
+ else:
57
+ last_status = status
58
+ status = '==INACTIVE=='
59
+ sleep_time = max(tolerance, seconds - tolerance)
60
+ pyautogui.press(key)
61
+ if verbose == 1 and status != last_status:
62
+ print(time_format(time.time()), status)
63
+ elif verbose == 2:
64
+ print(time_format(time.time()), status,
65
+ '\n\tinactive_time:', f'{inactive_time:.03f}', 'sleep_time:', f'{sleep_time:.03f}')
66
+ time.sleep(sleep_time)
@@ -1,141 +0,0 @@
1
-
2
- from pynput import keyboard, mouse
3
- from datetime import datetime
4
- import pyautogui
5
- import time
6
-
7
-
8
- class MonitorActivity:
9
- """
10
- MonitorActivity
11
- ----------------
12
- Class responsible for listening to keyboard and mouse events via pynput.
13
-
14
- Attributes
15
- ----------
16
- last_activity_time : float
17
- Timestamp of the last detected user activity.
18
-
19
- Methods
20
- -------
21
- start():
22
- Starts the keyboard and mouse listeners.
23
- stop():
24
- Stops the listeners.
25
-
26
- Internal Use
27
- ------------
28
- This class is used by monitor_keep_alive to determine the amount of time
29
- that has passed without any user interaction.
30
- """
31
-
32
- def __init__(self):
33
- self.last_activity_time = time.time()
34
- self._keyboard_listener = keyboard.Listener(
35
- on_press=self._on_keyboard_event,
36
- on_release=self._on_keyboard_event,
37
- )
38
- self._mouse_listener = mouse.Listener(
39
- on_move=self._on_mouse_event,
40
- on_click=self._on_mouse_event,
41
- on_scroll=self._on_mouse_event,
42
- )
43
-
44
- def _on_keyboard_event(self, *args, **kwargs):
45
- self.last_activity_time = time.time()
46
-
47
- def _on_mouse_event(self, *args, **kwargs):
48
- self.last_activity_time = time.time()
49
-
50
- def start(self):
51
- self._keyboard_listener.start()
52
- self._mouse_listener.start()
53
-
54
- def stop(self):
55
- self._keyboard_listener.stop()
56
- self._mouse_listener.stop()
57
-
58
-
59
- def monitor_keep_alive(seconds, key='ctrl', verbose=1):
60
- """
61
- monitor_keep_alive
62
- -------------------
63
- Continuously monitors user activity and triggers an automatic key press
64
- when no keyboard or mouse events occur within the specified interval.
65
-
66
- Parameters
67
- ----------
68
- seconds : int or float
69
- Base interval in seconds to evaluate user activity.
70
- key : str, optional
71
- The key to press when inactivity is detected.
72
- Examples: 'ctrl', 'shift', 'space', 'enter', 'f15', etc.
73
- verbose : int, optional
74
- Controls console logging:
75
- 0 → silent
76
- 1 → prints only when the user status changes (Active/Inactive)
77
- 2 → prints extended debug information (timings, cycle details)
78
-
79
- Behavior
80
- --------
81
- - Starts a real-time activity monitor (keyboard + mouse).
82
- - Every `seconds` seconds, checks whether activity occurred.
83
- - If activity occurred → does nothing.
84
- - If NO activity occurred → presses the configured key.
85
- - Dynamically adjusts sleep intervals based on the last activity time.
86
-
87
- Examples
88
- --------
89
- # Keep session alive by pressing CTRL every 3 seconds of inactivity
90
- monitor_keep_alive(3)
91
-
92
- # Use SHIFT instead of CTRL
93
- monitor_keep_alive(5, key='shift')
94
-
95
- # Verbose debug mode
96
- monitor_keep_alive(10, verbose=2)
97
-
98
- Notes
99
- -----
100
- - The function runs indefinitely; stop it manually when needed.
101
- - pyautogui may require accessibility permissions on macOS.
102
- - pynput listeners run on background threads.
103
-
104
- Warning
105
- -------
106
- This script simulates human activity. It may violate rules in certain
107
- applications, organizations, or platforms. Use at your own risk.
108
- """
109
-
110
- monitor = MonitorActivity()
111
- monitor.start()
112
- status = '==START=='
113
- sleep_start_time = time.time()
114
- time.sleep(seconds)
115
- if verbose:
116
- print(datetime.fromtimestamp(sleep_end_time).strftime("%Y-%m-%d %H:%M:%S"), status)
117
- while True:
118
- sleep_end_time = time.time()
119
- ####
120
- last_activity_time = monitor.last_activity_time
121
- is_active = (last_activity_time - 0.001 > sleep_start_time
122
- and last_activity_time < sleep_end_time)
123
- inactive_time = sleep_end_time-last_activity_time
124
- if is_active:
125
- last_status = status
126
- status = '==ACTIVE=='
127
- sleep_time = seconds - inactive_time
128
- else:
129
- last_status = status
130
- status = '==INACTIVE=='
131
- sleep_time = seconds
132
- pyautogui.press(key)
133
- if verbose == 1 and status != last_status:
134
- print(datetime.fromtimestamp(sleep_end_time).strftime(
135
- "%Y-%m-%d %H:%M:%S"), status)
136
- elif verbose == 2:
137
- print(datetime.fromtimestamp(sleep_end_time).strftime("%Y-%m-%d %H:%M:%S"), status,
138
- '\n\tInactive Time:', f'{inactive_time:.03f}', 'Sleep Time:', f'{sleep_time:.03f}')
139
-
140
- sleep_start_time = time.time()
141
- time.sleep(sleep_time)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes