psiutils 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl

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.
psiutils/_logger.py CHANGED
@@ -1,54 +1,95 @@
1
+ # logging_setup.py
1
2
  import logging
3
+ from pathlib import Path
2
4
  from logging.handlers import RotatingFileHandler
5
+ import appdirs
3
6
  import structlog
4
7
 
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
- ],
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,
14
43
  )
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
- ],
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
+ )
28
64
  )
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"),
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'),
41
89
  structlog.stdlib.add_log_level,
42
90
  structlog.stdlib.add_logger_name,
43
91
  structlog.stdlib.PositionalArgumentsFormatter(),
44
92
  structlog.processors.StackInfoRenderer(),
45
93
  structlog.processors.format_exc_info,
46
94
  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()
95
+ ]
psiutils/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.2.2'
1
+ __version__ = '0.2.4'
psiutils/buttons.py CHANGED
@@ -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}}')
Binary file
psiutils/known_paths.py CHANGED
@@ -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:
psiutils/utilities.py CHANGED
@@ -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,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,8 +1,8 @@
1
1
  psiutils/__init__.py,sha256=B8mLHCYCnD0LcwsGAtS3HM9kYdwVnhf6dNXL6i1H4kY,145
2
2
  psiutils/_about_frame.py,sha256=xZQivwvZc-k8C-SkwQne-dBr4gavDAL_32fv8E-7Wt0,7644
3
- psiutils/_logger.py,sha256=52Bn2dAcDm54G7VsH4jDIZRM1gMXHumVCPfVzOS-1X4,1725
4
- psiutils/_version.py,sha256=pKh2_hmFNUwG0JxbL18qxdmi_nmFpgRbR8X6IPZr08o,21
5
- psiutils/buttons.py,sha256=O_0lZD7KszUoP9hsVijxQSScrANpExDviH8-KWEJtAg,8521
3
+ psiutils/_logger.py,sha256=9nXxKWUyu4xqIILjTSUKRAMOG4cFy_EGJTTo0FtUAGU,2859
4
+ psiutils/_version.py,sha256=qfsPKGSX4YbTkJ_mvg4kfAF60Sx0Ol77KSinJilpkVw,21
5
+ psiutils/buttons.py,sha256=38vQc6A0ub1p4GHukNQB3ICx7vi-9M4RAt-eeuDCeBg,8567
6
6
  psiutils/constants.py,sha256=T8AjYBkmuziAWjXgON-Sj0AM62NAGluj_Y6glrOFbHc,1107
7
7
  psiutils/drag_manager.py,sha256=L04GzKIinWXzGCawvTnn18F1AEt8PUPS1U-HqCVrh48,3470
8
8
  psiutils/errors.py,sha256=tAypFkpzpbeOppO9zKQimlnRVyr3sMPoiwh0yK3Yc88,562
@@ -35,6 +35,7 @@ psiutils/icons/reset.png,sha256=UJHvnseHeMJoPNCn-WydTnfX-Lrlp9tgSlcIq6jWUBg,1296
35
35
  psiutils/icons/revert.png,sha256=vWIyLx7dQfTOkI8igLmVtmV8hnBfIh089QmKRRobVmk,1268
36
36
  psiutils/icons/save.png,sha256=RSIfWkVE537tMgchS5VFmHughVQqkKMyjMLY0ccDy98,613
37
37
  psiutils/icons/script.png,sha256=CG5MYayO1X5O7q40byfMwya1_52rp8BYq-FWUDBG5fg,803
38
+ psiutils/icons/search.png,sha256=egPfxgBOZGOeVIV8nvAnwCnv_4vstFzRVmZiqiOPBIA,1491
38
39
  psiutils/icons/send.png,sha256=4PSUVt8NAKLIxYEYpdMQHNigmfjyUSL8VAsMBmBxxmM,968
39
40
  psiutils/icons/start.png,sha256=lzi6occJJXKXlRoFjx76h1YhQy2lLk9IqCf-xDKadDc,1936
40
41
  psiutils/icons/update.png,sha256=buOCd1Mq5M8Jbcd2HX6eZjLAQPxM5psFTGNqkkI7c7c,2212
@@ -43,13 +44,13 @@ psiutils/icons/windows.png,sha256=orrq_I9aYHMcUJHdtfLWbAEJZwzPjja9VMwZPm9jsjg,22
43
44
  psiutils/images/icon-error.png,sha256=Fk1IMjyqXACUbfcCGTHM-Q1Gb_53hzzyRGO1JbxgNB8,5957
44
45
  psiutils/images/icon-info.png,sha256=JFCbGkYfO1BD7VRYQXmTMjOePPRrwjkl-KSFHtKIBE4,5979
45
46
  psiutils/images/icon-query.png,sha256=e18hqkew4eOxABvECKn7BGO2VTHTE-XLMWPSQfSW9dU,2808
46
- psiutils/known_paths.py,sha256=P5AR482y-9YQjL_4VUx4cUTPYhffiTrWLEqQGYL24R4,9158
47
+ psiutils/known_paths.py,sha256=Ydhk-Ie_q827ti35Hru8YwUx5awdO2FEG845k1N0hPo,9543
47
48
  psiutils/menus.py,sha256=4pUHb3fEYzLnsftxdKB_NjlPryj_pu7WN5Iy5Quy9-M,1746
48
49
  psiutils/messagebox.py,sha256=14W4KzEg8Fi1OCiOX9zZvomoSqibAYNipQpa95Tco5M,4969
49
50
  psiutils/text.py,sha256=5M5EOGfKLj_2JeH1lweDX8MJR6bk_aZl2pnRXj3FiHk,824
50
51
  psiutils/treeview.py,sha256=jtSzLWrnFIBDWV5YhWNRoZwgW-Kj8_7XVqZyusr-riQ,2826
51
- psiutils/utilities.py,sha256=rAtDvUYn_1AF22x8kRVeeTh7POLN58wXUA5G_nEULS4,2911
52
+ psiutils/utilities.py,sha256=KZkpCmkvQooRSRe3bfKFHbi4HHX3PSMSj0g4DIpbom0,2914
52
53
  psiutils/widgets.py,sha256=ooGAy0IU3ct9aeqnBqMUr2AVyqg_zKHG8Fm9Gm-Wn5Y,11114
53
- psiutils-0.2.2.dist-info/WHEEL,sha256=NHRAbdxxzyL9K3IO2LjmlNqKSyPZnKv2BD16YYVKo18,79
54
- psiutils-0.2.2.dist-info/METADATA,sha256=63d5U_djo-6I0hq9bvqrA6aXSQkyOEDuwLXnFB-M3NY,1976
55
- psiutils-0.2.2.dist-info/RECORD,,
54
+ psiutils-0.2.4.dist-info/WHEEL,sha256=Jb20R3Ili4n9P1fcwuLup21eQ5r9WXhs4_qy7VTrgPI,79
55
+ psiutils-0.2.4.dist-info/METADATA,sha256=RCRhoq5FsNq8a-RMpzTDAQ87y68V7LWSTSKDxnONBek,1976
56
+ psiutils-0.2.4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.8.14
2
+ Generator: uv 0.8.15
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any