libtextworker 0.1.4
Cross-platform, free and open library for Python projects
general.py
1 """
2 @package libtextworker.general
3 @brief Utilities (unable to describe)
4 """
5 
6 # A cross-platform library for Python apps.
7 # Copyright (C) 2023-2024 Le Bao Nguyen and contributors.
8 # This is a part of the libtextworker project.
9 # Licensed under the GNU General Public License version 3.0 or later.
10 
11 import logging
12 import os
13 import pathlib
14 import shutil
15 import sys
16 import warnings
17 
18 from importlib import import_module
19 from typing import Literal
20 
21 # @since version 0.1.3
22 # Available GUI toolkits that this library supports
23 available_toolkits = Literal["tk", "wx"]
24 
25 Importable = {}
26 TOPLV_DIR = ""
27 
28 # Classes
29 
30 
31 class Logger(logging.Logger):
32  """
33  A logging class for GUIs.
34  Available toolkits can be checked/set by the UseToolKit attribute.
35  """
36 
37  UseToolKit: available_toolkits | bool = False
38 
39  def UseGUIToolKit(self, toolkit: available_toolkits):
40  """
41  Set's the GUI toolkit to use.
42  Currently there is no support for Tkinter yet.
43  """
44  self.UseToolKitUseToolKit = toolkit
45 
46  def CallGUILog(self, name: Literal["CRITICAL", "DEBUG", "ERROR", "EXCEPTION", "NORMAL", "WARNING"],
47  msg: object, *args: object):
48  """
49  Call GUI toolkit logging function.
50  Do nothing if not able to.
51  """
52  if not self.UseToolKitUseToolKit: return
53  do = import_module(f"libtextworker.interface.{self.UseToolKit}.constants")
54  if args: msg = msg % args
55  getattr(do, "LOG_" + name)(msg)
56 
57  def critical(self, msg: object, *args: object, **kwds):
58  super().critical(msg, *args, **kwds)
59  self.CallGUILogCallGUILog("CRITICAL", msg, *args)
60 
61  def debug(self, msg: object, *args: object, **kwds):
62  super().debug(msg, *args, **kwds)
63  self.CallGUILogCallGUILog("DEBUG", msg, *args)
64 
65  def error(self, msg: object, *args: object, **kwds):
66  super().error(msg, *args, **kwds)
67  self.CallGUILogCallGUILog("ERROR", msg, *args)
68 
69  def exception(self, msg: object, *args: object, **kwds):
70  super().exception(msg, *args, **kwds)
71  self.CallGUILogCallGUILog("EXCEPTION", msg, *args)
72 
73  def log(self, level: int, msg: object, *args: object, **kwds):
74  super().log(level, msg, *args, **kwds)
75  self.CallGUILogCallGUILog("NORMAL", msg, *args)
76 
77  def warning(self, msg: object, *args: object, **kwds):
78  super().warning(msg, *args, **kwds)
79  self.CallGUILogCallGUILog("WARNING", msg, *args)
80 
81 
82 class libTewException(Exception):
83  """
84  Common exception class for libtextworker
85  """
86 
87  def __init__(this, msg: str):
88  logger.error(msg, exc_info=1)
89  Exception.__init__(this, msg)
90 
91 
92 # Functions
93 def CraftItems(*args: str | pathlib.Path) -> str:
94  """
95  Craft any >=2 paths, together.
96  @param *args (str|pathlib.Path)
97  @return str: Result
98  @raise Exception: not enough arguments (must be >=2)
99  """
100  if len(args) < 2:
101  raise Exception("Not enough arguments")
102 
103  final = pathlib.Path(args[0])
104 
105  for i in range(1, len(args)):
106  final /= str(args[i])
107 
108  # Why I didn't know this earlier?
109  # os.path.abspath(path) = os.path.normpath(os.path.join(os.getcwd() + path))
110  # Yeah
111  return os.path.normpath(final)
112 
113 
114 def CreateDirectory(directory: str, childs: list[str] = []):
115  """
116  Create a directory with optional sub-folders.
117  @param directory (str): Directory to create
118  @param childs (list of str): Sub-dirs under $directory
119  @throws Exception: Directory creation failed
120  """
121  if not os.path.isdir(directory):
122  os.mkdir(directory)
123  if childs:
124  for folder in childs:
125  folder = CraftItems(directory, folder)
126  if not os.path.isdir(folder):
127  os.mkdir(folder)
128 
129 
130 def WalkCreation(directory: str):
131  """
132  Create directory layer-to-layer.
133  How to understand this? Try this path: path1/path2/path3.
134  WalkCreation will create path1 first, then path1/path2 and path1/path2/path3. Skip existing dirs, of course.
135 
136  @param directory (str): Directory to create
137  @throws Exception: Directory creation failed
138  """
139  directory = directory.replace("\\", "/")
140  splits = directory.split("/")
141  firstdir = splits[0]
142  for item in range(1, len(splits)):
143  firstdir += "/" + splits[item]
144  if not os.path.isdir(firstdir):
145  os.mkdir(firstdir)
146 
147 
148 def GetCurrentDir(file: str, aspathobj: bool = False):
149  """
150  Get the current directory path.
151  @param file (str): File path
152  @param aspathobj (bool): Return a pathlib.Path object instead of a string.
153  @return pathlib.Path | str
154  """
155  result = pathlib.Path(file).parent
156  if aspathobj:
157  return result
158  return result.__str__()
159 
160 
162  """
163  Reset every configurations under $TOPLV_DIR to default.
164  Will close the app after completed.
165  @see TOPLV_DIR
166  """
167  if os.path.isdir(TOPLV_DIR):
168  shutil.rmtree(TOPLV_DIR)
169  CreateDirectory(TOPLV_DIR)
170  sys.exit()
171 
172 
173 def test_import(pkgname: str) -> bool:
174  """
175  Tests if the specified module name is importable.
176  Results is stored in Importable (dict[str, bool]).
177  @see Importable
178  """
179  try:
180  import_module(pkgname)
181  except ImportError:
182  Importable[pkgname] = False
183  warnings.warn("%s not found" % pkgname)
184  return False
185  else:
186  Importable[pkgname] = True
187  return True
188 
189 
191 logger = Logger("libtextworker", logging.INFO)
192 logging.captureWarnings(True)
193 formatter = logging.Formatter("[%(asctime)s %(levelname)s] %(message)s")
194 
195 
196 strhdlr = logging.StreamHandler()
197 strhdlr.setFormatter(formatter)
198 
199 
200 from time import strftime, localtime
201 logpath = os.path.expanduser(f"~/.logs/libtextworker-{strftime(r'%Y-%m-%d', localtime())}.log")
202 CreateDirectory(os.path.dirname(logpath))
203 if not os.path.isfile(logpath): open(logpath, "w").write("")
204 
205 filehdlr = logging.FileHandler(logpath)
206 filehdlr.setFormatter(formatter)
207 
208 logger.addHandler(strhdlr)
209 logger.addHandler(filehdlr)
bool test_import(str pkgname)
Definition: general.py:173
str CraftItems(*str|pathlib.Path args)
Definition: general.py:93
def WalkCreation(str directory)
Definition: general.py:130
def GetCurrentDir(str file, bool aspathobj=False)
Definition: general.py:148
def CreateDirectory(str directory, list[str] childs=[])
Definition: general.py:114
def UseGUIToolKit(self, available_toolkits toolkit)
Definition: general.py:39
def CallGUILog(self, Literal["CRITICAL", "DEBUG", "ERROR", "EXCEPTION", "NORMAL", "WARNING"] name, object msg, *object args)
Definition: general.py:47