psiutils 0.2.2__tar.gz → 0.2.4__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.
Files changed (58) hide show
  1. {psiutils-0.2.2 → psiutils-0.2.4}/PKG-INFO +1 -1
  2. {psiutils-0.2.2 → psiutils-0.2.4}/pyproject.toml +1 -1
  3. psiutils-0.2.4/src/psiutils/_logger.py +95 -0
  4. psiutils-0.2.4/src/psiutils/_version.py +1 -0
  5. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/buttons.py +11 -8
  6. psiutils-0.2.4/src/psiutils/icons/search.png +0 -0
  7. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/known_paths.py +28 -15
  8. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/utilities.py +1 -2
  9. psiutils-0.2.2/src/psiutils/_logger.py +0 -54
  10. psiutils-0.2.2/src/psiutils/_version.py +0 -1
  11. {psiutils-0.2.2 → psiutils-0.2.4}/README.md +0 -0
  12. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/__init__.py +0 -0
  13. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/_about_frame.py +0 -0
  14. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/constants.py +0 -0
  15. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/drag_manager.py +0 -0
  16. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/errors.py +0 -0
  17. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icecream_init.py +0 -0
  18. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/build.png +0 -0
  19. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/cancel.png +0 -0
  20. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/check.png +0 -0
  21. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/clear.png +0 -0
  22. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/code.png +0 -0
  23. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/compare.png +0 -0
  24. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/copy_clipboard.png +0 -0
  25. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/copy_docs.png +0 -0
  26. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/delete.png +0 -0
  27. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/diff.png +0 -0
  28. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/done.png +0 -0
  29. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/edit.png +0 -0
  30. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/gear.png +0 -0
  31. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/new.png +0 -0
  32. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/next.png +0 -0
  33. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/open.png +0 -0
  34. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/pause.png +0 -0
  35. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/preferences.png +0 -0
  36. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/previous.png +0 -0
  37. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/process.png +0 -0
  38. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/redo.png +0 -0
  39. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/refresh.png +0 -0
  40. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/rename.png +0 -0
  41. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/report.png +0 -0
  42. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/reset.png +0 -0
  43. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/revert.png +0 -0
  44. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/save.png +0 -0
  45. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/script.png +0 -0
  46. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/send.png +0 -0
  47. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/start.png +0 -0
  48. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/update.png +0 -0
  49. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/upgrade.png +0 -0
  50. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/icons/windows.png +0 -0
  51. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/images/icon-error.png +0 -0
  52. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/images/icon-info.png +0 -0
  53. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/images/icon-query.png +0 -0
  54. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/menus.py +0 -0
  55. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/messagebox.py +0 -0
  56. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/text.py +0 -0
  57. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/treeview.py +0 -0
  58. {psiutils-0.2.2 → psiutils-0.2.4}/src/psiutils/widgets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: psiutils
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Various TKinter utilities.
5
5
  Author: Jeff
6
6
  Author-email: Jeff <<jeffwatkins2000@gmail.com>>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "psiutils"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  description = "Various TKinter utilities."
5
5
  authors = [{name = "Jeff", "email" = "<jeffwatkins2000@gmail.com>"}]
6
6
  requires-python = '==3.13.*'
