absfuyu 5.10.0__py3-none-any.whl → 5.11.0__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.

Potentially problematic release.


This version of absfuyu might be problematic. Click here for more details.

Files changed (81) hide show
  1. absfuyu/__init__.py +1 -1
  2. absfuyu/__main__.py +2 -2
  3. absfuyu/cli/__init__.py +2 -2
  4. absfuyu/cli/color.py +2 -2
  5. absfuyu/cli/config_group.py +2 -2
  6. absfuyu/cli/do_group.py +2 -2
  7. absfuyu/cli/game_group.py +2 -2
  8. absfuyu/cli/tool_group.py +2 -2
  9. absfuyu/config/__init__.py +2 -2
  10. absfuyu/core/__init__.py +2 -2
  11. absfuyu/core/baseclass.py +20 -3
  12. absfuyu/core/baseclass2.py +2 -2
  13. absfuyu/core/decorator.py +2 -2
  14. absfuyu/core/docstring.py +2 -2
  15. absfuyu/core/dummy_cli.py +2 -2
  16. absfuyu/core/dummy_func.py +2 -2
  17. absfuyu/dxt/__init__.py +2 -2
  18. absfuyu/dxt/dictext.py +2 -2
  19. absfuyu/dxt/dxt_support.py +2 -2
  20. absfuyu/dxt/intext.py +2 -2
  21. absfuyu/dxt/listext.py +2 -2
  22. absfuyu/dxt/strext.py +2 -2
  23. absfuyu/extra/__init__.py +2 -2
  24. absfuyu/extra/beautiful.py +2 -2
  25. absfuyu/extra/da/__init__.py +2 -2
  26. absfuyu/extra/da/dadf.py +2 -2
  27. absfuyu/extra/da/dadf_base.py +2 -2
  28. absfuyu/extra/da/df_func.py +2 -2
  29. absfuyu/extra/da/mplt.py +2 -2
  30. absfuyu/extra/data_analysis.py +2 -2
  31. absfuyu/extra/pdf.py +2 -2
  32. absfuyu/extra/rclone.py +2 -2
  33. absfuyu/extra/xml.py +2 -2
  34. absfuyu/fun/__init__.py +2 -2
  35. absfuyu/fun/rubik.py +2 -2
  36. absfuyu/fun/tarot.py +2 -2
  37. absfuyu/game/__init__.py +2 -2
  38. absfuyu/game/game_stat.py +2 -2
  39. absfuyu/game/schulte.py +2 -2
  40. absfuyu/game/sudoku.py +2 -2
  41. absfuyu/game/tictactoe.py +2 -2
  42. absfuyu/game/wordle.py +2 -2
  43. absfuyu/general/__init__.py +2 -2
  44. absfuyu/general/content.py +2 -2
  45. absfuyu/general/human.py +2 -2
  46. absfuyu/general/shape.py +2 -2
  47. absfuyu/general/tax.py +324 -0
  48. absfuyu/logger.py +2 -2
  49. absfuyu/pkg_data/__init__.py +2 -2
  50. absfuyu/pkg_data/deprecated.py +2 -2
  51. absfuyu/pkg_data/logo.py +2 -2
  52. absfuyu/sort.py +2 -2
  53. absfuyu/tools/__init__.py +2 -2
  54. absfuyu/tools/checksum.py +2 -2
  55. absfuyu/tools/converter.py +2 -2
  56. absfuyu/tools/generator.py +2 -2
  57. absfuyu/tools/inspector.py +2 -2
  58. absfuyu/tools/keygen.py +2 -2
  59. absfuyu/tools/obfuscator.py +2 -2
  60. absfuyu/tools/passwordlib.py +2 -2
  61. absfuyu/tools/shutdownizer.py +2 -2
  62. absfuyu/tools/sw.py +138 -7
  63. absfuyu/tools/web.py +2 -2
  64. absfuyu/typings.py +2 -2
  65. absfuyu/util/__init__.py +2 -2
  66. absfuyu/util/api.py +2 -2
  67. absfuyu/util/gui.py +2 -2
  68. absfuyu/util/json_method.py +2 -2
  69. absfuyu/util/lunar.py +2 -2
  70. absfuyu/util/path.py +2 -2
  71. absfuyu/util/performance.py +2 -2
  72. absfuyu/util/shorten_number.py +2 -2
  73. absfuyu/util/text_table.py +2 -2
  74. absfuyu/util/zipped.py +2 -2
  75. absfuyu/version.py +2 -2
  76. {absfuyu-5.10.0.dist-info → absfuyu-5.11.0.dist-info}/METADATA +1 -1
  77. absfuyu-5.11.0.dist-info/RECORD +85 -0
  78. absfuyu-5.10.0.dist-info/RECORD +0 -84
  79. {absfuyu-5.10.0.dist-info → absfuyu-5.11.0.dist-info}/WHEEL +0 -0
  80. {absfuyu-5.10.0.dist-info → absfuyu-5.11.0.dist-info}/entry_points.txt +0 -0
  81. {absfuyu-5.10.0.dist-info → absfuyu-5.11.0.dist-info}/licenses/LICENSE +0 -0
