update-error-constants.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #!/usr/bin/env python
  2. from __future__ import print_function, absolute_import
  3. import operator
  4. import os.path
  5. import sys
  6. import xml.etree.ElementTree as ET
  7. BUILD_SOURCE_FILE = os.path.join("src", "lxml", "xmlerror.pxi")
  8. BUILD_DEF_FILE = os.path.join("src", "lxml", "includes", "xmlerror.pxd")
  9. # map enum name to Python variable name and alignment for constant name
  10. ENUM_MAP = {
  11. 'xmlErrorLevel' : ('__ERROR_LEVELS', 'XML_ERR_'),
  12. 'xmlErrorDomain' : ('__ERROR_DOMAINS', 'XML_FROM_'),
  13. 'xmlParserErrors' : ('__PARSER_ERROR_TYPES', 'XML_'),
  14. # 'xmlXPathError' : ('__XPATH_ERROR_TYPES', ''),
  15. # 'xmlSchemaValidError' : ('__XMLSCHEMA_ERROR_TYPES', 'XML_'),
  16. 'xmlRelaxNGValidErr' : ('__RELAXNG_ERROR_TYPES', 'XML_'),
  17. }
  18. ENUM_ORDER = (
  19. 'xmlErrorLevel',
  20. 'xmlErrorDomain',
  21. 'xmlParserErrors',
  22. # 'xmlXPathError',
  23. # 'xmlSchemaValidError',
  24. 'xmlRelaxNGValidErr')
  25. COMMENT = """
  26. # This section is generated by the script '%s'.
  27. """ % os.path.basename(sys.argv[0])
  28. def split(lines):
  29. lines = iter(lines)
  30. pre = []
  31. for line in lines:
  32. pre.append(line)
  33. if line.startswith('#') and "BEGIN: GENERATED CONSTANTS" in line:
  34. break
  35. pre.append('')
  36. old = []
  37. for line in lines:
  38. if line.startswith('#') and "END: GENERATED CONSTANTS" in line:
  39. break
  40. old.append(line.rstrip('\n'))
  41. post = ['', line]
  42. post.extend(lines)
  43. post.append('')
  44. return pre, old, post
  45. def regenerate_file(filename, result):
  46. new = COMMENT + '\n'.join(result)
  47. # read .pxi source file
  48. with open(filename, 'r', encoding="utf-8") as f:
  49. pre, old, post = split(f)
  50. if new.strip() == '\n'.join(old).strip():
  51. # no changes
  52. return False
  53. # write .pxi source file
  54. with open(filename, 'w', encoding="utf-8") as f:
  55. f.write(''.join(pre))
  56. f.write(new)
  57. f.write(''.join(post))
  58. return True
  59. def parse_enums(doc_dir, api_filename, enum_dict):
  60. tree = ET.parse(os.path.join(doc_dir, api_filename))
  61. for enum in tree.iterfind('symbols/enum'):
  62. enum_type = enum.get('type')
  63. if enum_type not in ENUM_MAP:
  64. continue
  65. entries = enum_dict.get(enum_type)
  66. if not entries:
  67. print("Found enum", enum_type)
  68. entries = enum_dict[enum_type] = []
  69. entries.append((
  70. enum.get('name'),
  71. int(enum.get('value')),
  72. enum.get('info', '').strip(),
  73. ))
  74. def main(doc_dir):
  75. enum_dict = {}
  76. parse_enums(doc_dir, 'libxml2-api.xml', enum_dict)
  77. #parse_enums(doc_dir, 'libxml-xmlerror.html', enum_dict)
  78. #parse_enums(doc_dir, 'libxml-xpath.html', enum_dict)
  79. #parse_enums(doc_dir, 'libxml-xmlschemas.html', enum_dict)
  80. #parse_enums(doc_dir, 'libxml-relaxng.html', enum_dict)
  81. # regenerate source files
  82. pxi_result = []
  83. append_pxi = pxi_result.append
  84. pxd_result = []
  85. append_pxd = pxd_result.append
  86. append_pxd('cdef extern from "libxml/xmlerror.h":')
  87. ctypedef_indent = ' '*4
  88. constant_indent = ctypedef_indent*2
  89. for enum_name in ENUM_ORDER:
  90. constants = enum_dict[enum_name]
  91. constants.sort(key=operator.itemgetter(1))
  92. pxi_name, prefix = ENUM_MAP[enum_name]
  93. append_pxd(ctypedef_indent + 'ctypedef enum %s:' % enum_name)
  94. append_pxi('cdef object %s = """\\' % pxi_name)
  95. prefix_len = len(prefix)
  96. length = 2 # each string ends with '\n\0'
  97. for name, val, descr in constants:
  98. if descr and descr != str(val):
  99. line = '%-50s = %7d # %s' % (name, val, descr)
  100. else:
  101. line = '%-50s = %7d' % (name, val)
  102. append_pxd(constant_indent + line)
  103. if name[:prefix_len] == prefix and len(name) > prefix_len:
  104. name = name[prefix_len:]
  105. line = '%s=%d' % (name, val)
  106. append_pxi(line)
  107. length += len(line) + 2 # + '\n\0'
  108. append_pxd('')
  109. append_pxi('"""')
  110. append_pxi('')
  111. # write source files
  112. print("Updating file %s" % BUILD_SOURCE_FILE)
  113. updated = regenerate_file(BUILD_SOURCE_FILE, pxi_result)
  114. if not updated:
  115. print("No changes.")
  116. print("Updating file %s" % BUILD_DEF_FILE)
  117. updated = regenerate_file(BUILD_DEF_FILE, pxd_result)
  118. if not updated:
  119. print("No changes.")
  120. print("Done")
  121. if __name__ == "__main__":
  122. if len(sys.argv) < 2 or sys.argv[1].lower() in ('-h', '--help'):
  123. print("This script generates the constants in file %s" % BUILD_SOURCE_FILE)
  124. print("Call as")
  125. print(sys.argv[0], "/path/to/libxml2-doc-dir")
  126. sys.exit(len(sys.argv) > 1)
  127. main(sys.argv[1])