MainShortcuts2 2.2.1__tar.gz → 2.2.3__tar.gz

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.
Files changed (24) hide show
  1. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/PKG-INFO +1 -1
  2. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/pyproject.toml +1 -1
  3. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/_module_info.py +1 -1
  4. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/cfg.py +2 -1
  5. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/core.py +1 -1
  6. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/path.py +15 -13
  7. mainshortcuts2-2.2.3/src/MainShortcuts2/types.py +147 -0
  8. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/utils.py +84 -0
  9. mainshortcuts2-2.2.1/src/MainShortcuts2/types.py +0 -55
  10. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/README.md +0 -0
  11. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/__init__.py +0 -0
  12. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/__main__.py +0 -0
  13. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/advanced.py +0 -0
  14. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/dict.py +0 -0
  15. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/dir.py +0 -0
  16. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/file.py +0 -0
  17. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/json.py +0 -0
  18. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/list.py +0 -0
  19. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/proc.py +0 -0
  20. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/regex.py +0 -0
  21. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/special_chars.py +0 -0
  22. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/str.py +0 -0
  23. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/term.py +0 -0
  24. {mainshortcuts2-2.2.1 → mainshortcuts2-2.2.3}/src/MainShortcuts2/win.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: MainShortcuts2
3
- Version: 2.2.1
3
+ Version: 2.2.3
4
4
  Summary: Сокращение и улучшение функций
5
5
  Home-page: https://github.com/MainPlay-TG/MainShortcuts2.py
6
6
  Author: MainPlay TG
@@ -1,5 +1,5 @@
1
1
  [tool.poetry]
2
- version = "2.2.1"
2
+ version = "2.2.3"
3
3
  name = "MainShortcuts2"
4
4
  description = "Сокращение и улучшение функций"
5
5
  authors = [ "MainPlay TG <xbox.roman6666666666@gmail.com>",]
@@ -1,2 +1,2 @@
1
1
  name = "MainShortcuts2"
2
- version = "2.2.1"
2
+ version = "2.2.3"
@@ -19,7 +19,8 @@ def _check_type(path: str, type: Union[str, None]):
19
19
  if not type in types:
20
20
  raise Exception("Type %r not supported" % type)
21
21
  return type
22
- _, ext = os.path.splitext(path).lower()
22
+ _, ext = os.path.splitext(path)
23
+ ext = ext[1:].lower()
23
24
  if ext in ext2type:
24
25
  return ext2type[ext]
25
26
  raise Exception("Cannot determine type by extension %r" % ext)
@@ -54,7 +54,7 @@ class MS2:
54
54
  self.import_code: str = "from MainShortcuts2 import ms\nms.prog_file,ms.prog_name=__file__,__name__\nms.reload()"
55
55
  self.log: Logger = NoLogger("MainShortcuts2") if logger is None else logger
56
56
  self.MAIN_FILE: Union[None, str] = _get_main_file()
57
- self.MAIN_DIR: Union[None, str] = None if self.MAIN_FILE is None else os.path.abspath(self.MAIN_FILE)
57
+ self.MAIN_DIR: Union[None, str] = None if self.MAIN_FILE is None else os.path.dirname(self.MAIN_FILE)
58
58
  self.prog_dir: Union[None, str] = None
59
59
  self.prog_file: Union[None, str] = __file__
60
60
  self.prog_name: Union[None, str] = __name__
@@ -45,13 +45,15 @@ def cwd(set_to: PATH_TYPES = None) -> str:
45
45
  class Path:
46
46
  """Информация и действия с объектом файловой системы"""
47
47
 
48
- def __init__(self, path: PATH_TYPES):
48
+ def __init__(self, path: PATH_TYPES, use_cache: bool = True):
49
+ self._path = path2str(path, to_abs=True)
49
50
  self.cp = self.copy
50
51
  self.ln = self.link
51
52
  self.mv = self.move
52
- self.path = path2str(path, to_abs=True)
53
+ self.reload(full=True)
53
54
  self.rm = self.delete
54
55
  self.rn = self.rename
56
+ self.use_cache = use_cache
55
57
 
56
58
  def reload(self, full: bool = False):
57
59
  """Удаление кешированной информации"""
@@ -81,7 +83,7 @@ class Path:
81
83
  def path(self, v):
82
84
  """Абсолютный путь к объекту"""