absfuyu/general/tax.py ADDED
@@ -0,0 +1,324 @@
1
+ """
2
+ Absfuyu: Tax calculator
3
+ -----------------------
4
+ Tax calculator
5
+
6
+ Version: 5.11.0
7
+ Date updated: 05/10/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["PersonalIncomeTaxCalculator"]
13
+
14
+
15
+ # Library
16
+ # ---------------------------------------------------------------------------
17
+ from dataclasses import dataclass
18
+ from typing import Literal, TypedDict, overload
19
+
20
+ from absfuyu.core.baseclass import BaseClass, BaseDataclass
21
+
22
+ # Class
23
+ # ---------------------------------------------------------------------------
24
+ TaxLevel = list[tuple[float | int | None, float]]
25
+
26
+
27
+ @dataclass
28
+ class TaxLevelResult(BaseDataclass):
29
+ """
30
+ Result for a single tax level.
31
+
32
+ Parameters
33
+ ----------
34
+ lower : float
35
+ Lower bound
36
+
37
+ upper : float | None
38
+ Upper bound. ``None`` means +infinite
39
+
40
+ rate : float
41
+ Tax percentage in decimal (0.1 for 10%)
42
+
43
+ amount_taxed : float
44
+ Amount to calculate in current tax level
45
+
46
+ tax : float
47
+ Tax amount in current tax level
48
+ """
49
+
50
+ lower: float
51
+ upper: float | None
52
+ rate: float
53
+ amount_taxed: float
54
+ tax: float
55
+
56
+
57
+ class TaxCalculationResult(TypedDict):
58
+ """
59
+ Tax calculation result.
60
+
61
+ Parameters
62
+ ----------
63
+ gross_income : float
64
+ The input gross income for the calculation.
65
+
66
+ deductions : float, optional
67
+ Amount to subtract from gross income before tax.
68
+
69
+ taxable_income : float
70
+ Gross income minus deductions.
71
+
72
+ per_tax_level : list[TaxLevelResult]
73
+ Detailed breakdown of income taxed at each tax level.
74
+
75
+ gross_tax : float
76
+ Total tax before credits.
77
+
78
+ tax_credits : float, optional
79
+ Amount subtracted from the computed tax after calculation.
80
+
81
+ net_tax : float
82
+ Total tax after applying credits.
83
+
84
+ effective_rate : float
85
+ Ratio of net tax to gross income (net_tax / gross_income).
86
+
87
+ marginal_rate : float
88
+ Tax rate applied to the last amount of money of taxable income.
89
+ """
90
+
91
+ gross_income: float
92
+ deductions: float
93
+ taxable_income: float
94
+ per_tax_level: list[TaxLevelResult]
95
+ gross_tax: float
96
+ tax_credits: float
97
+ net_tax: float
98
+ effective_rate: float
99
+ marginal_rate: float
100
+
101
+
102
+ class TaxCalculationResultRaw(TypedDict):
103
+ """
104
+ Tax calculation result.
105
+
106
+ Parameters
107
+ ----------
108
+ gross_income : float
109
+ The input gross income for the calculation.
110
+
111
+ deductions : float, optional
112
+ Amount to subtract from gross income before tax.
113
+
114
+ taxable_income : float
115
+ Gross income minus deductions.
116
+
117
+ per_tax_level : list[TaxLevelResult]
118
+ Detailed breakdown of income taxed at each tax level.
119
+
120
+ gross_tax : float
121
+ Total tax before credits.
122
+
123
+ tax_credits : float, optional
124
+ Amount subtracted from the computed tax after calculation.
125
+
126
+ net_tax : float
127
+ Total tax after applying credits.
128
+
129
+ effective_rate : float
130
+ Ratio of net tax to gross income (net_tax / gross_income).
131
+
132
+ marginal_rate : float
133
+ Tax rate applied to the last amount of money of taxable income.
134
+ """
135
+
136
+ gross_income: float
137
+ deductions: float
138
+ taxable_income: float
139
+ per_tax_level: list[dict[str, float | None]]
140
+ gross_tax: float
141
+ tax_credits: float
142
+ net_tax: float
143
+ effective_rate: float
144
+ marginal_rate: float
145
+
146
+
147
+ class PersonalIncomeTaxCalculator(BaseClass):
148
+ """
149
+ Progressive personal income tax calculator.
150
+
151
+ Parameters
152
+ ----------
153
+ tax_levels : list[tuple[float | int | None, float]], optional
154
+ Ordered list of tax levels.
155
+ Each tax level is represented as a tuple of (upper_bound, rate),
156
+ where ``upper_bound`` is the cumulative upper limit of the tax level.
157
+ Use ``None`` for the last tax level (no upper limit).
158
+ Example: ``[(10000, 0.10), (50000, 0.2), (None, 0.3)]``.
159
+ Set to ``None`` to have 0% tax rate, by default ``None``
160
+
161
+ deductions : float, optional
162
+ Amount to subtract from gross income before tax, by default 0.0
163
+
164
+ tax_credits : float, optional
165
+ Amount subtracted from the computed tax after calculation, by default 0.0
166
+
167
+ Attributes
168
+ ----------
169
+ gross_income : float
170
+ The input gross income for the calculation.
171
+
172
+ taxable_income : float
173
+ Gross income minus deductions.
174
+
175
+ per_tax_level : list[TaxLevelResult]
176
+ Detailed breakdown of income taxed at each tax level.
177
+
178
+ gross_tax : float
179
+ Total tax before credits.
180
+
181
+ net_tax : float
182
+ Total tax after applying credits.
183
+
184
+ effective_rate : float
185
+ Ratio of net tax to gross income.
186
+
187
+ marginal_rate : float
188
+ Tax rate applied to the last amount of money of taxable income.
189
+ """
190
+
191
+ def __init__(self, tax_levels: TaxLevel | None = None, deductions: float = 0.0, tax_credits: float = 0.0) -> None:
192
+ """
193
+ Progressive personal income tax calculator.
194
+
195
+ Parameters
196
+ ----------
197
+ tax_levels : list[tuple[float | int | None, float]], optional
198
+ Ordered list of tax levels.
199
+ Each tax level is represented as a tuple of (upper_bound, rate),
200
+ where ``upper_bound`` is the cumulative upper limit of the tax level.
201
+ Use ``None`` for the last tax level (no upper limit).
202
+ Example: ``[(10000, 0.10), (50000, 0.2), (None, 0.3)]``.
203
+ Set to ``None`` to have 0% tax rate, by default ``None``
204
+
205
+ deductions : float, optional
206
+ Amount to subtract from gross income before tax, by default 0.0
207
+
208
+ tax_credits : float, optional
209
+ Amount subtracted from the computed tax after calculation, by default 0.0
210
+ """
211
+ self.tax_levels = [] if tax_levels is None else tax_levels
212
+ self.deductions = deductions
213
+ self.tax_credits = tax_credits
214
+
215
+ # Results populated after calculation
216
+ self.gross_income: float = 0.0
217
+ self.taxable_income: float = 0.0
218
+ self.per_tax_level: list[TaxLevelResult] = []
219
+ self.gross_tax: float = 0.0
220
+ self.net_tax: float = 0.0
221
+ self.effective_rate: float = 0.0
222
+ self.marginal_rate: float = 0.0
223
+
224
+ def calculate(self, gross_income: float) -> None:
225
+ """
226
+ Compute tax for a given gross income.
227
+
228
+ Parameters
229
+ ----------
230
+ gross_income : float
231
+ Total gross income (unless tax levels are defined otherwise).
232
+ """
233
+ if gross_income < 0:
234
+ raise ValueError("gross_income must be non-negative")
235
+
236
+ self.gross_income = gross_income
237
+ self.taxable_income = max(0.0, gross_income - max(0.0, self.deductions))
238
+
239
+ self.per_tax_level = []
240
+ prev_upper = 0.0
241
+ remaining = self.taxable_income
242
+ self.gross_tax = 0.0
243
+ self.marginal_rate = 0.0
244
+
245
+ for upper, rate in self.tax_levels:
246
+ lower = prev_upper
247
+ if upper is None:
248
+ amount_taxed = remaining
249
+ else:
250
+ band = upper - prev_upper
251
+ amount_taxed = min(band, max(0.0, remaining))
252
+ tax = max(0.0, amount_taxed) * rate
253
+ self.per_tax_level.append(TaxLevelResult(lower, upper, rate, amount_taxed, tax))
254
+ self.gross_tax += tax
255
+ remaining -= amount_taxed
256
+ prev_upper = upper if upper is not None else prev_upper
257
+ if remaining <= 1e-9:
258
+ self.marginal_rate = rate
259
+ break
260
+ self.marginal_rate = rate
261
+
262
+ self.net_tax = max(0.0, self.gross_tax - max(0.0, self.tax_credits))
263
+ self.effective_rate = self.net_tax / gross_income if gross_income > 0 else 0.0
264
+
265
+ @overload
266
+ def to_dict(self) -> TaxCalculationResult: ... # type: ignore
267
+ @overload
268
+ def to_dict(self, *, raw: Literal[True] = ...) -> TaxCalculationResultRaw: ...
269
+ def to_dict(self, *, raw: bool = False) -> TaxCalculationResult | TaxCalculationResultRaw:
270
+ """
271
+ Returns calculation result in dict format
272
+
273
+ Parameters
274
+ ----------
275
+ raw : bool, optional
276
+ Convert every value to dict, by default ``False``
277
+
278
+ Returns
279
+ -------
280
+ TaxCalculationResult
281
+ Tax calculation result
282
+ """
283
+ result: TaxCalculationResult = {
284
+ "gross_income": self.gross_income,
285
+ "deductions": self.deductions,
286
+ "taxable_income": self.taxable_income,
287
+ "per_tax_level": self.per_tax_level,
288
+ "gross_tax": self.gross_tax,
289
+ "tax_credits": self.tax_credits,
290
+ "net_tax": self.net_tax,
291
+ "effective_rate": self.effective_rate,
292
+ "marginal_rate": self.marginal_rate,
293
+ }
294
+ if raw:
295
+ result_raw: TaxCalculationResultRaw = result # type: ignore
296
+ result_raw["per_tax_level"] = [x.to_dict() for x in result["per_tax_level"]]
297
+ return result
298
+ return result
299
+
300
+ def interpret_result(self) -> str:
301
+ result = self.to_dict()
302
+ text = ["===== Tax information ====="]
303
+
304
+ # text.append(f"Gross income: {result['gross_income']:,}")
305
+ # text.append(f"Deduction: {result['deductions']:,}")
306
+ # text.append(f"Taxable income: {result['taxable_income']:,}")
307
+ text.append(
308
+ f"Taxable income: {result['taxable_income']:,.2f} ({result['gross_income']:,.2f} - {result['deductions']:,.2f})"
309
+ )
310
+
311
+ for idx, tax_level in enumerate(result["per_tax_level"], start=1):
312
+ upper = f"{tax_level.upper:,.2f}" if tax_level.upper is not None else f"{self.gross_income:,.2f}"
313
+ text.append(
314
+ f"- Level {idx}: {tax_level.lower:,.2f} - {upper} @ {tax_level.rate*100:.1f}%: {tax_level.amount_taxed:,.2f} -> tax {tax_level.tax:,.2f}"
315
+ )
316
+
317
+ # text.append(f"Gross tax: {result['gross_tax']:,}")
318
+ # text.append(f"Tax credits: {result['tax_credits']:,}")
319
+ # text.append(f"Net tax: {result['net_tax']:,}")
320
+ text.append(f"Net tax: {result['net_tax']:,.2f} ({result['gross_tax']:,.2f} - {result['tax_credits']:,.2f})")
321
+
322
+ text.append(f"Effective rate: {result['effective_rate']*100:.2f}%")
323
+ text.append(f"Marginal rate: {result['marginal_rate']*100:.1f}%")
324
+ return "\n".join(text)
absfuyu/logger.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Logger
3
3
  ---------------
4
4
  Custom Logger Module
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
 
9
9
  Usage:
10
10
  ------
@@ -3,8 +3,8 @@ Absfuyu: Package data
3
3
  ---------------------
4
4
  Load package data
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Package data
3
3
  ---------------------
4
4
  Deprecated (but might have some use)
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Library
absfuyu/pkg_data/logo.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Logo
3
3
  -------------
4
4
  Absfuyu logo in base64 string for easy access
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  from enum import StrEnum
absfuyu/sort.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Sort
3
3
  -------------
4
4
  Sort Module
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
absfuyu/tools/__init__.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Tools
3
3
  --------------
4
4
  Some useful tools
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/tools/checksum.py CHANGED
@@ -3,8 +3,8 @@ Absufyu: Checksum
3
3
  -----------------
4
4
  Check MD5, SHA256, ...
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absufyu: Converter
3
3
  ------------------
4
4
  Convert stuff
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
 
9
9
  Feature:
10
10
  --------
@@ -3,8 +3,8 @@ Absfuyu: Generator
3
3
  ------------------
4
4
  This generate stuff (Not python's ``generator``)
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
 
9
9
  Features:
10
10
  ---------
@@ -3,8 +3,8 @@ Absfuyu: Inspector
3
3
  ------------------
4
4
  Inspector
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
absfuyu/tools/keygen.py CHANGED
@@ -5,8 +5,8 @@ Mod7 product key generator (90's)
5
5
 
6
6
  This is for educational and informative purposes only.
7
7
 
8
- Version: 5.10.0
9
- Date updated: 23/09/2025 (dd/mm/yyyy)
8
+ Version: 5.11.0
9
+ Date updated: 30/09/2025 (dd/mm/yyyy)
10
10
  """
11
11
 
12
12
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Obfuscator
3
3
  -------------------
4
4
  Obfuscate code
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Passwordlib
3
3
  --------------------
4
4
  Password library
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Shutdownizer
3
3
  ---------------------
4
4
  This shutdowns
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
absfuyu/tools/sw.py CHANGED
@@ -3,8 +3,8 @@ Absufyu: Software
3
3
  -----------------
4
4
  Software, pyinstaller related stuff
5
5
 
6
- Version: 5.10.0
7
- Date updated: 23/09/2025 (dd/mm/yyyy)
6
+ Version: 5.11.0
7
+ Date updated: 30/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -14,12 +14,14 @@ __all__ = [
14
14
  "get_system_info",
15
15
  "get_pyinstaller_exe_dir",
16
16
  "get_pyinstaller_resource_path",
17
+ "PyinstallerHelper",
17
18
  "HWIDgen",
18
19
  "LicenseKeySystem",
19
20
  "BasicSoftwareProtection",
20
21
  # Support
21
22
  "SystemInfo",
22
23
  "LicenseKey",
24
+ "PyinstallerHiddenImportPreset",
23
25
  ]
24
26
 
25
27
 
@@ -46,7 +48,7 @@ from absfuyu.tools.converter import Base64EncodeDecode
46
48
  from absfuyu.util import stop_after_day
