omlish 0.0.0.dev133__py3-none-any.whl → 0.0.0.dev177__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. omlish/.manifests.json +265 -7
  2. omlish/__about__.py +5 -3
  3. omlish/antlr/_runtime/__init__.py +0 -22
  4. omlish/antlr/_runtime/_all.py +24 -0
  5. omlish/antlr/_runtime/atn/ParserATNSimulator.py +1 -1
  6. omlish/antlr/_runtime/dfa/DFASerializer.py +1 -1
  7. omlish/antlr/_runtime/error/DiagnosticErrorListener.py +2 -1
  8. omlish/antlr/_runtime/xpath/XPath.py +7 -1
  9. omlish/antlr/_runtime/xpath/XPathLexer.py +1 -1
  10. omlish/antlr/delimit.py +106 -0
  11. omlish/antlr/dot.py +31 -0
  12. omlish/antlr/errors.py +11 -0
  13. omlish/antlr/input.py +96 -0
  14. omlish/antlr/parsing.py +19 -0
  15. omlish/antlr/runtime.py +102 -0
  16. omlish/antlr/utils.py +38 -0
  17. omlish/argparse/all.py +45 -0
  18. omlish/{argparse.py → argparse/cli.py} +112 -107
  19. omlish/asyncs/__init__.py +0 -35
  20. omlish/asyncs/all.py +35 -0
  21. omlish/asyncs/asyncio/all.py +7 -0
  22. omlish/asyncs/asyncio/channels.py +40 -0
  23. omlish/asyncs/asyncio/streams.py +45 -0
  24. omlish/asyncs/asyncio/subprocesses.py +238 -0
  25. omlish/asyncs/asyncio/timeouts.py +16 -0
  26. omlish/asyncs/bluelet/LICENSE +6 -0
  27. omlish/asyncs/bluelet/all.py +67 -0
  28. omlish/asyncs/bluelet/api.py +23 -0
  29. omlish/asyncs/bluelet/core.py +178 -0
  30. omlish/asyncs/bluelet/events.py +78 -0
  31. omlish/asyncs/bluelet/files.py +80 -0
  32. omlish/asyncs/bluelet/runner.py +416 -0
  33. omlish/asyncs/bluelet/sockets.py +214 -0
  34. omlish/bootstrap/sys.py +3 -3
  35. omlish/cached.py +2 -2
  36. omlish/check.py +49 -460
  37. omlish/codecs/__init__.py +72 -0
  38. omlish/codecs/base.py +106 -0
  39. omlish/codecs/bytes.py +119 -0
  40. omlish/codecs/chain.py +23 -0
  41. omlish/codecs/funcs.py +39 -0
  42. omlish/codecs/registry.py +139 -0
  43. omlish/codecs/standard.py +4 -0
  44. omlish/codecs/text.py +217 -0
  45. omlish/collections/cache/impl.py +50 -57
  46. omlish/collections/coerce.py +1 -0
  47. omlish/collections/mappings.py +1 -1
  48. omlish/configs/flattening.py +1 -1
  49. omlish/defs.py +1 -1
  50. omlish/diag/_pycharm/runhack.py +8 -2
  51. omlish/diag/procfs.py +8 -8
  52. omlish/docker/__init__.py +0 -36
  53. omlish/docker/all.py +31 -0
  54. omlish/docker/consts.py +4 -0
  55. omlish/{lite/docker.py → docker/detect.py} +18 -0
  56. omlish/docker/{helpers.py → timebomb.py} +0 -21
  57. omlish/formats/cbor.py +31 -0
  58. omlish/formats/cloudpickle.py +31 -0
  59. omlish/formats/codecs.py +93 -0
  60. omlish/formats/json/codecs.py +29 -0
  61. omlish/formats/json/delimted.py +4 -0
  62. omlish/formats/json/stream/errors.py +2 -0
  63. omlish/formats/json/stream/lex.py +12 -6
  64. omlish/formats/json/stream/parse.py +38 -22
  65. omlish/formats/json5.py +31 -0
  66. omlish/formats/pickle.py +31 -0
  67. omlish/formats/repr.py +25 -0
  68. omlish/formats/toml.py +17 -0
  69. omlish/formats/yaml.py +25 -0
  70. omlish/funcs/__init__.py +0 -0
  71. omlish/{genmachine.py → funcs/genmachine.py} +5 -4
  72. omlish/{matchfns.py → funcs/match.py} +1 -1
  73. omlish/funcs/pairs.py +215 -0
  74. omlish/http/__init__.py +0 -48
  75. omlish/http/all.py +48 -0
  76. omlish/http/coro/__init__.py +0 -0
  77. omlish/{lite/fdio/corohttp.py → http/coro/fdio.py} +21 -19
  78. omlish/{lite/http/coroserver.py → http/coro/server.py} +20 -21
  79. omlish/{lite/http → http}/handlers.py +3 -2
  80. omlish/{lite/http → http}/parsing.py +1 -0
  81. omlish/http/sessions.py +1 -1
  82. omlish/{lite/http → http}/versions.py +1 -0
  83. omlish/inject/managed.py +2 -2
  84. omlish/io/__init__.py +0 -3
  85. omlish/{lite/io.py → io/buffers.py} +8 -9
  86. omlish/io/compress/__init__.py +9 -0
  87. omlish/io/compress/abc.py +104 -0
  88. omlish/io/compress/adapters.py +148 -0
  89. omlish/io/compress/base.py +24 -0
  90. omlish/io/compress/brotli.py +47 -0
  91. omlish/io/compress/bz2.py +61 -0
  92. omlish/io/compress/codecs.py +78 -0
  93. omlish/io/compress/gzip.py +350 -0
  94. omlish/io/compress/lz4.py +91 -0
  95. omlish/io/compress/lzma.py +81 -0
  96. omlish/io/compress/snappy.py +34 -0
  97. omlish/io/compress/zlib.py +74 -0
  98. omlish/io/compress/zstd.py +44 -0
  99. omlish/io/fdio/__init__.py +1 -0
  100. omlish/{lite → io}/fdio/handlers.py +5 -5
  101. omlish/{lite → io}/fdio/kqueue.py +8 -8
  102. omlish/{lite → io}/fdio/manager.py +7 -7
  103. omlish/{lite → io}/fdio/pollers.py +13 -13
  104. omlish/io/generators/__init__.py +56 -0
  105. omlish/io/generators/consts.py +1 -0
  106. omlish/io/generators/direct.py +13 -0
  107. omlish/io/generators/readers.py +189 -0
  108. omlish/io/generators/stepped.py +191 -0
  109. omlish/io/pyio.py +5 -2
  110. omlish/iterators/__init__.py +24 -0
  111. omlish/iterators/iterators.py +132 -0
  112. omlish/iterators/recipes.py +18 -0
  113. omlish/iterators/tools.py +96 -0
  114. omlish/iterators/unique.py +67 -0
  115. omlish/lang/__init__.py +13 -1
  116. omlish/lang/functions.py +11 -2
  117. omlish/lang/generators.py +243 -0
  118. omlish/lang/iterables.py +46 -49
  119. omlish/lang/maybes.py +4 -4
  120. omlish/lite/cached.py +39 -6
  121. omlish/lite/check.py +438 -75
  122. omlish/lite/contextmanagers.py +17 -4
  123. omlish/lite/dataclasses.py +42 -0
  124. omlish/lite/inject.py +28 -45
  125. omlish/lite/logs.py +0 -270
  126. omlish/lite/marshal.py +309 -144
  127. omlish/lite/pycharm.py +47 -0
  128. omlish/lite/reflect.py +33 -0
  129. omlish/lite/resources.py +8 -0
  130. omlish/lite/runtime.py +4 -4
  131. omlish/lite/shlex.py +12 -0
  132. omlish/lite/socketserver.py +2 -2
  133. omlish/lite/strings.py +31 -0
  134. omlish/logs/__init__.py +0 -32
  135. omlish/logs/{_abc.py → abc.py} +0 -1
  136. omlish/logs/all.py +37 -0
  137. omlish/logs/{formatters.py → color.py} +1 -2
  138. omlish/logs/configs.py +7 -38
  139. omlish/logs/filters.py +10 -0
  140. omlish/logs/handlers.py +4 -1
  141. omlish/logs/json.py +56 -0
  142. omlish/logs/proxy.py +99 -0
  143. omlish/logs/standard.py +128 -0
  144. omlish/logs/utils.py +2 -2
  145. omlish/manifests/__init__.py +2 -0
  146. omlish/manifests/load.py +209 -0
  147. omlish/manifests/types.py +17 -0
  148. omlish/marshal/base.py +1 -1
  149. omlish/marshal/factories.py +1 -1
  150. omlish/marshal/forbidden.py +1 -1
  151. omlish/marshal/iterables.py +1 -1
  152. omlish/marshal/literals.py +50 -0
  153. omlish/marshal/mappings.py +1 -1
  154. omlish/marshal/maybes.py +1 -1
  155. omlish/marshal/standard.py +5 -1
  156. omlish/marshal/unions.py +1 -1
  157. omlish/os/__init__.py +0 -0
  158. omlish/os/atomics.py +205 -0
  159. omlish/os/deathsig.py +23 -0
  160. omlish/{os.py → os/files.py} +0 -9
  161. omlish/{lite → os}/journald.py +2 -1
  162. omlish/os/linux.py +484 -0
  163. omlish/os/paths.py +36 -0
  164. omlish/{lite → os}/pidfile.py +1 -0
  165. omlish/os/sizes.py +9 -0
  166. omlish/reflect/__init__.py +3 -0
  167. omlish/reflect/subst.py +2 -1
  168. omlish/reflect/types.py +126 -44
  169. omlish/secrets/pwhash.py +1 -1
  170. omlish/secrets/subprocesses.py +3 -1
  171. omlish/specs/jsonrpc/marshal.py +1 -1
  172. omlish/specs/openapi/marshal.py +1 -1
  173. omlish/sql/alchemy/asyncs.py +1 -1
  174. omlish/sql/queries/__init__.py +9 -1
  175. omlish/sql/queries/building.py +3 -0
  176. omlish/sql/queries/exprs.py +10 -27
  177. omlish/sql/queries/idents.py +48 -10
  178. omlish/sql/queries/names.py +80 -13
  179. omlish/sql/queries/params.py +64 -0
  180. omlish/sql/queries/rendering.py +1 -1
  181. omlish/subprocesses.py +340 -0
  182. omlish/term.py +29 -14
  183. omlish/testing/pytest/marks.py +2 -2
  184. omlish/testing/pytest/plugins/asyncs.py +6 -1
  185. omlish/testing/pytest/plugins/logging.py +1 -1
  186. omlish/testing/pytest/plugins/switches.py +1 -1
  187. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +7 -5
  188. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/RECORD +200 -117
  189. omlish/fnpairs.py +0 -496
  190. omlish/formats/json/cli/__main__.py +0 -11
  191. omlish/formats/json/cli/cli.py +0 -298
  192. omlish/formats/json/cli/formats.py +0 -71
  193. omlish/formats/json/cli/io.py +0 -74
  194. omlish/formats/json/cli/parsing.py +0 -82
  195. omlish/formats/json/cli/processing.py +0 -48
  196. omlish/formats/json/cli/rendering.py +0 -92
  197. omlish/iterators.py +0 -300
  198. omlish/lite/subprocesses.py +0 -130
  199. /omlish/{formats/json/cli → argparse}/__init__.py +0 -0
  200. /omlish/{lite/fdio → asyncs/asyncio}/__init__.py +0 -0
  201. /omlish/asyncs/{asyncio.py → asyncio/asyncio.py} +0 -0
  202. /omlish/{lite/http → asyncs/bluelet}/__init__.py +0 -0
  203. /omlish/collections/{_abc.py → abc.py} +0 -0
  204. /omlish/{fnpipes.py → funcs/pipes.py} +0 -0
  205. /omlish/io/{_abc.py → abc.py} +0 -0
  206. /omlish/sql/{_abc.py → abc.py} +0 -0
  207. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
  208. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
  209. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
  210. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/top_level.txt +0 -0