83
85
  self._path = path2str(v, to_abs=True)
84
- self.reload(True)
86
+ self.reload(full=True)
85
87
 
86
88
  @property
87
89
  def base_name(self) -> str:
@@ -93,14 +95,14 @@ class Path:
93
95
  @property
94
96
  def created_at(self) -> float:
95
97
  """timestamp создания объекта"""
96
- if self._created_at is None:
98
+ if self._created_at is None or (not self.use_cache):
97
99
  self._created_at = os.path.getctime(self.path)
98
100
  return self._created_at
99
101
 
100
102
  @property
101
103
  def exists(self) -> bool:
102
104
  """Существует ли объект"""
103
- if self._exists is None:
105
+ if self._exists is None or (not self.use_cache):
104
106
  self._exists = os.path.exists(self.path)
105
107
 
106
108
  @property
@@ -120,7 +122,7 @@ class Path:
120
122
  @property
121
123
  def is_dir(self) -> bool:
122
124
  """Является ли объект папкой"""
123
- if self._is_dir is None:
125
+ if self._is_dir is None or (not self.use_cache):
124
126
  self._is_dir = os.path.isdir(self.path)
125
127
  if self._is_dir:
126
128
  self._type = "dir"
@@ -129,7 +131,7 @@ class Path:
129
131
  @property
130
132
  def is_file(self) -> bool:
131
133
  """Является ли объект файлом"""
132
- if self._is_file is None:
134
+ if self._is_file is None or (not self.use_cache):
133
135
  self._is_file = os.path.isfile(self.path)
134
136
  if self._is_file:
135
137
  self._type = "file"
@@ -138,14 +140,14 @@ class Path:
138
140
  @property
139
141
  def is_link(self) -> bool:
140
142
  """Является ли объект ссылкой на другой объект"""
141
- if self._is_link is None:
143
+ if self._is_link is None or (not self.use_cache):
142
144
  self._is_link = os.path.islink(self.path)
143
145
  return self._is_link
144
146
 
145
147
  @property
146
148
  def modified_at(self) -> float:
147
149
  """timestamp изменения объекта"""
148
- if self._modified_at is None:
150
+ if self._modified_at is None or (not self.use_cache):
149
151
  self._modified_at = os.path.getmtime(self.path)
150
152
  return self._modified_at
151
153
 
@@ -159,7 +161,7 @@ class Path:
159
161
  @property
160
162
  def realpath(self) -> str:
161
163
  """Настоящий путь к объекту, если это ссылка (может быть неправильным)"""
162
- if self._realpath is None:
164
+ if self._realpath is None or (not self.use_cache):
163
165
  if self.is_link:
164
166
  self._realpath = os.readlink(self.path)
165
167
  else:
@@ -169,7 +171,7 @@ class Path:
169
171
  @property
170
172
  def size(self) -> int:
171
173
  """Размер объекта в байтах"""
172
- if self._size is None:
174
+ if self._size is None or (not self.use_cache):
173
175
  self._size = os.path.getsize(self.path)
174
176
  return self._size
175
177
 
@@ -183,7 +185,7 @@ class Path:
183
185
  @property
184
186
  def type(self) -> str:
185
187
  """Тип объекта (`dir` | `file`)"""
186
- if self._type is None:
188
+ if self._type is None or (not self.use_cache):
187
189
  if self.is_dir:
188
190
  self._type = "dir"
189
191
  if self.is_file:
@@ -195,7 +197,7 @@ class Path:
195
197
  @property
196
198
  def used_at(self) -> float:
197
199
  """timestamp последнего использования объекта"""
198
- if self._used_at is None:
200
+ if self._used_at is None or (not self.use_cache):
199
201
  self._used_at = os.path.getatime(self.path)
200
202
  return self._used_at
201
203
 