47
49
 
48
50
 
49
- # System Info
51
+ # MARK: System Info
50
52
  # ---------------------------------------------------------------------------
51
53
  class SystemInfo(NamedTuple):
52
54
  """System info"""
@@ -93,7 +95,7 @@ def get_system_info() -> SystemInfo:
93
95
  )
94
96
 
95
97
 
96
- # Pyinstaller
98
+ # MARK: Pyinstaller
97
99
  # ---------------------------------------------------------------------------
98
100
  @versionchanged("5.6.1", "Fixed behavior")
99
101
  def get_pyinstaller_exe_dir() -> Path:
@@ -123,7 +125,7 @@ def get_pyinstaller_resource_path(relative_path: str) -> Path:
123
125
  environments and PyInstaller-packaged executables.
124
126
 
125
127
  When running from a PyInstaller bundle, this function resolves the path relative
126
- to the temporary `_MEIPASS` folder. During normal execution, it resolves the path
128
+ to the temporary ``_MEIPASS`` folder. During normal execution, it resolves the path
127
129
  relative to the current script's directory.
128
130
 
129
131
  Parameters
@@ -151,7 +153,136 @@ def get_pyinstaller_resource_path(relative_path: str) -> Path:
151
153
  return base_path / relative_path
152
154
 
153
155
 
