omlish 0.0.0.dev252__py3-none-any.whl → 0.0.0.dev254__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.
@@ -0,0 +1,235 @@
1
+ """
2
+ Features:
3
+ - get (line, offset)
4
+ - invalidate
5
+ - filewatcher
6
+ - linecache interop
7
+ - tokenize.open - detect encoding - only for .py
8
+ - directory / path_parts tree?
9
+
10
+ TODO:
11
+ - read raw, decode (detect)
12
+ - ! index from byteofs -> charofs
13
+ - charofs -> lineno
14
+ - 1-based?
15
+ - maximums?
16
+ - lru? max_size/max_entries?
17
+ - collections.cache?
18
+
19
+ Notes:
20
+ - linecache is 1-based
21
+ - linecache appends final newline if missing:
22
+ if lines and not lines[-1].endswith('\n'):
23
+ lines[-1] += '\n'
24
+ """
25
+ import dataclasses as dc
26
+ import io
27
+ import os.path
28
+ import stat as stat_
29
+ import tokenize
30
+ import typing as ta
31
+
32
+ from .. import cached
33
+ from .. import lang
34
+ from ..os.paths import abs_real_path
35
+
36
+
37
+ ##
38
+
39
+
40
+ class TextFileCache:
41
+ class FileStat(ta.NamedTuple):
42
+ size: int
43
+ mtime: float
44
+
45
+ class Entry:
46
+ def __init__(
47
+ self,
48
+ cache: 'TextFileCache',
49
+ path: str,
50
+ stat: 'TextFileCache.FileStat',
51
+ *,
52
+ encoding: str | None = None,
53
+ ) -> None:
54
+ super().__init__()
55
+
56
+ self._cache = cache
57
+ self._path = path
58
+ self._stat = stat
59
+ self._given_encoding = encoding
60
+
61
+ def __repr__(self) -> str:
62
+ return lang.attr_repr(self, 'path', 'stat')
63
+
64
+ @property
65
+ def path(self) -> str:
66
+ return self._path
67
+
68
+ @property
69
+ def stat(self) -> 'TextFileCache.FileStat':
70
+ return self._stat
71
+
72
+ @cached.function
73
+ def raw(self) -> bytes:
74
+ return self._cache._read_file(self._path) # noqa
75
+
76
+ @cached.function
77
+ def encoding(self) -> str:
78
+ if (ge := self._given_encoding) is not None:
79
+ return ge
80
+ return self._cache._determine_encoding(self._path, self.raw()) # noqa
81
+
82
+ @cached.function
83
+ def text(self) -> str:
84
+ return self.raw().decode(self.encoding())
85
+
86
+ @cached.function
87
+ def lines(self) -> ta.Sequence[str]:
88
+ return self.text().splitlines(keepends=True)
89
+
90
+ def safe_line(self, n: int, default: str = '') -> str:
91
+ lines = self.lines()
92
+ if 0 <= n < len(lines):
93
+ return lines[n]
94
+ return default
95
+
96
+ #
97
+
98
+ @dc.dataclass(frozen=True)
99
+ class Config:
100
+ max_file_size: int | None = 5 * 1024 * 1024
101
+
102
+ default_encoding: str = 'utf-8'
103
+
104
+ def __init__(
105
+ self,
106
+ config: Config = Config(),
107
+ *,
108
+ locking: lang.DefaultLockable = None,
109
+ ) -> None:
110
+ super().__init__()
111
+
112
+ self._config = config
113
+
114
+ self._dct: dict[str, TextFileCache.Entry] = {}
115
+ self._lock = lang.default_lock(locking)()
116
+
117
+ #
118
+
119
+ class Error(Exception):
120
+ pass
121
+
122
+ @dc.dataclass()
123
+ class PathError(Error):
124
+ path: str
125
+
126
+ class PathTypeError(PathError):
127
+ pass
128
+
129
+ class FileTooBigError(PathError):
130
+ pass
131
+
132
+ #
133
+
134
+ def _normalize_path(self, path: str) -> str:
135
+ return abs_real_path(path)
136
+
137
+ #
138
+
139
+ def _try_stat_file(self, path: str) -> FileStat | Exception:
140
+ try:
141
+ st = os.stat(path)
142
+ except FileNotFoundError as e:
143
+ return e
144
+
145
+ if not stat_.S_ISREG(st.st_mode):
146
+ return TextFileCache.PathTypeError(path)
147
+
148
+ return TextFileCache.FileStat(
149
+ st.st_size,
150
+ st.st_mtime,
151
+ )
152
+
153
+ def _read_file(self, path: str) -> bytes:
154
+ with open(path, 'rb') as f:
155
+ return f.read()
156
+
157
+ def _determine_encoding(self, path: str, raw: bytes) -> str:
158
+ if path.endswith('.py'):
159
+ buf = io.BytesIO(raw)
160
+ encoding, _ = tokenize.detect_encoding(buf.readline)
161
+ return encoding
162
+
163
+ return self._config.default_encoding
164
+
165
+ #
166
+
167
+ def _get_entry(
168
+ self,
169
+ path: str,
170
+ *,
171
+ check_stat: bool = False,
172
+ or_raise: bool = False,
173
+ ) -> Entry:
174
+ st_: TextFileCache.FileStat | Exception | None = None
175
+ try:
176
+ e = self._dct[path]
177
+
178
+ except KeyError:
179
+ if or_raise:
180
+ raise
181
+
182
+ else:
183
+ if (
184
+ not check_stat or
185
+ (st_ := self._try_stat_file(path)) == e.stat
186
+ ):
187
+ return e
188
+
189
+ del self._dct[path]
190
+
191
+ st: TextFileCache.FileStat | Exception
192
+ if st_ is not None:
193
+ st = st_
194
+ else:
195
+ st = self._try_stat_file(path)
196
+
197
+ if isinstance(st, Exception):
198
+ raise st
199
+
200
+ if (mfs := self._config.max_file_size) is not None and st.size > mfs:
201
+ raise TextFileCache.FileTooBigError(path)
202
+
203
+ e = TextFileCache.Entry(
204
+ self,
205
+ path,
206
+ st,
207
+ )
208
+
209
+ self._dct[path] = e
210
+
211
+ return e
212
+
213
+ def get_entry(
214
+ self,
215
+ path: str,
216
+ *,
217
+ check_stat: bool = False,
218
+ or_raise: bool = False,
219
+ ) -> Entry:
220
+ path = self._normalize_path(path)
221
+
222
+ with self._lock:
223
+ return self._get_entry(
224
+ path,
225
+ check_stat=check_stat,
226
+ or_raise=or_raise,
227
+ )
228
+
229
+ #
230
+
231
+ def invalidate(self, path: str) -> bool:
232
+ path = self._normalize_path(path)
233
+
234
+ with self._lock:
235
+ return self._dct.pop(path, None) is not None
@@ -0,0 +1,80 @@
1
+ """
2
+ TODO:
3
+ - reserver / generator
4
+
5
+ ==
6
+
7
+ @dc.dataclass()
8
+ class _ReservedFilenameEntry:
9
+ unique_id: str
10
+ seq: int = 0
11
+
12
+
13
+ _RESERVED_FILENAME_UUID_THREAD_LOCAL = threading.local()
14
+
15
+
16
+ def reserve_filename(prefix: str) -> str:
17
+ try:
18
+ e = _RESERVED_FILENAME_UUID_THREAD_LOCAL.unique_id
19
+ except AttributeError:
20
+ e = _RESERVED_FILENAME_UUID_THREAD_LOCAL.unique_id = _ReservedFilenameEntry(str(uuid.uuid4()))
21
+ while True:
22
+ unique_filename = f'<generated:{prefix}:{e.seq}>'
23
+ cache_line = (1, None, (e.unique_id,), unique_filename)
24
+ e.seq += 1
25
+ if linecache.cache.setdefault(unique_filename, cache_line) == cache_line:
26
+ return unique_filename
27
+ """
28
+ import typing as ta
29
+
30
+
31
+ ##
32
+
33
+
34
+ class LinecacheKey(ta.NamedTuple):
35
+ size: int # os.stat().st_size
36
+ mtime: float | None # os.stat().st_mtime
37
+ lines: ta.Sequence[str]
38
+ fullname: str
39
+
40
+
41
+ LinecacheLazyLoader: ta.TypeAlias = tuple[ta.Callable[[], str]]
42
+
43
+ LinecacheEntry: ta.TypeAlias = LinecacheKey | LinecacheLazyLoader
44
+
45
+
46
+ class LinecacheProtocol(ta.Protocol):
47
+ @property
48
+ def cache(self) -> ta.MutableMapping[str, LinecacheEntry]: ...
49
+
50
+ def clearcache(self) -> None: ...
51
+
52
+ def getline(
53
+ self,
54
+ filename: str,
55
+ lineno: int,
56
+ module_globals: ta.Mapping[str, ta.Any] | None = None,
57
+ ) -> str: ...
58
+
59
+ def getlines(
60
+ self,
61
+ filename: str,
62
+ module_globals: ta.Mapping[str, ta.Any] | None = None,
63
+ ) -> ta.Sequence[str]: ...
64
+
65
+ def checkcache(
66
+ self,
67
+ filename: str | None = None,
68
+ ) -> None: ...
69
+
70
+ def updatecache(
71
+ self,
72
+ filename: str,
73
+ module_globals: ta.Mapping[str, ta.Any] | None = None,
74
+ ) -> ta.Sequence[str]: ...
75
+
76
+ def lazycache(
77
+ self,
78
+ filename: str,
79
+ module_globals: ta.Mapping[str, ta.Any],
80
+ ) -> bool: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omlish
3
- Version: 0.0.0.dev252
3
+ Version: 0.0.0.dev254
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=x26AIwDzScUvnX-p4xlq6Zc5QYrAo0Vmgf1qHc1KL_M,8253
2
- omlish/__about__.py,sha256=irK5EMsRK45Vveo-bLYvfj4i3jGa_hT0w6FNpRHdcso,3380
2
+ omlish/__about__.py,sha256=_UAdSthc_ThPG20zNClzsObtbtg1SgdxuKoWfomaXRw,3380
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
5
5
  omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
