Skip to content

Commit

Permalink
organize os_traits for the future
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jaypipes authored and EdLeafe committed Apr 5, 2017
1 parent 9e6b7c1 commit 23d81d4
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 119 deletions.
20 changes: 7 additions & 13 deletions os_traits/__init__.py
Expand Up @@ -20,8 +20,12 @@
__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):
Expand All @@ -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))
]

Expand All @@ -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))
]

Expand All @@ -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)
91 changes: 0 additions & 91 deletions os_traits/const.py

This file was deleted.

Empty file added os_traits/hw/__init__.py
Empty file.
Empty file added os_traits/hw/cpu/__init__.py
Empty file.
64 changes: 64 additions & 0 deletions os_traits/hw/cpu/x86.py
@@ -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')
Empty file added os_traits/storage/__init__.py
Empty file.
21 changes: 21 additions & 0 deletions os_traits/storage/disk.py
@@ -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
26 changes: 11 additions & 15 deletions os_traits/tests/test_os_traits.py
Expand Up @@ -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"])
Expand Down
56 changes: 56 additions & 0 deletions os_traits/utils.py
@@ -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)

0 comments on commit 23d81d4

Please sign in to comment.