omlish/os/linux.py ADDED
@@ -0,0 +1,484 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ """
4
+ ➜ ~ cat /etc/os-release
5
+ NAME="Amazon Linux"
6
+ VERSION="2"
7
+ ID="amzn"
8
+ ID_LIKE="centos rhel fedora"
9
+ VERSION_ID="2"
10
+ PRETTY_NAME="Amazon Linux 2"
11
+
12
+ ➜ ~ cat /etc/os-release
13
+ PRETTY_NAME="Ubuntu 22.04.5 LTS"
14
+ NAME="Ubuntu"
15
+ VERSION_ID="22.04"
16
+ VERSION="22.04.5 LTS (Jammy Jellyfish)"
17
+ VERSION_CODENAME=jammy
18
+ ID=ubuntu
19
+ ID_LIKE=debian
20
+ UBUNTU_CODENAME=jammy
21
+
22
+ ➜ omlish git:(master) docker run -i python:3.12 cat /etc/os-release
23
+ PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
24
+ NAME="Debian GNU/Linux"
25
+ VERSION_ID="12"
26
+ VERSION="12 (bookworm)"
27
+ VERSION_CODENAME=bookworm
28
+ ID=debian
29
+ """
30
+ import dataclasses as dc
31
+ import os.path
32
+ import typing as ta
33
+
34
+
35
+ @dc.dataclass(frozen=True)
36
+ class LinuxOsRelease:
37
+ """
38
+ https://man7.org/linux/man-pages/man5/os-release.5.html
39
+ """
40
+
41
+ raw: ta.Mapping[str, str]
42
+
43
+ # General information identifying the operating system
44
+
45
+ @property
46
+ def name(self) -> str:
47
+ """
48
+ A string identifying the operating system, without a version component, and suitable for presentation to the
49
+ user. If not set, a default of "NAME=Linux" may be used.
50
+
51
+ Examples: "NAME=Fedora", "NAME="Debian GNU/Linux"".
52
+ """
53
+
54
+ return self.raw['NAME']
55
+
56
+ @property
57
+ def id(self) -> str:
58
+ """
59
+ A lower-case string (no spaces or other characters outside of 0-9, a-z, ".", "_" and "-") identifying the
60
+ operating system, excluding any version information and suitable for processing by scripts or usage in generated
61
+ filenames. If not set, a default of "ID=linux" may be used. Note that even though this string may not include
62
+ characters that require shell quoting, quoting may nevertheless be used.
63
+
64
+ Examples: "ID=fedora", "ID=debian".
65
+ """
66
+
67
+ return self.raw['ID']
68
+
69
+ @property
70
+ def id_like(self) -> str:
71
+ """
72
+ A space-separated list of operating system identifiers in the same syntax as the ID= setting. It should list
73
+ identifiers of operating systems that are closely related to the local operating system in regards to packaging
74
+ and programming interfaces, for example listing one or more OS identifiers the local OS is a derivative from. An
75
+ OS should generally only list other OS identifiers it itself is a derivative of, and not any OSes that are
76
+ derived from it, though symmetric relationships are possible. Build scripts and similar should check this
77
+ variable if they need to identify the local operating system and the value of ID= is not recognized. Operating
78
+ systems should be listed in order of how closely the local operating system relates to the listed ones, starting
79
+ with the closest. This field is optional.
80
+
81
+ Examples: for an operating system with "ID=centos", an assignment of "ID_LIKE="rhel fedora"" would be
82
+ appropriate. For an operating system with "ID=ubuntu", an assignment of "ID_LIKE=debian" is appropriate.
83
+ """
84
+
85
+ return self.raw['ID_LIKE']
86
+
87
+ @property
88
+ def pretty_name(self) -> str:
89
+ """
90
+ A pretty operating system name in a format suitable for presentation to the user. May or may not contain a
91
+ release code name or OS version of some kind, as suitable. If not set, a default of "PRETTY_NAME="Linux"" may be
92
+ used
93
+
94
+ Example: "PRETTY_NAME="Fedora 17 (Beefy Miracle)"".
95
+ """
96
+
97
+ return self.raw['PRETTY_NAME']
98
+
99
+ @property
100
+ def cpe_name(self) -> str:
101
+ """
102
+ A CPE name for the operating system, in URI binding syntax, following the Common Platform Enumeration
103
+ Specification[4] as proposed by the NIST. This field is optional.
104
+
105
+ Example: "CPE_NAME="cpe:/o:fedoraproject:fedora:17""
106
+ """
107
+
108
+ return self.raw['CPE_NAME']
109
+
110
+ @property
111
+ def variant(self) -> str:
112
+ """
113
+ A string identifying a specific variant or edition of the operating system suitable for presentation to the
114
+ user. This field may be used to inform the user that the configuration of this system is subject to a specific
115
+ divergent set of rules or default configuration settings. This field is optional and may not be implemented on
116
+ all systems.
117
+
118
+ Examples: "VARIANT="Server Edition"", "VARIANT="Smart Refrigerator Edition"".
119
+
120
+ Note: this field is for display purposes only. The VARIANT_ID field should be used for making programmatic
121
+ decisions.
122
+
123
+ Added in version 220.
124
+ """
125
+
126
+ return self.raw['VARIANT']
127
+
128
+ @property
129
+ def variant_id(self) -> str:
130
+ """
131
+ A lower-case string (no spaces or other characters outside of 0-9, a-z, ".", "_" and "-"), identifying a
132
+ specific variant or edition of the operating system. This may be interpreted by other packages in order to
133
+ determine a divergent default configuration. This field is optional and may not be implemented on all systems.
134
+
135
+ Examples: "VARIANT_ID=server", "VARIANT_ID=embedded".
136
+
137
+ Added in version 220.
138
+ """
139
+
140
+ return self.raw['variant_id']
141
+
142
+ # Information about the version of the operating system
143
+
144
+ @property
145
+ def version(self) -> str:
146
+ """
147
+ A string identifying the operating system version, excluding any OS name information, possibly including a
148
+ release code name, and suitable for presentation to the user. This field is optional.
149
+
150
+ Examples: "VERSION=17", "VERSION="17 (Beefy Miracle)"".
151
+ """
152
+
153
+ return self.raw['VERSION']
154
+
155
+ @property
156
+ def version_id(self) -> str:
157
+ """
158
+ A lower-case string (mostly numeric, no spaces or other characters outside of 0-9, a-z, ".", "_" and "-")
159
+ identifying the operating system version, excluding any OS name information or release code name, and suitable
160
+ for processing by scripts or usage in generated filenames. This field is optional.
161
+
162
+ Examples: "VERSION_ID=17", "VERSION_ID=11.04".
163
+ """
164
+
165
+ return self.raw['VERSION_ID']
166
+
167
+ @property
168
+ def version_codename(self) -> str:
169
+ """
170
+ A lower-case string (no spaces or other characters outside of 0-9, a-z, ".", "_" and "-") identifying the
171
+ operating system release code name, excluding any OS name information or release version, and suitable for
172
+ processing by scripts or usage in generated filenames. This field is optional and may not be implemented on all
173
+ systems.
174
+
175
+ Examples: "VERSION_CODENAME=buster", "VERSION_CODENAME=xenial".
176
+
177
+ Added in version 231.
178
+ """
179
+
180
+ return self.raw['VERSION_CODENAME']
181
+
182
+ @property
183
+ def build_id(self) -> str:
184
+ """
185
+ A string uniquely identifying the system image originally used as the installation base. In most cases,
186
+ VERSION_ID or IMAGE_ID+IMAGE_VERSION are updated when the entire system image is replaced during an update.
187
+ BUILD_ID may be used in distributions where the original installation image version is important: VERSION_ID
188
+ would change during incremental system updates, but BUILD_ID would not. This field is optional.
189
+
190
+ Examples: "BUILD_ID="2013-03-20.3"", "BUILD_ID=201303203".
191
+
192
+ Added in version 200.
193
+ """
194
+
195
+ return self.raw['BUILD_ID']
196
+
197
+ @property
198
+ def image_id(self) -> str:
199
+ """
200
+ A lower-case string (no spaces or other characters outside of 0-9, a-z, ".", "_" and "-"), identifying a
201
+ specific image of the operating system. This is supposed to be used for environments where OS images are
202
+ prepared, built, shipped and updated as comprehensive, consistent OS images. This field is optional and may not
203
+ be implemented on all systems, in particularly not on those that are not managed via images but put together and
204
+ updated from individual packages and on the local system.
205
+
206
+ Examples: "IMAGE_ID=vendorx-cashier-system", "IMAGE_ID=netbook-image".
207
+
208
+ Added in version 249.
209
+ """
210
+
211
+ return self.raw['IMAGE_ID']
212
+
213
+ @property
214
+ def image_version(self) -> str:
215
+ """
216
+ A lower-case string (mostly numeric, no spaces or other characters outside of 0-9, a-z, ".", "_" and "-")
217
+ identifying the OS image version. This is supposed to be used together with IMAGE_ID described above, to discern
218
+ different versions of the same image.
219
+
220
+ Examples: "IMAGE_VERSION=33", "IMAGE_VERSION=47.1rc1".
221
+
222
+ Added in version 249.
223
+ """
224
+
225
+ return self.raw['IMAGE_VERSION']
226
+
227
+ # To summarize: if the image updates are built and shipped as comprehensive units, IMAGE_ID+IMAGE_VERSION is the
228
+ # best fit. Otherwise, if updates eventually completely replace previously installed contents, as in a typical
229
+ # binary distribution, VERSION_ID should be used to identify major releases of the operating system. BUILD_ID may
230
+ # be used instead or in addition to VERSION_ID when the original system image version is important.
231
+
232
+ #
233
+
234
+ # Presentation information and links
235
+
236
+ # Links to resources on the Internet related to the operating system. HOME_URL= should refer to the homepage of the
237
+ # operating system, or alternatively some homepage of the specific version of the operating system.
238
+ # DOCUMENTATION_URL= should refer to the main documentation page for this operating system. SUPPORT_URL= should
239
+ # refer to the main support page for the operating system, if there is any. This is primarily intended for operating
240
+ # systems which vendors provide support for. BUG_REPORT_URL= should refer to the main bug reporting page for the
241
+ # operating system, if there is any. This is primarily intended for operating systems that rely on community QA.
242
+ # PRIVACY_POLICY_URL= should refer to the main privacy policy page for the operating system, if there is any. These
243
+ # settings are optional, and providing only some of these settings is common. These URLs are intended to be exposed
244
+ # in "About this system" UIs behind links with captions such as "About this Operating System", "Obtain Support",
245
+ # "Report a Bug", or "Privacy Policy". The values should be in RFC3986 format[5], and should be "http:" or "https:"
246
+ # URLs, and possibly "mailto:" or "tel:". Only one URL shall be listed in each setting. If multiple resources need
247
+ # to be referenced, it is recommended to provide an online landing page linking all available resources.
248
+
249
+ # Examples: "HOME_URL="https://fedoraproject.org/"", "BUG_REPORT_URL="https://bugzilla.redhat.com/"".
250
+
251
+ @property
252
+ def home_url(self) -> str:
253
+ return self.raw['HOME_URL']
254
+
255
+ @property
256
+ def documentation_url(self) -> str:
257
+ return self.raw['DOCUMENTATION_URL']
258
+
259
+ @property
260
+ def support_url(self) -> str:
261
+ return self.raw['SUPPORT_URL']
262
+
263
+ @property
264
+ def bug_report_url(self) -> str:
265
+ return self.raw['BUG_REPORT_URL']
266
+
267
+ @property
268
+ def privacy_policy_url(self) -> str:
269
+ return self.raw['PRIVACY_POLICY_URL']
270
+
271
+ @property
272
+ def support_end(self) -> str:
273
+ """
274
+ The date at which support for this version of the OS ends. (What exactly "lack of support" means varies between
275
+ vendors, but generally users should assume that updates, including security fixes, will not be provided.) The
276
+ value is a date in the ISO 8601 format "YYYY-MM-DD", and specifies the first day on which support is not
277
+ provided.
278
+
279
+ For example, "SUPPORT_END=2001-01-01" means that the system was supported until the end of the last day of the
280
+ previous millennium.
281
+
282
+ Added in version 252.
283
+ """
284
+
285
+ return self.raw['SUPPORT_END']
286
+
287
+ @property
288
+ def logo(self) -> str:
289
+ """
290
+ A string, specifying the name of an icon as defined by freedesktop.org Icon Theme Specification[6]. This can be
291
+ used by graphical applications to display an operating system's or distributor's logo. This field is optional
292
+ and may not necessarily be implemented on all systems.
293
+
294
+ Examples: "LOGO=fedora-logo", "LOGO=distributor-logo-opensuse"
295
+
296
+ Added in version 240.
297
+ """
298
+
299
+ return self.raw['LOGO']
300
+
301
+ @property
302
+ def ansi_color(self) -> str:
303
+ """
304
+ A suggested presentation color when showing the OS name on the console. This should be specified as string
305
+ suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for setting graphical rendition. This field is
306
+ optional.
307
+
308
+ Examples: "ANSI_COLOR="0;31"" for red, "ANSI_COLOR="1;34"" for light blue, or "ANSI_COLOR="0;38;2;60;110;180""
309
+ for Fedora blue.
310
+ """
311
+
312
+ return self.raw['ANSI_COLOR']
313
+
314
+ @property
315
+ def vendor_name(self) -> str:
316
+ """
317
+ The name of the OS vendor. This is the name of the organization or company which produces the OS. This field is
318
+ optional.
319
+
320
+ This name is intended to be exposed in "About this system" UIs or software update UIs when needed to distinguish
321
+ the OS vendor from the OS itself. It is intended to be human readable.
322
+
323
+ Examples: "VENDOR_NAME="Fedora Project"" for Fedora Linux, "VENDOR_NAME="Canonical"" for Ubuntu.
324
+
325
+ Added in version 254.
326
+ """
327
+
328
+ return self.raw['VENDOR_NAME']
329
+
330
+ @property
331
+ def vendor_url(self) -> str:
332
+ """
333
+ The homepage of the OS vendor. This field is optional. The VENDOR_NAME= field should be set if this one is,
334
+ although clients must be robust against either field not being set.
335
+
336
+ The value should be in RFC3986 format[5], and should be "http:" or "https:" URLs. Only one URL shall be listed
337
+ in the setting.
338
+
339
+ Examples: "VENDOR_URL="https://fedoraproject.org/"", "VENDOR_URL="https://canonical.com/"".
340
+
341
+ Added in version 254.
342
+ """
343
+
344
+ return self.raw['VENDOR_URL']
345
+
346
+ # Distribution-level defaults and metadata
347
+
348
+ @property
349
+ def default_hostname(self) -> str:
350
+ """
351
+ A string specifying the hostname if hostname(5) is not present and no other configuration source specifies the
352
+ hostname. Must be either a single DNS label (a string composed of 7-bit ASCII lower-case characters and no
353
+ spaces or dots, limited to the format allowed for DNS domain name labels), or a sequence of such labels
354
+ separated by single dots that forms a valid DNS FQDN. The hostname must be at most 64 characters, which is a
355
+ Linux limitation (DNS allows longer names).
356
+
357
+ See org.freedesktop.hostname1(5) for a description of how systemd-hostnamed.service(8) determines the fallback
358
+ hostname.
359
+
360
+ Added in version 248.
361
+ """
362
+
363
+ return self.raw['DEFAULT_HOSTNAME']
364
+
365
+ @property
366
+ def architecture(self) -> str:
367
+ """
368
+ A string that specifies which CPU architecture the userspace binaries require. The architecture identifiers are
369
+ the same as for ConditionArchitecture= described in systemd.unit(5). The field is optional and should only be
370
+ used when just single architecture is supported. It may provide redundant information when used in a GPT
371
+ partition with a GUID type that already encodes the architecture. If this is not the case, the architecture
372
+ should be specified in e.g., an extension image, to prevent an incompatible host from loading it.
373
+
374
+ Added in version 252.
375
+ """
376
+
377
+ return self.raw['ARCHITECTURE']
378
+
379
+ @property
380
+ def sysext_level(self) -> str:
381
+ """
382
+ A lower-case string (mostly numeric, no spaces or other characters outside of 0-9, a-z, ".", "_" and "-")
383
+ identifying the operating system extensions support level, to indicate which extension images are supported. See
384
+ /usr/lib/extension-release.d/extension-release.IMAGE, initrd[2] and systemd-sysext(8)) for more information.
385
+
386
+ Examples: "SYSEXT_LEVEL=2", "SYSEXT_LEVEL=15.14".
387
+
388
+ Added in version 248.
389
+ """
390
+
391
+ return self.raw['SYSEXT_LEVEL']
392
+
393
+ @property
394
+ def confext_level(self) -> str:
395
+ """
396
+ Semantically the same as SYSEXT_LEVEL= but for confext images. See
397
+ /etc/extension-release.d/extension-release.IMAGE for more information.
398
+
399
+ Examples: "CONFEXT_LEVEL=2", "CONFEXT_LEVEL=15.14".
400
+
401
+ Added in version 254.
402
+ """
403
+
404
+ return self.raw['CONFEXT_LEVEL']
405
+
406
+ @property
407
+ def sysext_scope(self) -> str:
408
+ """
409
+ Takes a space-separated list of one or more of the strings "system", "initrd" and "portable". This field is only
410
+ supported in extension-release.d/ files and indicates what environments the system extension is applicable to:
411
+ i.e. to regular systems, to initrds, or to portable service images. If unspecified, "SYSEXT_SCOPE=system
412
+ portable" is implied, i.e. any system extension without this field is applicable to regular systems and to
413
+ portable service environments, but not to initrd environments.
414
+
415
+ Added in version 250.
416
+ """
417
+
418
+ return self.raw['SYSEXT_SCOPE']
419
+
420
+ @property
421
+ def confext_scope(self) -> str:
422
+ """
423
+ Semantically the same as SYSEXT_SCOPE= but for confext images.
424
+
425
+ Added in version 254.
426
+ """
427
+
428
+ return self.raw['CONFEXT_SCOPE']
429
+
430
+ @property
431
+ def portable_prefixes(self) -> str:
432
+ """
433
+ Takes a space-separated list of one or more valid prefix match strings for the Portable Services[3] logic. This
434
+ field serves two purposes: it is informational, identifying portable service images as such (and thus allowing
435
+ them to be distinguished from other OS images, such as bootable system images). It is also used when a portable
436
+ service image is attached: the specified or implied portable service prefix is checked against the list
437
+ specified here, to enforce restrictions how images may be attached to a system.
438
+
439
+ Added in version 250.
440
+ """
441
+
442
+ return self.raw['PORTABLE_PREFIXES']
443
+
444
+ #
445
+
446
+ DEFAULT_PATHS: ta.ClassVar[ta.Sequence[str]] = [
447
+ '/etc/os-release',
448
+ '/usr/lib/os-release',
449
+ ]
450
+
451
+ @classmethod
452
+ def read(cls, *paths: str) -> ta.Optional['LinuxOsRelease']:
453
+ for fp in (paths or cls.DEFAULT_PATHS):
454
+ if not os.path.isfile(fp):
455
+ continue
456
+ with open(fp) as f:
457
+ src = f.read()
458
+ break
459
+ else:
460
+ return None
461
+
462
+ raw = cls._parse_os_release(src)
463
+
464
+ return cls(raw)
465
+
466
+ @classmethod
467
+ def _parse_os_release(cls, src: str) -> ta.Mapping[str, str]:
468
+ dct: ta.Dict[str, str] = {}
469
+
470
+ for l in src.splitlines():
471
+ if not (l := l.strip()):
472
+ continue
473
+ if l.startswith('#') or '=' not in l:
474
+ continue
475
+
476
+ k, _, v = l.partition('=')
477
+ if k.startswith('"'):
478
+ k = k[1:-1]
479
+ if v.startswith('"'):
480
+ v = v[1:-1]
481
+
482
+ dct[k] = v
483
+
484
+ return dct
omlish/os/paths.py ADDED
@@ -0,0 +1,36 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ import os.path
4
+ import typing as ta
5
+
6
+
7
+ def abs_real_path(p: str) -> str:
8
+ return os.path.abspath(os.path.realpath(p))
9
+
10
+
11
+ def is_path_in_dir(base_dir: str, target_path: str) -> bool:
12
+ base_dir = abs_real_path(base_dir)
13
+ target_path = abs_real_path(target_path)
14
+
15
+ return target_path.startswith(base_dir + os.path.sep)
16
+
17
+
18
+ def relative_symlink(
19
+ src: str,
20
+ dst: str,
21
+ *,
22
+ target_is_directory: bool = False,
23
+ dir_fd: ta.Optional[int] = None,
24
+ make_dirs: bool = False,
25
+ **kwargs: ta.Any,
26
+ ) -> None:
27
+ if make_dirs:
28
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
29
+
30
+ os.symlink(
31
+ os.path.relpath(src, os.path.dirname(dst)),
32
+ dst,
33
+ target_is_directory=target_is_directory,
34
+ dir_fd=dir_fd,
35
+ **kwargs,
36
+ )
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP007
2
+ # @omlish-lite
2
3
  import fcntl
3
4
  import os
4
5
  import signal
omlish/os/sizes.py ADDED
@@ -0,0 +1,9 @@
1
+ import resource
2
+
3
+
4
+ PAGE_SIZE = resource.getpagesize()
5
+
6
+
7
+ def round_to_page_size(sz: int) -> int:
8
+ sz += PAGE_SIZE - 1
9
+ return sz - (sz % PAGE_SIZE)
@@ -27,10 +27,13 @@ from .types import ( # noqa
27
27
  Any,
28
28
  DEFAULT_REFLECTOR,
29
29
  Generic,
30
+ Literal,
30
31
  NewType,
32
+ ReflectTypeError,
31
33
  Reflector,
32
34
  TYPES,
33
35
  Type,
36
+ TypeInfo,
34
37
  Union,
35
38
  get_newtype_supertype,
36
39
  get_orig_bases,
omlish/reflect/subst.py CHANGED
@@ -8,6 +8,7 @@ from .ops import get_concrete_type
8
8
  from .ops import to_annotation
9
9
  from .types import Any
10
10
  from .types import Generic
11
+ from .types import Literal
11
12
  from .types import NewType
12
13
  from .types import Type
13
14
  from .types import Union
@@ -35,7 +36,7 @@ def replace_type_vars(
35
36
  update_aliases: bool = False,
36
37
  ) -> Type:
37
38
  def rec(cur):
38
- if isinstance(cur, (type, NewType, Any)):
39
+ if isinstance(cur, (type, NewType, Any, Literal)):
39
40
  return cur
40
41
 
41
42
  if isinstance(cur, Generic):