diff --git a/pyproject.toml b/pyproject.toml index fed528d4a..6b1808967 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,ObjectManager,Report" diff --git a/src/stratis_cli/_actions/_dynamic.py b/src/stratis_cli/_actions/_dynamic.py new file mode 100644 index 000000000..f14733a70 --- /dev/null +++ b/src/stratis_cli/_actions/_dynamic.py @@ -0,0 +1,58 @@ +# 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, REPORT_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) + OBJECT_MANAGER = ("ObjectManager", "org.freedesktop.DBUs.ObjectManager") + REPORT = ("Report", REPORT_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..5fe73d4cd 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: ") @@ -119,7 +118,7 @@ def get_report(namespace): # pylint: disable=import-outside-toplevel if namespace.report_name == ReportKey.MANAGED_OBJECTS.value: - from ._data import ObjectManager + ObjectManager = make_dyn_class(ClassKey.OBJECT_MANAGER) json_report = ObjectManager.Methods.GetManagedObjects( get_object(TOP_OBJECT), {} @@ -127,14 +126,14 @@ 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), {} ) else: - from ._data import Report + Report = make_dyn_class(ClassKey.REPORT) (report, return_code, message) = Report.Methods.GetReport( get_object(TOP_OBJECT), {"name": namespace.report_name} @@ -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)