Source code for chaintool
# -*- coding: utf-8 -*-
#
# Copyright 2021 Joel Baxter
#
# This file is part of chaintool.
#
# chaintool is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# chaintool is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with chaintool. If not, see <https://www.gnu.org/licenses/>.
"""Initialize the package's modules."""
__all__ = ["init", "current_export_schema_ver"]
import atexit
import sys
import colorama
from packaging.version import Version
from . import command_impl_core
from . import completions
from . import locks
from . import sequence_impl_core
from . import shared
from . import shortcuts
__version__ = "0.3.0.dev0"
INTERNAL_SCHEMA_CHANGE_VERSIONS = ["0.3.0"]
EXPORT_SCHEMA_CHANGE_VERSIONS = ["0.3.0"]
if sys.version_info < (3, 7):
sys.stderr.write("\nPython version 3.7 or later is required.\n")
sys.exit(1)
def schema_ver_for_package_ver(query_package_ver_str, schema_change_versions):
"""Return the requested schema version for a chaintool package version.
This function takes the base of the given package version (e.g. if given
"0.3.0.dev0" it will work with "0.3.0") and compares it to the
``schema_change_versions`` to determine the appropriate schema version
number used by that version of chaintool.
:param query_package_ver_str: package version to calculate schema for
:type query_package_ver_str: str
:param schema_change_versions: package versions at which schema changed
:type schema_change_versions: list[str]
:returns: schema version for the given package version, or None if the
input version string could not be evaluated
:rtype: int | None
"""
query_package_ver = Version(Version(query_package_ver_str).base_version)
if query_package_ver >= Version(schema_change_versions[-1]):
return len(schema_change_versions)
for prev_schema_ver, package_ver_str in reversed(
list(enumerate(schema_change_versions))
):
if query_package_ver < Version(package_ver_str):
return prev_schema_ver
return None
def internal_schema_ver_for_package_ver(query_package_ver_str):
"""Return the internal schema version for a chaintool package version.
Delegate to :func:`schema_ver_for_package_ver` using the given
``query_package_ver_str`` and the schema-change info from
:const:`INTERNAL_SCHEMA_CHANGE_VERSIONS`.
:param query_package_ver_str: package version to calculate schema for
:type query_package_ver_str: str
:returns: internal schema version for the given package version, or None
if the input version string could not be evaluated
:rtype: int | None
"""
return schema_ver_for_package_ver(
query_package_ver_str, INTERNAL_SCHEMA_CHANGE_VERSIONS
)
def init_modules():
"""If schema is changing, check for validity; then init modules.
Initialize the non-schema-dependent :mod:`colorama`, :mod:`.shared`, and
:mod:`.locks` modules. Then grab the meta-lock.
While holding the meta-lock, load the schema version for the current
stored config/data and compare it to the schema version used by our
current package. If the stored version is larger, that's bad... a newer
chaintool that uses a different format has been running, and has changed
the schema to something we don't understand. In that case, exit with error.
Otherwise, call the init functions for :mod:`.command_impl_core`,
:mod:`.sequence_impl_core`, :mod:`.shortcuts`, and :mod:`.completions`.
Pass them the old and new schema versions in case they need to update
their stored data formats.
Finally update the last-stored-version info for schema, the chaintool
package, and Python (last two are just informative). Release the meta-lock
and return.
"""
colorama.init()
atexit.register(colorama.deinit)
shared.init()
locks.init()
with locks.META_LOCK:
this_schema_ver = internal_schema_ver_for_package_ver(__version__)
last_schema_ver = shared.get_last_schema_version()
last_chaintool_ver = shared.get_last_chaintool_version()
last_python_ver = shared.get_last_python_version()
if last_schema_ver > this_schema_ver:
shared.errprint(
"\nA more recent version of chaintool ({}) has been run on"
" this system (using Python version {}). The version of"
" chaintool you are attempting to run ({}) cannot use the"
" newer config/data format that is now in place.\n".format(
last_chaintool_ver, last_python_ver, __version__
)
)
sys.exit(1)
command_impl_core.init(last_schema_ver, this_schema_ver)
sequence_impl_core.init(last_schema_ver, this_schema_ver)
shortcuts.init(last_schema_ver, this_schema_ver)
completions.init(last_schema_ver, this_schema_ver)
shared.set_last_schema_version(this_schema_ver)
shared.set_last_chaintool_version(__version__)
this_python_ver = sys.version
if " " in this_python_ver:
this_python_ver = this_python_ver[: this_python_ver.index(" ")]
shared.set_last_python_version(this_python_ver)
[docs]def init():
"""Idempotent initialization of chaintool's files and configurations.
This function must be called at least once before using chaintool for
the first time, and after any upgrade to a newer chaintool version. It
is automatically invoked with every use of the chaintool command; you
would only need to explicitly invoke it if you are calling functions in
the chaintool modules from other code.
"""
init_modules()
[docs]def current_export_schema_ver():
"""Return the export schema version understood by this chaintool version.
Delegate to :func:`schema_ver_for_package_ver` using the current
chaintool version and the schema-change info from
:const:`EXPORT_SCHEMA_CHANGE_VERSIONS`.
:returns: export schema version for the given package version, or None
if the current chaintool version string could not be evaluated
:rtype: int | None
"""
return schema_ver_for_package_ver(
__version__, EXPORT_SCHEMA_CHANGE_VERSIONS
)