From f60cbfbd7a53682672dc87a663dbd59034b57c61 Mon Sep 17 00:00:00 2001 From: mulhern Date: Mon, 27 Nov 2023 13:14:37 -0500 Subject: [PATCH] Use dynamic build for Manager class Signed-off-by: mulhern --- pyproject.toml | 3 ++ src/stratis_cli/_actions/_dynamic.py | 56 ++++++++++++++++++++++++++++ src/stratis_cli/_actions/_top.py | 12 +++--- 3 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 src/stratis_cli/_actions/_dynamic.py diff --git a/pyproject.toml b/pyproject.toml index fed528d4a..251a18f13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,6 @@ [build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta" + +[tool.pylint] +good-names="Manager" diff --git a/src/stratis_cli/_actions/_dynamic.py b/src/stratis_cli/_actions/_dynamic.py new file mode 100644 index 000000000..ad1e234e9 --- /dev/null +++ b/src/stratis_cli/_actions/_dynamic.py @@ -0,0 +1,56 @@ +# Copyright 2023 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Dynamic class generation +""" +# isort: STDLIB +import os +import xml.etree.ElementTree as ET # nosec B405 +from enum import Enum + +# isort: FIRSTPARTY +from dbus_python_client_gen import DPClientGenerationError, make_class + +from .._errors import StratisCliGenerationError +from ._constants import MANAGER_INTERFACE +from ._environment import get_timeout +from ._introspect import SPECS + +DBUS_TIMEOUT_SECONDS = 120 + +TIMEOUT = get_timeout( + os.environ.get("STRATIS_DBUS_TIMEOUT", DBUS_TIMEOUT_SECONDS * 1000) +) + + +class ClassKey(Enum): + """ + Keys for dynamically generated classes. + """ + + MANAGER = ("Manager", MANAGER_INTERFACE) + + +def make_dyn_class(key): + """ + Dynamically generate a class from introspection specification. + """ + try: + return make_class( + key.value[0], ET.fromstring(SPECS[key.value[1]]), TIMEOUT # nosec B314 + ) + except DPClientGenerationError as err: # pragma: no cover + raise StratisCliGenerationError( + "Failed to generate some class needed for invoking dbus-python methods" + ) from err diff --git a/src/stratis_cli/_actions/_top.py b/src/stratis_cli/_actions/_top.py index 7f2c79944..8b2a281e4 100644 --- a/src/stratis_cli/_actions/_top.py +++ b/src/stratis_cli/_actions/_top.py @@ -33,6 +33,7 @@ from .._stratisd_constants import ReportKey, StratisdErrors from ._connection import get_object from ._constants import TOP_OBJECT +from ._dynamic import ClassKey, make_dyn_class from ._formatting import print_table @@ -44,8 +45,7 @@ def _fetch_keylist(proxy): :rtype: list of str :raises StratisCliEngineError: """ - # pylint: disable=import-outside-toplevel - from ._data import Manager + Manager = make_dyn_class(ClassKey.MANAGER) (keys, return_code, message) = Manager.Methods.ListKeys(proxy, {}) if return_code != StratisdErrors.OK: # pragma: no cover @@ -68,8 +68,7 @@ def _add_update_key(proxy, key_desc, capture_key, *, keyfile_path): """ assert capture_key == (keyfile_path is None) - # pylint: disable=import-outside-toplevel - from ._data import Manager + Manager = make_dyn_class(ClassKey.MANAGER) if capture_key: password = getpass(prompt="Enter key data followed by the return key: ") @@ -127,7 +126,7 @@ def get_report(namespace): else: if namespace.report_name == ReportKey.ENGINE_STATE.value: - from ._data import Manager + Manager = make_dyn_class(ClassKey.MANAGER) (report, return_code, message) = Manager.Methods.EngineStateReport( get_object(TOP_OBJECT), {} @@ -242,8 +241,7 @@ def unset_key(namespace): :raises StratisCliNoChangeError: :raises StratisCliIncoherenceError: """ - # pylint: disable=import-outside-toplevel - from ._data import Manager + Manager = make_dyn_class(ClassKey.MANAGER) proxy = get_object(TOP_OBJECT)