@@ -0,0 +1,95 @@
1
+ # logging_setup.py
2
+ import logging
3
+ from pathlib import Path
4
+ from logging.handlers import RotatingFileHandler
5
+ import appdirs
6
+ import structlog
7
+
8
+ LOG_FILE_NAME = 'app.log'
9
+ MAX_BYTES = 5_000_000
10
+ BACKUP_COUNT = 5
11
+
12
+
13
+ def psi_logger(app_name: str, level=logging.INFO):
14
+ """
15
+ Creates and configures a logger for the specified application.
16
+
17
+ Args:
18
+ app_name (str): The name of the application.
19
+ level (int): The logging level (default is logging.INFO).
20
+
21
+ Returns:
22
+ Logger: A configured logger for the application.
23
+
24
+ Examples:
25
+ logger = psi_logger("my_app", logging.DEBUG)
26
+ """
27
+
28
+ log_file = _log_file(app_name)
29
+ console_handler = _console_handler(level)
30
+ file_handler = _file_handler(log_file, level)
31
+
32
+ root_logger = logging.getLogger()
33
+ root_logger.setLevel(level)
34
+ root_logger.addHandler(console_handler)
35
+ root_logger.addHandler(file_handler)
36
+
37
+ structlog.configure(
38
+ processors=_processors(),
39
+ wrapper_class=structlog.make_filtering_bound_logger(level),
40
+ context_class=dict,
41
+ logger_factory=structlog.stdlib.LoggerFactory(),
42
+ cache_logger_on_first_use=True,
43
+ )
44
+
45
+ return structlog.get_logger()
46
+
47
+
48
+ def _log_file(app_name: str) -> Path:
49
+ """Return the path to the application log file."""
50
+ log_dir = Path(appdirs.user_data_dir(app_name))
51
+ log_dir.mkdir(parents=True, exist_ok=True)
52
+ return Path(log_dir, LOG_FILE_NAME)
53
+
54
+
55
+ def _console_handler(level=logging.INFO) -> logging.StreamHandler:
56
+ """Return the console handler for the logger."""
57
+ console_handler = logging.StreamHandler()
58
+ console_handler.setLevel(level)
59
+ console_handler.setFormatter(
60
+ structlog.stdlib.ProcessorFormatter(
61
+ processor=structlog.dev.ConsoleRenderer(),
62
+ foreign_pre_chain=[structlog.processors.TimeStamper(fmt='iso')],
63
+ )
64
+ )
65
+ return console_handler
66
+
67
+
68
+ def _file_handler(log_file: Path, level=logging.INFO) -> RotatingFileHandler:
69
+ """Return the console handler for the logger."""
70
+ file_handler = RotatingFileHandler(
71
+ str(log_file),
72
+ maxBytes=MAX_BYTES,
73
+ backupCount=BACKUP_COUNT,
74
+ encoding='utf-8'
75
+ )
76
+ file_handler.setLevel(level)
77
+ file_handler.setFormatter(
78
+ structlog.stdlib.ProcessorFormatter(
79
+ processor=structlog.processors.JSONRenderer(),
80
+ foreign_pre_chain=[structlog.processors.TimeStamper(fmt='iso')],
81
+ )
82
+ )
83
+ return file_handler
84
+
85
+
86
+ def _processors() -> list:
87
+ return [
88
+ structlog.processors.TimeStamper(fmt='iso'),
89
+ structlog.stdlib.add_log_level,
90
+ structlog.stdlib.add_logger_name,
91
+ structlog.stdlib.PositionalArgumentsFormatter(),
92
+ structlog.processors.StackInfoRenderer(),
93
+ structlog.processors.format_exc_info,
94
+ structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
95
+ ]
@@ -0,0 +1 @@
1
+ __version__ = '0.2.4'
@@ -63,14 +63,14 @@ class IconButton(ttk.Frame):
63
63
 
64
64
  def bind_widgets(self):
65
65
  for widget in (self, self.button_label):
66
- widget.bind("<Button-1>", self._on_click)
67
- widget.bind("<Enter>", self._enter_button)
68
- widget.bind("<Leave>", lambda e: self.config(relief="raised"))
66
+ widget.bind('<Button-1>', self._on_click)
67
+ widget.bind('<Enter>', self._enter_button)
68
+ widget.bind('<Leave>', lambda e: self.config(relief='raised'))
69
69
 
70
70
  def _enter_button(self, event) -> None:
71
71
  if self._state == tk.DISABLED:
72
72
  return
73
- self.config(relief="sunken")
73
+ self.config(relief='sunken')
74
74
  event.widget.winfo_toplevel().config(cursor=HAND)
75
75
 
76
76
  def _on_click(self, *args):
@@ -122,8 +122,11 @@ class ButtonFrame(ttk.Frame):
122
122
  }
123
123
 
124
124
  def icon_button(
125
- self, id: str, dimmable: bool = False, command=None) -> IconButton:
126
- button = self.icon_buttons[id]
125
+ self,
126
+ id_: str,
127
+ dimmable: bool = False,
128
+ command: object = None) -> IconButton:
129
+ button = self.icon_buttons[id_]
127
130
  button.dimmable = dimmable
128
131
  button.command = command
129
132
  return button
@@ -267,5 +270,5 @@ def list_icon_buttons() -> None:
267
270
 
268
271
  for name, button in icon_buttons.items():
269
272
  print(f'{name:<{name_length}} '
270
- f'{button[0]:<{text_length}} '
271
- f'{button[1]:<{icon_length}}')
273
+ f'{button[0]:<{text_length}} '
274
+ f'{button[1]:<{icon_length}}')
@@ -24,23 +24,25 @@ from pathlib import Path
24
24
  import platform
25
25
  from typing import Any
26
26
 
27
- MS_FOLDERS = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
27
+ #MS_FOLDERS =
28
+ # r'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
28
29
 
29
30
 
30
31
  class GUID(ctypes.Structure):
31
32
  _fields_ = [
32
- ("Data1", wintypes.DWORD),
33
- ("Data2", wintypes.WORD),
34
- ("Data3", wintypes.WORD),
35
- ("Data4", wintypes.BYTE * 8)
33
+ ("Data1", ctypes.c_ulong),
34
+ ("Data2", ctypes.c_ushort),
35
+ ("Data3", ctypes.c_ushort),
36
+ ("Data4", ctypes.c_ubyte * 8),
36
37
  ]