@@ -13,6 +13,7 @@ omlish/outcome.py,sha256=ABIE0zjjTyTNtn-ZqQ_9_mUzLiBQ3sDAyqc9JVD8N2k,7852
13
13
  omlish/runmodule.py,sha256=PWvuAaJ9wQQn6bx9ftEL3_d04DyotNn8dR_twm2pgw0,700
14
14
  omlish/shlex.py,sha256=bsW2XUD8GiMTUTDefJejZ5AyqT1pTgWMPD0BMoF02jE,248
15
15
  omlish/sync.py,sha256=QJ79kxmIqDP9SeHDoZAf--DpFIhDQe1jACy8H4N0yZI,2928
16
+ omlish/_antlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
17
  omlish/algorithm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
18
  omlish/algorithm/all.py,sha256=FudUHwoaRLNNmqYM3jhP2Yd2BpmYhNBRPaVZzARMoSc,194
18
19
  omlish/algorithm/distribute.py,sha256=kO60F7HtMF3j-IxuhJdtsfgIR1Vpf1okQfacb4eNukQ,1533
@@ -414,7 +415,7 @@ omlish/lang/generators.py,sha256=5LX17j-Ej3QXhwBgZvRTm_dq3n9veC4IOUcVmvSu2vU,524
414
415
  omlish/lang/imports.py,sha256=Gdl6xCF89xiMOE1yDmdvKWamLq8HX-XPianO58Jdpmw,9218
