# Path: uchroma/dbus_utils.py
# def dbus_prepare(obj, variant: bool=False, camel_keys: bool=False) -> tuple:
#     """
#     Recursively walks obj and builds a D-Bus signature
#     by inspecting types. Variant types are created as
#     necessary, and the returned obj may have changed.
# 
#     :param obj: An arbitrary primitive or container type
#     :param variant: Force wrapping contained objects with variants
#     :param camel_keys: Convert dict keys to CamelCase
#     """
#     sig = ''
#     use_variant = variant
# 
#     try:
#         if isinstance(obj, Variant):
#             sig = 'v'
# 
#         elif isinstance(obj, bool):
#             sig = 'b'
# 
#         elif isinstance(obj, str):
#             sig = 's'
# 
#         elif isinstance(obj, int):
#             if obj < pow(2, 16):
#                 sig = 'n'
#             elif obj < pow(2, 32):
#                 sig = 'i'
#             else:
#                 sig = 'x'
# 
#         elif isinstance(obj, float):
#             sig = 'd'
# 
#         elif isinstance(obj, Color):
#             sig = 's'
#             obj = obj.html
# 
#         elif isinstance(obj, TraitType):
#             obj, sig = dbus_prepare(trait_as_dict(obj), variant=True)
# 
#         elif isinstance(obj, HasTraits):
#             obj, sig = dbus_prepare(class_traits_as_dict(obj), variant=True)
# 
#         elif hasattr(obj, '_asdict') and hasattr(obj, '_field_types'):
#             # typing.NamedTuple
#             obj, sig = dbus_prepare(obj._asdict(), variant=True)
# 
#         elif isinstance(obj, type) and issubclass(obj, enum.Enum):
#             # top level enum, tuple of string keys
#             obj = tuple(obj.__members__.keys())
#             sig = '(%s)' % ('s' * len(obj))
# 
#         elif isinstance(obj, enum.Enum):
#             obj = obj.name
#             sig = 's'
# 
#         elif isinstance(obj, np.ndarray):
#             dtype = obj.dtype.kind
#             if dtype == 'f':
#                 dtype = 'd'
#             sig = 'a' * obj.ndim + dtype
#             obj = obj.tolist()
# 
#         elif isinstance(obj, tuple):
#             tmp = []
#             sig = '('
# 
#             for item in obj:
#                 if item is None and use_variant:
#                     continue
#                 # struct of all items
#                 r_obj, r_sig = dbus_prepare(item)
#                 if r_obj is None:
#                     continue
#                 sig += r_sig
#                 tmp.append(r_obj)
#             if len(tmp) > 0:
#                 sig += ')'
#                 obj = tuple(tmp)
#             else:
#                 sig = ''
#                 obj = None
# 
#         elif isinstance(obj, list):
#             tmp = []
#             sig = 'a'
#             is_variant = use_variant or _check_variance(obj)
# 
#             for item in obj:
#                 if item is None and is_variant:
#                     continue
#                 r_obj, r_sig = dbus_prepare(item, variant=is_variant)
#                 if r_obj is None:
#                     continue
# 
#                 tmp.append(r_obj)
# 
#             if is_variant:
#                 sig += 'v'
#             else:
#                 sig += dbus_prepare(tmp[0])[1]
# 
#             obj = tmp
# 
#         elif isinstance(obj, (dict, frozendict)):
#             if isinstance(obj, frozendict):
#                 tmp = {}
#             else:
#                 tmp = obj.__class__()
#             sig = 'a{s'
#             vals = [x for x in obj.values() if x is not None]
#             is_variant = use_variant or _check_variance(vals)
# 
#             for k, v in obj.items():
#                 if v is None:
#                     continue
#                 r_obj, r_sig = dbus_prepare(v)
#                 if r_obj is None:
#                     continue
#                 if camel_keys:
#                     k = snake_to_camel(k)
#                 if is_variant:
#                     tmp[k] = Variant(r_sig, r_obj)
#                 else:
#                     tmp[k] = r_obj
# 
#             if is_variant:
#                 sig += 'v'
#             else:
#                 sig += dbus_prepare(vals[0])[1]
# 
#             obj = tmp
#             sig += '}'
# 
#         elif isinstance(obj, type):
#             obj = obj.__name__
#             sig = 's'
# 
#     except Exception as err:
#         logger.exception('obj: %s  sig: %s variant: %s', obj, sig, variant, exc_info=err)
#         raise
# 
#     return obj, sig


import sys
from enum import Enum
from gi.repository import GLib
from grapefruit import Color
from traitlets import Int
from uchroma.dbus_utils import dbus_prepare

def test_primitives():
    assert dbus_prepare(23)[1] == 'n'
    assert dbus_prepare(65536)[1] == 'i'
    assert dbus_prepare(sys.maxsize)[1] == 'x'
    assert dbus_prepare(False)[1] == 'b'
    assert dbus_prepare(23.0)[1] == 'd'
    assert dbus_prepare('asdf')[1] == 's'

def test_special_types():
    obj, sig = dbus_prepare(Color.NewFromHtml('black'))
    assert obj == '#000000'
    assert sig == 's'

    obj, sig = dbus_prepare(Int(5))
    assert isinstance(obj, dict)
    assert sig == 'a{sv}'
    for value in obj.values():
        assert isinstance(value, GLib.Variant)

    obj, sig = dbus_prepare(EnumTest)
    assert isinstance(obj, tuple)
    assert sig == '(sss)'

def test_dicts():
    simple = {'first': 1, 'second': 2}
    obj, sig = dbus_prepare(simple)
    assert obj == simple
    assert sig == 'a{sn}'