154
- # Key System
156
+ class PyinstallerHiddenImportPreset:
157
+ """
158
+ pyinstaller hidden import preset (library preset)
159
+
160
+ Example:
161
+ --------
162
+ >>> PyinstallerHelper(...).add_hidden_import(*PyinstallerHiddenImportPreset.ABSFUYU)
163
+ """
164
+
165
+ ABSFUYU = ["absfuyu"]
166
+ DF = ["pandas", "numpy", "openpyxl", "xlsxwriter"] # DataFrame
167
+ VISUAL = ["rich", "tqdm"]
168
+
169
+
170
+ @versionadded("5.11.0")
171
+ class PyinstallerHelper(BaseClass):
172
+ """pyinstaller helper"""
173
+
174
+ def __init__(
175
+ self,
176
+ path_to_file: str | Path,
177
+ relative_to_cwd: bool = True,
178
+ console: bool = True,
179
+ onefile: bool = False,
180
+ noconfirm: bool = False,
181
+ ) -> None:
182
+ """
183
+ pyinstaller cmd helper
184
+
185
+ Parameters
186
+ ----------
187
+ path_to_file : str | Path
188
+ Path to .py file to make .exe
189
+
190
+ relative_to_cwd : bool, optional
191
+ Is the file relative to cwd, by default True
192
+
193
+ console : bool, optional
194
+ Include console, by default True
195
+
196
+ onefile : bool, optional
197
+ Convert into one file, by default False
198
+
199
+ noconfirm : bool, optional
200
+ No confirmation, by default False
201
+ """
202
+ self.source_path = Path(path_to_file)
203
+ self.relative_to_cwd = relative_to_cwd
204
+ self.console = console
205
+ self.onefile = onefile
206
+ self.noconfirm = noconfirm
207
+
208
+ if self.relative_to_cwd:
209
+ # rel = self.source_path.relative_to(Path.cwd())
210
+ # self._base_cmd = ["pyinstaller", f"'.\\{Path('.').joinpath(rel)}'"]
211
+ self._base_cmd = ["pyinstaller", f"'.\\{self.source_path.relative_to(Path.cwd())}'"]
212
+ else:
213
+ self._base_cmd = ["pyinstaller", f"'{self.source_path.resolve()}'"]
214
+
215
+ self._hidden_import = []
216
+ self._icon = ""
217
+
218
+ def add_hidden_import(self, *library: str) -> None:
219
+ """
220
+ Add hidden import (library)
221
+ """
222
+ if len(self._hidden_import) < 1:
223
+ self._hidden_import = list(library)
224
+ else:
225
+ self._hidden_import.extend(list(library))
226
+
227
+ def add_icon(self, path_to_icon: str | Path, *, relative_to_cwd: bool | None = None) -> None:
228
+ """
229
+ Add icon to .exe
230
+
231
+ Parameters
232
+ ----------
233
+ path_to_icon : str | Path
234
+ Path to icon file
235
+
236
+ relative_to_cwd : bool | None, optional
237
+ Use boolean value to overwrite relative_to_cwd option of the main engine, by default None
238
+ """
239
+ p = Path(path_to_icon)
240
+ use_relative = self.relative_to_cwd if relative_to_cwd is None else relative_to_cwd
241
+
242
+ if use_relative:
243
+ rel = p.relative_to(Path.cwd())
244
+ # ensure it always shows with ./ prefix
245
+ # self._icon = f"'{Path('.').joinpath(rel)}'"
246
+ self._icon = f"'.\\{rel}'"
247
+ else:
248
+ self._icon = f"'{p.resolve()}'"
249
+
250
+ def export_cmd(self) -> str:
251
+ """
252
+ Export pyinstaller cmd
253
+
254
+ Returns
255
+ -------
256
+ str
257
+ pyinstaller command
258
+ """
259
+ cmd = self._base_cmd
260
+
261
+ # Hidden import
262
+ if len(self._hidden_import) > 0:
263
+ dat = (f"--hidden-import={x}" for x in list(set(self._hidden_import)))
264
+ cmd.append(" ".join(dat))
265
+
266
+ # Console
267
+ if not self.console:
268
+ cmd.append("--noconsole")
269
+
270
+ # One file
271
+ if self.onefile:
272
+ cmd.append("--onefile")
273
+
274
+ # No confirm
275
+ if self.noconfirm:
276
+ cmd.append("--noconfirm")
277
+
278
+ # Icon
279
+ if len(self._icon) > 0:
280
+ cmd.append(self._icon)
281
+
282
+ return " ".join(cmd)
283
+
284
+
285
+ # MARK: Key System
155
286
  # ---------------------------------------------------------------------------
156
287
  class HWIDgen(BaseClass):
157
288
  """
@@ -385,7 +516,7 @@ class LicenseKeySystem(BaseClass):
385
516
  return False
386
517
 
387
518
 
388
- # Software
519
+ # MARK: Software
389
520
  # ---------------------------------------------------------------------------
390
521
  class BasicSoftwareProtection(BaseClass):
391
522
  """