3

I'm using Google's PageSpeed Insights API in Python, and I've come across a baffling issue. The API presents me with a format string and arguments to that format string, and I need to figure out how to actually format the string. The problem is that the arguments are given in a really odd fashion.

This is how the arguments to the format string are presented to me (I'm showing it as an assignment to make it clearer):

args = [
    {
        'type':  'INT_LITERAL', 
        'value': '21', 
        'key':   'NUM_SCRIPTS'
    }, 
    {
        'type':  'INT_LITERAL', 
        'value': '20', 
        'key':   'NUM_CSS'
    }
]

And this is a sample format string, also given to me by the API:

format = 'Your page has {{NUM_SCRIPTS}} blocking script resources and {{NUM_CSS}} blocking CSS resources. This causes a delay in rendering your page.'

I know that sometimes people like to avoid answering the question asked and instead offer an answer that is tailored to their beliefs about coding 'right' and 'wrong,' so to reiterate, I am given both the arguments and the format string by the API. I am not creating them myself, so I can't do this in a more straightforward fashion.

What I need to know is how to extract the key field of the dicts in the args list so that I can use them with "".format in a way that will let me pass the value field to the named arguments.

I apologize if this is somehow super obvious; I'm fairly new to Python and I don't know a lot about the little details. I did do my due diligence and search for an answer before asking, but I didn't find anything and it's not an easily searchable problem.

EDIT: I thought maybe this 'list of dicts' thing was commonplace with Google's APIs or something, that maybe there was an automatic way to associate the arguments (like string.format_map). I ended up just doing it the simple way, without string.format.

for x in args:
    format = format.replace('{{' + x['key'] + '}}', x['value'])
bpunsky
  • 73
  • 8
  • Is this what you want? for NUM_CSS `(item for item in args if item["key"] == "NUM_CSS").next()['value']` and for NUM_SCRIPTS `(item for item in args if item["key"] == "NUM_SCRIPTS").next()['value']` – Rohanil Feb 27 '17 at 02:24
  • That list is, frankly, nonsense. Your goal is to look up values for `NUM_SCRIPTS` and `NUM_CSS`, but that list only allows you to search it for values for a certain key for which there's a different key with another value. It should be `args = {'NUM_SCRIPTS':21, 'NUM_CSS':20}` to be even remotely useful without a whole bunch of bad code. – TigerhawkT3 Feb 27 '17 at 02:26
  • @Rohanil - No, different format strings have different types and numbers of variables, I can't hard code the names. – bpunsky Feb 28 '17 at 02:00
  • @TigerhawkT3 I know it's nonsense. That's what the API gives me, so that's what I have to work with. If I could do it differently, I would. It looks like I could have to write out long, dirty if-elif-else chains to handle different PageSpeed rules manually, which is hardly future-proof. – bpunsky Feb 28 '17 at 02:01

2 Answers2

2

If your format string is like so:

fmt = 'blah {NUM_SCRIPTS} foo {NUM_CSS} bar'

And you have a dictionary of keys and values called args, then you can format the string using the dictionary as so:

fmt.format(**arg)

This would print:

"blah 21 foo 20 bar"
  • Except the values are stored in separate dicts inside a list, not necessarily ordered, so I need to somehow use the key field of each dict to associate it with the named argument. – bpunsky Feb 28 '17 at 00:52
0

Assuming your format string only had one set of brackets, you could do:

format_dict = {}
for d in args:
    format_dict[d['key']] = d['value']

str_to_format = 'Your page has {NUM_SCRIPTS} blocking script resources and {NUM_CSS} blocking CSS resources. This causes a delay in rendering your page.'

formatted = str_to_format.format(**format_dict)

print(formatted)
# Your page has 21 blocking script resources and 20 blocking CSS resources. This causes a delay in rendering your page.

but as format is currently written (with two brackets per substitution variable), I'm not sure how to get format() to play nice. If you want brackets to surround each substitution variable after the substitution variable has been made, you need an extra set of brackets:

format_dict = {}
for d in args:
    format_dict[d['key']] = d['value']

str_to_format = 'Your page has {{{NUM_SCRIPTS}}} blocking script resources and {{{NUM_CSS}}} blocking CSS resources. This causes a delay in rendering your page.'

formatted = str_to_format.format(**format_dict)

print(formatted)
# Your page has {21} blocking script resources and {20} blocking CSS resources. This causes a delay in rendering your page.

So, depending on your requirements, either add or remove a set of brackets. To add brackets, you could do something like this

import re
str_to_format = re.sub('{{|}}', lambda x: x.group(0) + x.group(0)[0], str_to_format)
Community
  • 1
  • 1
Crispin
  • 2,070
  • 1
  • 13
  • 16