1#!/usr/bin/env python3 2""" 3Utility for parsing HTML5 entity definitions available from: 4 5 http://dev.w3.org/html5/spec/entities.json 6 7Written by Ezio Melotti and Iuliia Proskurnia. 8 9""" 10 11import os 12import sys 13import json 14from urllib.request import urlopen 15from html.entities import html5 16 17entities_url = 'http://dev.w3.org/html5/spec/entities.json' 18 19def get_json(url): 20 """Download the json file from the url and returns a decoded object.""" 21 with urlopen(url) as f: 22 data = f.read().decode('utf-8') 23 return json.loads(data) 24 25def create_dict(entities): 26 """Create the html5 dict from the decoded json object.""" 27 new_html5 = {} 28 for name, value in entities.items(): 29 new_html5[name.lstrip('&')] = value['characters'] 30 return new_html5 31 32def compare_dicts(old, new): 33 """Compare the old and new dicts and print the differences.""" 34 added = new.keys() - old.keys() 35 if added: 36 print('{} entitie(s) have been added:'.format(len(added))) 37 for name in sorted(added): 38 print(' {!r}: {!r}'.format(name, new[name])) 39 removed = old.keys() - new.keys() 40 if removed: 41 print('{} entitie(s) have been removed:'.format(len(removed))) 42 for name in sorted(removed): 43 print(' {!r}: {!r}'.format(name, old[name])) 44 changed = set() 45 for name in (old.keys() & new.keys()): 46 if old[name] != new[name]: 47 changed.add((name, old[name], new[name])) 48 if changed: 49 print('{} entitie(s) have been modified:'.format(len(changed))) 50 for item in sorted(changed): 51 print(' {!r}: {!r} -> {!r}'.format(*item)) 52 53def write_items(entities, file=sys.stdout): 54 """Write the items of the dictionary in the specified file.""" 55 # The keys in the generated dictionary should be sorted 56 # in a case-insensitive way, however, when two keys are equal, 57 # the uppercase version should come first so that the result 58 # looks like: ['Aacute', 'aacute', 'Aacute;', 'aacute;', ...] 59 # To do this we first sort in a case-sensitive way (so all the 60 # uppercase chars come first) and then sort with key=str.lower. 61 # Since the sorting is stable the uppercase keys will eventually 62 # be before their equivalent lowercase version. 63 keys = sorted(entities.keys()) 64 keys = sorted(keys, key=str.lower) 65 print('html5 = {', file=file) 66 for name in keys: 67 print(' {!r}: {!a},'.format(name, entities[name]), file=file) 68 print('}', file=file) 69 70 71if __name__ == '__main__': 72 # without args print a diff between html.entities.html5 and new_html5 73 # with --create print the new html5 dict 74 # with --patch patch the Lib/html/entities.py file 75 new_html5 = create_dict(get_json(entities_url)) 76 if '--create' in sys.argv: 77 print('# map the HTML5 named character references to the ' 78 'equivalent Unicode character(s)') 79 print('# Generated by {}. Do not edit manually.'.format(__file__)) 80 write_items(new_html5) 81 elif '--patch' in sys.argv: 82 fname = 'Lib/html/entities.py' 83 temp_fname = fname + '.temp' 84 with open(fname) as f1, open(temp_fname, 'w') as f2: 85 skip = False 86 for line in f1: 87 if line.startswith('html5 = {'): 88 write_items(new_html5, file=f2) 89 skip = True 90 continue 91 if skip: 92 # skip the old items until the } 93 if line.startswith('}'): 94 skip = False 95 continue 96 f2.write(line) 97 os.remove(fname) 98 os.rename(temp_fname, fname) 99 else: 100 if html5 == new_html5: 101 print('The current dictionary is updated.') 102 else: 103 compare_dicts(html5, new_html5) 104 print('Run "./python {0} --patch" to update Lib/html/entities.html ' 105 'or "./python {0} --create" to see the generated ' 'dictionary.'.format(__file__)) 106