# Path: uchroma/dbus_utils.py
# def dbus_prepare(obj, variant: bool=False, camel_keys: bool=False) -> tuple:
#     """
#     Recursively walks obj and builds a D-Bus signature
#     by inspecting types. Variant types are created as
#     necessary, and the returned obj may have changed.
# 
#     :param obj: An arbitrary primitive or container type
#     :param variant: Force wrapping contained objects with variants
#     :param camel_keys: Convert dict keys to CamelCase
#     """
#     sig = ''
#     use_variant = variant
# 
#     try:
#         if isinstance(obj, Variant):
#             sig = 'v'
# 
#         elif isinstance(obj, bool):
#             sig = 'b'
# 
#         elif isinstance(obj, str):
#             sig = 's'
# 
#         elif isinstance(obj, int):
#             if obj < pow(2, 16):
#                 sig = 'n'
#             elif obj < pow(2, 32):
#                 sig = 'i'
#             else:
#                 sig = 'x'
# 
#         elif isinstance(obj, float):
#             sig = 'd'
# 
#         elif isinstance(obj, Color):
#             sig = 's'
#             obj = obj.html
# 
#         elif isinstance(obj, TraitType):
#             obj, sig = dbus_prepare(trait_as_dict(obj), variant=True)
# 
#         elif isinstance(obj, HasTraits):
#             obj, sig = dbus_prepare(class_traits_as_dict(obj), variant=True)
# 
#         elif hasattr(obj, '_asdict') and hasattr(obj, '_field_types'):
#             # typing.NamedTuple
#             obj, sig = dbus_prepare(obj._asdict(), variant=True)
# 
#         elif isinstance(obj, type) and issubclass(obj, enum.Enum):
#             # top level enum, tuple of string keys
#             obj = tuple(obj.__members__.keys())
#             sig = '(%s)' % ('s' * len(obj))
# 
#         elif isinstance(obj, enum.Enum):
#             obj = obj.name
#             sig = 's'
# 
#         elif isinstance(obj, np.ndarray):
#             dtype = obj.dtype.kind
#             if dtype == 'f':
#                 dtype = 'd'
#             sig = 'a' * obj.ndim + dtype
#             obj = obj.tolist()
# 
#         elif isinstance(obj, tuple):
#             tmp = []
#             sig = '('
# 
#             for item in obj:
#                 if item is None and use_variant:
#                     continue
#                 # struct of all items
#                 r_obj, r_sig = dbus_prepare(item)
#                 if r_obj is None:
#                     continue
#                 sig += r_sig
#                 tmp.append(r_obj)
#             if len(tmp) > 0:
#                 sig += ')'
#                 obj = tuple(tmp)
#             else:
#                 sig = ''
#                 obj = None
# 
#         elif isinstance(obj, list):
#             tmp = []
#             sig = 'a'
#             is_variant = use_variant or _check_variance(obj)
# 
#             for item in obj:
#                 if item is None and is_variant:
#                     continue
#                 r_obj, r_sig = dbus_prepare(item, variant=is_variant)
#                 if r_obj is None:
#                     continue
# 
#                 tmp.append(r_obj)
# 
#             if is_variant:
#                 sig += 'v'
#             else:
#                 sig += dbus_prepare(tmp[0])[1]
# 
#             obj = tmp
# 
#         elif isinstance(obj, (dict, frozendict)):
#             if isinstance(obj, frozendict):
#                 tmp = {}
#             else:
#                 tmp = obj.__class__()
#             sig = 'a{s'
#             vals = [x for x in obj.values() if x is not None]
#             is_variant = use_variant or _check_variance(vals)
# 
#             for k, v in obj.items():
#                 if v is None:
#                     continue
#                 r_obj, r_sig = dbus_prepare(v)
#                 if r_obj is None:
#                     continue
#                 if camel_keys:
#                     k = snake_to_camel(k)
#                 if is_variant:
#                     tmp[k] = Variant(r_sig, r_obj)
#                 else:
#                     tmp[k] = r_obj
# 
#             if is_variant:
#                 sig += 'v'
#             else:
#                 sig += dbus_prepare(vals[0])[1]
# 
#             obj = tmp
#             sig += '}'
# 
#         elif isinstance(obj, type):
#             obj = obj.__name__
#             sig = 's'
# 
#     except Exception as err:
#         logger.exception('obj: %s  sig: %s variant: %s', obj, sig, variant, exc_info=err)
#         raise
# 
#     return obj, sig


# Path: tests/test_dbus_utils.py
import sys
from enum import Enum
from gi.repository import GLib
from grapefruit import Color
from traitlets import Int
from uchroma.dbus_utils import dbus_prepare


def test_primitives():
    assert dbus_prepare(23)[1] == 'n'
    assert dbus_prepare(65536)[1] == 'i'
    assert dbus_prepare(sys.maxsize)[1] == 'x'
    assert dbus_prepare(False)[1] == 'b'
    assert dbus_prepare(23.0)[1] == 'd'
    assert dbus_prepare('asdf')[1] == 's'

def test_special_types():
    obj, sig = dbus_prepare(Color.NewFromHtml('black'))
    assert obj == '#000000'
    assert sig == 's'

    obj, sig = dbus_prepare(Int(5))
    assert isinstance(obj, dict)
    assert sig == 'a{sv}'
    for value in obj.values():
        assert isinstance(value, GLib.Variant)

    obj, sig = dbus_prepare(EnumTest)
    assert isinstance(obj, tuple)
    assert sig == '(sss)'

def test_dicts():
    simple = {'first': 1, 'second': 2}
    obj, sig = dbus_prepare(simple)
    assert obj == simple
    assert sig == 'a{sn}'


    obj, sig = dbus_prepare(simple, variant=True)