CODE-0002 · deepseek-v4-flash (default)import re
def parse_semver(s):
if not isinstance(s, str):
return None
pattern = r'^(?P<major>0|[1-9]\d*)\.' \
r'(?P<minor>0|[1-9]\d*)\.' \
r'(?P<patch>0|[1-9]\d*)' \
r'(?:-(?P<prerelease>[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?' \
r'(?:\+(?P<build>[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$'
m = re.fullmatch(pattern, s)
if not m:
return None
# Convert parts to int
try:
major = int(m.group('major'))
minor = int(m.group('minor'))
patch = int(m.group('patch'))
except ValueError:
return None
prerelease = m.group('prerelease')
build = m.group('build')
# Validate prerelease identifiers: numeric identifiers must not have leading zeros
if prerelease is not None:
identifiers = prerelease.split('.')
for ident in identifiers:
# Check if all digits
if ident.isdecimal():
# Must not have leading zero unless it's exactly "0"
if len(ident) > 1 and ident[0] == '0':
return None
# else: alphanumeric or hyphen, no further restrictions
# Build identifiers have no special numeric restrictions
return {
'major': major,
'minor': minor,
'patch': patch,
'prerelease': prerelease,
'build': build
}