organize os_traits for the future

Sean Mooney had a good idea that for future-proofing the os-traits library and
ensuring that we don't have to deal with one giant const.py file, that we break
the library into various modules corresponding to the higher-level namespaces.

This patch adds some symbol-registration foo into a utils module and allows the
os_traits module and "leaf modules" to be called in the following way:

 import os_traits
 from os_traits.hw.cpu import x86

 assert os_traits.HW_CPU_X86_SSE42 == x86.SSE42
 assert x86.SSE42 == 'HW_CPU_X86_SSE42'

Change-Id: I0e8f50822ab67cb3be85ed3b935dd6cdb4436dbf
This commit is contained in:
Jay Pipes 2017-03-21 17:02:52 -04:00 committed by EdLeafe
parent 9e6b7c1aba
commit 23d81d4451
9 changed files with 159 additions and 119 deletions

View File

@ -20,8 +20,12 @@ import six
__version__ = pbr.version.VersionInfo(
'os_traits').version_string()
# Conveniently import all the constants into the main module "namespace"
from os_traits.const import * # noqa
# Any user-specified feature/trait is prefixed with the custom namespace
CUSTOM_NAMESPACE = 'CUSTOM_'
# Each submodule registers its symbols with the os_traits module namespace
from os_traits.hw.cpu import x86 # noqa
from os_traits.storage import disk # noqa
def get_symbol_names(prefix=None):
@ -30,15 +34,10 @@ def get_symbol_names(prefix=None):
:param prefix: Optional string prefix to filter by. e.g. 'HW_'
"""
excluded_keys = ('NAMESPACES',)
excluded_values = NAMESPACES.values()
return [
k for k, v in sys.modules[__name__].__dict__.items()
if isinstance(v, six.string_types) and
not k.startswith('_') and
k not in excluded_keys and
v not in excluded_values and
(prefix is None or v.startswith(prefix))
]
@ -49,15 +48,10 @@ def get_traits(prefix=None):
:param prefix: Optional string prefix to filter by. e.g. 'HW_'
"""
excluded_keys = ('NAMESPACES',)
excluded_values = NAMESPACES.values()
return [
v for k, v in sys.modules[__name__].__dict__.items()
if isinstance(v, six.string_types) and
not k.startswith('_') and
k not in excluded_keys and
v not in excluded_values and
(prefix is None or v.startswith(prefix))
]
@ -82,4 +76,4 @@ def is_custom(trait):
:param trait: String name of the trait
"""
return trait.startswith(NAMESPACES['CUSTOM'])
return trait.startswith(CUSTOM_NAMESPACE)

View File

@ -1,91 +0,0 @@
# -*- 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.
# Any user-specified feature/trait is prefixed with the custom namespace
_CUSTOM_NS = 'CUSTOM_'
# All hardware-specific features are prefixed with this namespace
_HW_NS = 'HW_'
# All CPU-specific features are prefixed with this namespace
_CPU_NS = _HW_NS + 'CPU_'
_CPU_X86_NS = _CPU_NS + 'X86_'
# ref: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
HW_CPU_X86_AVX = _CPU_X86_NS + 'AVX'
HW_CPU_X86_AVX2 = _CPU_X86_NS + 'AVX2'
HW_CPU_X86_CLMUL = _CPU_X86_NS + 'CLMUL'
HW_CPU_X86_FMA3 = _CPU_X86_NS + 'FMA3'
HW_CPU_X86_FMA4 = _CPU_X86_NS + 'FMA4'
HW_CPU_X86_F16C = _CPU_X86_NS + 'F16C'
HW_CPU_X86_MMX = _CPU_X86_NS + 'MMX'
HW_CPU_X86_SSE = _CPU_X86_NS + 'SSE'
HW_CPU_X86_SSE2 = _CPU_X86_NS + 'SSE2'
HW_CPU_X86_SSE3 = _CPU_X86_NS + 'SSE3'
HW_CPU_X86_SSSE3 = _CPU_X86_NS + 'SSSE3'
HW_CPU_X86_SSE41 = _CPU_X86_NS + 'SSE41'
HW_CPU_X86_SSE42 = _CPU_X86_NS + 'SSE42'
HW_CPU_X86_SSE4A = _CPU_X86_NS + 'SSE4A'
HW_CPU_X86_XOP = _CPU_X86_NS + 'XOP'
HW_CPU_X86_3DNOW = _CPU_X86_NS + '3DNOW'
# ref: https://en.wikipedia.org/wiki/AVX-512
HW_CPU_X86_AVX512F = _CPU_X86_NS + 'AVX512F' # foundation
HW_CPU_X86_AVX512CD = _CPU_X86_NS + 'AVX512CD' # conflict detection
HW_CPU_X86_AVX512PF = _CPU_X86_NS + 'AVX512PF' # prefetch
HW_CPU_X86_AVX512ER = _CPU_X86_NS + 'AVX512ER' # exponential + reciprocal
HW_CPU_X86_AVX512VL = _CPU_X86_NS + 'AVX512VL' # vector length extensions
HW_CPU_X86_AVX512BW = _CPU_X86_NS + 'AVX512BW' # byte + word
HW_CPU_X86_AVX512DQ = _CPU_X86_NS + 'AVX512DQ' # double word + quad word
# ref: https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets
HW_CPU_X86_ABM = _CPU_X86_NS + 'ABM'
HW_CPU_X86_BMI = _CPU_X86_NS + 'BMI'
HW_CPU_X86_BMI2 = _CPU_X86_NS + 'BMI2'
HW_CPU_X86_TBM = _CPU_X86_NS + 'TBM'
# ref: https://en.wikipedia.org/wiki/AES_instruction_set
HW_CPU_X86_AESNI = _CPU_X86_NS + 'AES-NI'
# ref: https://en.wikipedia.org/wiki/Intel_SHA_extensions
HW_CPU_X86_SHA = _CPU_X86_NS + 'SHA'
# ref: https://en.wikipedia.org/wiki/Intel_MPX
HW_CPU_X86_MPX = _CPU_X86_NS + 'MPX'
# ref: https://en.wikipedia.org/wiki/Software_Guard_Extensions
HW_CPU_X86_SGX = _CPU_X86_NS + 'SGX'
# ref: https://en.wikipedia.org/wiki/Transactional_Synchronization_Extensions
HW_CPU_X86_TSX = _CPU_X86_NS + 'TSX'
# ref: https://en.wikipedia.org/wiki/Advanced_Synchronization_Facility
HW_CPU_X86_ASF = _CPU_X86_NS + 'ASF'
# ref: https://en.wikipedia.org/wiki/VT-x
HW_CPU_X86_VMX = _CPU_X86_NS + 'VMX'
# ref: https://en.wikipedia.org/wiki/AMD-V
HW_CPU_X86_SVM = _CPU_X86_NS + 'SVM'
# All storage-specific features are prefixed with this namespace
_STORAGE_NS = 'STORAGE_'
# All disk storage-specific features are prefixed with this namespace (as
# opposed to object storage)
_DISK_NS = _STORAGE_NS + 'DISK_'
STORAGE_DISK_HDD = _DISK_NS + 'HDD' # spinning oxide
STORAGE_DISK_SSD = _DISK_NS + 'SSD' # solid-state disks
NAMESPACES = {
'CUSTOM': _CUSTOM_NS,
'HARDWARE': _HW_NS,
'HW': _HW_NS,
'CPU': _CPU_NS,
'X86': _CPU_X86_NS,
'STORAGE': _STORAGE_NS,
'DISK': _DISK_NS,
}

0
os_traits/hw/__init__.py Normal file
View File

View File

64
os_traits/hw/cpu/x86.py Normal file
View File

@ -0,0 +1,64 @@
# -*- 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.
from os_traits import utils
register = utils.register_fn(__name__)
# ref: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
register('AVX')
register('AVX2')
register('CLMUL')
register('FMA3')
register('FMA4')
register('F16C')
register('MMX')
register('SSE')
register('SSE2')
register('SSE3')
register('SSSE3')
register('SSE41')
register('SSE42')
register('SSE4A')
register('XOP')
register('3DNOW')
# ref: https://en.wikipedia.org/wiki/AVX-512
register('AVX512F') # foundation
register('AVX512CD') # conflict detection
register('AVX512PF') # prefetch
register('AVX512ER') # exponential + reciprocal
register('AVX512VL') # vector length extensions
register('AVX512BW') # byte + word
register('AVX512DQ') # double word + quad word
# ref: https://en.wikipedia.org/wiki/Bit_Manipulation_Instruction_Sets
register('ABM')
register('BMI')
register('BMI2')
register('TBM')
# ref: https://en.wikipedia.org/wiki/AES_instruction_set
register('AES-NI')
# ref: https://en.wikipedia.org/wiki/Intel_SHA_extensions
register('SHA')
# ref: https://en.wikipedia.org/wiki/Intel_MPX
register('MPX')
# ref: https://en.wikipedia.org/wiki/Software_Guard_Extensions
register('SGX')
# ref: https://en.wikipedia.org/wiki/Transactional_Synchronization_Extensions
register('TSX')
# ref: https://en.wikipedia.org/wiki/Advanced_Synchronization_Facility
register('ASF')
# ref: https://en.wikipedia.org/wiki/VT-x
register('VMX')
# ref: https://en.wikipedia.org/wiki/AMD-V
register('SVM')

View File

21
os_traits/storage/disk.py Normal file
View File

@ -0,0 +1,21 @@
# -*- 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.
from os_traits import utils
register = utils.register_fn(__name__)
register('HDD') # spinning oxide
register('SSD') # solid-state disks

View File

@ -12,37 +12,33 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
test_os_traits
----------------------------------
Tests for `os_traits` module.
"""
import os_traits as ot
from os_traits.hw.cpu import x86
from os_traits.tests import base
class TestOs_traits(base.TestCase):
class TestSymbols(base.TestCase):
def test_trait(self):
"""Simply tests that the constants from submodules are imported into
the primary os_traits module space.
"""
trait = ot.HW_CPU_X86_SSE42
self.assertEqual("HW_CPU_X86_SSE42", trait)
# And the "leaf-module" namespace...
self.assertEqual(x86.SSE42, ot.HW_CPU_X86_SSE42)
def test_get_symbol_names(self):
names = ot.get_symbol_names()
self.assertIn("HW_CPU_X86_AVX2", names)
self.assertIn("STORAGE_DISK_SSD", names)
def test_namespaces(self):
namespaces = ot.NAMESPACES
self.assertIn(("HARDWARE", "HW_"), namespaces.items())
self.assertEqual(7, len(namespaces))
def test_get_traits(self):
traits = ot.get_traits(ot.NAMESPACES['X86'])
traits = ot.get_traits('HW_CPU')
self.assertIn("HW_CPU_X86_SSE42", traits)
self.assertEqual(35, len(traits))
self.assertIn(ot.HW_CPU_X86_AVX2, traits)
self.assertNotIn(ot.STORAGE_DISK_SSD, traits)
def test_check_traits(self):
traits = set(["HW_CPU_X86_SSE42", "HW_CPU_X86_XOP"])

