omdev 0.0.53__tar.gz → 0.0.54__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.
- {omdev-0.0.53/omdev.egg-info → omdev-0.0.54}/PKG-INFO +12 -12
- omdev-0.0.54/omdev/home/secretinject.py +330 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/home/secrets.py +22 -12
- omdev-0.0.54/omdev/qr/LICENSE +27 -0
- omdev-0.0.54/omdev/qr/__init__.py +1 -0
- omdev-0.0.54/omdev/qr/consts.py +610 -0
- omdev-0.0.54/omdev/qr/encoding.py +849 -0
- omdev-0.0.54/omdev/qr/segments.py +235 -0
- omdev-0.0.54/omdev/qr/utils.py +129 -0
- omdev-0.0.54/omdev/qr/writers.py +100 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/cards.py +3 -2
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/__init__.py +2 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/engine.py +17 -2
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/options.py +44 -1
- {omdev-0.0.53 → omdev-0.0.54/omdev.egg-info}/PKG-INFO +12 -12
- {omdev-0.0.53 → omdev-0.0.54}/omdev.egg-info/SOURCES.txt +8 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev.egg-info/requires.txt +11 -11
- {omdev-0.0.53 → omdev-0.0.54}/pyproject.toml +12 -12
- {omdev-0.0.53 → omdev-0.0.54}/LICENSE +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/MANIFEST.in +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/README.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/.om-manifests.json +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/README.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/__about__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/cli/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/cli/main.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/gen/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/gen/gen.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/gen/imports.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/gen/manifests.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/gen/resources.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/gen/srcfiles.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/gen/strip.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/gen/types.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/gen/typing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/info.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/amalg/sources.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/compute/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/compute/cache.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/compute/contexts.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/compute/currents.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/compute/fns.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/compute/resolvers.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/compute/storage.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/compute/types.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/data/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/data/actions.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/data/cache.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/data/consts.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/data/defaults.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/data/manifests.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cache/data/specs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/capi/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/capi/darwin/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/capi/darwin/aps.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/capi/darwin/ax.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/capi/darwin/cf.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/capi/darwin/cg.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/capi/linux/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/capi/linux/x11.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cc/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cc/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cc/cdeps.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cc/cdeps.toml +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cc/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cc/srclangs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_boilerplate.cc +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/LICENSE +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/build_ext.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/compilers/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/compilers/ccompiler.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/compilers/options.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/compilers/unixccompiler.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/dir_util.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/errors.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/extension.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/file_util.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/modified.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/spawn.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/sysconfig.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/util.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/_distutils/version.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/build.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/cmake.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/configs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/importhook.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/magic.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cexts/scan.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/cache.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/ci.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/compose.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/consts.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/buildcaching.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/cache.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/cacheserved/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/cacheserved/cache.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/cacheserved/manifests.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/cmds.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/dataserver.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/imagepulling.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/inject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/packing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/repositories.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/docker/utils.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/api/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/api/clients.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/api/v1/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/api/v1/api.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/api/v1/client.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/api/v2/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/api/v2/api.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/api/v2/azure.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/api/v2/client.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/bootstrap.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/cache.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/env.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/github/inject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/inject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/requirements.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/shell.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/ci/utils.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cli/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cli/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cli/_pathhack.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cli/clicli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cli/install.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cli/main.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cli/managers.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cli/types.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/clipboard/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/clipboard/capi/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/clipboard/capi/darwin_cf.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/clipboard/capi/linux_x11.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/clipboard/commands.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/clipboard/platform.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/clipboard/types.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cmake.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cmdlog/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cmdlog/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cmdlog/_cmdlog.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cmdlog/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/cmdlog/cmdlog.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataclasses/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataclasses/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataclasses/_dumping.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataclasses/_template.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataclasses/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataclasses/codegen.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataclasses/dumping.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataserver/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataserver/handlers.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataserver/http.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataserver/routes.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataserver/server.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dataserver/targets.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/build.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/config.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/config.toml +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/content.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/discovery.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/gen.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/helpers.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/ops.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/rendering.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/.python-versions.json +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/configs/.gdbinit +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/configs/.sqliterc +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/configs/.tmux.conf +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/configs/.vimrc +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/depsets/python.toml +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/depsets/system.toml +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/depsets/x11.toml +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/apt.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/chgrp.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/docker.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/firefox.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/go.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/jdk.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/man.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/nvm.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/om.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/pyenv.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/rbenv.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/rust.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/sshd.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/user.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/uv.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/vcpkg.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/workdir.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/fragments/zig.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/scripts/install-claude +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/scripts/install-codex +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/scripts/install-pi +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/scripts/run-dbus +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/scripts/run-mysql +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/scripts/run-postgres +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/resources/shift-uid.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/run.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/dockerdev/utils.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/magic.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/revisions.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/shallow.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/status.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/aborting.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/diffing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/errors.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/journals.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/merging.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/paths.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/pulling.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/runners.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/specs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/git/vendor/upstreams.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/home/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/home/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/home/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/home/paths.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/home/shadow.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/imgur.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/intellij/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/intellij/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/intellij/ides.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/intellij/open.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/default.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/inject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/inspect.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/providers/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/providers/base.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/providers/inject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/providers/running.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/providers/standalone.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/providers/system.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/pyenv/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/pyenv/inject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/pyenv/install.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/pyenv/provider.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/pyenv/pyenv.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/resolvers.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/types.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/uv/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/uv/inject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/uv/provider.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/uv/uv.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/interp/venvs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/java/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/java/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/java/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/java/pomgen.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/java/utils.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/java/xml.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/LICENSE +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/TODO.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_pyqjsng.c +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_pyqjsng.pyi +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/LICENSE +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/builtin-array-fromasync.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/builtin-iterator-zip-keyed.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/builtin-iterator-zip.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/cutils.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/dtoa.c +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/dtoa.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/libregexp-opcode.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/libregexp.c +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/libregexp.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/libunicode-table.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/libunicode.c +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/libunicode.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/list.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/quickjs-atom.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/quickjs-c-atomics.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/quickjs-libc.c +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/quickjs-libc.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/quickjs-opcode.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/quickjs.c +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/_quickjs/quickjs.h +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/js/quickjs/snapshots.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/magic/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/magic/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/magic/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/magic/find.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/magic/magic.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/magic/prepare.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/magic/styles.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/manifests/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/manifests/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/manifests/_dumping.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/manifests/building.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/manifests/dumping.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/manifests/main.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/mypy/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/mypy/debug.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/mypy/report.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/packaging/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/packaging/marshal.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/packaging/names.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/packaging/requirements.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/packaging/revisions.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/packaging/specifiers.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/packaging/versions.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/packaging/wheelfile.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pip.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/base.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/blanklines.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/caches.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/git.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/imports.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/lite.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/main.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/manifests.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/scripts.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/precheck/unicode.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/asts/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/asts/parents.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/asts/toplevel.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/asts/visitors.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/attrdocs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/bracepy.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/classdot.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/docstrings/LICENSE +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/docstrings/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/docstrings/attrdoc.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/docstrings/common.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/docstrings/epydoc.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/docstrings/google.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/docstrings/numpydoc.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/docstrings/parser.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/docstrings/rest.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/findimports.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/reprs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/scripts/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/scripts/bumpversion.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/scripts/importtrace.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/srcheaders.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/tokens/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/tokens/all.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/tokens/tokenizert.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/tokens/utils.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/tools/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/tools/fixdocstrings.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/tools/importscan.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/tools/mkrelimp.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py/tools/pipdepup.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/py.typed +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/configs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/inject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/pkg.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/reqs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/resources/dockerdev.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/resources/python.sh +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/tools/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/tools/aboutdeps.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/tools/buildrs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/tools/pyversions.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/venvs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyproject/versions.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyright/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/pyright/report.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/rs/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/ci.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/ghajunit.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/interp.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/lib/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/lib/formats/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/lib/formats/goyaml.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/lib/inject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/lib/io/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/lib/io/pipelines.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/lib/io/streambufs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/lib/logs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/lib/marshal.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/pidfiles.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/pyproject.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/slowcat.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/scripts/tmpexec.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/messages/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/messages/base.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/messages/formats.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/messages/messages.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/messages/parsing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/numerics/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/numerics/formats.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/numerics/numerics.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/numerics/types.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/LICENSE +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/consts.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/errors.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/message.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/nuh.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/parsing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/rendering.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/tags.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/irc/protocol/utils.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/analysis.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/config.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/errors.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/generator.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/ir.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/names.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/rendering.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/transforms.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/jsonschema/codegen/typing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/building.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/compression.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/data.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/datarefs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/dataserver.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/loading.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/media.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/pack/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/pack/packing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/pack/repositories.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/pack/unpacking.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/repositories.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/specs/oci/tars.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tagstrings.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/cloc.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/diff.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/doc.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/docker.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/git/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/git/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/git/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/git/cloning.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/git/consts.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/git/messages.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/json/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/json/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/json/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/json/formats.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/json/io.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/json/parsing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/json/processing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/json/rendering.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/jsonview/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/jsonview/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/jsonview/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/jsonview/resources/jsonview.css +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/jsonview/resources/jsonview.html.j2 +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/jsonview/resources/jsonview.js +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/linehisto.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/mkenv.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/notebook.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/ocr.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/pawk/README.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/pawk/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/pawk/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/pawk/pawk.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/pip.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/prof.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/qr.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/shadow.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/shell.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tools/sqlrepl.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/diff/LICENSE +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/diff/README.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/diff/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/diff/__main__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/diff/cli.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/diff/rendering.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/diff/terminal.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/diff/themes.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/README.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/_dataclasses.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/_devdocs/design.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/_devdocs/dev_00_initial.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/_devdocs/dev_01_parity_push.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/_devdocs/intro.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/_devdocs/requirements.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/_devdocs/research.md +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/base.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/history.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/markdown.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/spinners.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/stacks.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/static.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/status.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/suggestions.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/controls/textarea.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/docs/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/docs/cursors.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/docs/documents.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/docs/edits.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/docs/highlighting.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/docs/positions.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/docs/searching.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/docs/treesitter.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/events/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/events/keymaps.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/events/keys.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/events/parsing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/events/types.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/events/xterm.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/runtime/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/runtime/asyncio.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/runtime/base.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/runtime/jobcontrol.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/runtime/sync.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/runtime/timers.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/screens/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/screens/cells.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/screens/diffs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/surfaces/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/surfaces/alts.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/surfaces/base.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/surfaces/inlines.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/surfaces/writers.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/markdown/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/markdown/backends.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/markdown/base.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/markdown/flattening.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/markdown/markdownit.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/markdown/pdcmark.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/parts.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/rendering.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/segments.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/styles.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/themes.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/text/wrap.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/tty/__init__.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/tty/terminals.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/_dataclasses.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/modes.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/motions.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/parsing.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/registers.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/scans.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/status.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/substitutes.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev/tui/minitui/vim/textobjs.py +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev.egg-info/dependency_links.txt +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev.egg-info/entry_points.txt +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/omdev.egg-info/top_level.txt +0 -0
- {omdev-0.0.53 → omdev-0.0.54}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omdev
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.54
|
|
4
4
|
Summary: omdev
|
|
5
5
|
Author: wrmsr
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -14,11 +14,11 @@ Classifier: Programming Language :: Python :: 3.15
|
|
|
14
14
|
Requires-Python: >=3.14
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: omcore==0.0.
|
|
17
|
+
Requires-Dist: omcore==0.0.54
|
|
18
18
|
Provides-Extra: ominfra
|
|
19
|
-
Requires-Dist: ominfra==0.0.
|
|
19
|
+
Requires-Dist: ominfra==0.0.54; extra == "ominfra"
|
|
20
20
|
Provides-Extra: omllm
|
|
21
|
-
Requires-Dist: omllm==0.0.
|
|
21
|
+
Requires-Dist: omllm==0.0.54; extra == "omllm"
|
|
22
22
|
Provides-Extra: black
|
|
23
23
|
Requires-Dist: black~=26.5; extra == "black"
|
|
24
24
|
Provides-Extra: c
|
|
@@ -59,14 +59,14 @@ Requires-Dist: tree-sitter-toml~=0.7; extra == "tui-syntax"
|
|
|
59
59
|
Requires-Dist: tree-sitter-xml~=0.7; extra == "tui-syntax"
|
|
60
60
|
Requires-Dist: tree-sitter-yaml~=0.7; extra == "tui-syntax"
|
|
61
61
|
Provides-Extra: cext
|
|
62
|
-
Requires-Dist: omdev-cext==0.0.
|
|
62
|
+
Requires-Dist: omdev-cext==0.0.54; extra == "cext"
|
|
63
63
|
Provides-Extra: rs
|
|
64
|
-
Requires-Dist: omdev-rs==0.0.
|
|
64
|
+
Requires-Dist: omdev-rs==0.0.54; extra == "rs"
|
|
65
65
|
Provides-Extra: cli
|
|
66
|
-
Requires-Dist: omdev-cli==0.0.
|
|
66
|
+
Requires-Dist: omdev-cli==0.0.54; extra == "cli"
|
|
67
67
|
Provides-Extra: all
|
|
68
|
-
Requires-Dist: ominfra==0.0.
|
|
69
|
-
Requires-Dist: omllm==0.0.
|
|
68
|
+
Requires-Dist: ominfra==0.0.54; extra == "all"
|
|
69
|
+
Requires-Dist: omllm==0.0.54; extra == "all"
|
|
70
70
|
Requires-Dist: black~=26.5; extra == "all"
|
|
71
71
|
Requires-Dist: pycparser~=3.0; extra == "all"
|
|
72
72
|
Requires-Dist: pcpp~=1.30; extra == "all"
|
|
@@ -95,9 +95,9 @@ Requires-Dist: tree-sitter-sql~=0.3; extra == "all"
|
|
|
95
95
|
Requires-Dist: tree-sitter-toml~=0.7; extra == "all"
|
|
96
96
|
Requires-Dist: tree-sitter-xml~=0.7; extra == "all"
|
|
97
97
|
Requires-Dist: tree-sitter-yaml~=0.7; extra == "all"
|
|
98
|
-
Requires-Dist: omdev-cext==0.0.
|
|
99
|
-
Requires-Dist: omdev-rs==0.0.
|
|
100
|
-
Requires-Dist: omdev-cli==0.0.
|
|
98
|
+
Requires-Dist: omdev-cext==0.0.54; extra == "all"
|
|
99
|
+
Requires-Dist: omdev-rs==0.0.54; extra == "all"
|
|
100
|
+
Requires-Dist: omdev-cli==0.0.54; extra == "all"
|
|
101
101
|
Dynamic: license-file
|
|
102
102
|
|
|
103
103
|
# Overview
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# @om-lite
|
|
3
|
+
# @om-script
|
|
4
|
+
# ruff: noqa: UP006 UP045
|
|
5
|
+
"""
|
|
6
|
+
inject_secret.py -- atomically upsert keys into a JSON object file, safely in the presence of concurrent invocations of
|
|
7
|
+
itself.
|
|
8
|
+
|
|
9
|
+
printf '{"db_password": "%s", "legacy_token": null}' "$DB_PASSWORD" | inject_secret.py /etc/app/config.json
|
|
10
|
+
|
|
11
|
+
Stdin is a JSON object, shallowly merged into the file (which must hold a JSON object, or not exist yet): each key is
|
|
12
|
+
upserted wholesale -- values can be anything, but dicts and lists are replaced, never merged -- and a null value removes
|
|
13
|
+
the key.
|
|
14
|
+
|
|
15
|
+
Key order records modification order: keys not mentioned on stdin are written in the order they were read, followed by
|
|
16
|
+
every key set on stdin -- whether or not its value actually changed -- in the order they appear there. Removed keys are
|
|
17
|
+
simply dropped. If the result is identical to the current contents (same keys, values and order, compared by JSON
|
|
18
|
+
serialization, so 1, 1.0 and true are distinct) the file is not rewritten.
|
|
19
|
+
|
|
20
|
+
Output is pretty-printed with a two space indent, except that an empty object is written as exactly `{}`.
|
|
21
|
+
|
|
22
|
+
Guarantees (Linux only; needs nothing newer than flock(2), link(2), rename(2)):
|
|
23
|
+
|
|
24
|
+
* Nobody ever observes a partial, empty or invalid file at the path. The path is only ever populated by link(2)
|
|
25
|
+
(create) or rename(2) (replace) of a fully written and fsync'd temp file in the same directory.
|
|
26
|
+
* Concurrent invocations never lose each other's updates, whether or not the file exists beforehand.
|
|
27
|
+
|
|
28
|
+
How the lock and the atomic replace are combined:
|
|
29
|
+
|
|
30
|
+
flock(2) locks an *inode*, not a path, and rename(2) swaps the inode behind the path. A writer that holds the lock on
|
|
31
|
+
the old inode and renames a new file into place therefore leaves waiters holding a lock on a stale inode. The fix:
|
|
32
|
+
after acquiring the lock, compare fstat(fd) with stat(path); if they differ, release and start over against the
|
|
33
|
+
current file. Every replacement is performed by the holder of the lock on the inode currently at the path, so writers
|
|
34
|
+
are fully serialized.
|
|
35
|
+
|
|
36
|
+
Creation: rename(2) clobbers unconditionally, so bootstrapping via rename could overwrite a file a concurrent writer
|
|
37
|
+
just populated. link(2) fails with EEXIST if the target exists, so it is the atomic create-if-absent.
|
|
38
|
+
|
|
39
|
+
Exit status: 0 ok, 1 error, 2 usage, 3 lock timeout.
|
|
40
|
+
"""
|
|
41
|
+
import argparse
|
|
42
|
+
import collections
|
|
43
|
+
import errno
|
|
44
|
+
import fcntl
|
|
45
|
+
import json
|
|
46
|
+
import os
|
|
47
|
+
import random
|
|
48
|
+
import stat
|
|
49
|
+
import sys
|
|
50
|
+
import tempfile
|
|
51
|
+
import time
|
|
52
|
+
import typing as ta
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# seconds; jittered so waiters don't wake in lockstep
|
|
59
|
+
POLL_MIN = 0.005
|
|
60
|
+
POLL_MAX = 0.05
|
|
61
|
+
|
|
62
|
+
# mode for a file we create; existing files keep theirs
|
|
63
|
+
NEW_FILE_MODE = 0o600
|
|
64
|
+
|
|
65
|
+
JsonObject = ta.OrderedDict[str, ta.Any] # ta.TypeAlias
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def write_all(fd: int, data: bytes) -> None:
|
|
69
|
+
view = memoryview(data)
|
|
70
|
+
while view:
|
|
71
|
+
view = view[os.write(fd, view):]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def read_all(fd: int) -> bytes:
|
|
75
|
+
chunks: ta.List[bytes] = []
|
|
76
|
+
while True:
|
|
77
|
+
chunk = os.read(fd, 1 << 16)
|
|
78
|
+
if not chunk:
|
|
79
|
+
return b''.join(chunks)
|
|
80
|
+
chunks.append(chunk)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def fsync_dir(dirname: str) -> None:
|
|
84
|
+
"""Persist the directory entry (the link/rename itself) after a crash."""
|
|
85
|
+
|
|
86
|
+
fd = os.open(dirname, os.O_RDONLY)
|
|
87
|
+
try:
|
|
88
|
+
os.fsync(fd)
|
|
89
|
+
except OSError:
|
|
90
|
+
pass # some filesystems refuse fsync on directories; not fatal
|
|
91
|
+
finally:
|
|
92
|
+
os.close(fd)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def write_temp(
|
|
96
|
+
dirname: str,
|
|
97
|
+
basename: str,
|
|
98
|
+
data: bytes,
|
|
99
|
+
mode: int,
|
|
100
|
+
*,
|
|
101
|
+
uid_gid: ta.Optional[ta.Tuple[int, int]] = None,
|
|
102
|
+
) -> str:
|
|
103
|
+
"""Write data to a fresh, fsync'd temp file next to the target. Returns its path."""
|
|
104
|
+
|
|
105
|
+
fd, tmp = tempfile.mkstemp(prefix='.' + basename + '.', suffix='.tmp', dir=dirname)
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
if uid_gid is not None:
|
|
109
|
+
uid, gid = uid_gid
|
|
110
|
+
try:
|
|
111
|
+
os.fchown(fd, uid, gid)
|
|
112
|
+
except PermissionError:
|
|
113
|
+
pass # not root; the file keeps our ownership
|
|
114
|
+
|
|
115
|
+
os.fchmod(fd, mode)
|
|
116
|
+
write_all(fd, data)
|
|
117
|
+
os.fsync(fd)
|
|
118
|
+
|
|
119
|
+
except BaseException:
|
|
120
|
+
os.close(fd)
|
|
121
|
+
os.unlink(tmp)
|
|
122
|
+
raise
|
|
123
|
+
|
|
124
|
+
os.close(fd)
|
|
125
|
+
return tmp
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def parse_object(text: str) -> JsonObject:
|
|
129
|
+
data = json.loads(text, object_pairs_hook=collections.OrderedDict)
|
|
130
|
+
if not isinstance(data, collections.OrderedDict):
|
|
131
|
+
raise ValueError('top-level JSON value is not an object') # noqa
|
|
132
|
+
return data
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def load(raw: bytes) -> JsonObject:
|
|
136
|
+
text = raw.decode('utf-8')
|
|
137
|
+
if not text.strip():
|
|
138
|
+
return collections.OrderedDict() # tolerate an empty/whitespace-only file (e.g. `touch`ed)
|
|
139
|
+
return parse_object(text)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def dump(data: JsonObject) -> bytes:
|
|
143
|
+
if not data:
|
|
144
|
+
return b'{}'
|
|
145
|
+
return (json.dumps(data, indent=2, ensure_ascii=False) + '\n').encode('utf-8')
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def values_equal(a: ta.Any, b: ta.Any) -> bool:
|
|
149
|
+
"""Compare by JSON serialization: 1, 1.0 and true are all distinct, and key order within nested objects matters."""
|
|
150
|
+
|
|
151
|
+
return json.dumps(a) == json.dumps(b)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def merge(
|
|
155
|
+
existing: JsonObject,
|
|
156
|
+
update: JsonObject,
|
|
157
|
+
) -> ta.Tuple[JsonObject, bool]:
|
|
158
|
+
"""
|
|
159
|
+
Shallowly apply `update` to `existing`: a None value removes the key, any other value replaces it wholesale. Returns
|
|
160
|
+
the merged object -- keys not in `update` first, in their existing order, then every key set by `update` in `update`
|
|
161
|
+
order, even if its value is unchanged -- and whether the result differs from `existing` at all.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
merged: JsonObject = collections.OrderedDict(
|
|
165
|
+
(k, v) for k, v in existing.items() if k not in update
|
|
166
|
+
)
|
|
167
|
+
merged.update((k, v) for k, v in update.items() if v is not None)
|
|
168
|
+
return merged, not values_equal(merged, existing)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def try_create(
|
|
172
|
+
path: str,
|
|
173
|
+
dirname: str,
|
|
174
|
+
basename: str,
|
|
175
|
+
data: JsonObject,
|
|
176
|
+
) -> bool:
|
|
177
|
+
"""Atomically create `path` with `data` iff it does not exist. True on success."""
|
|
178
|
+
|
|
179
|
+
tmp = write_temp(
|
|
180
|
+
dirname,
|
|
181
|
+
basename,
|
|
182
|
+
dump(data),
|
|
183
|
+
NEW_FILE_MODE,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
os.link(tmp, path)
|
|
188
|
+
|
|
189
|
+
except FileExistsError:
|
|
190
|
+
return False # someone beat us to it; caller will lock and update that file
|
|
191
|
+
|
|
192
|
+
except OSError as e:
|
|
193
|
+
if e.errno in (errno.EPERM, errno.EOPNOTSUPP, errno.EXDEV, errno.EMLINK):
|
|
194
|
+
raise OSError(
|
|
195
|
+
e.errno,
|
|
196
|
+
f'filesystem does not allow hard links, which are needed to create the file atomically: {e.strerror}',
|
|
197
|
+
path,
|
|
198
|
+
) from None
|
|
199
|
+
|
|
200
|
+
raise
|
|
201
|
+
|
|
202
|
+
finally:
|
|
203
|
+
os.unlink(tmp)
|
|
204
|
+
|
|
205
|
+
fsync_dir(dirname)
|
|
206
|
+
return True
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def inject(
|
|
210
|
+
path: str,
|
|
211
|
+
update: JsonObject,
|
|
212
|
+
timeout: float,
|
|
213
|
+
) -> None:
|
|
214
|
+
path = os.path.realpath(path) # operate on the real file if `path` is a symlink
|
|
215
|
+
dirname, basename = os.path.split(path)
|
|
216
|
+
deadline = time.monotonic() + timeout
|
|
217
|
+
|
|
218
|
+
while True:
|
|
219
|
+
try:
|
|
220
|
+
fd = os.open(path, os.O_RDONLY)
|
|
221
|
+
|
|
222
|
+
except FileNotFoundError:
|
|
223
|
+
merged, _ = merge(collections.OrderedDict(), update)
|
|
224
|
+
|
|
225
|
+
if try_create(
|
|
226
|
+
path,
|
|
227
|
+
dirname,
|
|
228
|
+
basename,
|
|
229
|
+
merged,
|
|
230
|
+
):
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
continue # lost the create race; lock whatever exists now
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
try:
|
|
237
|
+
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
238
|
+
except BlockingIOError:
|
|
239
|
+
if time.monotonic() >= deadline:
|
|
240
|
+
raise TimeoutError(f'timed out waiting for lock on {path}') from None
|
|
241
|
+
time.sleep(random.uniform(POLL_MIN, POLL_MAX))
|
|
242
|
+
continue
|
|
243
|
+
|
|
244
|
+
# We hold a lock on *an* inode. Make sure it is still the one at `path`: a concurrent writer may have
|
|
245
|
+
# rename()d a new file over it while we waited.
|
|
246
|
+
st = os.fstat(fd)
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
cur = os.stat(path)
|
|
250
|
+
except FileNotFoundError:
|
|
251
|
+
continue # deleted underneath us; go recreate
|
|
252
|
+
|
|
253
|
+
if (st.st_dev, st.st_ino) != (cur.st_dev, cur.st_ino):
|
|
254
|
+
continue # stale inode; retry against the current file
|
|
255
|
+
if not stat.S_ISREG(st.st_mode):
|
|
256
|
+
raise OSError(errno.EINVAL, 'not a regular file', path)
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
existing = load(read_all(fd))
|
|
260
|
+
except ValueError as e:
|
|
261
|
+
raise ValueError(f'{path}: {e}') from None
|
|
262
|
+
|
|
263
|
+
merged, changed = merge(existing, update)
|
|
264
|
+
if not changed:
|
|
265
|
+
return # nothing to do; don't churn the file
|
|
266
|
+
|
|
267
|
+
tmp = write_temp(
|
|
268
|
+
dirname,
|
|
269
|
+
basename,
|
|
270
|
+
dump(merged),
|
|
271
|
+
stat.S_IMODE(st.st_mode),
|
|
272
|
+
uid_gid=(st.st_uid, st.st_gid),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
os.rename(tmp, path) # atomic replace, done while holding the lock
|
|
277
|
+
except BaseException:
|
|
278
|
+
os.unlink(tmp)
|
|
279
|
+
raise
|
|
280
|
+
|
|
281
|
+
fsync_dir(dirname)
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
finally:
|
|
285
|
+
os.close(fd) # releases the flock
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def main(argv: ta.Any = None) -> int:
|
|
289
|
+
ap = argparse.ArgumentParser(
|
|
290
|
+
description='Atomically upsert keys into a JSON object file; the update is a JSON object read from stdin.',
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
ap.add_argument(
|
|
294
|
+
'file',
|
|
295
|
+
help='JSON file to update (created if missing)',
|
|
296
|
+
)
|
|
297
|
+
ap.add_argument(
|
|
298
|
+
'--timeout',
|
|
299
|
+
type=float,
|
|
300
|
+
default=30.0,
|
|
301
|
+
help='max seconds to wait for the lock (default: 30)',
|
|
302
|
+
)
|
|
303
|
+
ap.add_argument(
|
|
304
|
+
'--description',
|
|
305
|
+
help='ignored; only serves as a note in process listings and execution logs',
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
args = ap.parse_args(argv)
|
|
309
|
+
|
|
310
|
+
try:
|
|
311
|
+
update = parse_object(sys.stdin.buffer.read().decode('utf-8'))
|
|
312
|
+
except ValueError as e: # covers UnicodeDecodeError and json.JSONDecodeError
|
|
313
|
+
print(f'error: stdin is not a JSON object: {e}', file=sys.stderr)
|
|
314
|
+
return 1
|
|
315
|
+
|
|
316
|
+
try:
|
|
317
|
+
inject(args.file, update, args.timeout)
|
|
318
|
+
except TimeoutError as e:
|
|
319
|
+
print(f'error: {e}', file=sys.stderr)
|
|
320
|
+
return 3
|
|
321
|
+
|
|
322
|
+
except (OSError, ValueError) as e:
|
|
323
|
+
print(f'error: {e}', file=sys.stderr)
|
|
324
|
+
return 1
|
|
325
|
+
|
|
326
|
+
return 0
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
if __name__ == '__main__':
|
|
330
|
+
sys.exit(main())
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import os.path
|
|
2
2
|
import typing as ta
|
|
3
3
|
|
|
4
|
-
from omcore
|
|
4
|
+
from omcore import lang
|
|
5
|
+
from omcore.configs.formats import DEFAULT_CONFIG_FILE_LOADER
|
|
5
6
|
from omcore.os.environ import EnvVar
|
|
6
7
|
from omcore.secrets import all as sec
|
|
7
8
|
|
|
@@ -11,25 +12,34 @@ from .paths import get_home_paths
|
|
|
11
12
|
##
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
INJECTED_SECRETS_FILE_ENV_VAR = EnvVar('OM_SECRETS')
|
|
16
|
+
DEFAULT_INJECTED_SECRETS_FILE_NAME: ta.Final = 'secrets-injected.json'
|
|
16
17
|
|
|
18
|
+
USER_SECRETS_FILE_ENV_VAR = EnvVar('OM_SECRETS_INJECTED')
|
|
19
|
+
DEFAULT_USER_SECRETS_FILE_NAME: ta.Final = 'secrets.yml'
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
def _get_secrets_file_paths() -> lang.SequenceNotStr[str]:
|
|
23
|
+
return [
|
|
24
|
+
os.path.expanduser(p)
|
|
25
|
+
for ev, dfn in [
|
|
26
|
+
(INJECTED_SECRETS_FILE_ENV_VAR, DEFAULT_INJECTED_SECRETS_FILE_NAME),
|
|
27
|
+
(USER_SECRETS_FILE_ENV_VAR, DEFAULT_USER_SECRETS_FILE_NAME),
|
|
28
|
+
]
|
|
29
|
+
for p_ in ev.get(lambda: os.path.join(get_home_paths().config_dir, dfn)).split(os.pathsep)
|
|
30
|
+
if (p := p_.strip())
|
|
31
|
+
]
|
|
22
32
|
|
|
23
33
|
|
|
24
34
|
def load_secrets() -> sec.Secrets:
|
|
25
35
|
dct: dict[str, sec.Secret] = {}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
for k, v in
|
|
36
|
+
for fp in _get_secrets_file_paths():
|
|
37
|
+
try:
|
|
38
|
+
for k, v in DEFAULT_CONFIG_FILE_LOADER.load_file(fp).as_map().items():
|
|
29
39
|
if isinstance(v, str):
|
|
30
40
|
dct[k] = sec.Secret(key=k, value=v)
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
except FileNotFoundError:
|
|
42
|
+
pass
|
|
33
43
|
return sec.MappingSecrets(dct)
|
|
34
44
|
|
|
35
45
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Copyright (c) 2016 - 2025, Lars Heuer
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
|
15
|
+
may be used to endorse or promote products derived from this software
|
|
16
|
+
without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# https://github.com/heuer/segno/tree/1.6.6
|