def convert_file_input(config: ParserConfig, children: Sequence[Any]) -> Any: *body, footer = children if len(body) == 0: # If there's no body, the header and footer are ambiguous. The header is more # important, and should own the EmptyLine nodes instead of the footer. header = footer footer = () if (len(config.lines) == 2 and NEWLINE_RE.fullmatch(config.lines[0]) and config.lines[1] == ""): # This is an empty file (not even a comment), so special-case this to an # empty list instead of a single dummy EmptyLine (which is what we'd # normally parse). header = () else: # Steal the leading lines from the first statement, and move them into the # header. first_stmt = body[0] header = first_stmt.leading_lines body[0] = first_stmt.with_changes(leading_lines=()) return Module( header=header, body=body, footer=footer, encoding=config.encoding, default_indent=config.default_indent, default_newline=config.default_newline, has_trailing_newline=config.has_trailing_newline, )
def _detect_trailing_newline(source_str: str) -> bool: if len(source_str) == 0 or not NEWLINE_RE.fullmatch(source_str[-1]): return False # Make sure that the last newline wasn't following a continuation return not ( _CONTINUATION_RE.fullmatch(source_str[-2:]) or _CONTINUATION_RE.fullmatch(source_str[-3:]) )
def __post_init__(self) -> None: raw_python_version = self.python_version if isinstance(raw_python_version, AutoConfig): # If unspecified, we'll try to pick the same as the running # interpreter. There will always be at least one entry. parsed_python_version = _pick_compatible_python_version() else: # If the caller specified a version, we require that to be a known # version (because we don't want to encourage doing duplicate work # when there weren't syntax changes). # `parse_version_string` will raise a ValueError if the version is # invalid. parsed_python_version = parse_version_string(raw_python_version) if not any( parsed_python_version == parse_version_string(v) for v in KNOWN_PYTHON_VERSION_STRINGS ): comma_versions = ", ".join(KNOWN_PYTHON_VERSION_STRINGS) raise ValueError( "LibCST can only parse code using one of the following versions of " + f"Python's grammar: {comma_versions}. More versions may be " + "supported by future releases." ) # We use object.__setattr__ because the dataclass is frozen. See: # https://docs.python.org/3/library/dataclasses.html#frozen-instances # This should be safe behavior inside of `__post_init__`. object.__setattr__(self, "parsed_python_version", parsed_python_version) encoding = self.encoding if not isinstance(encoding, AutoConfig): try: codecs.lookup(encoding) except LookupError: raise ValueError(f"{repr(encoding)} is not a supported encoding") newline = self.default_newline if ( not isinstance(newline, AutoConfig) and NEWLINE_RE.fullmatch(newline) is None ): raise ValueError( f"Got an invalid value for default_newline: {repr(newline)}" ) indent = self.default_indent if not isinstance(indent, AutoConfig) and _INDENT_RE.fullmatch(indent) is None: raise ValueError(f"Got an invalid value for default_indent: {repr(indent)}")
def __post_init__(self) -> None: raw_python_version = self.python_version # `parse_version_string` will raise a ValueError if the version is invalid. # # We use object.__setattr__ because the dataclass is frozen. See: # https://docs.python.org/3/library/dataclasses.html#frozen-instances # This should be safe behavior inside of `__post_init__`. parsed_python_version = parse_version_string(None if isinstance( raw_python_version, AutoConfig) else raw_python_version) # Once we add support for more versions of Python, we can change this to detect # the supported version range. if parsed_python_version not in ( PythonVersionInfo(3, 5), PythonVersionInfo(3, 6), PythonVersionInfo(3, 7), PythonVersionInfo(3, 8), ): raise ValueError( "LibCST can only parse code using one of the following versions of " + "Python's grammar: 3.5, 3.6, 3.7, 3.8. More versions may be " + "supported by future releases.") object.__setattr__(self, "parsed_python_version", parsed_python_version) encoding = self.encoding if not isinstance(encoding, AutoConfig): try: codecs.lookup(encoding) except LookupError: raise ValueError( f"{repr(encoding)} is not a supported encoding") newline = self.default_newline if (not isinstance(newline, AutoConfig) and NEWLINE_RE.fullmatch(newline) is None): raise ValueError( f"Got an invalid value for default_newline: {repr(newline)}") indent = self.default_indent if not isinstance(indent, AutoConfig) and _INDENT_RE.fullmatch(indent) is None: raise ValueError( f"Got an invalid value for default_indent: {repr(indent)}")