from __future__ import annotations import os import re from librt.internal import ReadBuffer from mypy import errorcodes as codes from mypy.cache import read_int from mypy.errors import Errors from mypy.nodes import FileRawData, MypyFile from mypy.options import Options def parse( source: str | bytes, fnam: str, module: str | None, errors: Errors, options: Options, raise_on_error: bool = False, imports_only: bool = False, ) -> MypyFile: """Parse a source file, without doing any semantic analysis. Return the parse tree. If errors is not provided, raise ParseError on failure. Otherwise, use the errors object to report parse errors. The python_version (major, minor) option determines the Python syntax variant. """ if options.native_parser: # Native parser only works with actual files on disk # Fall back to fastparse for in-memory source or non-existent files if os.path.exists(fnam): import mypy.nativeparse ignore_errors = options.ignore_errors or fnam in errors.ignored_files # If errors are ignored, we can drop many function bodies to speed up type checking. strip_function_bodies = ignore_errors and not options.preserve_asts errors.set_file(fnam, module, options=options) tree, parse_errors, type_ignores = mypy.nativeparse.native_parse( fnam, options, skip_function_bodies=strip_function_bodies, imports_only=imports_only, ) # Convert type ignores list to dict tree.ignored_lines = dict(type_ignores) # Set is_stub based on file extension tree.is_stub = fnam.endswith(".pyi") # Note: tree.imports is populated directly by native_parse with deserialized # import metadata, so we don't need to collect imports via AST traversal # Report parse errors for error in parse_errors: message = error["message"] # Standardize error message by capitalizing the first word message = re.sub(r"^(\s*\w)", lambda m: m.group(1).upper(), message) # Respect blocker status from error, default to True for syntax errors is_blocker = error.get("blocker", True) error_code = error.get("code") if error_code is None: error_code = codes.SYNTAX else: # Fallback to [syntax] for backwards compatibility. error_code = codes.error_codes.get(error_code) or codes.SYNTAX errors.report( error["line"], error["column"], message, blocker=is_blocker, code=error_code ) if raise_on_error and errors.is_errors(): errors.raise_error() return tree # Fall through to fastparse for non-existent files assert not imports_only if options.transform_source is not None: source = options.transform_source(source) import mypy.fastparse tree = mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options) if raise_on_error and errors.is_errors(): errors.raise_error() return tree def load_from_raw( fnam: str, module: str | None, raw_data: FileRawData, errors: Errors, options: Options ) -> MypyFile: """Load AST from parsed binary data. This essentially replicates parse() above but expects FileRawData instead of actually parsing the source code in the file. """ from mypy.nativeparse import State, deserialize_imports, read_statements # This part mimics the logic in native_parse(). data = ReadBuffer(raw_data.defs) n = read_int(data) state = State(options) defs = read_statements(state, data, n) imports = deserialize_imports(raw_data.imports) tree = MypyFile(defs, imports) tree.path = fnam tree.ignored_lines = raw_data.ignored_lines tree.is_partial_stub_package = raw_data.is_partial_stub_package tree.uses_template_strings = raw_data.uses_template_strings tree.is_stub = fnam.endswith(".pyi") # Report parse errors, this replicates the logic in parse(). all_errors = raw_data.raw_errors + state.errors errors.set_file(fnam, module, options=options) for error in all_errors: message = error["message"] message = re.sub(r"^(\s*\w)", lambda m: m.group(1).upper(), message) is_blocker = error.get("blocker", True) error_code = error.get("code") if error_code is None: error_code = codes.SYNTAX else: error_code = codes.error_codes.get(error_code) or codes.SYNTAX # Note we never raise in this function, so it should not be called in coordinator. errors.report(error["line"], error["column"], message, blocker=is_blocker, code=error_code) return tree