# Released under the MIT License. See LICENSE for details.#"""Misc util calls/etc.Ideally the stuff in here should migrate to more descriptive module names."""from__future__importannotationsfromtypingimportTYPE_CHECKINGifTYPE_CHECKING:fromtypingimportSequence,LiteralfrompathlibimportPath
[docs]defexplicit_bool(value:bool)->bool:"""Simply return input value; can avoid unreachable-code type warnings."""returnvalue
[docs]defreplace_section(text:str,begin_marker:str,end_marker:str,replace_text:str='',*,keep_markers:bool=False,error_if_missing:bool=True,)->str:"""Replace all text between two marker strings (including the markers)."""ifbegin_markernotintext:iferror_if_missing:raiseRuntimeError(f"Marker not found in text: '{begin_marker}'.")returntextsplits=text.split(begin_marker)iflen(splits)!=2:raiseRuntimeError(f"Expected one marker '{begin_marker}'"f'; found {text.count(begin_marker)}.')before_begin,after_begin=splitssplits=after_begin.split(end_marker)iflen(splits)!=2:raiseRuntimeError(f"Expected one marker '{end_marker}'"f'; found {text.count(end_marker)}.')_before_end,after_end=splitsifkeep_markers:replace_text=f'{begin_marker}{replace_text}{end_marker}'returnf'{before_begin}{replace_text}{after_end}'
[docs]defreadfile(path:str|Path)->str:"""Read a utf-8 text file into a string."""withopen(path,encoding='utf-8')asinfile:returninfile.read()
[docs]defwritefile(path:str|Path,txt:str)->None:"""Write a string to a utf-8 text file."""withopen(path,'w',encoding='utf-8')asoutfile:outfile.write(txt)
[docs]defreplace_exact(opstr:str,old:str,new:str,count:int=1,label:str|None=None)->str:"""Replace text ensuring that exactly x occurrences are replaced. Useful when filtering data in some predefined way to ensure the original has not changed. """found=opstr.count(old)label_str=f' in {label}'iflabelisnotNoneelse''iffound!=count:raiseRuntimeError(f'Expected {count} string occurrence(s){label_str};'f' found {found}. String: {repr(old)}')returnopstr.replace(old,new)
[docs]defget_files_hash(filenames:Sequence[str|Path],extrahash:str='',int_only:bool=False,hashtype:Literal['md5','sha256']='md5',)->str:"""Return a hash for the given files."""importhashlibifnotisinstance(filenames,list):raiseRuntimeError(f'Expected a list; got a {type(filenames)}.')ifTYPE_CHECKING:# Help Mypy infer the right type for this.hashobj=hashlib.md5()else:hashobj=getattr(hashlib,hashtype)()forfnameinfilenames:withopen(fname,'rb')asinfile:whileTrue:data=infile.read(2**20)ifnotdata:breakhashobj.update(data)hashobj.update(extrahash.encode())ifint_only:returnstr(int.from_bytes(hashobj.digest(),byteorder='big'))returnhashobj.hexdigest()
[docs]defget_string_hash(value:str,int_only:bool=False,hashtype:Literal['md5','sha256']='md5',)->str:"""Return a hash for the given files."""importhashlibifnotisinstance(value,str):raiseTypeError('Expected a str.')ifTYPE_CHECKING:# Help Mypy infer the right type for this.hashobj=hashlib.md5()else:hashobj=getattr(hashlib,hashtype)()hashobj.update(value.encode())ifint_only:returnstr(int.from_bytes(hashobj.digest(),byteorder='big'))returnhashobj.hexdigest()
[docs]defwsl_windows_build_path_description()->str:"""Describe where wsl windows builds need to live."""return'anywhere under /mnt/c/'
[docs]defis_wsl_windows_build_path(path:str)->bool:"""Return whether a path is used for wsl windows builds. Building Windows Visual Studio builds through WSL is currently only supported in specific locations; namely anywhere under /mnt/c/. This is enforced because building on the Linux filesystem errors due to case-sensitivity issues, and also because a number of workarounds need to be employed to deal with filesystem/permission quirks, so we want to keep things as consistent as possible. Note that said quirk workarounds WILL be applied if this returns true, so this check should be as specific as possible. """returnpath.startswith('/mnt/c/')