36

Python Version 3.5

I'm trying to make an API call to configure a device using json as the format. Some of the json will vary depending on the desired naming, so I need to call a variable in the string. I am able to accomplish this using the old style %s... % (variable), but not with the new style {}... .format(variable).

Failed EX:

(Testing with {"fvAp":{"attributes":{"name":(variable)}}})

a = "\"app-name\""

app_config = ''' { "fvAp": { "attributes": { "name": {} }, "children": [ { "fvAEPg": { "attributes": { "name": "app" }, "children": [ { "fvRsBd": { "attributes": { "tnFvBDName": "default" }, } } ] } }, { "fvAEPg": { "attributes": { "name": "db" }, "children": [ { "fvRsBd": { "attributes": { "tnFvBDName": "default" }, } } ] } } ] } } '''.format(a)

print(app_config)

Traceback (most recent call last): File "C:/..., line 49, in '''.format('a') KeyError: '\n "fvAp"'

Working EX:

a = "\"app-name\""

app_config = ''' { "fvAp": { "attributes": { "name": %s }, "children": [ { "fvAEPg": { "attributes": { "name": "app" }, "children": [ { "fvRsBd": { "attributes": { "tnFvBDName": "default" }, } } ] } }, { "fvAEPg": { "attributes": { "name": "db" }, "children": [ { "fvRsBd": { "attributes": { "tnFvBDName": "default" }, } } ] } } ] } } ''' % a

print(app_config)

How do I get this to work using str.format method?

martineau
  • 119,623
  • 25
  • 170
  • 301
mcgoo298
  • 373
  • 1
  • 3
  • 4
  • Possible duplicate of [How can I print a literal "{}" characters in python string and also use .format on it?](http://stackoverflow.com/questions/5466451/how-can-i-print-a-literal-characters-in-python-string-and-also-use-format) – Simon MᶜKenzie Oct 10 '16 at 01:44

2 Answers2

59

Format String Syntax section says:

Format strings contain “replacement fields” surrounded by curly braces {}. Anything that is not contained in braces is considered literal text, which is copied unchanged to the output. If you need to include a brace character in the literal text, it can be escaped by doubling: {{ and }}.

So if you want to use .format method, you need to escape all JSON curly braces in your template string:

>>> '{{"fvAp": {{"attributes": {{"name": {}}}}}}}'.format('"app-name"')
'{"fvAp": {"attributes": {"name": "app-name"}}}'

That looks really bad.

There's a better way to do that with string.Template:

>>> from string import Template
>>> t = Template('{"fvAp": {"attributes": {"name": "${name}"}}')
>>> t.substitute(name='StackOverflow')
'{"fvAp": {"attributes": {"name": "StackOverflow"}}'

Though I suggest abandoning the idea of generating configs this way altogether and using a factory function and json.dumps instead:

>>> import json
>>> def make_config(name):
...     return {'fvAp': {'attributes': {'name': name}}}
>>> app_config = make_config('StackOverflow')
>>> json.dumps(app_config)
'{"fvAp": {"attributes": {"name": "StackOverflow"}}}'
skovorodkin
  • 9,394
  • 1
  • 39
  • 30
0

Looks like you are trying to substitute dynamic values within a json file with their runtime counterparts. You can use PLACEHOLDER in this scenario. Construct your json using PLACEHOLDER values where you would like to substitute them with dynamic values.

For example:

{
    "services": [{
            "name": "service_a",
            "config": {
                "dynamic_value_1": "PLACEHOLDER",
                "dynamic_value_2": "PLACEHOLDER"
            }
        },
        {
            "name": "service_b",
            "config": {
                "dynamic_value_1": "PLACEHOLDER",
                "dynamic_value_2": "PLACEHOLDER"
            }
        }
    ]
}

You can then read the json file and perform an update by passing a dictionary of values you want to substitute.


def dynamic_values() -> Dict:
        """
        Provides a map of placeholder values with their dynamic counterparts
        """
        return {
            "service_a": {
                "dynamic_value_1": service_a_method_1(),
                "dynamic_value_2": service_a_method_2(),
            },
            "service_b": {
                "dynamic_value_1": service_b_method_1(),
                "dynamic_value_2": service_b_method_1(),
            },
        }


def load_dynamic_values(dynamic_values) -> List:
        """
        Overrides placeholder values in json file with dynamic values at runtime
        """
        with open(file_name, "r") as f:
            data = json.load(f)

        services = data["services"]
        for s in services:
            override = dynamic_values.get(s["name"], None)
            if override:
                s["config"].update(override)
        return services