omdev 0.0.0.dev416__py3-none-any.whl → 0.0.0.dev500__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 omdev might be problematic. Click here for more details.
- omdev/{.manifests.json → .omlish-manifests.json} +23 -47
- omdev/README.md +51 -0
- omdev/__about__.py +12 -8
- omdev/amalg/cli/main.py +1 -2
- omdev/amalg/gen/gen.py +49 -6
- omdev/amalg/gen/imports.py +1 -1
- omdev/amalg/gen/manifests.py +1 -1
- omdev/amalg/gen/resources.py +1 -1
- omdev/amalg/gen/srcfiles.py +26 -3
- omdev/amalg/gen/strip.py +1 -1
- omdev/amalg/gen/types.py +1 -1
- omdev/amalg/gen/typing.py +1 -1
- omdev/amalg/info.py +32 -0
- omdev/cache/compute/storage.py +3 -1
- omdev/cache/data/actions.py +1 -1
- omdev/cache/data/cache.py +2 -2
- omdev/cache/data/specs.py +1 -1
- omdev/cexts/_boilerplate.cc +2 -3
- omdev/cexts/_distutils/build_ext.py +5 -2
- omdev/cexts/_distutils/compilers/ccompiler.py +5 -2
- omdev/cexts/_distutils/compilers/options.py +3 -0
- omdev/cexts/_distutils/compilers/unixccompiler.py +6 -2
- omdev/cexts/_distutils/dir_util.py +6 -2
- omdev/cexts/_distutils/errors.py +3 -0
- omdev/cexts/_distutils/extension.py +3 -0
- omdev/cexts/_distutils/file_util.py +6 -2
- omdev/cexts/_distutils/modified.py +3 -0
- omdev/cexts/_distutils/spawn.py +6 -2
- omdev/cexts/_distutils/sysconfig.py +3 -0
- omdev/cexts/_distutils/util.py +6 -2
- omdev/cexts/_distutils/version.py +3 -0
- omdev/cexts/cmake.py +5 -3
- omdev/cexts/scan.py +1 -2
- omdev/ci/cache.py +7 -3
- omdev/ci/cli.py +6 -4
- omdev/ci/docker/buildcaching.py +3 -1
- omdev/ci/docker/cache.py +2 -1
- omdev/ci/docker/cacheserved/cache.py +4 -1
- omdev/ci/docker/cacheserved/manifests.py +2 -2
- omdev/ci/docker/dataserver.py +2 -2
- omdev/ci/docker/imagepulling.py +2 -1
- omdev/ci/docker/packing.py +1 -1
- omdev/ci/docker/repositories.py +2 -1
- omdev/ci/github/api/clients.py +8 -4
- omdev/ci/github/api/v1/client.py +4 -1
- omdev/ci/github/api/v2/api.py +2 -0
- omdev/ci/github/api/v2/azure.py +4 -1
- omdev/ci/github/api/v2/client.py +4 -1
- omdev/cli/clicli.py +37 -7
- omdev/clipboard/clipboard.py +1 -1
- omdev/cmake.py +2 -1
- omdev/cmdlog/cli.py +1 -2
- omdev/dataclasses/_dumping.py +1960 -0
- omdev/dataclasses/_template.py +22 -0
- omdev/dataclasses/cli.py +7 -2
- omdev/dataclasses/codegen.py +342 -62
- omdev/dataclasses/dumping.py +200 -0
- omdev/dataserver/handlers.py +3 -2
- omdev/dataserver/targets.py +2 -2
- omdev/imgur.py +2 -2
- omdev/interp/cli.py +1 -1
- omdev/interp/inspect.py +2 -1
- omdev/interp/providers/base.py +3 -2
- omdev/interp/providers/standalone.py +4 -1
- omdev/interp/providers/system.py +2 -2
- omdev/interp/pyenv/install.py +2 -1
- omdev/interp/pyenv/provider.py +2 -2
- omdev/interp/types.py +3 -2
- omdev/interp/uv/provider.py +40 -2
- omdev/interp/uv/uv.py +2 -2
- omdev/interp/venvs.py +3 -2
- omdev/irc/messages/base.py +50 -0
- omdev/irc/messages/formats.py +92 -0
- omdev/irc/messages/messages.py +775 -0
- omdev/irc/messages/parsing.py +99 -0
- omdev/irc/numerics/formats.py +97 -0
- omdev/irc/numerics/numerics.py +865 -0
- omdev/irc/numerics/types.py +59 -0
- omdev/irc/protocol/LICENSE +11 -0
- omdev/irc/protocol/__init__.py +61 -0
- omdev/irc/protocol/consts.py +6 -0
- omdev/irc/protocol/errors.py +30 -0
- omdev/irc/protocol/message.py +21 -0
- omdev/irc/protocol/nuh.py +55 -0
- omdev/irc/protocol/parsing.py +158 -0
- omdev/irc/protocol/rendering.py +153 -0
- omdev/irc/protocol/tags.py +102 -0
- omdev/irc/protocol/utils.py +30 -0
- omdev/manifests/_dumping.py +529 -136
- omdev/manifests/building.py +6 -3
- omdev/manifests/main.py +1 -1
- omdev/markdown/__init__.py +0 -0
- omdev/markdown/incparse.py +116 -0
- omdev/markdown/tokens.py +51 -0
- omdev/oci/data.py +2 -2
- omdev/oci/datarefs.py +2 -2
- omdev/oci/media.py +2 -2
- omdev/oci/repositories.py +3 -2
- omdev/packaging/marshal.py +9 -9
- omdev/packaging/requires.py +6 -6
- omdev/packaging/revisions.py +5 -2
- omdev/packaging/specifiers.py +41 -42
- omdev/packaging/versions.py +10 -10
- omdev/packaging/wheelfile.py +4 -2
- omdev/precheck/blanklines.py +66 -0
- omdev/precheck/caches.py +1 -1
- omdev/precheck/imports.py +14 -1
- omdev/precheck/lite.py +2 -2
- omdev/precheck/main.py +5 -5
- omdev/precheck/unicode.py +39 -15
- omdev/py/asts/__init__.py +0 -0
- omdev/py/asts/parents.py +28 -0
- omdev/py/asts/toplevel.py +123 -0
- omdev/py/asts/visitors.py +18 -0
- omdev/py/attrdocs.py +6 -7
- omdev/py/bracepy.py +12 -4
- omdev/py/docstrings/numpydoc.py +4 -4
- omdev/py/reprs.py +32 -0
- omdev/py/scripts/execstat.py +31 -26
- omdev/py/srcheaders.py +1 -1
- omdev/py/tokens/__init__.py +0 -0
- omdev/{tokens → py/tokens}/utils.py +2 -1
- omdev/py/tools/importscan.py +2 -2
- omdev/py/tools/mkrelimp.py +3 -4
- omdev/py/tools/pipdepup.py +686 -0
- omdev/pyproject/cli.py +1 -1
- omdev/pyproject/pkg.py +197 -48
- omdev/pyproject/reqs.py +36 -10
- omdev/pyproject/tools/__init__.py +0 -0
- omdev/pyproject/tools/aboutdeps.py +60 -0
- omdev/pyproject/venvs.py +12 -2
- omdev/rs/__init__.py +0 -0
- omdev/scripts/ci.py +9551 -6982
- omdev/scripts/interp.py +1323 -892
- omdev/scripts/lib/__init__.py +0 -0
- omdev/scripts/lib/inject.py +2086 -0
- omdev/scripts/lib/logs.py +2175 -0
- omdev/scripts/lib/marshal.py +1731 -0
- omdev/scripts/pyproject.py +4979 -1874
- omdev/tools/docker.py +19 -7
- omdev/tools/git/cli.py +56 -16
- omdev/tools/git/messages.py +2 -2
- omdev/tools/json/cli.py +6 -6
- omdev/tools/json/formats.py +2 -0
- omdev/tools/json/parsing.py +5 -5
- omdev/tools/json/processing.py +6 -3
- omdev/tools/json/rendering.py +2 -2
- omdev/tools/jsonview/cli.py +49 -65
- omdev/tools/jsonview/resources/jsonview.html.j2 +43 -0
- omdev/tools/pawk/README.md +195 -0
- omdev/tools/pawk/pawk.py +2 -2
- omdev/tools/pip.py +8 -0
- omdev/tui/__init__.py +0 -0
- omdev/tui/apps/__init__.py +0 -0
- omdev/tui/apps/edit/__init__.py +0 -0
- omdev/tui/apps/edit/main.py +167 -0
- omdev/tui/apps/irc/__init__.py +0 -0
- omdev/tui/apps/irc/__main__.py +4 -0
- omdev/tui/apps/irc/app.py +286 -0
- omdev/tui/apps/irc/client.py +187 -0
- omdev/tui/apps/irc/commands.py +175 -0
- omdev/tui/apps/irc/main.py +26 -0
- omdev/tui/apps/markdown/__init__.py +0 -0
- omdev/tui/apps/markdown/__main__.py +11 -0
- omdev/{ptk → tui/apps}/markdown/cli.py +5 -7
- omdev/tui/rich/__init__.py +46 -0
- omdev/tui/rich/console2.py +20 -0
- omdev/tui/rich/markdown2.py +186 -0
- omdev/tui/textual/__init__.py +265 -0
- omdev/tui/textual/app2.py +16 -0
- omdev/tui/textual/autocomplete/LICENSE +21 -0
- omdev/tui/textual/autocomplete/__init__.py +33 -0
- omdev/tui/textual/autocomplete/matching.py +226 -0
- omdev/tui/textual/autocomplete/paths.py +202 -0
- omdev/tui/textual/autocomplete/widget.py +612 -0
- omdev/tui/textual/debug/__init__.py +10 -0
- omdev/tui/textual/debug/dominfo.py +151 -0
- omdev/tui/textual/debug/screen.py +24 -0
- omdev/tui/textual/devtools.py +187 -0
- omdev/tui/textual/drivers2.py +55 -0
- omdev/tui/textual/logging2.py +20 -0
- omdev/tui/textual/types.py +45 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/METADATA +18 -12
- omdev-0.0.0.dev500.dist-info/RECORD +386 -0
- omdev/ptk/__init__.py +0 -103
- omdev/ptk/apps/ncdu.py +0 -167
- omdev/ptk/confirm.py +0 -60
- omdev/ptk/markdown/LICENSE +0 -22
- omdev/ptk/markdown/__init__.py +0 -10
- omdev/ptk/markdown/__main__.py +0 -11
- omdev/ptk/markdown/border.py +0 -94
- omdev/ptk/markdown/markdown.py +0 -390
- omdev/ptk/markdown/parser.py +0 -42
- omdev/ptk/markdown/styles.py +0 -29
- omdev/ptk/markdown/tags.py +0 -299
- omdev/ptk/markdown/utils.py +0 -366
- omdev/pyproject/cexts.py +0 -110
- omdev/tools/antlr/__main__.py +0 -11
- omdev/tools/antlr/cli.py +0 -62
- omdev/tools/antlr/consts.py +0 -7
- omdev/tools/antlr/gen.py +0 -188
- omdev-0.0.0.dev416.dist-info/RECORD +0 -332
- /omdev/{ptk/apps → irc}/__init__.py +0 -0
- /omdev/{tokens → irc/messages}/__init__.py +0 -0
- /omdev/{tools/antlr → irc/numerics}/__init__.py +0 -0
- /omdev/{tokens → py/tokens}/all.py +0 -0
- /omdev/{tokens → py/tokens}/tokenizert.py +0 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/top_level.txt +0 -0
omdev/manifests/building.py
CHANGED
|
@@ -39,7 +39,7 @@ from omlish.lite.cached import cached_nullary
|
|
|
39
39
|
from omlish.lite.check import check
|
|
40
40
|
from omlish.lite.imports import import_attr
|
|
41
41
|
from omlish.lite.json import json_dumps_pretty
|
|
42
|
-
from omlish.
|
|
42
|
+
from omlish.logs.modules import get_module_logger
|
|
43
43
|
from omlish.manifests.base import ModAttrManifest
|
|
44
44
|
from omlish.manifests.globals import GlobalManifestLoader
|
|
45
45
|
from omlish.manifests.types import Manifest
|
|
@@ -53,6 +53,9 @@ T = ta.TypeVar('T')
|
|
|
53
53
|
ManifestDumperTarget = ta.Union['InlineManifestDumperTarget', 'AttrManifestDumperTarget'] # ta.TypeAlias
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
log = get_module_logger(globals()) # noqa
|
|
57
|
+
|
|
58
|
+
|
|
56
59
|
##
|
|
57
60
|
|
|
58
61
|
|
|
@@ -407,7 +410,7 @@ class ManifestBuilder:
|
|
|
407
410
|
])))
|
|
408
411
|
|
|
409
412
|
if write:
|
|
410
|
-
with open(os.path.join(pkg_dir, '.manifests.json'), 'w') as f: # noqa
|
|
413
|
+
with open(os.path.join(pkg_dir, '.omlish-manifests.json'), 'w') as f: # noqa
|
|
411
414
|
f.write(json_dumps_pretty([dc.asdict(m) for m in manifests]))
|
|
412
415
|
f.write('\n')
|
|
413
416
|
|
|
@@ -425,7 +428,7 @@ def check_package_manifests(
|
|
|
425
428
|
if not os.path.isdir(pkg_dir) or not os.path.isfile(os.path.join(pkg_dir, '__init__.py')):
|
|
426
429
|
raise Exception(pkg_dir)
|
|
427
430
|
|
|
428
|
-
manifests_file = os.path.join(pkg_dir, '.manifests.json')
|
|
431
|
+
manifests_file = os.path.join(pkg_dir, '.omlish-manifests.json')
|
|
429
432
|
if not os.path.isfile(manifests_file):
|
|
430
433
|
raise Exception(f'No manifests file: {manifests_file}')
|
|
431
434
|
|
omdev/manifests/main.py
CHANGED
|
@@ -5,7 +5,7 @@ import multiprocessing as mp
|
|
|
5
5
|
import os.path
|
|
6
6
|
|
|
7
7
|
from omlish.lite.json import json_dumps_pretty
|
|
8
|
-
from omlish.logs.standard import configure_standard_logging
|
|
8
|
+
from omlish.logs.std.standard import configure_standard_logging
|
|
9
9
|
|
|
10
10
|
from .building import ManifestBuilder
|
|
11
11
|
from .building import check_package_manifests
|
|
File without changes
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
with lang.auto_proxy_import(globals()):
|
|
8
|
+
import markdown_it as md
|
|
9
|
+
import markdown_it.token # noqa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IncrementalMarkdownParser:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
*,
|
|
19
|
+
parser: ta.Optional['md.MarkdownIt'] = None,
|
|
20
|
+
) -> None:
|
|
21
|
+
super().__init__()
|
|
22
|
+
|
|
23
|
+
if parser is None:
|
|
24
|
+
parser = md.MarkdownIt()
|
|
25
|
+
self._parser = parser
|
|
26
|
+
|
|
27
|
+
self._stable_tokens: list[md.token.Token] = []
|
|
28
|
+
self._buffer = ''
|
|
29
|
+
self._num_stable_lines = 0 # Number of lines in stable tokens
|
|
30
|
+
|
|
31
|
+
class FeedOutput(ta.NamedTuple):
|
|
32
|
+
stable: ta.Sequence['md.token.Token']
|
|
33
|
+
new_stable: ta.Sequence['md.token.Token']
|
|
34
|
+
unstable: ta.Sequence['md.token.Token']
|
|
35
|
+
|
|
36
|
+
def feed2(self, chunk: str) -> FeedOutput:
|
|
37
|
+
self._buffer += chunk
|
|
38
|
+
|
|
39
|
+
# Parse the current buffer
|
|
40
|
+
new_tokens = self._parser.parse(self._buffer)
|
|
41
|
+
|
|
42
|
+
# Adjust ALL tokens to account for stable lines from previous parses (new_tokens have line numbers relative to
|
|
43
|
+
# current buffer)
|
|
44
|
+
adjusted_tokens = self._adjust_token_line_numbers(new_tokens, self._num_stable_lines)
|
|
45
|
+
|
|
46
|
+
# Find stable tokens (all but the last parent and its children)
|
|
47
|
+
stable_count = self._find_stable_token_count(adjusted_tokens)
|
|
48
|
+
|
|
49
|
+
newly_stable: ta.Sequence[md.token.Token]
|
|
50
|
+
if stable_count > 0:
|
|
51
|
+
# Extract newly stable tokens (already have adjusted line numbers)
|
|
52
|
+
newly_stable = adjusted_tokens[:stable_count]
|
|
53
|
+
|
|
54
|
+
# Calculate how many lines these stable tokens cover
|
|
55
|
+
max_line = 0
|
|
56
|
+
for token in newly_stable:
|
|
57
|
+
if token.map:
|
|
58
|
+
max_line = max(max_line, token.map[1])
|
|
59
|
+
|
|
60
|
+
# Update buffer to only contain unstable content
|
|
61
|
+
if max_line > self._num_stable_lines:
|
|
62
|
+
# max_line is absolute, num_stable_lines is the current buffer offset
|
|
63
|
+
lines_to_remove = max_line - self._num_stable_lines
|
|
64
|
+
lines = self._buffer.split('\n')
|
|
65
|
+
self._buffer = '\n'.join(lines[lines_to_remove:])
|
|
66
|
+
|
|
67
|
+
# Store newly stable tokens (with adjusted line numbers)
|
|
68
|
+
self._stable_tokens.extend(newly_stable)
|
|
69
|
+
self._num_stable_lines = max_line
|
|
70
|
+
|
|
71
|
+
else:
|
|
72
|
+
newly_stable = ()
|
|
73
|
+
|
|
74
|
+
return IncrementalMarkdownParser.FeedOutput(
|
|
75
|
+
stable=self._stable_tokens,
|
|
76
|
+
new_stable=newly_stable,
|
|
77
|
+
unstable=adjusted_tokens[stable_count:],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def feed(self, chunk: str) -> list['md.token.Token']:
|
|
81
|
+
out = self.feed2(chunk)
|
|
82
|
+
return [*out.stable, *out.unstable]
|
|
83
|
+
|
|
84
|
+
def _find_stable_token_count(self, tokens: list['md.token.Token']) -> int:
|
|
85
|
+
if not tokens:
|
|
86
|
+
return 0
|
|
87
|
+
|
|
88
|
+
# Find indices of all parent-level tokens (nesting = 0)
|
|
89
|
+
parent_indices = []
|
|
90
|
+
for i, token in enumerate(tokens):
|
|
91
|
+
if token.nesting in (1, 0) and token.level == 0:
|
|
92
|
+
parent_indices.append(i)
|
|
93
|
+
|
|
94
|
+
if len(parent_indices) < 2:
|
|
95
|
+
# Need at least 2 parent tokens to have stable content
|
|
96
|
+
return 0
|
|
97
|
+
|
|
98
|
+
# The last parent and everything after it is unstable. Everything before the second-to-last parent is stable.
|
|
99
|
+
return parent_indices[-2]
|
|
100
|
+
|
|
101
|
+
def _adjust_token_line_numbers(
|
|
102
|
+
self,
|
|
103
|
+
tokens: list['md.token.Token'],
|
|
104
|
+
line_offset: int,
|
|
105
|
+
) -> list['md.token.Token']:
|
|
106
|
+
adjusted = []
|
|
107
|
+
for token in tokens:
|
|
108
|
+
if token.map:
|
|
109
|
+
token = dc.replace(
|
|
110
|
+
token,
|
|
111
|
+
map=[token.map[0] + line_offset, token.map[1] + line_offset],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
adjusted.append(token)
|
|
115
|
+
|
|
116
|
+
return adjusted
|
omdev/markdown/tokens.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# ruff: noqa: TC002
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
with lang.auto_proxy_import(globals()):
|
|
8
|
+
import markdown_it as md
|
|
9
|
+
import markdown_it.token # noqa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def token_repr(t: 'md.token.Token') -> str:
|
|
16
|
+
return ''.join([
|
|
17
|
+
'Token(',
|
|
18
|
+
f'type={t.type!r}',
|
|
19
|
+
*([f', tag={t.tag!r}'] if t.tag else []),
|
|
20
|
+
*([f', nesting={t.nesting!r}'] if t.nesting else []),
|
|
21
|
+
*([f', attrs={t.attrs!r}'] if t.attrs else []),
|
|
22
|
+
*([f', map={t.map!r}'] if t.map else []),
|
|
23
|
+
*([f', level={t.level!r}'] if t.level else []),
|
|
24
|
+
*([f', children={t.children!r}'] if t.children else []),
|
|
25
|
+
*([f', content={t.content!r}'] if t.content else []),
|
|
26
|
+
*([f', markup={t.markup!r}'] if t.markup else []),
|
|
27
|
+
*([f', info={t.info!r}'] if t.info else []),
|
|
28
|
+
*([f', meta={t.meta!r}'] if t.meta else []),
|
|
29
|
+
*([f', block={t.block!r}'] if t.block else []),
|
|
30
|
+
*([f', hidden={t.hidden!r}'] if t.hidden else []),
|
|
31
|
+
')',
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def flatten_tokens(
|
|
39
|
+
tokens: ta.Iterable['md.token.Token'],
|
|
40
|
+
*,
|
|
41
|
+
filter: ta.Callable[['md.token.Token'], bool] | None = None, # noqa
|
|
42
|
+
) -> ta.Iterable['md.token.Token']:
|
|
43
|
+
def rec(tks: ta.Iterable['md.token.Token']) -> ta.Iterator['md.token.Token']:
|
|
44
|
+
for tk in tks:
|
|
45
|
+
if tk.children and not (filter is not None and not filter(tk)):
|
|
46
|
+
yield from rec(tk.children)
|
|
47
|
+
|
|
48
|
+
else:
|
|
49
|
+
yield tk
|
|
50
|
+
|
|
51
|
+
return rec(tokens)
|
omdev/oci/data.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# ruff: noqa: UP006 UP007 UP045
|
|
2
2
|
# @omlish-lite
|
|
3
|
-
import abc
|
|
4
3
|
import dataclasses as dc
|
|
5
4
|
import enum
|
|
6
5
|
import typing as ta
|
|
7
6
|
|
|
7
|
+
from omlish.lite.abstract import Abstract
|
|
8
8
|
from omlish.lite.check import check
|
|
9
9
|
from omlish.lite.marshal import OBJ_MARSHALER_FIELD_KEY
|
|
10
10
|
from omlish.lite.marshal import OBJ_MARSHALER_OMIT_IF_NONE
|
|
@@ -17,7 +17,7 @@ from .datarefs import OciDataRef
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@dc.dataclass()
|
|
20
|
-
class OciDataclass(
|
|
20
|
+
class OciDataclass(Abstract):
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
|
omdev/oci/datarefs.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# ruff: noqa: UP006 UP007 UP045
|
|
2
2
|
# @omlish-lite
|
|
3
|
-
import abc
|
|
4
3
|
import dataclasses as dc
|
|
5
4
|
import functools
|
|
6
5
|
import hashlib
|
|
@@ -10,6 +9,7 @@ import shutil
|
|
|
10
9
|
import tarfile
|
|
11
10
|
import typing as ta
|
|
12
11
|
|
|
12
|
+
from omlish.lite.abstract import Abstract
|
|
13
13
|
from omlish.lite.cached import cached_nullary
|
|
14
14
|
from omlish.lite.check import check
|
|
15
15
|
|
|
@@ -18,7 +18,7 @@ from omlish.lite.check import check
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@dc.dataclass(frozen=True)
|
|
21
|
-
class OciDataRef(
|
|
21
|
+
class OciDataRef(Abstract):
|
|
22
22
|
pass
|
|
23
23
|
|
|
24
24
|
|
omdev/oci/media.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# ruff: noqa: UP006 UP007 UP045
|
|
2
2
|
# @omlish-lite
|
|
3
|
-
import abc
|
|
4
3
|
import dataclasses as dc
|
|
5
4
|
import typing as ta
|
|
6
5
|
|
|
6
|
+
from omlish.lite.abstract import Abstract
|
|
7
7
|
from omlish.lite.check import check
|
|
8
8
|
from omlish.lite.marshal import OBJ_MARSHALER_FIELD_KEY
|
|
9
9
|
from omlish.lite.marshal import OBJ_MARSHALER_OMIT_IF_NONE
|
|
@@ -23,7 +23,7 @@ OCI_MEDIA_FIELDS: ta.Collection[str] = frozenset([
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
@dc.dataclass()
|
|
26
|
-
class OciMediaDataclass(
|
|
26
|
+
class OciMediaDataclass(Abstract):
|
|
27
27
|
SCHEMA_VERSION: ta.ClassVar[int]
|
|
28
28
|
|
|
29
29
|
@property
|
omdev/oci/repositories.py
CHANGED
|
@@ -5,6 +5,7 @@ import os.path
|
|
|
5
5
|
import tarfile
|
|
6
6
|
import typing as ta
|
|
7
7
|
|
|
8
|
+
from omlish.lite.abstract import Abstract
|
|
8
9
|
from omlish.lite.check import check
|
|
9
10
|
from omlish.os.paths import is_path_in_dir
|
|
10
11
|
|
|
@@ -17,7 +18,7 @@ from .datarefs import TarFileOciDataRef
|
|
|
17
18
|
##
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
class OciRepository(
|
|
21
|
+
class OciRepository(Abstract):
|
|
21
22
|
@abc.abstractmethod
|
|
22
23
|
def contains_blob(self, digest: str) -> bool:
|
|
23
24
|
raise NotImplementedError
|
|
@@ -57,7 +58,7 @@ class OciRepository(abc.ABC):
|
|
|
57
58
|
raise TypeError(obj)
|
|
58
59
|
|
|
59
60
|
|
|
60
|
-
class FileOciRepository(OciRepository,
|
|
61
|
+
class FileOciRepository(OciRepository, Abstract):
|
|
61
62
|
@abc.abstractmethod
|
|
62
63
|
def read_file(self, path: str) -> bytes:
|
|
63
64
|
raise NotImplementedError
|
omdev/packaging/marshal.py
CHANGED
|
@@ -8,7 +8,6 @@ import typing as ta
|
|
|
8
8
|
from omlish import lang
|
|
9
9
|
from omlish import marshal as msh
|
|
10
10
|
from omlish import reflect as rfl
|
|
11
|
-
from omlish.funcs import match as mfs
|
|
12
11
|
|
|
13
12
|
from .requires import RequiresMarkerItem
|
|
14
13
|
from .requires import RequiresMarkerList
|
|
@@ -43,12 +42,13 @@ class RequiresMarkerListMarshaler(msh.Marshaler):
|
|
|
43
42
|
return [inner(e) for e in o]
|
|
44
43
|
|
|
45
44
|
|
|
46
|
-
class RequiresMarkerListMarshalerFactory(msh.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
ctx.
|
|
45
|
+
class RequiresMarkerListMarshalerFactory(msh.MarshalerFactory):
|
|
46
|
+
def make_marshaler(self, ctx: msh.MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Marshaler] | None:
|
|
47
|
+
if rty is not MarshalRequiresMarkerList:
|
|
48
|
+
return None
|
|
49
|
+
return lambda: RequiresMarkerListMarshaler(
|
|
50
|
+
ctx.make_marshaler(RequiresMarkerItem),
|
|
51
|
+
ctx.make_marshaler(RequiresNode),
|
|
52
52
|
)
|
|
53
53
|
|
|
54
54
|
|
|
@@ -56,7 +56,7 @@ class RequiresMarkerListMarshalerFactory(msh.MarshalerFactoryMatchClass):
|
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
@lang.static_init
|
|
59
|
-
def
|
|
59
|
+
def _install_standard_marshaling() -> None:
|
|
60
60
|
requires_node_poly = msh.Polymorphism(
|
|
61
61
|
RequiresNode,
|
|
62
62
|
[
|
|
@@ -75,7 +75,7 @@ def _install_standard_marshalling() -> None:
|
|
|
75
75
|
RequiresMarkerListMarshalerFactory(),
|
|
76
76
|
)
|
|
77
77
|
|
|
78
|
-
msh.
|
|
78
|
+
msh.register_global_config(
|
|
79
79
|
RequiresMarkerList,
|
|
80
80
|
msh.ReflectOverride(MarshalRequiresMarkerList),
|
|
81
81
|
identity=True,
|
omdev/packaging/requires.py
CHANGED
|
@@ -33,6 +33,12 @@ from omlish.lite.check import check
|
|
|
33
33
|
from .specifiers import Specifier
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
RequiresMarkerVar = ta.Union['RequiresVariable', 'RequiresValue'] # ta.TypeAlias
|
|
37
|
+
|
|
38
|
+
RequiresMarkerAtom = ta.Union['RequiresMarkerItem', ta.Sequence['RequiresMarkerAtom']] # ta.TypeAlias
|
|
39
|
+
RequiresMarkerList = ta.Sequence[ta.Union['RequiresMarkerList', 'RequiresMarkerAtom', str]] # ta.TypeAlias
|
|
40
|
+
|
|
41
|
+
|
|
36
42
|
##
|
|
37
43
|
|
|
38
44
|
|
|
@@ -229,12 +235,6 @@ class RequiresOp(RequiresNode):
|
|
|
229
235
|
return str(self)
|
|
230
236
|
|
|
231
237
|
|
|
232
|
-
RequiresMarkerVar = ta.Union['RequiresVariable', 'RequiresValue']
|
|
233
|
-
|
|
234
|
-
RequiresMarkerAtom = ta.Union['RequiresMarkerItem', ta.Sequence['RequiresMarkerAtom']]
|
|
235
|
-
RequiresMarkerList = ta.Sequence[ta.Union['RequiresMarkerList', 'RequiresMarkerAtom', str]]
|
|
236
|
-
|
|
237
|
-
|
|
238
238
|
class RequiresMarkerItem(ta.NamedTuple):
|
|
239
239
|
l: ta.Union[RequiresVariable, RequiresValue]
|
|
240
240
|
op: RequiresOp
|
omdev/packaging/revisions.py
CHANGED
|
@@ -14,13 +14,16 @@ import zipfile
|
|
|
14
14
|
|
|
15
15
|
from omlish.lite.cached import cached_nullary
|
|
16
16
|
from omlish.lite.check import check
|
|
17
|
-
from omlish.
|
|
18
|
-
from omlish.logs.standard import configure_standard_logging
|
|
17
|
+
from omlish.logs.modules import get_module_logger
|
|
18
|
+
from omlish.logs.std.standard import configure_standard_logging
|
|
19
19
|
|
|
20
20
|
from ..git.revisions import get_git_revision
|
|
21
21
|
from .wheelfile import WheelFile
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
log = get_module_logger(globals()) # noqa
|
|
25
|
+
|
|
26
|
+
|
|
24
27
|
##
|
|
25
28
|
|
|
26
29
|
|
omdev/packaging/specifiers.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# ruff: noqa: UP006 UP007 UP045
|
|
1
2
|
# @omlish-lite
|
|
2
3
|
# Copyright (c) Donald Stufft and individual contributors.
|
|
3
4
|
# All rights reserved.
|
|
@@ -20,8 +21,7 @@
|
|
|
20
21
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This file is dual licensed under the terms of the
|
|
21
22
|
# Apache License, Version 2.0, and the BSD License. See the LICENSE file in the root of this repository for complete
|
|
22
23
|
# details.
|
|
23
|
-
# https://github.com/pypa/packaging/blob/
|
|
24
|
-
# ruff: noqa: UP006 UP007 UP045
|
|
24
|
+
# https://github.com/pypa/packaging/blob/48125006684bb2d7d28c50af48a03176da45942d/src/packaging/specifiers.py
|
|
25
25
|
import abc
|
|
26
26
|
import itertools
|
|
27
27
|
import re
|
|
@@ -31,9 +31,9 @@ from .versions import Version
|
|
|
31
31
|
from .versions import canonicalize_version
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
UnparsedVersion = ta.Union['Version', str]
|
|
34
|
+
UnparsedVersion = ta.Union['Version', str] # ta.TypeAlias
|
|
35
35
|
UnparsedVersionVar = ta.TypeVar('UnparsedVersionVar', bound=UnparsedVersion)
|
|
36
|
-
CallableVersionOperator = ta.Callable[['Version', str], bool]
|
|
36
|
+
CallableVersionOperator = ta.Callable[['Version', str], bool] # ta.TypeAlias
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
##
|
|
@@ -181,7 +181,7 @@ class Specifier(BaseSpecifier):
|
|
|
181
181
|
) -> None:
|
|
182
182
|
match = self._regex.search(spec)
|
|
183
183
|
if not match:
|
|
184
|
-
raise InvalidSpecifier(f
|
|
184
|
+
raise InvalidSpecifier(f'Invalid specifier: {spec!r}')
|
|
185
185
|
|
|
186
186
|
self._spec: ta.Tuple[str, str] = (
|
|
187
187
|
match.group('operator').strip(),
|
|
@@ -196,7 +196,7 @@ class Specifier(BaseSpecifier):
|
|
|
196
196
|
return self._prereleases
|
|
197
197
|
|
|
198
198
|
operator, version = self._spec
|
|
199
|
-
if operator in ['==', '>=', '<=', '~=', '===']:
|
|
199
|
+
if operator in ['==', '>=', '<=', '~=', '===', '>', '<']:
|
|
200
200
|
if operator == '==' and version.endswith('.*'):
|
|
201
201
|
version = version[:-2]
|
|
202
202
|
|
|
@@ -322,40 +322,38 @@ class Specifier(BaseSpecifier):
|
|
|
322
322
|
return self.contains(item)
|
|
323
323
|
|
|
324
324
|
def contains(self, item: UnparsedVersion, prereleases: ta.Optional[bool] = None) -> bool:
|
|
325
|
-
|
|
326
|
-
prereleases = self.prereleases
|
|
327
|
-
|
|
328
|
-
normalized_item = _coerce_version(item)
|
|
329
|
-
|
|
330
|
-
if normalized_item.is_prerelease and not prereleases:
|
|
331
|
-
return False
|
|
332
|
-
|
|
333
|
-
operator_callable: CallableVersionOperator = self._get_operator(self.operator)
|
|
334
|
-
return operator_callable(normalized_item, self.version)
|
|
325
|
+
return bool(list(self.filter([item], prereleases=prereleases)))
|
|
335
326
|
|
|
336
327
|
def filter(
|
|
337
328
|
self,
|
|
338
329
|
iterable: ta.Iterable[UnparsedVersionVar],
|
|
339
330
|
prereleases: ta.Optional[bool] = None,
|
|
340
331
|
) -> ta.Iterator[UnparsedVersionVar]:
|
|
341
|
-
|
|
342
|
-
|
|
332
|
+
prereleases_versions = []
|
|
333
|
+
found_non_prereleases = False
|
|
334
|
+
|
|
335
|
+
include_prereleases = (
|
|
336
|
+
prereleases if prereleases is not None else self.prereleases
|
|
337
|
+
)
|
|
343
338
|
|
|
344
|
-
|
|
339
|
+
operator_callable = self._get_operator(self.operator)
|
|
345
340
|
|
|
346
341
|
for version in iterable:
|
|
347
342
|
parsed_version = _coerce_version(version)
|
|
348
343
|
|
|
349
|
-
if
|
|
350
|
-
if parsed_version.is_prerelease
|
|
351
|
-
|
|
352
|
-
else:
|
|
353
|
-
yielded = True
|
|
344
|
+
if operator_callable(parsed_version, self.version):
|
|
345
|
+
if not parsed_version.is_prerelease or include_prereleases:
|
|
346
|
+
found_non_prereleases = True
|
|
354
347
|
yield version
|
|
348
|
+
elif prereleases is None and self._prereleases is not False:
|
|
349
|
+
prereleases_versions.append(version)
|
|
355
350
|
|
|
356
|
-
if
|
|
357
|
-
|
|
358
|
-
|
|
351
|
+
if (
|
|
352
|
+
not found_non_prereleases and
|
|
353
|
+
prereleases is None and
|
|
354
|
+
self._prereleases is not False
|
|
355
|
+
):
|
|
356
|
+
yield from prereleases_versions
|
|
359
357
|
|
|
360
358
|
|
|
361
359
|
_version_prefix_regex = re.compile(r'^([0-9]+)((?:a|b|c|rc)[0-9]+)$')
|
|
@@ -406,12 +404,15 @@ def _pad_version(left: ta.List[str], right: ta.List[str]) -> ta.Tuple[ta.List[st
|
|
|
406
404
|
class SpecifierSet(BaseSpecifier):
|
|
407
405
|
def __init__(
|
|
408
406
|
self,
|
|
409
|
-
specifiers: str = '',
|
|
407
|
+
specifiers: ta.Union[str, ta.Iterable['Specifier']] = '',
|
|
410
408
|
prereleases: ta.Optional[bool] = None,
|
|
411
409
|
) -> None:
|
|
412
|
-
|
|
410
|
+
if isinstance(specifiers, str):
|
|
411
|
+
split_specifiers = [s.strip() for s in specifiers.split(',') if s.strip()]
|
|
412
|
+
self._specs = frozenset(map(Specifier, split_specifiers))
|
|
413
|
+
else:
|
|
414
|
+
self._specs = frozenset(specifiers)
|
|
413
415
|
|
|
414
|
-
self._specs = frozenset(map(Specifier, split_specifiers))
|
|
415
416
|
self._prereleases = prereleases
|
|
416
417
|
|
|
417
418
|
@property
|
|
@@ -422,7 +423,10 @@ class SpecifierSet(BaseSpecifier):
|
|
|
422
423
|
if not self._specs:
|
|
423
424
|
return None
|
|
424
425
|
|
|
425
|
-
|
|
426
|
+
if any(s.prereleases for s in self._specs):
|
|
427
|
+
return True
|
|
428
|
+
|
|
429
|
+
return None
|
|
426
430
|
|
|
427
431
|
@prereleases.setter
|
|
428
432
|
def prereleases(self, value: bool) -> None:
|
|
@@ -447,7 +451,7 @@ class SpecifierSet(BaseSpecifier):
|
|
|
447
451
|
if isinstance(other, str):
|
|
448
452
|
other = SpecifierSet(other)
|
|
449
453
|
elif not isinstance(other, SpecifierSet):
|
|
450
|
-
return NotImplemented
|
|
454
|
+
return NotImplemented
|
|
451
455
|
|
|
452
456
|
specifier = SpecifierSet()
|
|
453
457
|
specifier._specs = frozenset(self._specs | other._specs)
|
|
@@ -467,6 +471,7 @@ class SpecifierSet(BaseSpecifier):
|
|
|
467
471
|
if isinstance(other, (str, Specifier)):
|
|
468
472
|
other = SpecifierSet(str(other))
|
|
469
473
|
elif not isinstance(other, SpecifierSet):
|
|
474
|
+
|
|
470
475
|
return NotImplemented
|
|
471
476
|
|
|
472
477
|
return self._specs == other._specs
|
|
@@ -489,28 +494,22 @@ class SpecifierSet(BaseSpecifier):
|
|
|
489
494
|
if not isinstance(item, Version):
|
|
490
495
|
item = Version(item)
|
|
491
496
|
|
|
492
|
-
if prereleases is None:
|
|
493
|
-
prereleases = self.prereleases
|
|
494
|
-
|
|
495
|
-
if not prereleases and item.is_prerelease:
|
|
496
|
-
return False
|
|
497
|
-
|
|
498
497
|
if installed and item.is_prerelease:
|
|
499
|
-
|
|
498
|
+
prereleases = True
|
|
500
499
|
|
|
501
|
-
return
|
|
500
|
+
return bool(list(self.filter([item], prereleases=prereleases)))
|
|
502
501
|
|
|
503
502
|
def filter(
|
|
504
503
|
self,
|
|
505
504
|
iterable: ta.Iterable[UnparsedVersionVar],
|
|
506
505
|
prereleases: ta.Optional[bool] = None,
|
|
507
506
|
) -> ta.Iterator[UnparsedVersionVar]:
|
|
508
|
-
if prereleases is None:
|
|
507
|
+
if prereleases is None and self.prereleases is not None:
|
|
509
508
|
prereleases = self.prereleases
|
|
510
509
|
|
|
511
510
|
if self._specs:
|
|
512
511
|
for spec in self._specs:
|
|
513
|
-
iterable = spec.filter(iterable, prereleases=
|
|
512
|
+
iterable = spec.filter(iterable, prereleases=prereleases)
|
|
514
513
|
return iter(iterable)
|
|
515
514
|
|
|
516
515
|
else:
|
omdev/packaging/versions.py
CHANGED
|
@@ -27,13 +27,13 @@ import re
|
|
|
27
27
|
import typing as ta
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
VersionLocalType = ta.Tuple[ta.Union[int, str], ...]
|
|
30
|
+
VersionLocalType = ta.Tuple[ta.Union[int, str], ...] # ta.TypeAlias
|
|
31
31
|
|
|
32
|
-
VersionCmpPrePostDevType = ta.Union['InfinityVersionType', 'NegativeInfinityVersionType', ta.Tuple[str, int]]
|
|
33
|
-
_VersionCmpLocalType0 = ta.Tuple[ta.Union[ta.Tuple[int, str], ta.Tuple['NegativeInfinityVersionType', ta.Union[int, str]]], ...] # noqa
|
|
34
|
-
VersionCmpLocalType = ta.Union['NegativeInfinityVersionType', _VersionCmpLocalType0]
|
|
35
|
-
VersionCmpKey = ta.Tuple[int, ta.Tuple[int, ...], VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpLocalType] # noqa
|
|
36
|
-
VersionComparisonMethod = ta.Callable[[VersionCmpKey, VersionCmpKey], bool]
|
|
32
|
+
VersionCmpPrePostDevType = ta.Union['InfinityVersionType', 'NegativeInfinityVersionType', ta.Tuple[str, int]] # ta.TypeAlias # noqa
|
|
33
|
+
_VersionCmpLocalType0 = ta.Tuple[ta.Union[ta.Tuple[int, str], ta.Tuple['NegativeInfinityVersionType', ta.Union[int, str]]], ...] # ta.TypeAlias # noqa
|
|
34
|
+
VersionCmpLocalType = ta.Union['NegativeInfinityVersionType', _VersionCmpLocalType0] # ta.TypeAlias
|
|
35
|
+
VersionCmpKey = ta.Tuple[int, ta.Tuple[int, ...], VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpLocalType] # ta.TypeAlias # noqa
|
|
36
|
+
VersionComparisonMethod = ta.Callable[[VersionCmpKey, VersionCmpKey], bool] # ta.TypeAlias
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
##
|
|
@@ -121,12 +121,12 @@ class _BaseVersion:
|
|
|
121
121
|
|
|
122
122
|
def __lt__(self, other: '_BaseVersion') -> bool:
|
|
123
123
|
if not isinstance(other, _BaseVersion):
|
|
124
|
-
return NotImplemented
|
|
124
|
+
return NotImplemented
|
|
125
125
|
return self._key < other._key
|
|
126
126
|
|
|
127
127
|
def __le__(self, other: '_BaseVersion') -> bool:
|
|
128
128
|
if not isinstance(other, _BaseVersion):
|
|
129
|
-
return NotImplemented
|
|
129
|
+
return NotImplemented
|
|
130
130
|
return self._key <= other._key
|
|
131
131
|
|
|
132
132
|
def __eq__(self, other: object) -> bool:
|
|
@@ -136,12 +136,12 @@ class _BaseVersion:
|
|
|
136
136
|
|
|
137
137
|
def __ge__(self, other: '_BaseVersion') -> bool:
|
|
138
138
|
if not isinstance(other, _BaseVersion):
|
|
139
|
-
return NotImplemented
|
|
139
|
+
return NotImplemented
|
|
140
140
|
return self._key >= other._key
|
|
141
141
|
|
|
142
142
|
def __gt__(self, other: '_BaseVersion') -> bool:
|
|
143
143
|
if not isinstance(other, _BaseVersion):
|
|
144
|
-
return NotImplemented
|
|
144
|
+
return NotImplemented
|
|
145
145
|
return self._key > other._key
|
|
146
146
|
|
|
147
147
|
def __ne__(self, other: object) -> bool:
|
omdev/packaging/wheelfile.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
# @omlish-lite
|
|
2
1
|
# ruff: noqa: UP006 UP007 UP045
|
|
3
|
-
#
|
|
2
|
+
# @omlish-lite
|
|
4
3
|
# MIT License
|
|
5
4
|
#
|
|
6
5
|
# Copyright (c) 2012 Daniel Holth <dholth@fastmail.fm> and contributors
|
|
@@ -17,6 +16,7 @@
|
|
|
17
16
|
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
17
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
19
18
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
19
|
+
# https://github.com/pypa/wheel/blob/7bb46d7727e6e89fe56b3c78297b3af2672bbbe2/src/wheel/wheelfile.py
|
|
20
20
|
import base64
|
|
21
21
|
import csv
|
|
22
22
|
import hashlib
|
|
@@ -116,11 +116,13 @@ class WheelFile(zipfile.ZipFile):
|
|
|
116
116
|
@staticmethod
|
|
117
117
|
def _urlsafe_b64encode(data: bytes) -> bytes:
|
|
118
118
|
"""urlsafe_b64encode without padding"""
|
|
119
|
+
|
|
119
120
|
return base64.urlsafe_b64encode(data).rstrip(b'=')
|
|
120
121
|
|
|
121
122
|
@staticmethod
|
|
122
123
|
def _urlsafe_b64decode(data: bytes) -> bytes:
|
|
123
124
|
"""urlsafe_b64decode without padding"""
|
|
125
|
+
|
|
124
126
|
pad = b'=' * (4 - (len(data) & 3))
|
|
125
127
|
return base64.urlsafe_b64decode(data + pad)
|
|
126
128
|
|