415
416
  omlish/lang/iterables.py,sha256=HOjcxOwyI5bBApDLsxRAGGhTTmw7fdZl2kEckxRVl-0,1994
416
417
  omlish/lang/maybes.py,sha256=dAgrUoAhCgyrHRqa73CkaGnpXwGc-o9n-NIThrNXnbU,3416
417
- omlish/lang/objects.py,sha256=65XsD7UtblRdNe2ID1-brn_QvRkJhBIk5nyZWcQNeqU,4574
418
+ omlish/lang/objects.py,sha256=ih3z47DysrW11Vf3vF8rdALsnhK19Afp6wTU8AHAPWM,4982
418
419
  omlish/lang/params.py,sha256=QmNVBfJsfxjDG5ilDPgHV7sK4UwRztkSQdLTo0umb8I,6648
419
420
  omlish/lang/resolving.py,sha256=OuN2mDTPNyBUbcrswtvFKtj4xgH4H4WglgqSKv3MTy0,1606
420
421
  omlish/lang/resources.py,sha256=WKkAddC3ctMK1bvGw-elGe8ZxAj2IaUTKVSu2nfgHTo,2839
@@ -669,6 +670,14 @@ omlish/sql/api/errors.py,sha256=YtC2gz5DqRTT3uCJniUOufVH1GEnFIc5ElkYLK3BHwM,230
669
670
  omlish/sql/api/funcs.py,sha256=-H6V-o9JPSHFXsxdHtutB4mP2LwJfCzlLbRrPHunmB4,990