37
38
 
38
- def __init__(self, uuid_: Any) -> None:
39
+ def __init__(self, guid_string):
40
+ str_uuid = UUID(guid_string)
39
41
  ctypes.Structure.__init__(self)
40
- (self.Data1, self.Data2, self.Data3,
41
- self.Data4[0], self.Data4[1], rest) = uuid_.fields
42
- for index in range(2, 8):
43
- self.Data4[index] = rest >> (8 - index - 1)*8 & 0xff
42
+ self.Data1 = str_uuid.time_low
43
+ self.Data2 = str_uuid.time_mid
44
+ self.Data3 = str_uuid.time_hi_version
45
+ self.Data4[:] = str_uuid.bytes[8:]
44
46
 
45
47
 
46
48
  class folder_id:
@@ -163,11 +165,22 @@ class PathNotFoundException(Exception):
163
165
 
164
166
 
165
167
  def get_path(folder: str) -> str | None:
166
- import winreg
167
- fid = f'{{{getattr(folder_id, folder)}}}'
168
- with winreg.OpenKey(winreg.HKEY_CURRENT_USER, MS_FOLDERS) as key:
169
- location = winreg.QueryValueEx(key, fid)[0]
170
- return location
168
+ # FOLDERID_Documents GUID
169
+ fid = GUID(f'{{{getattr(folder_id, folder)}}}')
170
+
171
+ SHGetKnownFolderPath = ctypes.windll.shell32.SHGetKnownFolderPath
172
+ SHGetKnownFolderPath.argtypes = [
173
+ ctypes.POINTER(GUID),
174
+ wintypes.DWORD, wintypes.HANDLE,
175
+ ctypes.POINTER(ctypes.c_wchar_p)]
176
+ SHGetKnownFolderPath.restype = ctypes.HRESULT
177
+
178
+ path_ptr = ctypes.c_wchar_p()
179
+ result = SHGetKnownFolderPath(
180
+ ctypes.byref(fid), 0, None, ctypes.byref(path_ptr))
181
+ if result != 0:
182
+ raise OSError("SHGetKnownFolderPath failed")
183
+ return Path(path_ptr.value)
171
184
 
172
185
 
173
186
  def get_downloads_dir() -> Path | str:
@@ -7,8 +7,7 @@ import platform
7
7
 
8
8
  from psiconfig import TomlConfig
9
9
  import psiutils.text as txt
10
- from psiutils._logger import logger
11
-
10
+ from psiutils._logger import psi_logger
12
11
 
13
12
  DEFAULT_GEOMETRY = '500x400'
14
13
 
@@ -1,54 +0,0 @@
1
- import logging
2
- from logging.handlers import RotatingFileHandler
3
- import structlog
4
-
5
- # === 1. Console handler with pretty dev output ===
6
- console_handler = logging.StreamHandler()
7
- console_handler.setLevel(logging.INFO)
8
- console_handler.setFormatter(
9
- structlog.stdlib.ProcessorFormatter(
10
- processor=structlog.dev.ConsoleRenderer(),
11
- foreign_pre_chain=[
12
- structlog.processors.TimeStamper(fmt="iso"),
13
- ],
14
- )
15
- )
16
-
17
- # === 2. File handler with JSON output ===
18
- file_handler = RotatingFileHandler(
19
- "app.log", maxBytes=5_000_000, backupCount=5, encoding="utf-8"
20
- )
21
- file_handler.setLevel(logging.INFO)
22
- file_handler.setFormatter(
23
- structlog.stdlib.ProcessorFormatter(
24
- processor=structlog.processors.JSONRenderer(), # Structured JSON
25
- foreign_pre_chain=[
26
- structlog.processors.TimeStamper(fmt="iso"),
27
- ],
28
- )
29
- )
30
-
31
- # === 3. Attach both handlers ===
32
- root_logger = logging.getLogger()
33
- root_logger.setLevel(logging.INFO)
34
- root_logger.addHandler(console_handler)
35
- root_logger.addHandler(file_handler)
36
-
37
- # === 4. Configure structlog to use stdlib logging ===
38
- structlog.configure(
39
- processors=[
40
- structlog.processors.TimeStamper(fmt="iso"),
41
- structlog.stdlib.add_log_level,
42
- structlog.stdlib.add_logger_name,
43
- structlog.stdlib.PositionalArgumentsFormatter(),
44
- structlog.processors.StackInfoRenderer(),
45
- structlog.processors.format_exc_info,
46
- structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
47
- ],
48
- wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
49
- context_class=dict,
50
- logger_factory=structlog.stdlib.LoggerFactory(),
51
- cache_logger_on_first_use=True,
52
- )
53
-
54
- logger = structlog.get_logger()
@@ -1 +0,0 @@
1
- __version__ = '0.2.2'
File without changes
File without changes
File without changes