865cf20732
Only one user here which is no longer needed in a Python 3 only world. Change-Id: Iac33e324d07d67368a5b544030cd5414d0586955 Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
165 lines
5.6 KiB
Python
165 lines
5.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# 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.
|
|
|
|
import importlib
|
|
import pkgutil
|
|
import re
|
|
import sys
|
|
|
|
import pbr.version
|
|
|
|
THIS_NAME = __name__
|
|
THIS_LIB = sys.modules[THIS_NAME]
|
|
TEST_DIR = "%s.tests" % THIS_NAME
|
|
|
|
__version__ = pbr.version.VersionInfo(THIS_NAME).version_string()
|
|
|
|
# Any user-specified feature/trait is prefixed with the custom namespace
|
|
CUSTOM_NAMESPACE = 'CUSTOM_'
|
|
|
|
|
|
def _symbolize(mod_name, props):
|
|
"""Given a reference to a Python module object and an iterable of short
|
|
string names for traits, registers symbols in the module corresponding to
|
|
the full namespaced name for each trait.
|
|
"""
|
|
for prop in props:
|
|
leaf_mod = sys.modules[mod_name]
|
|
value_base = '_'.join([m.upper() for m in mod_name.split('.')[1:]])
|
|
value = value_base + '_' + prop.upper()
|
|
setattr(THIS_LIB, value, value) # os_traits.HW_CPU_X86_SSE
|
|
setattr(leaf_mod, prop, value) # os_traits.hw.cpu.x86.SSE
|
|
|
|
|
|
def _visualize(mod_name, props, seen=None):
|
|
if mod_name in seen:
|
|
return
|
|
seen.add(mod_name)
|
|
components = mod_name.split('.')
|
|
tab = ' '
|
|
# Print the module name
|
|
indent = tab * (len(components) - 1)
|
|
print('%s%s:' % (indent, components[-1].upper()))
|
|
# Print the properties
|
|
indent = tab * len(components)
|
|
if props:
|
|
print('%s%s' % (indent, ', '.join(props)))
|
|
|
|
|
|
def _walk_submodules(package, recursive, callback, **kwargs):
|
|
"""Recursively walk the repository's submodules and invoke a callback for
|
|
each module with the list of short trait names found therein.
|
|
|
|
:param package: The package (name or module obj) to start from.
|
|
:param recursive: If True, recurse depth-first.
|
|
:param callback: Callable to be invoked for each module. The signature is::
|
|
|
|
callback(mod_name, props, **kwargs)
|
|
|
|
* mod_name: the string name of the module (e.g. 'os_traits.hw.cpu').
|
|
* props: an iterable of short string names for traits, gleaned from the
|
|
TRAITS member of that module, defaulting to [].
|
|
* kwargs: The same kwargs as passed to _walk_submodules, useful for
|
|
tracking data across calls.
|
|
:param kwargs: Arbitrary keyword arguments to be passed to the callback on
|
|
each invocation.
|
|
"""
|
|
if isinstance(package, str):
|
|
package = importlib.import_module(package)
|
|
for loader, mod_name, is_pkg in pkgutil.walk_packages(
|
|
package.__path__, package.__name__ + '.'):
|
|
if TEST_DIR in mod_name:
|
|
continue
|
|
imported = importlib.import_module(mod_name)
|
|
props = getattr(imported, "TRAITS", [])
|
|
callback(mod_name, props, **kwargs)
|
|
if recursive and is_pkg:
|
|
_walk_submodules(mod_name, recursive, callback, **kwargs)
|
|
|
|
|
|
# This is where the names defined in submodules are imported by recursively
|
|
# importing all submodules/subpackages and symbolizing their TRAITS
|
|
_walk_submodules(sys.modules.get(__name__), True, _symbolize)
|
|
|
|
|
|
def get_traits(prefix=None, suffix=None):
|
|
"""Returns the trait strings in the os_traits module, optionally filtered
|
|
by a supplied prefix and suffix.
|
|
|
|
:param prefix: Optional string prefix to filter by. e.g. 'HW_'
|
|
:param suffix: Optional string suffix to filter by, e.g. 'SSE'
|
|
"""
|
|
prefix = prefix or ""
|
|
suffix = suffix or ""
|
|
return [
|
|
v for k, v in sys.modules[__name__].__dict__.items()
|
|
if isinstance(v, str) and
|
|
not k.startswith('_') and
|
|
v.startswith(prefix) and
|
|
v.endswith(suffix) and
|
|
# skip module constants
|
|
k not in ('CUSTOM_NAMESPACE', 'THIS_NAME', 'THIS_LIB', 'TEST_DIR')
|
|
]
|
|
|
|
|
|
def check_traits(traits, prefix=None):
|
|
"""Returns a tuple of two trait string sets, the first set contains valid
|
|
traits, and the second contains others.
|
|
|
|
:param traits: An iterable contains trait strings.
|
|
:param prefix: Optional string prefix to filter by. e.g. 'HW_'
|
|
"""
|
|
trait_set = set(traits)
|
|
valid_trait_set = set(get_traits(prefix))
|
|
|
|
valid_traits = trait_set & valid_trait_set
|
|
|
|
return (valid_traits, trait_set - valid_traits)
|
|
|
|
|
|
def is_custom(trait):
|
|
"""Returns True if the trait string represents a custom trait, or False
|
|
otherwise.
|
|
|
|
:param trait: String name of the trait
|
|
"""
|
|
return trait.startswith(CUSTOM_NAMESPACE)
|
|
|
|
|
|
def normalize_name(name):
|
|
"""Converts an input string to a legal* custom trait name.
|
|
|
|
Legal custom trait names are prefixed with CUSTOM_ and contain only the
|
|
characters A-Z, 0-9, and _ (underscore).
|
|
|
|
*Does not attempt to handle length restrictions.
|
|
|
|
:param name: A string to be converted.
|
|
:return: A legal* custom trait name.
|
|
"""
|
|
if name is None:
|
|
return None
|
|
# Replace non-alphanumeric characters with underscores
|
|
norm_name = re.sub('[^0-9A-Za-z]+', '_', name)
|
|
# Bug #1762789: Do .upper after replacing non alphanumerics.
|
|
return CUSTOM_NAMESPACE + norm_name.upper()
|
|
|
|
|
|
def print_tree():
|
|
"""Print (to stdout) a visual representation of all the namespaces and the
|
|
(short) trait names defined therein.
|
|
|
|
"""
|
|
_walk_submodules(sys.modules.get(__name__), True, _visualize, seen=set())
|