670
671
  omlish/sql/api/queries.py,sha256=IgB8_sDe40-mKE-ByTmZ4GVOCdLLJDzJGJCevMd8R5s,1207
671
672
  omlish/sql/api/rows.py,sha256=MEK9LNYEe8vLEJXQJD63MpnSOiE22cawJL-dUWQD6sU,1246
673
+ omlish/sql/parsing/Minisql.g4,sha256=DlriPFcnGT4QYG7O7VH2EmDVJQZq5zewoQ-tpXsSbIY,4748
674
+ omlish/sql/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
675
+ omlish/sql/parsing/parsing.py,sha256=hYIZdEKik4q22aEHHOX6lCpMa1jvgUk2aC1Llx10NMI,3343
676
+ omlish/sql/parsing/_antlr/MinisqlLexer.py,sha256=f4WzRrwqM7tW6sNn01035hRWPZL7xtuvba9EMR06j2E,17611
677
+ omlish/sql/parsing/_antlr/MinisqlListener.py,sha256=2py_bfDQeEtJaJh9rtpKghbSrQlEGAEKf5ZBLErKqsI,16633
678
+ omlish/sql/parsing/_antlr/MinisqlParser.py,sha256=y9SFjXdQlWYJa2PbPm30d5SfcYM_8M8ts46IHhENbNc,132457
679
+ omlish/sql/parsing/_antlr/MinisqlVisitor.py,sha256=NCPorucLLOZ-Q99BtNbDOAfHytQl8wyroR8pI1uVovg,10030
680
+ omlish/sql/parsing/_antlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
672
681
  omlish/sql/queries/__init__.py,sha256=N8oQFKY99g_MQhrPmvlBAkMeGIRURE9UxMO244mytzY,1332
673
682
  omlish/sql/queries/base.py,sha256=_8O3MbH_OEjBnhp2oIJUZ3ClaQ8l4Sj9BdPdsP0Ie-g,224
674
683
  omlish/sql/queries/binary.py,sha256=dcEzeEn104AMPuQ7QrJU2O-YCN3SUdxB5S4jaWKOUqY,2253
@@ -699,7 +708,7 @@ omlish/subprocesses/base.py,sha256=W6El-PUKKF9KLAks5LB6kzqs_n3FfkblJ-JOv6NFQbY,6
699
708
  omlish/subprocesses/run.py,sha256=3jwSnQJvFMDMHmJvtAkrrK5D-i7_8cw12vX84EWTuJo,3668
700
709
  omlish/subprocesses/sync.py,sha256=HKmKM99_Y7tkJRg_n5onXrw41IZt5M5fqU0281LY-mo,3671
701
710
  omlish/subprocesses/utils.py,sha256=MJb6hvKhZceTmBeFVqlc5oM7rDxWkUzSzK9nKvbIvM8,396
702
- omlish/subprocesses/wrap.py,sha256=v7vrbDhl_FVWESnXIOJ3oGtE3Y-OHQa5sH3MTtFTBUE,504
711
+ omlish/subprocesses/wrap.py,sha256=HMvCZrO2H227oGNN03KjB3FI-M5bAICqp19W8oG2f5M,763
703
712
  omlish/term/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
704
713
  omlish/term/codes.py,sha256=d8oii_9UJDm_TAowXYH3XDckX0XNBf2kiEPJKkVMMow,6149
705
714
  omlish/term/progressbar.py,sha256=TiwdmPSMa5jQj35i1NQURTWQGy4eWUNx_XiPM38JtvQ,3184
@@ -737,16 +746,19 @@ omlish/testing/pytest/plugins/asyncs/backends/trio.py,sha256=xty9TR7-Kk6n0cdOqEr
737
746
  omlish/testing/pytest/plugins/asyncs/backends/trio_asyncio.py,sha256=VcGVwf4V-1ZFK_70FrFS9b11EU1dOy1ozhhIDXGNSEo,3169
738
747
  omlish/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
739
748
  omlish/text/asdl.py,sha256=AS3irh-sag5pqyH3beJif78PjCbOaFso1NeKq-HXuTs,16867
749
+ omlish/text/decoding.py,sha256=sQWGckWzRslRHYKpj1SBeoo6AVqXm5HFlWFRARN1QpM,1286
740
750
  omlish/text/delimit.py,sha256=Y0ID9Y9nfgQu3tYCiMS3hLa-ugA2cc-29PV4fF1343g,4927
751
+ omlish/text/filecache.py,sha256=ls08QSqBlhVXvjDwJpUXiP-U9HLyCstGAxtBOuWJmVY,5414
741
752
  omlish/text/glyphsplit.py,sha256=kqqjglRdxGo0czYZxOz9Vi8aBmVsCOq8h6lPwRA5xe0,3803
742
753
  omlish/text/indent.py,sha256=YjtJEBYWuk8--b9JU_T6q4yxV85_TR7VEVr5ViRCFwk,1336
754
+ omlish/text/linecache.py,sha256=hRYlEhD63ZfA6_ZOTkQIcnON-3W56QMAhcG3vEJqj9M,1858
743
755
  omlish/text/mangle.py,sha256=kfzFLfvepH-chl1P89_mdc5vC4FSqyPA2aVtgzuB8IY,1133
744
756
  omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
745
757
  omlish/text/parts.py,sha256=JkNZpyR2tv2CNcTaWJJhpQ9E4F0yPR8P_YfDbZfMtwQ,6182
746
758
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
747
- omlish-0.0.0.dev252.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
748
- omlish-0.0.0.dev252.dist-info/METADATA,sha256=d3I-8A12dfY6BUTJyIZ70ySFdyaJLUuDZstZPmrYvD0,4176
749
- omlish-0.0.0.dev252.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
750
- omlish-0.0.0.dev252.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
751
- omlish-0.0.0.dev252.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
752
- omlish-0.0.0.dev252.dist-info/RECORD,,
759
+ omlish-0.0.0.dev254.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
760
+ omlish-0.0.0.dev254.dist-info/METADATA,sha256=-Mvy0_hjjJoMNilZrvKn9X-FWgQvLcItEjlbdvLO2vE,4176
761
+ omlish-0.0.0.dev254.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
762
+ omlish-0.0.0.dev254.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
763
+ omlish-0.0.0.dev254.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
764
+ omlish-0.0.0.dev254.dist-info/RECORD,,