lyrpy 2025.0.1__py3-none-any.whl → 2025.0.2__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 lyrpy might be problematic. Click here for more details.
- SRC/LIB/__init__.py +21 -0
- SRC/lyrpy/LUConsole.py +402 -0
- SRC/lyrpy/LUConst.py +358 -0
- SRC/lyrpy/LUDateTime.py +205 -0
- SRC/lyrpy/LUDecotators.py +417 -0
- SRC/lyrpy/LUDict.py +116 -0
- SRC/lyrpy/LUDoc.py +62 -0
- SRC/lyrpy/LUErrors.py +79 -0
- SRC/lyrpy/LUFile.py +1228 -0
- SRC/lyrpy/LUFileUtils.py +501 -0
- SRC/lyrpy/LULog.py +2324 -0
- SRC/lyrpy/LUNetwork.py +277 -0
- SRC/lyrpy/LUNumUtils.py +305 -0
- SRC/lyrpy/LUObjects.py +208 -0
- SRC/lyrpy/LUObjectsYT.py +846 -0
- SRC/lyrpy/LUParserARG.py +364 -0
- SRC/lyrpy/LUParserINI.py +376 -0
- SRC/lyrpy/LUParserREG.py +514 -0
- SRC/lyrpy/LUProc.py +110 -0
- SRC/lyrpy/LUQThread.py +141 -0
- SRC/lyrpy/LUQTimer.py +197 -0
- SRC/lyrpy/LUSheduler.py +941 -0
- SRC/lyrpy/LUStrDecode.py +223 -0
- SRC/lyrpy/LUStrUtils.py +633 -0
- SRC/lyrpy/LUSupport.py +124 -0
- SRC/lyrpy/LUTelegram.py +428 -0
- SRC/lyrpy/LUThread.py +177 -0
- SRC/lyrpy/LUTimer.py +141 -0
- SRC/lyrpy/LUVersion.py +383 -0
- SRC/lyrpy/LUYouTube.py +203 -0
- SRC/lyrpy/LUos.py +807 -0
- SRC/lyrpy/LUsys.py +47 -0
- SRC/lyrpy/__init__.py +21 -0
- SRC/lyrpy/__main__.py +20 -0
- __SRC/LIB/__init__.py +0 -0
- __SRC/LIB/lyrpy/LUConsole.py +402 -0
- __SRC/LIB/lyrpy/LUConst.py +358 -0
- __SRC/LIB/lyrpy/LUDateTime.py +205 -0
- __SRC/LIB/lyrpy/LUDecotators.py +417 -0
- __SRC/LIB/lyrpy/LUDict.py +116 -0
- __SRC/LIB/lyrpy/LUDoc.py +62 -0
- __SRC/LIB/lyrpy/LUErrors.py +79 -0
- __SRC/LIB/lyrpy/LUFile.py +1228 -0
- __SRC/LIB/lyrpy/LUFileUtils.py +501 -0
- __SRC/LIB/lyrpy/LULog.py +2324 -0
- __SRC/LIB/lyrpy/LUNetwork.py +277 -0
- __SRC/LIB/lyrpy/LUNumUtils.py +305 -0
- __SRC/LIB/lyrpy/LUObjects.py +208 -0
- __SRC/LIB/lyrpy/LUObjectsYT.py +846 -0
- __SRC/LIB/lyrpy/LUParserARG.py +364 -0
- __SRC/LIB/lyrpy/LUParserINI.py +376 -0
- __SRC/LIB/lyrpy/LUParserREG.py +514 -0
- __SRC/LIB/lyrpy/LUProc.py +110 -0
- __SRC/LIB/lyrpy/LUQThread.py +141 -0
- __SRC/LIB/lyrpy/LUQTimer.py +197 -0
- __SRC/LIB/lyrpy/LUSheduler.py +941 -0
- __SRC/LIB/lyrpy/LUStrDecode.py +223 -0
- __SRC/LIB/lyrpy/LUStrUtils.py +633 -0
- __SRC/LIB/lyrpy/LUSupport.py +124 -0
- __SRC/LIB/lyrpy/LUTelegram.py +428 -0
- __SRC/LIB/lyrpy/LUThread.py +177 -0
- __SRC/LIB/lyrpy/LUTimer.py +141 -0
- __SRC/LIB/lyrpy/LUVersion.py +383 -0
- __SRC/LIB/lyrpy/LUYouTube.py +203 -0
- __SRC/LIB/lyrpy/LUos.py +807 -0
- __SRC/LIB/lyrpy/LUsys.py +47 -0
- __SRC/LIB/lyrpy/__init__.py +21 -0
- __SRC/LIB/lyrpy/__main__.py +20 -0
- __SRC/__init__.py +0 -0
- ____src/__init__.py +0 -0
- ____src/lyrpy/LUConsole.py +402 -0
- ____src/lyrpy/LUConst.py +358 -0
- ____src/lyrpy/LUDateTime.py +205 -0
- ____src/lyrpy/LUDecotators.py +417 -0
- ____src/lyrpy/LUDict.py +116 -0
- ____src/lyrpy/LUDoc.py +62 -0
- ____src/lyrpy/LUErrors.py +79 -0
- ____src/lyrpy/LUFile.py +1228 -0
- ____src/lyrpy/LUFileUtils.py +501 -0
- ____src/lyrpy/LULog.py +2324 -0
- ____src/lyrpy/LUNetwork.py +277 -0
- ____src/lyrpy/LUNumUtils.py +305 -0
- ____src/lyrpy/LUObjects.py +208 -0
- ____src/lyrpy/LUObjectsYT.py +846 -0
- ____src/lyrpy/LUParserARG.py +364 -0
- ____src/lyrpy/LUParserINI.py +376 -0
- ____src/lyrpy/LUParserREG.py +514 -0
- ____src/lyrpy/LUProc.py +110 -0
- ____src/lyrpy/LUQThread.py +141 -0
- ____src/lyrpy/LUQTimer.py +197 -0
- ____src/lyrpy/LUSheduler.py +941 -0
- ____src/lyrpy/LUStrDecode.py +223 -0
- ____src/lyrpy/LUStrUtils.py +633 -0
- ____src/lyrpy/LUSupport.py +124 -0
- ____src/lyrpy/LUTelegram.py +428 -0
- ____src/lyrpy/LUThread.py +177 -0
- ____src/lyrpy/LUTimer.py +141 -0
- ____src/lyrpy/LUVersion.py +383 -0
- ____src/lyrpy/LUYouTube.py +203 -0
- ____src/lyrpy/LUos.py +807 -0
- ____src/lyrpy/LUsys.py +47 -0
- ____src/lyrpy/__init__.py +21 -0
- ____src/lyrpy/__main__.py +20 -0
- {lyrpy-2025.0.1.dist-info → lyrpy-2025.0.2.dist-info}/METADATA +1 -1
- lyrpy-2025.0.2.dist-info/RECORD +145 -0
- lyrpy-2025.0.1.dist-info/RECORD +0 -43
- {lyrpy-2025.0.1.dist-info → lyrpy-2025.0.2.dist-info}/WHEEL +0 -0
- {lyrpy-2025.0.1.dist-info → lyrpy-2025.0.2.dist-info}/licenses/LICENSE +0 -0
- {lyrpy-2025.0.1.dist-info → lyrpy-2025.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
"""LUsys.py"""
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
__annotations__ = """
|
|
4
|
+
=======================================================
|
|
5
|
+
Copyright (c) 2023-2024
|
|
6
|
+
Author:
|
|
7
|
+
Lisitsin Y.R.
|
|
8
|
+
Project:
|
|
9
|
+
LU_PY
|
|
10
|
+
Python (LU)
|
|
11
|
+
Module:
|
|
12
|
+
LUDecotators.py
|
|
13
|
+
|
|
14
|
+
=======================================================
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
#------------------------------------------
|
|
18
|
+
# БИБЛИОТЕКИ python
|
|
19
|
+
#------------------------------------------
|
|
20
|
+
import time
|
|
21
|
+
from functools import wraps
|
|
22
|
+
import smtplib
|
|
23
|
+
import traceback
|
|
24
|
+
from email.mime.text import MIMEText
|
|
25
|
+
|
|
26
|
+
#------------------------------------------
|
|
27
|
+
# БИБЛИОТЕКИ сторонние
|
|
28
|
+
#------------------------------------------
|
|
29
|
+
|
|
30
|
+
#------------------------------------------
|
|
31
|
+
# БИБЛИОТЕКИ LU
|
|
32
|
+
#------------------------------------------
|
|
33
|
+
import lyrpy.LULog as LULog
|
|
34
|
+
|
|
35
|
+
#---------------------------------------------------------------
|
|
36
|
+
# TIMING
|
|
37
|
+
#---------------------------------------------------------------
|
|
38
|
+
def TIMING(func):
|
|
39
|
+
#beginfunction
|
|
40
|
+
|
|
41
|
+
def wrapper(*args, **kwargs):
|
|
42
|
+
#beginfunction
|
|
43
|
+
start_time = time.time()
|
|
44
|
+
result = func(*args, **kwargs)
|
|
45
|
+
end_time = time.time()
|
|
46
|
+
s = f"Функция {func.__name__} работала {end_time - start_time} секунд..."
|
|
47
|
+
LULog.LoggerAdd (LULog.LoggerTOOLS, LULog.DEBUGTEXT, s)
|
|
48
|
+
return result
|
|
49
|
+
#endfunction
|
|
50
|
+
|
|
51
|
+
return wrapper
|
|
52
|
+
#endfunction
|
|
53
|
+
|
|
54
|
+
#---------------------------------------------------------------
|
|
55
|
+
# retry
|
|
56
|
+
#---------------------------------------------------------------
|
|
57
|
+
"""
|
|
58
|
+
1. Декоратор retry
|
|
59
|
+
В проектах по обработке данных и разработке программного обеспечения очень много случаев, когда мы зависим от внешних систем. Не всё всегда находятся под нашим контролем.
|
|
60
|
+
Иногда происходят неожиданное событие, во время которых нам бы хотелось, чтобы внешняя система сама исправляла возникнувшие ошибки и перезапускалась.
|
|
61
|
+
Я предпочитаю реализовывать эту логику с помощью декоратора retry, который позволяет повторно выполнять программу через N-ное количество времени.
|
|
62
|
+
"""
|
|
63
|
+
def retry(max_tries=3, delay_seconds=1):
|
|
64
|
+
def decorator_retry(func):
|
|
65
|
+
@wraps(func)
|
|
66
|
+
def wrapper_retry(*args, **kwargs):
|
|
67
|
+
tries = 0
|
|
68
|
+
while tries < max_tries:
|
|
69
|
+
try:
|
|
70
|
+
return func(*args, **kwargs)
|
|
71
|
+
except Exception as e:
|
|
72
|
+
tries += 1
|
|
73
|
+
if tries == max_tries:
|
|
74
|
+
raise e
|
|
75
|
+
time.sleep(delay_seconds)
|
|
76
|
+
return wrapper_retry
|
|
77
|
+
return decorator_retry
|
|
78
|
+
@retry(max_tries=5, delay_seconds=2)
|
|
79
|
+
def call_dummy_api():
|
|
80
|
+
response = None
|
|
81
|
+
# response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
|
|
82
|
+
return response
|
|
83
|
+
#endfunction
|
|
84
|
+
|
|
85
|
+
#---------------------------------------------------------------
|
|
86
|
+
# memoize
|
|
87
|
+
#---------------------------------------------------------------
|
|
88
|
+
"""
|
|
89
|
+
2. Результаты функции кэширования
|
|
90
|
+
Некоторые части нашего кода редко меняют своё поведение. Тем не менее, если такое всё-таки произойдёт, это может отнять большую часть наших вычислительных мощностей. В таких ситуациях мы можем использовать декоратор для кэширования вызовов функций
|
|
91
|
+
Функция будет запущена только один раз, если входные данные совпадают. При каждом последующем запуске результаты будут извлекаться из кэша. Следовательно, нам не нужно будет постоянно выполнять дорогостоящие вычисления.
|
|
92
|
+
"""
|
|
93
|
+
def memoize(func):
|
|
94
|
+
cache = {}
|
|
95
|
+
def wrapper(*args):
|
|
96
|
+
if args in cache:
|
|
97
|
+
return cache[args]
|
|
98
|
+
else:
|
|
99
|
+
result = func(*args)
|
|
100
|
+
cache[args] = result
|
|
101
|
+
return result
|
|
102
|
+
return wrapper
|
|
103
|
+
#endfunction
|
|
104
|
+
|
|
105
|
+
#---------------------------------------------------------------
|
|
106
|
+
# email_on_failure
|
|
107
|
+
#---------------------------------------------------------------
|
|
108
|
+
"""
|
|
109
|
+
5. Декоратор Notification
|
|
110
|
+
Наконец, очень полезным декоратором в производственных системах является декоратор Notification.
|
|
111
|
+
Ещё раз, даже при нескольких повторных попытках хорошо протестированная кодовая база может потерпеть неудачу. И когда это произойдет, нам нужно сообщить кому-нибудь об этом, чтобы принять быстрые меры.
|
|
112
|
+
Это не ново, если вы когда-либо создавали конвейер данных и надеялись, что он всегда будет работать без перебоев.
|
|
113
|
+
Следующий декоратор отправляет электронное письмо всякий раз, когда выполнение внутренней функции завершается неудачей. В вашем случае это не обязательно должно быть уведомление по электронной почте. Вы можете настроить его для отправки уведомлений Teams / slack:
|
|
114
|
+
"""
|
|
115
|
+
def email_on_failure (sender_email, password, recipient_email):
|
|
116
|
+
def decorator (func):
|
|
117
|
+
def wrapper (*args, **kwargs):
|
|
118
|
+
try:
|
|
119
|
+
return func (*args, **kwargs)
|
|
120
|
+
except Exception as e:
|
|
121
|
+
# format the error message and traceback
|
|
122
|
+
err_msg = f"Error: {str (e)}\n\nTraceback:\n{traceback.format_exc ()}"
|
|
123
|
+
|
|
124
|
+
# create the email message
|
|
125
|
+
message = MIMEText (err_msg)
|
|
126
|
+
message ['Subject'] = f"{func.__name__} failed"
|
|
127
|
+
message ['From'] = sender_email
|
|
128
|
+
message ['To'] = recipient_email
|
|
129
|
+
|
|
130
|
+
# send the email
|
|
131
|
+
with smtplib.SMTP_SSL ('smtp.gmail.com', 465) as smtp:
|
|
132
|
+
smtp.login (sender_email, password)
|
|
133
|
+
smtp.sendmail (sender_email, recipient_email, message.as_string ())
|
|
134
|
+
|
|
135
|
+
# re-raise the exception
|
|
136
|
+
raise
|
|
137
|
+
|
|
138
|
+
return wrapper
|
|
139
|
+
return decorator
|
|
140
|
+
#endfunction
|
|
141
|
+
|
|
142
|
+
#---------------------------------------------------------------
|
|
143
|
+
# my_function
|
|
144
|
+
#---------------------------------------------------------------
|
|
145
|
+
@email_on_failure (sender_email = 'your_email@gmail.com', password = 'your_password',
|
|
146
|
+
recipient_email = 'recipient_email@gmail.com')
|
|
147
|
+
def my_function ():
|
|
148
|
+
# code that might fail
|
|
149
|
+
...
|
|
150
|
+
#endfunction
|
|
151
|
+
|
|
152
|
+
#---------------------------------------------------------------
|
|
153
|
+
# timeit
|
|
154
|
+
#---------------------------------------------------------------
|
|
155
|
+
"""
|
|
156
|
+
To overcome this, created the @timeit decorator which allows you to measure the execution time of the method/function by just adding the @timeit decorator on the method.
|
|
157
|
+
@timeit decorator:
|
|
158
|
+
"""
|
|
159
|
+
def timeit(method):
|
|
160
|
+
def timed(*args, **kw):
|
|
161
|
+
ts = time.time()
|
|
162
|
+
result = method(*args, **kw)
|
|
163
|
+
te = time.time()
|
|
164
|
+
if 'log_time' in kw:
|
|
165
|
+
name = kw.get('log_name', method.__name__.upper())
|
|
166
|
+
kw['log_time'][name] = int((te - ts) * 1000)
|
|
167
|
+
else:
|
|
168
|
+
print ('%r %2.2f ms' % (method.__name__, (te - ts) * 1000))
|
|
169
|
+
return result
|
|
170
|
+
return timed
|
|
171
|
+
#endfunction
|
|
172
|
+
|
|
173
|
+
#---------------------------------------------------------------
|
|
174
|
+
# get_all_employee_details
|
|
175
|
+
#---------------------------------------------------------------
|
|
176
|
+
# Adding decorator to the method
|
|
177
|
+
@timeit
|
|
178
|
+
def get_all_employee_details(**kwargs):
|
|
179
|
+
print ('employee details')
|
|
180
|
+
# The code will look like this after removing the redundant code.
|
|
181
|
+
# logtime_data = {}
|
|
182
|
+
# employees = Employee.get_all_employee_details(log_time=logtime_data)
|
|
183
|
+
# Hurray!! All that messy code is gone and now it looks simple and clear.
|
|
184
|
+
# log_time and log_name are optional. Make use of them accordingly when needed.
|
|
185
|
+
|
|
186
|
+
#---------------------------------------------------------------
|
|
187
|
+
# timeit
|
|
188
|
+
#---------------------------------------------------------------
|
|
189
|
+
def timeit(func):
|
|
190
|
+
@wraps(func)
|
|
191
|
+
def timeit_wrapper(*args, **kwargs):
|
|
192
|
+
start_time = time.perf_counter()
|
|
193
|
+
result = func(*args, **kwargs)
|
|
194
|
+
end_time = time.perf_counter()
|
|
195
|
+
total_time = end_time - start_time
|
|
196
|
+
print(f'Function {func.__name__}{args} {kwargs} Took {total_time:.4f} seconds')
|
|
197
|
+
return result
|
|
198
|
+
return timeit_wrapper
|
|
199
|
+
#endfunction
|
|
200
|
+
|
|
201
|
+
@timeit
|
|
202
|
+
def calculate_something(num):
|
|
203
|
+
"""
|
|
204
|
+
Simple function that returns sum of all numbers up to the square of num.
|
|
205
|
+
"""
|
|
206
|
+
total = sum((x for x in range(0, num**2)))
|
|
207
|
+
return total
|
|
208
|
+
#endfunction
|
|
209
|
+
|
|
210
|
+
class Calculator:
|
|
211
|
+
@timeit
|
|
212
|
+
def calculate_something(self, num):
|
|
213
|
+
"""
|
|
214
|
+
an example function that returns sum of all numbers up to the square of num
|
|
215
|
+
"""
|
|
216
|
+
total = sum((x for x in range(0, num**2)))
|
|
217
|
+
return total
|
|
218
|
+
|
|
219
|
+
def __repr__(self):
|
|
220
|
+
return f'calc_object:{id(self)}'
|
|
221
|
+
|
|
222
|
+
#---------------------------------------------------------------
|
|
223
|
+
#
|
|
224
|
+
#---------------------------------------------------------------
|
|
225
|
+
"""
|
|
226
|
+
01.Свойства @property
|
|
227
|
+
Декоратор @property облегчает создание свойств в классах Python. Свойства выглядят как обычные атрибуты (поля) класса, но при их чтении вызывается геттер (getter), при записи – сеттер (setter), а при удалении – делитер (deleter). Геттер и делитер опциональны.
|
|
228
|
+
|
|
229
|
+
02.Статические и классовые методы
|
|
230
|
+
Методы могут быть не только у экземпляра класса, но и у самого класса, которые вызываются без какого-то экземпляра (без self). Декораторы @staticmethod и @classmethod как раз делают метод таким (статическим или классовым). Эти декораторы встроены и видны без import.
|
|
231
|
+
|
|
232
|
+
Статический метод – это способ поместить функцию в класс, если она логически относится к этому классу. Статический метод ничего не знает о классе, из которого его вызвали.
|
|
233
|
+
|
|
234
|
+
class Foo:
|
|
235
|
+
@staticmethod
|
|
236
|
+
def help():
|
|
237
|
+
print('help for Foo class')
|
|
238
|
+
Foo.help()
|
|
239
|
+
|
|
240
|
+
Классовый метод напротив знает, из какого класса его вызывают. Он принимает неявный первый аргумент (обычно его зовут cls), который содержит вызывающий класс. Классовые методы прекрасно подходят, когда нужно учесть иерархию наследования. Пример: метод group создает список из нескольких людей. Причем для Person – список Person, а для Worker – список Worker. Со @staticmethod такое бы не вышло:
|
|
241
|
+
|
|
242
|
+
class Person:
|
|
243
|
+
@classmethod
|
|
244
|
+
def group(cls, n):
|
|
245
|
+
# cls именно тот класс, который вызвал
|
|
246
|
+
return [cls() for _ in range(n)]
|
|
247
|
+
def __repr__(self):
|
|
248
|
+
return 'Person'
|
|
249
|
+
class Worker(Person):
|
|
250
|
+
def __repr__(self):
|
|
251
|
+
return 'Worker'
|
|
252
|
+
print(Person.group(3))
|
|
253
|
+
# [Person, Person, Person]
|
|
254
|
+
print(Worker.group(2))
|
|
255
|
+
# [Worker, Worker]
|
|
256
|
+
|
|
257
|
+
03.@contextmanager
|
|
258
|
+
Этот декоратор позволяет получить из генератора – контекст менеджер. Находится в стандартном модуле contextlib. Пример открытие файла.
|
|
259
|
+
|
|
260
|
+
from contextlib import contextmanager
|
|
261
|
+
@contextmanager
|
|
262
|
+
def my_open(name, mode='r'):
|
|
263
|
+
# тут код для получения ресурса
|
|
264
|
+
f = open(name, mode)
|
|
265
|
+
print('Файл открыт:', name)
|
|
266
|
+
try:
|
|
267
|
+
yield f
|
|
268
|
+
finally:
|
|
269
|
+
# Code to release resource, e.g.:
|
|
270
|
+
f.close()
|
|
271
|
+
print('Файл закрыт:', name)
|
|
272
|
+
# использование
|
|
273
|
+
with my_open('1.txt', 'w') as f:
|
|
274
|
+
f.write('Hello')
|
|
275
|
+
f.fooooo() # <- error
|
|
276
|
+
# Файл открыт: 1.txt
|
|
277
|
+
# Traceback (most recent call last):
|
|
278
|
+
# Файл закрыт: 1.txt
|
|
279
|
+
|
|
280
|
+
В этом генераторе есть единственный yield – он возвращает как раз нужный ресурс. Все, что до него – код захвата ресурса (будет выполнен в методе __enter__), например, открытие файла. Мы оборачиваем yield в try/finally, чтобы если даже в блоке кода with произойдет ошибка, то исключение выбросится из yield, но код закрытия файла в блоке finally будет выполнен в любом случае. Код закрытия выполняется в методе __exit__ менеджера контекста.
|
|
281
|
+
|
|
282
|
+
Асинхронная версия этого декоратора – @asynccontextmanager. Пример:
|
|
283
|
+
|
|
284
|
+
from contextlib import asynccontextmanager
|
|
285
|
+
@asynccontextmanager
|
|
286
|
+
async def get_connection():
|
|
287
|
+
conn = await acquire_db_connection()
|
|
288
|
+
try:
|
|
289
|
+
yield conn
|
|
290
|
+
finally:
|
|
291
|
+
await release_db_connection(conn)
|
|
292
|
+
# использование
|
|
293
|
+
async def get_all_users():
|
|
294
|
+
async with get_connection() as conn:
|
|
295
|
+
return conn.query('SELECT ...')
|
|
296
|
+
|
|
297
|
+
@functools.wraps
|
|
298
|
+
Декоратор @functools.wraps полезен при разработке других декораторов. Передает имя, документацию и прочую мета-информацию из декорируемой функции к ее обертке. Подробнее в статье про декораторы.
|
|
299
|
+
|
|
300
|
+
@atexit.register
|
|
301
|
+
Декоратор @atexit.register регистрирует функцию для вызова ее при завершении работы процесса Python.
|
|
302
|
+
|
|
303
|
+
import atexit
|
|
304
|
+
@atexit.register
|
|
305
|
+
def goodbye():
|
|
306
|
+
print("You are now leaving the Python sector.")
|
|
307
|
+
Измерение времени @timeit
|
|
308
|
+
Переходим к самописным декораторам.
|
|
309
|
+
|
|
310
|
+
Этот декоратор измеряет время выполнения функции, которую декорирует.
|
|
311
|
+
|
|
312
|
+
import time
|
|
313
|
+
from functools import wraps
|
|
314
|
+
def timeit(method):
|
|
315
|
+
@wraps(method)
|
|
316
|
+
def timed(*args, **kw):
|
|
317
|
+
ts = time.monotonic()
|
|
318
|
+
result = method(*args, **kw)
|
|
319
|
+
te = time.monotonic()
|
|
320
|
+
ms = (te - ts) * 1000
|
|
321
|
+
all_args = ', '.join(tuple(f'{a!r}' for a in args)
|
|
322
|
+
+ tuple(f'{k}={v!r}' for k, v in kw.items()))
|
|
323
|
+
print(f'{method.__name__}({all_args}): {ms:2.2f} ms')
|
|
324
|
+
return result
|
|
325
|
+
return timed
|
|
326
|
+
# использование:
|
|
327
|
+
@timeit
|
|
328
|
+
def slow_func(x, y, sleep):
|
|
329
|
+
time.sleep(sleep)
|
|
330
|
+
return x + y
|
|
331
|
+
slow_func(10, 20, sleep=2)
|
|
332
|
+
# печатает: slow_func(10, 20, sleep=2): 2004.65 ms
|
|
333
|
+
|
|
334
|
+
Как видите, нам не нужно вмешиваться в код функции, не нужно каждый раз писать измеритель времени, декоратор отлично экономит нашу работу: надо измерить время – дописали @timeit и видим все, как на ладони.
|
|
335
|
+
|
|
336
|
+
Повторитель
|
|
337
|
+
Повторяет вызов функции n раз, возвращает последний результат.
|
|
338
|
+
|
|
339
|
+
from functools import wraps
|
|
340
|
+
def repeat(_func=None, *, num_times=2):
|
|
341
|
+
def decorator_repeat(func):
|
|
342
|
+
@wraps(func)
|
|
343
|
+
def wrapper_repeat(*args, **kwargs):
|
|
344
|
+
value = None
|
|
345
|
+
for _ in range(num_times):
|
|
346
|
+
value = func(*args, **kwargs)
|
|
347
|
+
return value
|
|
348
|
+
return wrapper_repeat
|
|
349
|
+
if _func is None:
|
|
350
|
+
return decorator_repeat
|
|
351
|
+
else:
|
|
352
|
+
return decorator_repeat(_func)
|
|
353
|
+
@repeat(num_times=5)
|
|
354
|
+
def foo():
|
|
355
|
+
print('текст')
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
"""
|
|
359
|
+
Замедлитель
|
|
360
|
+
Замедляет исполнение функции на нужное число секунд. Бывает полезно для отладки.
|
|
361
|
+
|
|
362
|
+
from functools import wraps
|
|
363
|
+
import time
|
|
364
|
+
def slow_down(seconds=1):
|
|
365
|
+
def _slow_down(func):
|
|
366
|
+
# Sleep 1 second before calling the function
|
|
367
|
+
@wraps(func)
|
|
368
|
+
def wrapper_slow_down(*args, **kwargs):
|
|
369
|
+
time.sleep(seconds)
|
|
370
|
+
return func(*args, **kwargs)
|
|
371
|
+
return wrapper_slow_down
|
|
372
|
+
return _slow_down
|
|
373
|
+
@slow_down(seconds=0.5)
|
|
374
|
+
def foo():
|
|
375
|
+
print('foo')
|
|
376
|
+
def bar():
|
|
377
|
+
foo() # каждый foo по полсекунды
|
|
378
|
+
foo()
|
|
379
|
+
|
|
380
|
+
Помощник для отладки
|
|
381
|
+
Этот декоратор будет логгировать все вызовы функции и печатать ее аргументы и возвращаемое значение.
|
|
382
|
+
|
|
383
|
+
from functools import wraps
|
|
384
|
+
# Печатает сигнатуру вызова и возвращаемое значение
|
|
385
|
+
import functools
|
|
386
|
+
def debug(func):
|
|
387
|
+
@wraps(func)
|
|
388
|
+
def wrapper_debug(*args, **kwargs):
|
|
389
|
+
args_repr = [repr(a) for a in args]
|
|
390
|
+
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
|
|
391
|
+
signature = ", ".join(args_repr + kwargs_repr)
|
|
392
|
+
print(f"Calling {func.__name__}({signature})")
|
|
393
|
+
value = func(*args, **kwargs)
|
|
394
|
+
print(f"{func.__name__!r} returned {value!r}")
|
|
395
|
+
return value
|
|
396
|
+
return wrapper_debug
|
|
397
|
+
@debug
|
|
398
|
+
def testee(x, y):
|
|
399
|
+
print(x + y)
|
|
400
|
+
"""
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
#------------------------------------------
|
|
404
|
+
def main ():
|
|
405
|
+
#beginfunction
|
|
406
|
+
print('main LUDecotators.py ...')
|
|
407
|
+
#endfunction
|
|
408
|
+
|
|
409
|
+
#------------------------------------------
|
|
410
|
+
#
|
|
411
|
+
#------------------------------------------
|
|
412
|
+
#beginmodule
|
|
413
|
+
if __name__ == "__main__":
|
|
414
|
+
main()
|
|
415
|
+
#endif
|
|
416
|
+
|
|
417
|
+
#endmodule
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""LUDict.py"""
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
__annotations__ = """
|
|
4
|
+
=======================================================
|
|
5
|
+
Copyright (c) 2023-2024
|
|
6
|
+
Author:
|
|
7
|
+
Lisitsin Y.R.
|
|
8
|
+
Project:
|
|
9
|
+
LU_PY
|
|
10
|
+
Python (LU)
|
|
11
|
+
Module:
|
|
12
|
+
LUDict.py
|
|
13
|
+
|
|
14
|
+
=======================================================
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
#------------------------------------------
|
|
18
|
+
# БИБЛИОТЕКИ python
|
|
19
|
+
#------------------------------------------
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
#------------------------------------------
|
|
23
|
+
# БИБЛИОТЕКИ сторонние
|
|
24
|
+
#------------------------------------------
|
|
25
|
+
import json
|
|
26
|
+
|
|
27
|
+
#------------------------------------------
|
|
28
|
+
# БИБЛИОТЕКИ LU
|
|
29
|
+
#------------------------------------------
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
data = {}
|
|
33
|
+
data ['people'] = []
|
|
34
|
+
data ['people'].append ({
|
|
35
|
+
'name': 'Scott',
|
|
36
|
+
'website': 'pythonist.ru',
|
|
37
|
+
'from': 'Nebraska'
|
|
38
|
+
})
|
|
39
|
+
data ['people'].append ({
|
|
40
|
+
'name': 'Larry',
|
|
41
|
+
'website': 'pythonist.ru',
|
|
42
|
+
'from': 'Michigan'
|
|
43
|
+
})
|
|
44
|
+
data ['people'].append ({
|
|
45
|
+
'name': 'Tim',
|
|
46
|
+
'website': 'pythonist.ru',
|
|
47
|
+
'from': 'Alabama'
|
|
48
|
+
})
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
#---------------------------------------------------------------
|
|
52
|
+
# PrintDict
|
|
53
|
+
#---------------------------------------------------------------
|
|
54
|
+
def PrintDict (ADict: dict):
|
|
55
|
+
"""PrintDict"""
|
|
56
|
+
#beginfunction
|
|
57
|
+
|
|
58
|
+
# LResult = AARGS.get (AARGName).get (AARGValue)
|
|
59
|
+
|
|
60
|
+
for key, value in ADict.items():
|
|
61
|
+
# print(key, value)
|
|
62
|
+
try:
|
|
63
|
+
print("key:\t" + key)
|
|
64
|
+
# ADict (value)
|
|
65
|
+
except AttributeError:
|
|
66
|
+
print("value:\t" + str(value))
|
|
67
|
+
#endtry
|
|
68
|
+
#endfor
|
|
69
|
+
#endfunction
|
|
70
|
+
|
|
71
|
+
#---------------------------------------------------------------
|
|
72
|
+
# SaveDictJSON
|
|
73
|
+
#---------------------------------------------------------------
|
|
74
|
+
def SaveDictJSON (ADict, AFileName):
|
|
75
|
+
"""SaveDictJSON"""
|
|
76
|
+
#beginfunction
|
|
77
|
+
with open (AFileName, 'w') as LFileDictJSON:
|
|
78
|
+
json.dump (ADict, LFileDictJSON)
|
|
79
|
+
#endwith
|
|
80
|
+
#endfunction
|
|
81
|
+
|
|
82
|
+
#---------------------------------------------------------------
|
|
83
|
+
# SaveDictSTR
|
|
84
|
+
#---------------------------------------------------------------
|
|
85
|
+
def SaveDictSTR (ADict, AFileName):
|
|
86
|
+
"""SaveDictSTR"""
|
|
87
|
+
#beginfunction
|
|
88
|
+
LDict = json.dumps (ADict, ensure_ascii = False, indent = 4)
|
|
89
|
+
# Save a reference to the original standard output
|
|
90
|
+
original_stdout = sys.stdout
|
|
91
|
+
with open (AFileName, 'w') as LFileDictSTR:
|
|
92
|
+
# Change the standard output to the file we created.
|
|
93
|
+
sys.stdout = LFileDictSTR
|
|
94
|
+
print (LDict)
|
|
95
|
+
#endwith
|
|
96
|
+
# Reset the standard output to its original value
|
|
97
|
+
sys.stdout = original_stdout
|
|
98
|
+
#endfunction
|
|
99
|
+
|
|
100
|
+
#---------------------------------------------------------------
|
|
101
|
+
#
|
|
102
|
+
#---------------------------------------------------------------
|
|
103
|
+
def main ():
|
|
104
|
+
#beginfunction
|
|
105
|
+
print('main LUDict.py...')
|
|
106
|
+
#endfunction
|
|
107
|
+
|
|
108
|
+
#------------------------------------------
|
|
109
|
+
#
|
|
110
|
+
#------------------------------------------
|
|
111
|
+
#beginmodule
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
main()
|
|
114
|
+
#endif
|
|
115
|
+
|
|
116
|
+
#endmodule
|
__SRC/LIB/lyrpy/LUDoc.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""LUDoc.py"""
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
__annotations__ = """
|
|
4
|
+
------------------------------------------------------
|
|
5
|
+
Copyright (c) 2023-2024
|
|
6
|
+
Author:
|
|
7
|
+
Lisitsin Y.R.
|
|
8
|
+
Project:
|
|
9
|
+
LU_PY
|
|
10
|
+
Python (LU)
|
|
11
|
+
Module:
|
|
12
|
+
LUDoc.py
|
|
13
|
+
|
|
14
|
+
------------------------------------------------------
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
#------------------------------------------
|
|
18
|
+
# БИБЛИОТЕКИ python
|
|
19
|
+
#------------------------------------------
|
|
20
|
+
#import sys
|
|
21
|
+
|
|
22
|
+
#------------------------------------------
|
|
23
|
+
# БИБЛИОТЕКИ сторонние
|
|
24
|
+
#------------------------------------------
|
|
25
|
+
|
|
26
|
+
#------------------------------------------
|
|
27
|
+
# БИБЛИОТЕКИ LU
|
|
28
|
+
#------------------------------------------
|
|
29
|
+
import lyrpy.LULog as LULog
|
|
30
|
+
|
|
31
|
+
#---------------------------------------------------------------
|
|
32
|
+
# PrintInfoObject
|
|
33
|
+
#---------------------------------------------------------------
|
|
34
|
+
def PrintInfoObject (AObject):
|
|
35
|
+
#beginfunction
|
|
36
|
+
# print (sys._getframe (0).f_code.co_name, '...')
|
|
37
|
+
# print (inspect.currentframe().f_code.co_name, '...')
|
|
38
|
+
# print (inspect.stack () [0] [3], '...')
|
|
39
|
+
# print (traceback.extract_stack () [-1].name, '...')
|
|
40
|
+
s = f'{AObject}'
|
|
41
|
+
#LULog.LoggerAdd (LULog.LoggerTOOLS, LULog.DEBUGTEXT, s)
|
|
42
|
+
print (s)
|
|
43
|
+
#endfunction
|
|
44
|
+
|
|
45
|
+
#---------------------------------------------------------------
|
|
46
|
+
# main
|
|
47
|
+
#---------------------------------------------------------------
|
|
48
|
+
def main ():
|
|
49
|
+
#beginfunction
|
|
50
|
+
print('main LUDoc.py...')
|
|
51
|
+
...
|
|
52
|
+
#endfunction
|
|
53
|
+
|
|
54
|
+
#------------------------------------------
|
|
55
|
+
#
|
|
56
|
+
#------------------------------------------
|
|
57
|
+
#beginmodule
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
main()
|
|
60
|
+
#endif
|
|
61
|
+
|
|
62
|
+
#endmodule
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""LUErrors.py"""
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
__annotations__ = """
|
|
4
|
+
=======================================================
|
|
5
|
+
Copyright (c) 2023-2024
|
|
6
|
+
Author:
|
|
7
|
+
Lisitsin Y.R.
|
|
8
|
+
Project:
|
|
9
|
+
LU_PY
|
|
10
|
+
Python (LU)
|
|
11
|
+
Module:
|
|
12
|
+
LUErrors.py
|
|
13
|
+
|
|
14
|
+
=======================================================
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
#------------------------------------------
|
|
18
|
+
# БИБЛИОТЕКИ python
|
|
19
|
+
#------------------------------------------
|
|
20
|
+
|
|
21
|
+
#------------------------------------------
|
|
22
|
+
# БИБЛИОТЕКИ сторонние
|
|
23
|
+
#------------------------------------------
|
|
24
|
+
|
|
25
|
+
#---------------------------------------------------------------
|
|
26
|
+
# class LUFileError_FileERROR
|
|
27
|
+
#---------------------------------------------------------------
|
|
28
|
+
class LUFileError_FileERROR(Exception):
|
|
29
|
+
"""Ошибки в модуле LUFile при обработке файла"""
|
|
30
|
+
"""
|
|
31
|
+
Атрибуты:
|
|
32
|
+
AFileName:
|
|
33
|
+
AMessage: объяснение ошибки
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, AFileName: str, AMessage="Файл не существует."):
|
|
37
|
+
self.__FFileName: str = AFileName
|
|
38
|
+
self.__FMessage: str = AMessage
|
|
39
|
+
super().__init__(self.__FMessage)
|
|
40
|
+
|
|
41
|
+
# переопределяем метод '__str__'
|
|
42
|
+
def __str__(self):
|
|
43
|
+
return f'{self.__FFileName} -> {self.__FMessage}'
|
|
44
|
+
|
|
45
|
+
#--------------------------------------------------
|
|
46
|
+
# @property Message
|
|
47
|
+
#--------------------------------------------------
|
|
48
|
+
# getter
|
|
49
|
+
@property
|
|
50
|
+
def Message(self):
|
|
51
|
+
#beginfunction
|
|
52
|
+
return self.__FMessage
|
|
53
|
+
#endfunction
|
|
54
|
+
@Message.setter
|
|
55
|
+
def Message(self, Value: str):
|
|
56
|
+
#beginfunction
|
|
57
|
+
self.__FMessage: str = Value
|
|
58
|
+
#endfunction
|
|
59
|
+
|
|
60
|
+
#endclass
|
|
61
|
+
|
|
62
|
+
#------------------------------------------
|
|
63
|
+
# main
|
|
64
|
+
#------------------------------------------
|
|
65
|
+
def main ():
|
|
66
|
+
#beginfunction
|
|
67
|
+
print('main LUEditors.py ...')
|
|
68
|
+
#endfunction
|
|
69
|
+
|
|
70
|
+
#------------------------------------------
|
|
71
|
+
#
|
|
72
|
+
#------------------------------------------
|
|
73
|
+
#beginmodule
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
main()
|
|
76
|
+
#endif
|
|
77
|
+
|
|
78
|
+
#endmodule
|
|
79
|
+
|