gh-103977: compile re expressions in platform.py only if required (#103981)

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
Pieter Eendebak 2023-04-30 17:36:19 +02:00 committed by GitHub
parent ed95e8cbd4
commit f186557dc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 36 deletions

View File

@ -136,11 +136,11 @@ _ver_stages = {
'pl': 200, 'p': 200, 'pl': 200, 'p': 200,
} }
_component_re = re.compile(r'([0-9]+|[._+-])')
def _comparable_version(version): def _comparable_version(version):
component_re = re.compile(r'([0-9]+|[._+-])')
result = [] result = []
for v in _component_re.split(version): for v in component_re.split(version):
if v not in '._+-': if v not in '._+-':
try: try:
v = int(v, 10) v = int(v, 10)
@ -152,11 +152,6 @@ def _comparable_version(version):
### Platform specific APIs ### Platform specific APIs
_libc_search = re.compile(b'(__libc_init)'
b'|'
b'(GLIBC_([0-9.]+))'
b'|'
br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
def libc_ver(executable=None, lib='', version='', chunksize=16384): def libc_ver(executable=None, lib='', version='', chunksize=16384):
@ -190,6 +185,12 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
# sys.executable is not set. # sys.executable is not set.
return lib, version return lib, version
libc_search = re.compile(b'(__libc_init)'
b'|'
b'(GLIBC_([0-9.]+))'
b'|'
br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
V = _comparable_version V = _comparable_version
# We use os.path.realpath() # We use os.path.realpath()
# here to work around problems with Cygwin not being # here to work around problems with Cygwin not being
@ -200,7 +201,7 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
pos = 0 pos = 0
while pos < len(binary): while pos < len(binary):
if b'libc' in binary or b'GLIBC' in binary: if b'libc' in binary or b'GLIBC' in binary:
m = _libc_search.search(binary, pos) m = libc_search.search(binary, pos)
else: else:
m = None m = None
if not m or m.end() == len(binary): if not m or m.end() == len(binary):
@ -247,9 +248,6 @@ def _norm_version(version, build=''):
version = '.'.join(strings[:3]) version = '.'.join(strings[:3])
return version return version
_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
r'.*'
r'\[.* ([\d.]+)\])')
# Examples of VER command output: # Examples of VER command output:
# #
@ -295,9 +293,13 @@ def _syscmd_ver(system='', release='', version='',
else: else:
return system, release, version return system, release, version
ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
r'.*'
r'\[.* ([\d.]+)\])')
# Parse the output # Parse the output
info = info.strip() info = info.strip()
m = _ver_output.match(info) m = ver_output.match(info)
if m is not None: if m is not None:
system, release, version = m.groups() system, release, version = m.groups()
# Strip trailing dots from version and release # Strip trailing dots from version and release
@ -1033,18 +1035,6 @@ def processor():
### Various APIs for extracting information from sys.version ### Various APIs for extracting information from sys.version
_sys_version_parser = re.compile(
r'([\w.+]+)\s*' # "version<space>"
r'\(#?([^,]+)' # "(#buildno"
r'(?:,\s*([\w ]*)' # ", builddate"
r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
_pypy_sys_version_parser = re.compile(
r'([\w.+]+)\s*'
r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
r'\[PyPy [^\]]+\]?')
_sys_version_cache = {} _sys_version_cache = {}
def _sys_version(sys_version=None): def _sys_version(sys_version=None):
@ -1076,10 +1066,17 @@ def _sys_version(sys_version=None):
if result is not None: if result is not None:
return result return result
sys_version_parser = re.compile(
r'([\w.+]+)\s*' # "version<space>"
r'\(#?([^,]+)' # "(#buildno"
r'(?:,\s*([\w ]*)' # ", builddate"
r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)<space>"
r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
if sys.platform.startswith('java'): if sys.platform.startswith('java'):
# Jython # Jython
name = 'Jython' name = 'Jython'
match = _sys_version_parser.match(sys_version) match = sys_version_parser.match(sys_version)
if match is None: if match is None:
raise ValueError( raise ValueError(
'failed to parse Jython sys.version: %s' % 'failed to parse Jython sys.version: %s' %
@ -1091,8 +1088,13 @@ def _sys_version(sys_version=None):
elif "PyPy" in sys_version: elif "PyPy" in sys_version:
# PyPy # PyPy
pypy_sys_version_parser = re.compile(
r'([\w.+]+)\s*'
r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
r'\[PyPy [^\]]+\]?')
name = "PyPy" name = "PyPy"
match = _pypy_sys_version_parser.match(sys_version) match = pypy_sys_version_parser.match(sys_version)
if match is None: if match is None:
raise ValueError("failed to parse PyPy sys.version: %s" % raise ValueError("failed to parse PyPy sys.version: %s" %
repr(sys_version)) repr(sys_version))
@ -1101,7 +1103,7 @@ def _sys_version(sys_version=None):
else: else:
# CPython # CPython
match = _sys_version_parser.match(sys_version) match = sys_version_parser.match(sys_version)
if match is None: if match is None:
raise ValueError( raise ValueError(
'failed to parse CPython sys.version: %s' % 'failed to parse CPython sys.version: %s' %
@ -1290,13 +1292,6 @@ def platform(aliased=False, terse=False):
### freedesktop.org os-release standard ### freedesktop.org os-release standard
# https://www.freedesktop.org/software/systemd/man/os-release.html # https://www.freedesktop.org/software/systemd/man/os-release.html
# NAME=value with optional quotes (' or "). The regular expression is less
# strict than shell lexer, but that's ok.
_os_release_line = re.compile(
"^(?P<name>[a-zA-Z0-9_]+)=(?P<quote>[\"\']?)(?P<value>.*)(?P=quote)$"
)
# unescape five special characters mentioned in the standard
_os_release_unescape = re.compile(r"\\([\\\$\"\'`])")
# /etc takes precedence over /usr/lib # /etc takes precedence over /usr/lib
_os_release_candidates = ("/etc/os-release", "/usr/lib/os-release") _os_release_candidates = ("/etc/os-release", "/usr/lib/os-release")
_os_release_cache = None _os_release_cache = None
@ -1311,10 +1306,18 @@ def _parse_os_release(lines):
"PRETTY_NAME": "Linux", "PRETTY_NAME": "Linux",
} }
# NAME=value with optional quotes (' or "). The regular expression is less
# strict than shell lexer, but that's ok.
os_release_line = re.compile(
"^(?P<name>[a-zA-Z0-9_]+)=(?P<quote>[\"\']?)(?P<value>.*)(?P=quote)$"
)
# unescape five special characters mentioned in the standard
os_release_unescape = re.compile(r"\\([\\\$\"\'`])")
for line in lines: for line in lines:
mo = _os_release_line.match(line) mo = os_release_line.match(line)
if mo is not None: if mo is not None:
info[mo.group('name')] = _os_release_unescape.sub( info[mo.group('name')] = os_release_unescape.sub(
r"\1", mo.group('value') r"\1", mo.group('value')
) )

View File

@ -0,0 +1 @@
Improve import time of :mod:`platform` module.