56
os_traits/utils.py Normal file
View File

@ -0,0 +1,56 @@
# -*- 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 functools
import sys
import os_traits
def symbolize(mod_name, name):
"""Given a reference to a Python module object and a short string name for
a trait, registers a symbol in the module that corresponds to the full
namespaced trait name.
For example, if called like so:
:code:
# In file /os_traits/hw/cpu/x86.py
import functools
from os_traits import utils
mod_register = functools.partial(utils.symbolize, __name__)
mod_register('AVX2')
mod_register('SSE')
Would end up creating the following symbols:
os_traits.hw.cpu.x86.AVX2 with the value of 'HW_CPU_X86_AVX2'
os_traits.hw.cpu.x86.SSE with the value of 'HW_CPU_X86_SSE'
os_traits.HW_CPU_X86_AVX2 with the value of 'HW_CPU_X86_AVX2'
os_traits.HW_CPU_X86_SSE with the value of 'HW_CPU_X86_SSE'
"""
leaf_mod = sys.modules[mod_name]
value_base = '_'.join([m.upper() for m in mod_name.split('.')[1:]])
value = value_base + '_' + name.upper()
setattr(os_traits, value, value) # os_traits.HW_CPU_X86_SSE
setattr(leaf_mod, name.upper(), value) # os_traits.hw.cpu.x86.SSE
def register_fn(mod_name):
return functools.partial(symbolize, mod_name)