2

I am facing a problem while formating the SPARQL query using python code. There error shown is:

  'SELECT Distinct ?article ?item ?itemLabel ?itemDescription (GROUP_CONCAT(DISTINCT(?entity_type); separator = ", ") AS ?entity_type_list) ?main_category ?wikipediaLabel (GROUP_CONCAT(DISTINCT(?altLabel); separator = ", ") AS ?altLabel_list)  WHERE'
                                                                                                                                                                                                                                                            ^
SyntaxError: f-string: single '}' is not allowed

I don't know what I am missing, can anyone help?

def search_wikidata_label(label,lang='ar'):
  sparql_query = (
'SELECT Distinct ?article ?item ?itemLabel ?itemDescription (GROUP_CONCAT(DISTINCT(?entity_type); separator = ", ") AS ?entity_type_list) ?main_category ?wikipediaLabel (GROUP_CONCAT(DISTINCT(?altLabel); separator = ", ") AS ?altLabel_list)  WHERE'
'{SERVICE wikibase:mwapi' 
         '{ bd:serviceParam wikibase:api "EntitySearch". bd:serviceParam wikibase:endpoint "www.wikidata.org".'
             f'bd:serviceParam mwapi:search "{label}".'
          f'bd:serviceParam mwapi:language "{lang}" .'
          '?item wikibase:apiOutputItem mwapi:item .'
          '?num wikibase:apiOrdinal true .}'
   '?item wdt:P31 ?entity_type .'
 'MINUS { ?item wdt:P31 wd:Q4167410}'
'OPTIONAL{    ?item wdt:P910 ?main_category}'
 'OPTIONAL { ?item skos:altLabel ?altLabel .' 
           f'FILTER (lang(?altLabel) = "{lang}") }'
 'OPTIONAL{ ?article schema:about ?item;schema:isPartOf <https://ar.wikipedia.org/>;schema:name ?wikipediaLabel}'
 'OPTIONAL{ ?article schema:about ?item;schema:isPartOf <https://ar.wikipedia.org/>;schema:name ?wikipediaLabel}'
 'SERVICE wikibase:label {'
  f'bd:serviceParam wikibase:language "{lang}" .}'
'}'
 'GROUP BY ?article ?item ?itemLabel ?itemDescription  ?main_category ?wikipediaLabel' 
    )
  #to query another endpoint, change the URL for the service and the query
  sparql_service_url = "https://query.wikidata.org/sparql"
  result_table = query_wikidata(sparql_query, sparql_service_url)
  return result_table
Mai
  • 121
  • 1
  • 10

3 Answers3

3

Because SPARQL uses { and } in its syntax, and because you're only substituting a very few values, I'd avoid f-strings and use a template string because that uses $ to introduce variables. More info see https://docs.python.org/3/library/string.html?highlight=string#template-strings.

If you really want to stick with f-string then double up where you want a literal { or } in the result to be {{ or }} - but that's pretty horrible especially if you ever want to update the query. Using template strings you don't have to modify the query in any way other than putting $ variable references in.

Also, I find making the template variables uppercase makes them easier to spot in the template, for example (I also tidied up your SPARQL layout):

import string

