Esempio n. 1
0
    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)}")
Esempio n. 2
0
 def test_pick_compatible(self) -> None:
     self.assertEqual(PythonVersionInfo(3, 1),
                      _pick_compatible_python_version("3.2"))
     self.assertEqual(PythonVersionInfo(3, 1),
                      _pick_compatible_python_version("3.1"))
     self.assertEqual(PythonVersionInfo(3, 8),
                      _pick_compatible_python_version("3.9"))
     self.assertEqual(PythonVersionInfo(3, 8),
                      _pick_compatible_python_version("3.10"))
     self.assertEqual(PythonVersionInfo(3, 8),
                      _pick_compatible_python_version("4.0"))
     with self.assertRaisesRegex(
             ValueError,
         (r"No version found older than 1\.0 \(PythonVersionInfo\(" +
          r"major=1, minor=0\)\) while running on"),
     ):
         _pick_compatible_python_version("1.0")
Esempio n. 3
0
def tokenize_lines(  # noqa: C901
    lines: Iterable[str],
    version_info: PythonVersionInfo,
    start_pos: Tuple[int, int] = (1, 0),
) -> Generator[PythonToken, None, None]:
    token_collection = _get_token_collection(version_info)
    if version_info >= PythonVersionInfo(3, 7):
        return _tokenize_lines_py37_or_above(
            lines, version_info, token_collection, start_pos=start_pos
        )
    else:
        return _tokenize_lines_py36_or_below(
            lines, version_info, token_collection, start_pos=start_pos
        )
Esempio n. 4
0
class VersionCompareTest(UnitTest):
    @data_provider((
        # Simple equality
        ("==3.6", PythonVersionInfo(3, 6), True),
        ("!=3.6", PythonVersionInfo(3, 6), False),
        # Equal or GT/LT
        (">=3.6", PythonVersionInfo(3, 5), False),
        (">=3.6", PythonVersionInfo(3, 6), True),
        (">=3.6", PythonVersionInfo(3, 7), True),
        ("<=3.6", PythonVersionInfo(3, 5), True),
        ("<=3.6", PythonVersionInfo(3, 6), True),
        ("<=3.6", PythonVersionInfo(3, 7), False),
        # GT/LT
        (">3.6", PythonVersionInfo(3, 5), False),
        (">3.6", PythonVersionInfo(3, 6), False),
        (">3.6", PythonVersionInfo(3, 7), True),
        ("<3.6", PythonVersionInfo(3, 5), True),
        ("<3.6", PythonVersionInfo(3, 6), False),
        ("<3.6", PythonVersionInfo(3, 7), False),
        # Multiple checks
        (">3.6,<3.8", PythonVersionInfo(3, 6), False),
        (">3.6,<3.8", PythonVersionInfo(3, 7), True),
        (">3.6,<3.8", PythonVersionInfo(3, 8), False),
    ))
    def test_tokenize(
        self,
        requested_version: str,
        actual_version: PythonVersionInfo,
        expected_result: bool,
    ) -> None:
        self.assertEqual(_should_include(requested_version, actual_version),
                         expected_result)
