Source code for qct_interfaces

"""
Package to hold interface definitions for elements of the Qualcomm
Device Configuration Tool using the zope.interface mechanism.

This module defines a number of utilities methods to make the use of
Zope's interface, component and configuration APIs a little easier.

Some understanding of the `zope interface
<http://docs.zope.org/zope.interface/README.html>`_ component might be
useful: it's definitely worth a read to help make sense of how the
Config Tool uses it.
"""

#
# Copyright Qualcomm Technologies Inc, 2019.
# All Rights Reserved
#

# Python imports
import logging
import pathlib
import sys
from typing import Tuple, List
import pkg_resources

# Third-party imports
from zope.component import queryMultiAdapter
from zope.configuration.xmlconfig import xmlconfig


[docs]def make_registered_instance(iface, **kwargs): """Create an instance of the interface ``iface``, and sets attributes on that instance. This could be seen as a simple factory method. This function works in the simple case where an adapter has been defined for the "null" adaption. This would be done in the ZCML like this:: <adapter for="" factory="packageA.module1.MyImplementation" provides="packageB.module2.IMyInterface" /> Then in the Python world you can create an instance of ``MyImplementation`` with the following:: my_obj = make_registered_instance(packageB.module2.IMyInterface, param1=1, param2=2) Note that in the definition of the interface, ``param1`` and ``param2`` ought to be defined as zope Attributes of that interface. """ obj = queryMultiAdapter((), iface) for key, val in kwargs.items(): setattr(obj, key, val) return obj
[docs]def make_registered_named_instance(iface, name, **kwargs): """Create an instance of the interface ``iface``, **registered with the specifed name**, and sets attributes on that instance. This could be seen as a simple factory method. This function works in the simple case where an adapter has been defined for the "null" adaption, but must have been named. This would be done in the ZCML like this:: <adapter for="" factory="packageA.module1.MyImplementation" provides="packageB.module2.IMyInterface" name="foo" /> Then in the Python world you can create an instance of ``MyImplementation`` with the following:: my_obj = make_registered_named_instance(packageB.module2.IMyInterface, "foo", param1=1, param2=2) Note that in the definition of the interface, ``param1`` and ``param2`` ought to be defined as zope Attributes of that interface. """ obj = queryMultiAdapter((), iface, name) if not obj: return None for key, val in kwargs.items(): setattr(obj, key, val) return obj
[docs]def load_components() -> None: """This method finds all the ``configure.zcml`` files that have been defined as "data-files" for all the installed packages in the currently running Python environment. It achieves this by asking the :py:mod:`pkg_resources` module to find all the installed packages. It returns a list of :py:obj:`pkg_resources.Distribution`-derived objects, which can be queried to find out if a ``config_tool_plugin`` entry point has been declared for that package. The package called :py:mod:`pyconfigtool` is put at the beginning of the list so that any other installed packages can override definitions made in the default :py:mod:`pyconfigtool` package. It then asks the :py:mod:`zope.configuration` package to use these ZCML files to configure utilities, adapters and subscribers using the :py:mod:`zope.interface` mechanisms. """ if getattr(sys, 'frozen', False): conf_file = pathlib.Path(sys.executable).parent / 'pyconfigtool' / 'configure.zcml' logging.debug("Loading ZCML file: %s", conf_file) with conf_file.open('r') as ifile: xmlconfig(ifile) else: entry_points = [] for package in iter(pkg_resources.working_set): entries = package.get_entry_map('config_tool_plugin') for entry in entries.values(): if entry.module_name == 'pyconfigtool.main' and entry_points: entry_points.insert(0, entry) else: entry_points.append(entry) for entry_point in entry_points: logging.debug("Get config file from package '%s'.", entry_point.module_name) try: entry_point = entry_point.load() conf_file = entry_point() # call the entry-point function if conf_file: logging.debug("Loading ZCML file: '%s'.", conf_file) with open(conf_file, 'r') as ifile: xmlconfig(ifile) # we have no idea what exception a plugin may throw except Exception as exc: # pylint:disable=broad-except logging.error("%s: %s", type(exc).__name__, str(exc)) logging.warning("Failed to load package '%s'", entry_point.module_name)
[docs]def clear_components() -> None: """This method is unlikely to be used in an application context, but it is useful in running unit tests. It clears out the zope configuration registry. """ from zope.configuration.xmlconfig import _clearContext _clearContext()
[docs]def list_components() -> List[Tuple[str, str]]: """This method finds all the plugins for this tool. A plugin is defined as an installed Python package that has ``config_tool_plugin`` entry point defined for it. :return: a list of two-tuples: where the tuple contains the name of the Python package and its version. """ packages = list(pkg_resources.working_set) plugin_packages = [(e.project_name, e.version) for e in packages \ if e.get_entry_map('config_tool_plugin') and \ e.project_name != 'pyconfigtool'] return plugin_packages
[docs]def list_packages() -> List[Tuple[str, str]]: """This method finds all the packages installed into this virtualenv, excluding the ones that look like config tool plugins. :return: a list of two-tuples: where the tuples contain the name of the Python package and its version. """ packages = list(pkg_resources.working_set) packages = [(e.project_name, e.version) for e in packages \ if not e.get_entry_map('config_tool_plugin')] return packages