# Released under the MIT License. See LICENSE for details.#"""Stuff intended to be used from emacs"""from__future__importannotationsfrompathlibimportPathfromtypingimportTYPE_CHECKINGifTYPE_CHECKING:passdef_py_symbol_at_column(line:str,col:int)->str:start=colwhilestart>0andline[start-1]!=' ':start-=1end=colwhileend<len(line)andline[end]!=' ':end+=1returnline[start:end]
[docs]defpy_examine(projroot:Path,filename:Path,line:int,column:int,selection:str|None,operation:str,)->None:"""Given file position info, performs some code inspection."""# pylint: disable=too-many-positional-arguments# pylint: disable=too-many-locals# pylint: disable=cyclic-importimportastroidimportrefromefrotoolsimportcode# Pull in our pylint plugin which really just adds astroid filters.# That way our introspection here will see the same thing as pylint's does.withopen(filename,encoding='utf-8')asinfile:fcontents=infile.read()if'#@'infcontents:raiseRuntimeError('#@ marker found in file; this breaks examinations.')flines=fcontents.splitlines()ifoperation=='pylint_infer':# See what asteroid can infer about the target symbol.symbol=(selectionifselectionisnotNoneelse_py_symbol_at_column(flines[line-1],column))# Insert a line after the provided one which is just the symbol so# that we can ask for its value alone.match=re.match(r'\s*',flines[line-1])whitespace=match.group()ifmatchisnotNoneelse''sline=whitespace+symbol+' #@'flines=flines[:line]+[sline]+flines[line:]node=astroid.extract_node('\n'.join(flines))inferred=list(node.infer())print(symbol+':',', '.join([str(i)foriininferred]))elifoperationin('mypy_infer','mypy_locals'):# Ask mypy for the type of the target symbol.symbol=(selectionifselectionisnotNoneelse_py_symbol_at_column(flines[line-1],column))# Insert a line after the provided one which is just the symbol so# that we can ask for its value alone.match=re.match(r'\s*',flines[line-1])whitespace=match.group()ifmatchisnotNoneelse''ifoperation=='mypy_infer':sline=whitespace+'reveal_type('+symbol+')'else:sline=whitespace+'reveal_locals()'flines=flines[:line]+[sline]+flines[line:]# Write a temp file and run the check on it.# Let's use ' flycheck_*' for the name since pipeline scripts# are already set to ignore those files.tmppath=Path(filename.parent,'flycheck_mp_'+filename.name)withtmppath.open('w',encoding='utf-8')asoutfile:outfile.write('\n'.join(flines))try:code.mypy_files(projroot,[str(tmppath)],check=False)exceptExceptionasexc:print('error running mypy:',exc)tmppath.unlink()elifoperation=='pylint_node':flines[line-1]+=' #@'node=astroid.extract_node('\n'.join(flines))print(node)elifoperation=='pylint_tree':flines[line-1]+=' #@'node=astroid.extract_node('\n'.join(flines))print(node.repr_tree())else:print('unknown operation: '+operation)