Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camel killer box without data mangling #279

Open
jacobtomlinson opened this issue Sep 17, 2024 · 2 comments
Open

Camel killer box without data mangling #279

jacobtomlinson opened this issue Sep 17, 2024 · 2 comments
Labels

Comments

@jacobtomlinson
Copy link

jacobtomlinson commented Sep 17, 2024

The camel killer box is a great feature. I work a lot with data structures from Kubernetes which uses camel case everywhere, and it's really nice to be able to access them via snake case attributes.

>>> from box import Box
>>> data = Box({"loadBalancer": "foo"}, camel_killer_box=True)
>>> data.load_balancer
'foo'

I need to be able to modify these data structures and then send them back to the Kubernetes API. Unfortunately because Box defaults to destructively updating the underlying dictionary to use snake case there is no way for me to get a data structure out that the Kubernetes API will understand.

>>> data.load_balancer = "bar"
>>> data.to_dict()
{'load_balancer': 'bar'}  # Kubernetes wants {'loadBalancer': 'bar'}

Would it be possible to add some way to round trip a dict through a Box without mangling it, and still get the benefits of accessing snake case attributes?

@cdgriffith
Copy link
Owner

Hi @jacobtomlinson unfortunately it's named camel_killer as it does straight up destroy the original names and stores them as snake case.

You could subclass Box and add a function like to_camel_case_dict. Basic example:

from box import Box

class CamelBox(Box):

    def __init__(cls, *args, **kwargs):
        kwargs["camel_killer_box"] = True
        super().__init__(*args, **kwargs)

    def to_camel_case_dict(self, the_dict=None):
        def to_camel_case(snake_str):
            components = snake_str.split('_')
            return components[0] + ''.join(x.title() for x in components[1:])

        if not the_dict:
            the_dict = self.to_dict()

        out_dict = dict()
        for k, v in the_dict.items():
            if isinstance(v, dict):
                out_dict[to_camel_case(k)] = self.to_camel_case_dict(v)
            else:
                out_dict[to_camel_case(k)] = v
        return out_dict

Resulting in:

my_dict = test_dict = {
   "key1": "value1",
   "BigCamel": "hi",
   "alist": [{"a": 1}],
   "lowerCamel": {"Key 3": "Value 3", "Key4": {"lowerCamelNumber2Or3Or5": "Value5"}},
}


camel_box = CamelBox(my_dict)

print(camel_box.to_dict())
# {'key1': 'value1', 'big_camel': 'hi', 'alist': [{'a': 1}], 'lower_camel': {'key 3': 'Value 3', 'key4': {'lower_camel_number2_or3_or5': 'Value5'}}}

print(camel_box.to_camel_case_dict())
# {'key1': 'value1', 'bigCamel': 'hi', 'alist': [{'a': 1}], 'lowerCamel': {'key 3': 'Value 3', 'key4': {'lowerCamelNumber2Or3Or5': 'Value5'}}}

@jacobtomlinson
Copy link
Author

Thanks for the response @cdgriffith.

The trouble with that approach is you assume that everything should be camel case. I expect in these large user provided data structures there will be a mix of both. The example you have converts everything to camel case. So the data just gets mangled in a different way.

my_dict = {
    "BigCamel": "hi",
    "sneaky_snake": "hisss, I'm already snakey",
}

print(CamelBox(my_dict).to_camel_case_dict())
# {'bigCamel': 'hi', 'sneakySnake': "hisss, I'm already snakey"}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants