您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

136 行
4.6KB

  1. import logging
  2. import re
  3. from typing import Optional, TypeAlias
  4. from xml.etree import ElementTree
  5. from xmlutils import pom_namespace as ns, find_tag_text
  6. logger = logging.getLogger(__name__)
  7. Properties: TypeAlias = dict[str, Optional[str]]
  8. class PropertyMissing(Exception):
  9. def __init__(self, prop: str):
  10. self.prop = prop
  11. class PackageError(Exception):
  12. pass
  13. class PackagePOM:
  14. name: str
  15. is_bom: bool
  16. parent: Optional[str]
  17. group_id: Optional[str]
  18. artifact_id: Optional[str]
  19. version: Optional[str]
  20. properties: Properties
  21. _raw_root: ElementTree.Element
  22. def __init__(self, name: str, pom: str):
  23. logger.debug(f'{name}: Parsing POM')
  24. self.name = name
  25. self._raw_root = ElementTree.fromstring(pom)
  26. self.group_id, self.artifact_id, self.version = name.split(':')
  27. if self.group_id is None or self.artifact_id is None or self.version is None:
  28. logger.warning(
  29. f'{name}: One of groupId={self.group_id}, artifactId={self.artifact_id}, version={self.version} was '
  30. f'None. This can cause issues with dependency resolution'
  31. )
  32. self.parent = None
  33. if (parent_tag := self._raw_root.find('parent', ns)) is not None:
  34. parent_group = find_tag_text(parent_tag, 'groupId')
  35. parent_artifact = find_tag_text(parent_tag, 'artifactId')
  36. parent_version = find_tag_text(parent_tag, 'version')
  37. if parent_group is None or parent_artifact is None or parent_version is None:
  38. raise PackageError(f'Invalid parent {parent_group}:{parent_artifact}:{parent_version}')
  39. else:
  40. self.parent = f'{parent_group}:{parent_artifact}:{parent_version}'
  41. if (packaging := self._raw_root.find('packaging', ns)) is not None:
  42. self.is_bom = packaging == 'pom'
  43. else:
  44. self.is_bom = False
  45. self.set_properties({})
  46. @property
  47. def dependency_management(self) -> list[str]:
  48. return [
  49. self._package_from_xml_dep(dep)
  50. for dep in self._raw_root.find('dependencyManagement/dependencies', ns) or []
  51. ]
  52. def set_properties(self, parent_properties: Optional[Properties]):
  53. logger.debug(f'{self.name}: Parsing properties')
  54. props: Properties = parent_properties or {}
  55. for prop_tag in self._raw_root.findall('.//properties/*', ns):
  56. prop = prop_tag.tag.replace(f'{{{ns[""]}}}', '')
  57. value = prop_tag.text if prop_tag.text is not None else ''
  58. logger.debug(f'{self.name}: Setting prop {prop}={value}')
  59. props[prop] = value
  60. changed = True
  61. while changed:
  62. changed = False
  63. for prop, value in props.items():
  64. new_value = self._prop_replace(value, props, True)
  65. if new_value != value:
  66. changed = True
  67. logger.debug(f'{self.name}: Setting prop {prop}={new_value}')
  68. props[prop] = new_value
  69. self.properties = props
  70. def _prop_replace(self, text, props: Optional[Properties] = None, quiet: bool = False) -> str:
  71. def lookup_prop(match) -> str:
  72. prop = match.group(1)
  73. if prop == 'project.groupId':
  74. value = str(self.group_id)
  75. elif prop == 'project.artifactId':
  76. value = str(self.artifact_id)
  77. elif prop == 'project.version':
  78. value = str(self.version)
  79. elif prop.startswith('project.build') or prop.startswith('env.') or prop.startswith('maven.'):
  80. value = None
  81. elif prop in ['project.basedir', 'basedir', 'user.home', 'debug.port']:
  82. value = None
  83. else:
  84. try:
  85. value = props[prop] if props is not None else self.properties[prop]
  86. except KeyError:
  87. value = None
  88. if value is None and not quiet:
  89. raise PropertyMissing(prop)
  90. else:
  91. logger.debug(f'{self.name}: Replacing property {prop} with {value}')
  92. return value
  93. return re.sub(
  94. r'\$\{([^}]*)}',
  95. lookup_prop,
  96. text,
  97. )
  98. def _package_from_xml_dep(self, dep: ElementTree.Element) -> str:
  99. def prop_replace_tag(tag) -> str:
  100. return self._prop_replace(
  101. elem.text or '' if (elem := dep.find(tag, ns)) is not None else '',
  102. )
  103. return f"{prop_replace_tag('groupId')}:{prop_replace_tag('artifactId')}:{prop_replace_tag('version')}"