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.
- absfuyu/__init__.py +1 -1
- absfuyu/__main__.py +2 -2
- absfuyu/cli/__init__.py +2 -2
- absfuyu/cli/color.py +2 -2
- absfuyu/cli/config_group.py +2 -2
- absfuyu/cli/do_group.py +2 -2
- absfuyu/cli/game_group.py +2 -2
- absfuyu/cli/tool_group.py +2 -2
- absfuyu/config/__init__.py +2 -2
- absfuyu/core/__init__.py +2 -2
- absfuyu/core/baseclass.py +20 -3
- absfuyu/core/baseclass2.py +2 -2
- absfuyu/core/decorator.py +2 -2
- absfuyu/core/docstring.py +2 -2
- absfuyu/core/dummy_cli.py +2 -2
- absfuyu/core/dummy_func.py +2 -2
- absfuyu/dxt/__init__.py +2 -2
- absfuyu/dxt/dictext.py +2 -2
- absfuyu/dxt/dxt_support.py +2 -2
- absfuyu/dxt/intext.py +2 -2
- absfuyu/dxt/listext.py +2 -2
- absfuyu/dxt/strext.py +2 -2
- absfuyu/extra/__init__.py +2 -2
- absfuyu/extra/beautiful.py +2 -2
- absfuyu/extra/da/__init__.py +2 -2
- absfuyu/extra/da/dadf.py +2 -2
- absfuyu/extra/da/dadf_base.py +2 -2
- absfuyu/extra/da/df_func.py +2 -2
- absfuyu/extra/da/mplt.py +2 -2
- absfuyu/extra/data_analysis.py +2 -2
- absfuyu/extra/pdf.py +2 -2
- absfuyu/extra/rclone.py +2 -2
- absfuyu/extra/xml.py +2 -2
- absfuyu/fun/__init__.py +2 -2
- absfuyu/fun/rubik.py +2 -2
- absfuyu/fun/tarot.py +2 -2
- absfuyu/game/__init__.py +2 -2
- absfuyu/game/game_stat.py +2 -2
- absfuyu/game/schulte.py +2 -2
- absfuyu/game/sudoku.py +2 -2
- absfuyu/game/tictactoe.py +2 -2
- absfuyu/game/wordle.py +2 -2
- absfuyu/general/__init__.py +2 -2
- absfuyu/general/content.py +2 -2
- absfuyu/general/human.py +2 -2
- absfuyu/general/shape.py +2 -2
- absfuyu/general/tax.py +324 -0
- absfuyu/logger.py +2 -2
- absfuyu/pkg_data/__init__.py +2 -2
- absfuyu/pkg_data/deprecated.py +2 -2
- absfuyu/pkg_data/logo.py +2 -2
- absfuyu/sort.py +2 -2
- absfuyu/tools/__init__.py +2 -2
- absfuyu/tools/checksum.py +2 -2
- absfuyu/tools/converter.py +2 -2
- absfuyu/tools/generator.py +2 -2
- absfuyu/tools/inspector.py +2 -2
- absfuyu/tools/keygen.py +2 -2
- absfuyu/tools/obfuscator.py +2 -2
- absfuyu/tools/passwordlib.py +2 -2
- absfuyu/tools/shutdownizer.py +2 -2
- absfuyu/tools/sw.py +138 -7
- absfuyu/tools/web.py +2 -2
- absfuyu/typings.py +2 -2
- absfuyu/util/__init__.py +2 -2
- absfuyu/util/api.py +2 -2
- absfuyu/util/gui.py +2 -2
- absfuyu/util/json_method.py +2 -2
- absfuyu/util/lunar.py +2 -2
- absfuyu/util/path.py +2 -2
- absfuyu/util/performance.py +2 -2
- absfuyu/util/shorten_number.py +2 -2
- absfuyu/util/text_table.py +2 -2
- absfuyu/util/zipped.py +2 -2
- absfuyu/version.py +2 -2
- {absfuyu-5.10.0.dist-info → absfuyu-5.11.0.dist-info}/METADATA +1 -1
- absfuyu-5.11.0.dist-info/RECORD +85 -0
- absfuyu-5.10.0.dist-info/RECORD +0 -84
- {absfuyu-5.10.0.dist-info → absfuyu-5.11.0.dist-info}/WHEEL +0 -0
- {absfuyu-5.10.0.dist-info → absfuyu-5.11.0.dist-info}/entry_points.txt +0 -0
- {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
absfuyu/pkg_data/__init__.py
CHANGED
absfuyu/pkg_data/deprecated.py
CHANGED
absfuyu/pkg_data/logo.py
CHANGED
absfuyu/sort.py
CHANGED
absfuyu/tools/__init__.py
CHANGED
absfuyu/tools/checksum.py
CHANGED
absfuyu/tools/converter.py
CHANGED
absfuyu/tools/generator.py
CHANGED
absfuyu/tools/inspector.py
CHANGED
absfuyu/tools/keygen.py
CHANGED
absfuyu/tools/obfuscator.py
CHANGED
absfuyu/tools/passwordlib.py
CHANGED
absfuyu/tools/shutdownizer.py
CHANGED
absfuyu/tools/sw.py
CHANGED
|
@@ -3,8 +3,8 @@ Absufyu: Software
|
|
|
3
3
|
-----------------
|
|
4
4
|
Software, pyinstaller related stuff
|
|
5
5
|
|
|
6
|
-
Version: 5.
|
|
7
|
-
Date updated:
|
|
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
|
|
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
|
-
|
|
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
|
"""
|