sparql_query = '''
SELECT Distinct
    ?article
    ?item
    ?itemLabel
    ?itemDescription
    (GROUP_CONCAT(DISTINCT(?entity_type); separator = ", ") AS ?entity_type_list)
    ?main_category
    ?wikipediaLabel
    (GROUP_CONCAT(DISTINCT(?altLabel); separator = ", ") AS ?altLabel_list)
WHERE {
    SERVICE wikibase:mwapi
        {
            bd:serviceParam wikibase:api "EntitySearch". bd:serviceParam wikibase:endpoint "www.wikidata.org" .
            bd:serviceParam mwapi:search "$LABEL" .
            bd:serviceParam mwapi:language "$LANG" .
            ?item wikibase:apiOutputItem mwapi:item .
            ?num wikibase:apiOrdinal true .
        }
    ?item wdt:P31 ?entity_type .
    MINUS { ?item wdt:P31 wd:Q4167410}
    OPTIONAL {
            ?item wdt:P910 ?main_category
        }
    OPTIONAL {
            ?item skos:altLabel ?altLabel .
            f'FILTER (lang(?altLabel) = "$LANG")
       }
    OPTIONAL {
            ?article schema:about ?item;schema:isPartOf <https://ar.wikipedia.org/>;schema:name ?wikipediaLabel
        }
    OPTIONAL {
            ?article schema:about ?item;schema:isPartOf <https://ar.wikipedia.org/>;schema:name ?wikipediaLabel
        }
    SERVICE wikibase:label {
            bd:serviceParam wikibase:language "$LANG" .
        }
    }
GROUP BY ?article ?item ?itemLabel ?itemDescription  ?main_category ?wikipediaLabel
'''

result = string.Template(sparql_query).substitute( LANG="ENGLISH", LABEL="MYLABEL" )

print( result )

result:

SELECT Distinct
    ?article
    ?item
    ?itemLabel
    ?itemDescription
    (GROUP_CONCAT(DISTINCT(?entity_type); separator = ", ") AS ?entity_type_list)
    ?main_category
    ?wikipediaLabel
    (GROUP_CONCAT(DISTINCT(?altLabel); separator = ", ") AS ?altLabel_list)
WHERE {
    SERVICE wikibase:mwapi
        {
            bd:serviceParam wikibase:api "EntitySearch". bd:serviceParam wikibase:endpoint "www.wikidata.org" .
            bd:serviceParam mwapi:search "MYLABEL" .
            bd:serviceParam mwapi:language "ENGLISH" .
            ?item wikibase:apiOutputItem mwapi:item .
            ?num wikibase:apiOrdinal true .
        }
    ?item wdt:P31 ?entity_type .
    MINUS { ?item wdt:P31 wd:Q4167410}
    OPTIONAL {
            ?item wdt:P910 ?main_category
        }
    OPTIONAL {
            ?item skos:altLabel ?altLabel .
            f'FILTER (lang(?altLabel) = "ENGLISH")
       }
    OPTIONAL {
            ?article schema:about ?item;schema:isPartOf <https://ar.wikipedia.org/>;schema:name ?wikipediaLabel
        }
    OPTIONAL {
            ?article schema:about ?item;schema:isPartOf <https://ar.wikipedia.org/>;schema:name ?wikipediaLabel
        }
    SERVICE wikibase:label {
            bd:serviceParam wikibase:language "ENGLISH" .
        }
    }
GROUP BY ?article ?item ?itemLabel ?itemDescription  ?main_category ?wikipediaLabel
  • Thank you, man, also I find a workaround method, but your answer is what I was looking for. – Mai Nov 01 '21 at 21:29
1

If you want to have an escaped curly bracket, you need to double them up:

>>> lang = "lang"
>>> f'FILTER (lang(?altLabel) = "{lang}") }}'
'FILTER (lang(?altLabel) = "lang") }'
>>> f'FILTER (lang(?altLabel) = "{lang}") }'
  File "<stdin>", line 1
SyntaxError: f-string: single '}' is not allowed

Check: How can I print literal curly-brace characters in a string and also use .format on it?

Tzane
  • 2,752
  • 1
  • 10
  • 21
0

When it comes to python f-string and for dynamic sparql-queries, I searched but did not find any good documentation. Hence this might help someone:

  • use single curly braces around python variables {py_var} provided sparql expect eval(py_var)
  • use double curly braces {{}} around sparql-code {{?uri cpmeta:hasName ?name}} provided sparql expect curly braces {?uri cpmeta:hasName ?name }
  • use triple curly braces {{{py_var}}} provided sparql expect {eval(py_var)}