Minimum necessary to add index and get capability and tests
This commit is contained in:
parent
88cbbc6a90
commit
5eeb88e2b7
33
kmk/keys.py
33
kmk/keys.py
@ -21,10 +21,6 @@ ALL_NUMBERS = '1234567890'
|
||||
ALL_NUMBER_ALIASES = tuple(f'N{x}' for x in ALL_NUMBERS)
|
||||
|
||||
|
||||
class InfiniteLoopDetected(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# this is a bit of an FP style thing - combining a pipe operator a-la F# with
|
||||
# a bootleg Maybe monad to clean up these make_key sequences
|
||||
def left_pipe_until_some(candidate, functor, *args_iter):
|
||||
@ -61,13 +57,30 @@ def maybe_make_consumer_key(candidate, code, names):
|
||||
return make_consumer_key(code=code, names=names)
|
||||
|
||||
|
||||
class KeyAttrDict(AttrDict):
|
||||
def __getattr__(self, key, depth=0):
|
||||
if depth > 1:
|
||||
raise InfiniteLoopDetected()
|
||||
class KeyAttrDict:
|
||||
__cache = {}
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if DEBUG_OUTPUT:
|
||||
print(f'__setitem__ {key}, {value}')
|
||||
self.__cache.__setitem__(key, value)
|
||||
|
||||
def __getattr__(self, key):
|
||||
if DEBUG_OUTPUT:
|
||||
print(f'__getattr__ {key}')
|
||||
return self.__getitem__(key)
|
||||
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
return super(KeyAttrDict, self).__getattr__(key)
|
||||
return self.__getitem__(key)
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
def __getitem__(self, key):
|
||||
if DEBUG_OUTPUT:
|
||||
print(f'__getitem__ {key}')
|
||||
try:
|
||||
return self.__cache[key]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@ -365,7 +378,7 @@ class KeyAttrDict(AttrDict):
|
||||
if not maybe_key:
|
||||
raise ValueError('Invalid key')
|
||||
|
||||
return self.__getattr__(key, depth=depth + 1)
|
||||
return self.__cache[key]
|
||||
|
||||
|
||||
# Global state, will be filled in throughout this file, and
|
||||
|
@ -1,6 +1,6 @@
|
||||
import unittest
|
||||
|
||||
from kmk.keys import KC, Key, ModifierKey
|
||||
from kmk.keys import KC, Key, ModifierKey, KeyAttrDict, make_key
|
||||
from tests.keyboard_test import KeyboardTest
|
||||
|
||||
|
||||
@ -97,5 +97,198 @@ class TestKmkKeys(unittest.TestCase):
|
||||
assert not isinstance(KC.RALT(KC.Q), ModifierKey)
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
from kmk.keys import KC, KeyAttrDict, make_key
|
||||
|
||||
|
||||
class TestKeys_dot(unittest.TestCase):
|
||||
def setUp(self):
|
||||
global KC
|
||||
KC = KeyAttrDict()
|
||||
|
||||
def test_expected_code_uppercase(self):
|
||||
assert 4 == KC.A.code
|
||||
|
||||
def test_expected_code_lowercase(self):
|
||||
assert 4 == KC.a.code
|
||||
|
||||
def test_case_ignored_alpha(self):
|
||||
upper_key = KC.A
|
||||
lower_key = KC.a
|
||||
assert upper_key is lower_key
|
||||
|
||||
def test_case_requested_order_irrelevant(self):
|
||||
lower_key = KC.a
|
||||
upper_key = KC.A
|
||||
assert upper_key is lower_key
|
||||
|
||||
def test_secondary_name(self):
|
||||
primary_key = KC.NO
|
||||
secondary_key = KC.XXXXXXX
|
||||
assert primary_key is secondary_key
|
||||
|
||||
def test_invalid_key_upper(self):
|
||||
with self.assertRaises(ValueError):
|
||||
KC.INVALID_KEY
|
||||
|
||||
def test_invalid_key_lower(self):
|
||||
with self.assertRaises(ValueError):
|
||||
KC.invalid_key
|
||||
|
||||
def test_custom_key(self):
|
||||
created = make_key(
|
||||
KC.N2.code,
|
||||
names=(
|
||||
'EURO',
|
||||
'€',
|
||||
),
|
||||
has_modifiers={KC.LSFT.code, KC.ROPT.code},
|
||||
)
|
||||
assert created is KC.get('EURO')
|
||||
assert created is KC.get('€')
|
||||
|
||||
def test_match_exactly_case(self):
|
||||
created = make_key(names=('ThIs_Is_A_StRaNgE_kEy',))
|
||||
assert created is KC.get('ThIs_Is_A_StRaNgE_kEy')
|
||||
|
||||
|
||||
class TestKeys_index(unittest.TestCase):
|
||||
def setUp(self):
|
||||
global KC
|
||||
KC = KeyAttrDict()
|
||||
|
||||
def test_expected_code_uppercase(self):
|
||||
assert 4 == KC['A'].code
|
||||
|
||||
def test_expected_code_lowercase(self):
|
||||
assert 4 == KC['a'].code
|
||||
|
||||
def test_case_ignored_alpha(self):
|
||||
upper_key = KC['A']
|
||||
lower_key = KC['a']
|
||||
assert upper_key is lower_key
|
||||
|
||||
def test_case_requested_order_irrelevant(self):
|
||||
lower_key = KC['a']
|
||||
upper_key = KC['A']
|
||||
assert upper_key is lower_key
|
||||
|
||||
def test_invalid_key_upper(self):
|
||||
with self.assertRaises(ValueError):
|
||||
KC['NOT_A_VALID_KEY']
|
||||
|
||||
def test_invalid_key_lower(self):
|
||||
with self.assertRaises(ValueError):
|
||||
KC['not_a_valid_key']
|
||||
|
||||
def test_custom_key(self):
|
||||
created = make_key(
|
||||
KC['N2'].code,
|
||||
names=(
|
||||
'EURO',
|
||||
'€',
|
||||
),
|
||||
has_modifiers={KC['LSFT'].code, KC['ROPT'].code},
|
||||
)
|
||||
assert created is KC['EURO']
|
||||
assert created is KC['€']
|
||||
|
||||
def test_match_exactly_case(self):
|
||||
created = make_key(names=('ThIs_Is_A_StRaNgE_kEy',))
|
||||
assert created is KC['ThIs_Is_A_StRaNgE_kEy']
|
||||
|
||||
|
||||
class TestKeys_get(unittest.TestCase):
|
||||
def setUp(self):
|
||||
global KC
|
||||
KC = KeyAttrDict()
|
||||
|
||||
def test_expected_code_uppercase(self):
|
||||
assert 4 == KC.get('A').code
|
||||
|
||||
def test_expected_code_lowercase(self):
|
||||
assert 4 == KC.get('a').code
|
||||
|
||||
def test_case_ignored_alpha(self):
|
||||
upper_key = KC.get('A')
|
||||
lower_key = KC.get('a')
|
||||
assert upper_key is lower_key
|
||||
|
||||
def test_case_requested_order_irrelevant(self):
|
||||
lower_key = KC.get('a')
|
||||
upper_key = KC.get('A')
|
||||
assert upper_key is lower_key
|
||||
|
||||
def test_secondary_name(self):
|
||||
primary_key = KC.NO
|
||||
secondary_key = KC.XXXXXXX
|
||||
assert primary_key is secondary_key
|
||||
|
||||
def test_invalid_key_upper(self):
|
||||
assert KC.get('INVALID_KEY') is None
|
||||
|
||||
def test_invalid_key_lower(self):
|
||||
assert KC.get('not_a_valid_key') is None
|
||||
|
||||
def test_custom_key(self):
|
||||
created = make_key(
|
||||
KC.get('N2').code,
|
||||
names=(
|
||||
'EURO',
|
||||
'€',
|
||||
),
|
||||
has_modifiers={KC.get('LSFT').code, KC.get('ROPT').code},
|
||||
)
|
||||
assert created is KC.get('EURO')
|
||||
assert created is KC.get('€')
|
||||
|
||||
def test_match_exactly_case(self):
|
||||
created = make_key(names=('ThIs_Is_A_StRaNgE_kEy',))
|
||||
assert created is KC.get('ThIs_Is_A_StRaNgE_kEy')
|
||||
|
||||
|
||||
# Some of these test appear silly, but they're testing we get the
|
||||
# same, single, instance back when requested through KC and that
|
||||
# order of request doesn't matter
|
||||
class TestKeys_instances(unittest.TestCase):
|
||||
def setUp(self):
|
||||
global KC
|
||||
KC = KeyAttrDict()
|
||||
|
||||
def test_make_key_new_instance(self):
|
||||
key1 = make_key(code=1)
|
||||
key2 = make_key(code=1)
|
||||
assert key1 is not key2
|
||||
assert key1.code == key2.code
|
||||
|
||||
def test_index_is_index(self):
|
||||
assert KC['A'] is KC['A']
|
||||
|
||||
def test_index_is_dot(self):
|
||||
assert KC['A'] is KC.A
|
||||
|
||||
def test_index_is_get(self):
|
||||
assert KC['A'] is KC.get('A')
|
||||
|
||||
def test_dot_is_dot(self):
|
||||
assert KC.A is KC.A
|
||||
|
||||
def test_dot_is_index(self):
|
||||
assert KC.A is KC['A']
|
||||
|
||||
def test_dot_is_get(self):
|
||||
assert KC.A is KC.get('A')
|
||||
|
||||
def test_get_is_get(self):
|
||||
assert KC.get('A') is KC.get('A')
|
||||
|
||||
def test_get_is_index(self):
|
||||
assert KC.get('A') is KC['A']
|
||||
|
||||
def test_get_is_dot(self):
|
||||
assert KC.get('A') is KC.A
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user