Esempio n. 5
0
class TestDetectConfig(UnitTest):
    @data_provider({
        "empty_input": {
            "source":
            b"",
            "partial":
            PartialParserConfig(python_version="3.7"),
            "detect_trailing_newline":
            True,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=["\n", ""],
                encoding="utf-8",
                default_indent="    ",
                default_newline="\n",
                has_trailing_newline=False,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "detect_trailing_newline_disabled": {
            "source":
            b"",
            "partial":
            PartialParserConfig(python_version="3.7"),
            "detect_trailing_newline":
            False,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=[""],  # the trailing newline isn't inserted
                encoding="utf-8",
                default_indent="    ",
                default_newline="\n",
                has_trailing_newline=False,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "detect_default_newline_disabled": {
            "source":
            b"pass\r",
            "partial":
            PartialParserConfig(python_version="3.7"),
            "detect_trailing_newline":
            False,
            "detect_default_newline":
            False,
            "expected_config":
            ParserConfig(
                lines=["pass\r", ""],  # the trailing newline isn't inserted
                encoding="utf-8",
                default_indent="    ",
                default_newline="\n",
                has_trailing_newline=False,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "newline_inferred": {
            "source":
            b"first_line\r\n\nsomething\n",
            "partial":
            PartialParserConfig(python_version="3.7"),
            "detect_trailing_newline":
            True,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=["first_line\r\n", "\n", "something\n", ""],
                encoding="utf-8",
                default_indent="    ",
                default_newline="\r\n",
                has_trailing_newline=True,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "newline_partial_given": {
            "source":
            b"first_line\r\nsecond_line\r\n",
            "partial":
            PartialParserConfig(default_newline="\n", python_version="3.7"),
            "detect_trailing_newline":
            True,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=["first_line\r\n", "second_line\r\n", ""],
                encoding="utf-8",
                default_indent="    ",
                default_newline="\n",  # The given partial disables inference
                has_trailing_newline=True,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "indent_inferred": {
            "source":
            b"if test:\n\t  something\n",
            "partial":
            PartialParserConfig(python_version="3.7"),
            "detect_trailing_newline":
            True,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=["if test:\n", "\t  something\n", ""],
                encoding="utf-8",
                default_indent="\t  ",
                default_newline="\n",
                has_trailing_newline=True,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "indent_partial_given": {
            "source":
            b"if test:\n\t  something\n",
            "partial":
            PartialParserConfig(default_indent="      ", python_version="3.7"),
            "detect_trailing_newline":
            True,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=["if test:\n", "\t  something\n", ""],
                encoding="utf-8",
                default_indent="      ",
                default_newline="\n",
                has_trailing_newline=True,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "encoding_inferred": {
            "source":
            b"#!/usr/bin/python3\n# -*- coding: latin-1 -*-\npass\n",
            "partial":
            PartialParserConfig(python_version="3.7"),
            "detect_trailing_newline":
            True,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=[
                    "#!/usr/bin/python3\n",
                    "# -*- coding: latin-1 -*-\n",
                    "pass\n",
                    "",
                ],
                encoding="iso-8859-1",  # this is an alias for latin-1
                default_indent="    ",
                default_newline="\n",
                has_trailing_newline=True,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "encoding_partial_given": {
            "source":
            b"#!/usr/bin/python3\n# -*- coding: latin-1 -*-\npass\n",
            "partial":
            PartialParserConfig(encoding="us-ascii", python_version="3.7"),
            "detect_trailing_newline":
            True,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=[
                    "#!/usr/bin/python3\n",
                    "# -*- coding: latin-1 -*-\n",
                    "pass\n",
                    "",
                ],
                encoding="us-ascii",
                default_indent="    ",
                default_newline="\n",
                has_trailing_newline=True,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "encoding_str_not_bytes_disables_inference": {
            "source":
            "#!/usr/bin/python3\n# -*- coding: latin-1 -*-\npass\n",
            "partial":
            PartialParserConfig(python_version="3.7"),
            "detect_trailing_newline":
            True,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=[
                    "#!/usr/bin/python3\n",
                    "# -*- coding: latin-1 -*-\n",
                    "pass\n",
                    "",
                ],
                encoding="utf-8",  # because source is a str, don't infer latin-1
                default_indent="    ",
                default_newline="\n",
                has_trailing_newline=True,
                version=PythonVersionInfo(3, 7),
            ),
        },
        "encoding_non_ascii_compatible_utf_16_with_bom": {
            "source":
            b"\xff\xfet\x00e\x00s\x00t\x00",
            "partial":
            PartialParserConfig(encoding="utf-16", python_version="3.7"),
            "detect_trailing_newline":
            True,
            "detect_default_newline":
            True,
            "expected_config":
            ParserConfig(
                lines=["test\n", ""],
                encoding="utf-16",
                default_indent="    ",
                default_newline="\n",
                has_trailing_newline=False,
                version=PythonVersionInfo(3, 7),
            ),
        },
    })
    def test_detect_module_config(
        self,
        *,
        source: Union[str, bytes],
        partial: PartialParserConfig,
        detect_trailing_newline: bool,
        detect_default_newline: bool,
        expected_config: ParserConfig,
    ) -> None:
        self.assertEqual(
            detect_config(
                source,
                partial=partial,
                detect_trailing_newline=detect_trailing_newline,
                detect_default_newline=detect_default_newline,
            ).config,
            expected_config,
        )