@@ -0,0 +1,147 @@
1
+ """Различные объекты и исключения"""
2
+ from typing import Union
3
+
4
+
5
+ class Base:
6
+ def __init__(self, *args, **kwargs):
7
+ self.args = args
8
+ self.kwargs = kwargs
9
+ self.type = type(self)
10
+
11
+
12
+ class UserError(Exception):
13
+ pass
14
+ """Ошибка, которую допустил пользователь. Например неправильно указал входные данные"""
15
+
16
+
17
+ class AccessDeniedError(UserError):
18
+ pass
19
+ """Ошибка доступа"""
20
+
21
+
22
+ class Empty(Base):
23
+ pass
24
+ """Пустота (не равно `None`)"""
25
+
26
+
27
+ class Infinity(Base):
28
+ pass
29
+ """Бесконечное число"""
30
+
31
+
32
+ class NotAFileError(Exception):
33
+ pass
34
+ """Ошибка 'это не файл'"""
35
+
36
+
37
+ class NotANumber(Base):
38
+ pass
39
+ """Не число"""
40
+
41
+
42
+ class NotFound(Base):
43
+ pass
44
+ """Не найдено"""
45
+
46
+
47
+ class NotFoundError(Exception):
48
+ pass
49
+ """Ошибка 'не найдено'"""
50
+
51
+
52
+ class Action:
53
+ def __init__(self, func, *args, **kwargs):
54
+ self._closed = False
55
+ self._completed = False
56
+ self._error = None
57
+ self._launched = False
58
+ self._result = None
59
+ self.args: tuple = args
60
+ self.func = func
61
+ self.kwargs: dict = kwargs
62
+
63
+ def __enter__(self):
64
+ return self
65
+
66
+ def __exit__(self, a, b, c):
67
+ self.close()
68
+
69
+ def _check(self, launched: bool = None, completed: bool = None, closed: bool = None):
70
+ if not launched is None:
71
+ if launched:
72
+ if not self.launched:
73
+ raise RuntimeError("The action has not yet been launched")
74
+ else:
75
+ if self.launched:
76
+ raise RuntimeError("The action has already been launched")
77
+ if not completed is None:
78
+ if completed:
79
+ if not self.completed:
80
+ raise RuntimeError("The action has not yet been completed")
81
+ else:
82
+ if self.completed:
83
+ raise RuntimeError("The action has already been completed")
84
+ if not closed is None:
85
+ if closed:
86
+ if not self.closed:
87
+ raise RuntimeError("The action has not yet been closed")
88
+ else:
89
+ if self.closed:
90
+ raise RuntimeError("The action has already been closed")
91
+
92
+ def close(self):
93
+ self._closed = True
94
+ self._error = None
95
+ self._result = None
96
+ self.args = None
97
+ self.func = None
98
+ self.kwargs = None
99
+
100
+ @property
101
+ def launched(self) -> bool:
102
+ return self._launched
103
+
104
+ @property
105
+ def completed(self) -> bool:
106
+ return self._completed
107
+
108
+ @property
109
+ def exception(self) -> Union[None, Exception]:
110
+ self._check(launched=True, completed=True, closed=False)
111
+ return self._error
112
+
113
+ @property
114
+ def result(self):
115
+ self._check(launched=True, completed=True, closed=False)
116
+ if not self._error is None:
117
+ raise self._error # type: ignore
118
+ return self._result
119
+
120
+ @property
121
+ def closed(self) -> bool:
122
+ return self._closed
123
+
124
+ def run(self):
125
+ self._check(launched=False, completed=False, closed=False)
126
+ self._launched = True
127
+ try:
128
+ self._result = self.func(*self.args, **self.kwargs) # type: ignore
129
+ except Exception as err:
130
+ self._error = err
131
+ self._completed = True
132
+
133
+
134
+ class OnlyOneInstanceError(BaseException):
135
+ """Ошибка для `.utils.OnlyOneInstance`"""
136
+
137
+ def __init__(self, *args):
138
+ if len(args) == 0:
139
+ args = ("Another instance is already running",)
140
+ BaseException.__init__(self, *args)
141
+
142
+
143
+ Error401 = AccessDeniedError
144
+ Error403 = AccessDeniedError
145
+ Error404 = NotFoundError
146
+ Inf = Infinity
147
+ NaN = NotANumber
@@ -4,6 +4,7 @@ import sys
4
4
  from .core import ms
5
5
  from functools import wraps
6
6
  from typing import *
7
+ cache = {}
7
8
 
8
9
 
9
10
  class MiddlewareBase:
@@ -345,5 +346,88 @@ def shebang_file(path: str, *, exe_name: Union[None, str] = None, exe_path: Unio
345
346
  return ms.file.write(path, result)
346
347
 
347
348
 
349
+ def remove_ANSI(text: str) -> str:
350
+ """Убрать ANSI коды из текста | `re`"""
351
+ cache_id = "remove_ANSI", 0
352
+ if not cache_id in cache:
353
+ import re
354
+ cache[cache_id] = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
355
+ return cache[cache_id].sub("", text)
356
+
357
+
358
+ OnlyOneInstanceError = ms.types.OnlyOneInstanceError
359
+
360
+
361
+ class OnlyOneInstance:
362
+ """Запретить запуск одной программы несколько раз | `tempfile`, `fcntl`"""
363
+ _win = sys.platform == "win32"
364
+
365
+ def __init__(self, name: str = "main", lock_path: str = None):
366
+ import tempfile
367
+ self.name: str = name
368
+ if lock_path is None:
369
+ lock_path = tempfile.gettempdir() + "/" + ms.MAIN_FILE.replace(":", "").replace("/", "_") + "." + name + ".lock"
370
+ self.lock = ms.path.Path(lock_path, use_cache=False)
371
+ if self._win:
372
+ flags = os.O_CREAT | os.O_EXCL | os.O_RDWR
373
+
374
+ def _enter():
375
+ try:
376
+ if self.lock.exists:
377
+ os.unlink(self.lock.path)
378
+ self.fd = os.open(self.lockfile, flags)
379
+ except OSError as err:
380
+ if err.errno == 13:
381
+ raise OnlyOneInstanceError()
382
+ raise
383
+
384
+ def _exit():
385
+ os.close(self.fd)
386
+ os.unlink(self.lock.path)
387
+ else:
388
+ import fcntl
389
+ flags = fcntl.LOCK_EX | fcntl.LOCK_NB
390
+
391
+ def _enter():
392
+ self.fp = open(self.lock.path, "w")
393
+ self.fp.flush()
394
+ try:
395
+ fcntl.lockf(self.fp, flags)
396
+ except IOError:
397
+ raise OnlyOneInstanceError()
398
+
399
+ def _exit():
400
+ fcntl.lockf(self.fp, fcntl.LOCK_UN)
401
+ os.close(self.fp)
402
+ if self.lock.exists:
403
+ os.unlink(self.lock.path)
404
+ self._enter = _enter
405
+ self._exit = _exit
406
+
407
+ def __enter__(self):
408
+ if self.running:
409
+ raise OnlyOneInstanceError()
410
+ self._enter()
411
+ self.running = True
412
+ try:
413
+ self.on_enter()
414
+ except Exception:
415
+ pass
416
+ return self
417
+
418
+ def __exit__(self, a, b, c):
419
+ if not self.running:
420
+ return
421
+ self._exit()
422
+ self.running = False
423
+ self.on_exit()
424
+
425
+ def on_enter(self):
426
+ pass
427
+
428
+ def on_exit(self):
429
+ pass
430
+
431
+
348
432
  download_file = sync_download_file
349
433
  request = sync_request
@@ -1,55 +0,0 @@
1
- """Различные объекты, которые не имеют функционала"""
2
-
3
-
4
- class Base:
5
- def __init__(self, *args, **kwargs):
6
- self.args = args
7
- self.kwargs = kwargs
8
- self.type = type(self)
9
-
10
-
11
- class UserError(Exception):
12
- pass
13
- """Ошибка, которую допустил пользователь. Например неправильно указал входные данные"""
14
-
15
-
16
- class AccessDeniedError(UserError):
17
- pass
18
- """Ошибка доступа"""
19
-
20
-
21
- class Empty(Base):
22
- pass
23
- """Пустота (не равно `None`)"""
24
-
25
-
26
- class Infinity(Base):
27
- pass
28
- """Бесконечное число"""
29
-
30
-
31
- class NotAFileError(Exception):
32
- pass
33
- """Ошибка 'это не файл'"""
34
-
35
-
36
- class NotANumber(Base):
37
- pass
38
- """Не число"""
39
-
40
-
41
- class NotFound(Base):
42
- pass
43
- """Не найдено"""
44
-
45
-
46
- class NotFoundError(Exception):
47
- pass
48
- """Ошибка 'не найдено'"""
49
-
50
-
51
- Error401 = AccessDeniedError
52
- Error403 = AccessDeniedError
53
- Error404 = NotFoundError
54
- Inf = Infinity
55
- NaN = NotANumber
File without changes