gh-113317: Move global utility functions into libclinic (#113986)

Establish Tools/clinic/libclinic/utils.py and move the following
functions over there:

- compute_checksum()
- create_regex()
- write_file()
This commit is contained in:
Erlend E. Aasland 2024-01-14 19:26:09 +01:00 committed by GitHub
parent 77b45fa6d0
commit 5dbcdfdeb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 56 deletions

View file

@ -16,7 +16,6 @@ import copy
import dataclasses as dc import dataclasses as dc
import enum import enum
import functools import functools
import hashlib
import inspect import inspect
import io import io
import itertools import itertools
@ -1792,21 +1791,6 @@ class CLanguage(Language):
return clinic.get_destination('block').dump() return clinic.get_destination('block').dump()
def create_regex(
before: str,
after: str,
word: bool = True,
whole_line: bool = True
) -> re.Pattern[str]:
"""Create an re object for matching marker lines."""
group_re = r"\w+" if word else ".+"
pattern = r'{}({}){}'
if whole_line:
pattern = '^' + pattern + '$'
pattern = pattern.format(re.escape(before), group_re, re.escape(after))
return re.compile(pattern)
@dc.dataclass(slots=True, repr=False) @dc.dataclass(slots=True, repr=False)
class Block: class Block:
r""" r"""
@ -1905,8 +1889,9 @@ class BlockParser:
self.language = language self.language = language
before, _, after = language.start_line.partition('{dsl_name}') before, _, after = language.start_line.partition('{dsl_name}')
assert _ == '{dsl_name}' assert _ == '{dsl_name}'
self.find_start_re = create_regex(before, after, whole_line=False) self.find_start_re = libclinic.create_regex(before, after,
self.start_re = create_regex(before, after) whole_line=False)
self.start_re = libclinic.create_regex(before, after)
self.verify = verify self.verify = verify
self.last_checksum_re: re.Pattern[str] | None = None self.last_checksum_re: re.Pattern[str] | None = None
self.last_dsl_name: str | None = None self.last_dsl_name: str | None = None
@ -1995,7 +1980,7 @@ class BlockParser:
else: else:
before, _, after = self.language.checksum_line.format(dsl_name=dsl_name, arguments='{arguments}').partition('{arguments}') before, _, after = self.language.checksum_line.format(dsl_name=dsl_name, arguments='{arguments}').partition('{arguments}')
assert _ == '{arguments}' assert _ == '{arguments}'
checksum_re = create_regex(before, after, word=False) checksum_re = libclinic.create_regex(before, after, word=False)
self.last_dsl_name = dsl_name self.last_dsl_name = dsl_name
self.last_checksum_re = checksum_re self.last_checksum_re = checksum_re
assert checksum_re is not None assert checksum_re is not None
@ -2029,7 +2014,7 @@ class BlockParser:
else: else:
checksum = d['checksum'] checksum = d['checksum']
computed = compute_checksum(output, len(checksum)) computed = libclinic.compute_checksum(output, len(checksum))
if checksum != computed: if checksum != computed:
fail("Checksum mismatch! " fail("Checksum mismatch! "
f"Expected {checksum!r}, computed {computed!r}. " f"Expected {checksum!r}, computed {computed!r}. "
@ -2142,8 +2127,8 @@ class BlockPrinter:
write(output) write(output)
arguments = "output={output} input={input}".format( arguments = "output={output} input={input}".format(
output=compute_checksum(output, 16), output=libclinic.compute_checksum(output, 16),
input=compute_checksum(input, 16) input=libclinic.compute_checksum(input, 16)
) )
write(self.language.checksum_line.format(dsl_name=dsl_name, arguments=arguments)) write(self.language.checksum_line.format(dsl_name=dsl_name, arguments=arguments))
write("\n") write("\n")
@ -2245,27 +2230,6 @@ extensions: LangDict = { name: CLanguage for name in "c cc cpp cxx h hh hpp hxx"
extensions['py'] = PythonLanguage extensions['py'] = PythonLanguage
def write_file(filename: str, new_contents: str) -> None:
try:
with open(filename, encoding="utf-8") as fp:
old_contents = fp.read()
if old_contents == new_contents:
# no change: avoid modifying the file modification time
return
except FileNotFoundError:
pass
# Atomic write using a temporary file and os.replace()
filename_new = f"{filename}.new"
with open(filename_new, "w", encoding="utf-8") as fp:
fp.write(new_contents)
try:
os.replace(filename_new, filename)
except:
os.unlink(filename_new)
raise
ClassDict = dict[str, "Class"] ClassDict = dict[str, "Class"]
DestinationDict = dict[str, Destination] DestinationDict = dict[str, Destination]
ModuleDict = dict[str, "Module"] ModuleDict = dict[str, "Module"]
@ -2505,7 +2469,8 @@ impl_definition block
core_includes=True, core_includes=True,
limited_capi=self.limited_capi, limited_capi=self.limited_capi,
header_includes=self.includes) header_includes=self.includes)
write_file(destination.filename, printer_2.f.getvalue()) libclinic.write_file(destination.filename,
printer_2.f.getvalue())
continue continue
return printer.f.getvalue() return printer.f.getvalue()
@ -2578,18 +2543,7 @@ def parse_file(
limited_capi=limited_capi) limited_capi=limited_capi)
cooked = clinic.parse(raw) cooked = clinic.parse(raw)
write_file(output, cooked) libclinic.write_file(output, cooked)
def compute_checksum(
input: str | None,
length: int | None = None
) -> str:
input = input or ''
s = hashlib.sha1(input.encode('utf-8')).hexdigest()
if length:
s = s[:length]
return s
class PythonParser: class PythonParser:

View file

@ -15,6 +15,11 @@ from .formatting import (
wrap_declarations, wrap_declarations,
wrapped_c_string_literal, wrapped_c_string_literal,
) )
from .utils import (
create_regex,
compute_checksum,
write_file,
)
__all__ = [ __all__ = [
@ -32,6 +37,11 @@ __all__ = [
"suffix_all_lines", "suffix_all_lines",
"wrap_declarations", "wrap_declarations",
"wrapped_c_string_literal", "wrapped_c_string_literal",
# Utility functions
"create_regex",
"compute_checksum",
"write_file",
] ]

View file

@ -0,0 +1,45 @@
import hashlib
import re
import os
def write_file(filename: str, new_contents: str) -> None:
"""Write new content to file, iff the content changed."""
try:
with open(filename, encoding="utf-8") as fp:
old_contents = fp.read()
if old_contents == new_contents:
# no change: avoid modifying the file modification time
return
except FileNotFoundError:
pass
# Atomic write using a temporary file and os.replace()
filename_new = f"{filename}.new"
with open(filename_new, "w", encoding="utf-8") as fp:
fp.write(new_contents)
try:
os.replace(filename_new, filename)
except:
os.unlink(filename_new)
raise
def compute_checksum(input_: str, length: int | None = None) -> str:
checksum = hashlib.sha1(input_.encode("utf-8")).hexdigest()
if length:
checksum = checksum[:length]
return checksum
def create_regex(
before: str, after: str, word: bool = True, whole_line: bool = True
) -> re.Pattern[str]:
"""Create a regex object for matching marker lines."""
group_re = r"\w+" if word else ".+"
before = re.escape(before)
after = re.escape(after)
pattern = fr"{before}({group_re}){after}"
if whole_line:
pattern = fr"^{pattern}$"
return re.compile(pattern)