Esempio n. 1
0
def test_imports_banned() -> None:
    parser = Parser(target_type_aliases=[], object_aliases=BuildFileAliases())
    with pytest.raises(ParseError) as exc:
        parser.parse(
            "dir/BUILD", "\nx = 'hello'\n\nimport os\n", BuildFilePreludeSymbols(FrozenDict())
        )
    assert "Import used in dir/BUILD at line 4" in str(exc.value)
Esempio n. 2
0
 def test_empty(self) -> None:
     """Test that parsing an empty BUILD file results in an empty AddressFamily."""
     address_mapper = AddressMapper(
         parser=JsonParser(TEST_TABLE),
         prelude_glob_patterns=(),
         build_file_imports_behavior=BuildFileImportsBehavior.error,
     )
     af = run_rule(
         parse_address_family,
         rule_args=[
             address_mapper,
             BuildFilePreludeSymbols(FrozenDict()),
             Dir("/dev/null")
         ],
         mock_gets=[
             MockGet(
                 product_type=Snapshot,
                 subject_type=PathGlobs,
                 mock=lambda _: Snapshot(Digest("abc", 10),
                                         ("/dev/null/BUILD", ), ()),
             ),
             MockGet(
                 product_type=FilesContent,
                 subject_type=Digest,
                 mock=lambda _: FilesContent(
                     [FileContent(path="/dev/null/BUILD", content=b"")]),
             ),
         ],
     )
     self.assertEqual(len(af.objects_by_name), 0)
Esempio n. 3
0
def parse_address_map(build_file: str) -> AddressMap:
    path = "/dev/null"
    parser = Parser(target_type_aliases=["thing"],
                    object_aliases=BuildFileAliases())
    address_map = AddressMap.parse(path, build_file, parser,
                                   BuildFilePreludeSymbols(FrozenDict()))
    assert path == address_map.path
    return address_map
Esempio n. 4
0
 def test_no_import_sideeffects(self) -> None:
     # A parser with no symbols registered.
     parser = LegacyPythonCallbacksParser(
         SymbolTable({}),
         BuildFileAliases(),
         build_file_imports_behavior=BuildFileImportsBehavior.warn,
     )
     # Call to import a module should succeed.
     parser.parse(
         "/dev/null",
         b"""import os; os.path.join('x', 'y')""",
         BuildFilePreludeSymbols(FrozenDict()),
     )
     # But the imported module should not be visible as a symbol in further parses.
     with self.assertRaises(NameError):
         parser.parse("/dev/null", b"""os.path.join('x', 'y')""",
                      BuildFilePreludeSymbols(FrozenDict()))
Esempio n. 5
0
def test_unrecognized_symbol() -> None:
    parser = Parser(
        target_type_aliases=["tgt"],
        object_aliases=BuildFileAliases(
            objects={"obj": 0},
            context_aware_object_factories={
                "caof": lambda parse_context: lambda _: None
            },
        ),
    )
    prelude_symbols = BuildFilePreludeSymbols(FrozenDict({"prelude": 0}))
    with pytest.raises(ParseError) as exc:
        parser.parse("dir/BUILD", "fake", prelude_symbols)
    assert (
        str(exc.value) ==
        "Name 'fake' is not defined.\n\nAll registered symbols: ['caof', 'obj', 'prelude', 'tgt']"
    )
Esempio n. 6
0
def test_parse_address_family_empty() -> None:
    """Test that parsing an empty BUILD file results in an empty AddressFamily."""
    af = run_rule_with_mocks(
        parse_address_family,
        rule_args=[
            Parser(build_root="", target_type_aliases=[], object_aliases=BuildFileAliases()),
            BuildFileOptions(("BUILD",)),
            BuildFilePreludeSymbols(FrozenDict()),
            AddressFamilyDir("/dev/null"),
        ],
        mock_gets=[
            MockGet(
                output_type=DigestContents,
                input_type=PathGlobs,
                mock=lambda _: DigestContents([FileContent(path="/dev/null/BUILD", content=b"")]),
            ),
        ],
    )
    assert len(af.name_to_target_adaptors) == 0
Esempio n. 7
0
    def perform_test(extra_targets: list[str], dym: str) -> None:

        parser = Parser(
            target_type_aliases=["tgt", *extra_targets],
            object_aliases=BuildFileAliases(
                objects={"obj": 0},
                context_aware_object_factories={"caof": lambda parse_context: lambda _: None},
            ),
        )
        prelude_symbols = BuildFilePreludeSymbols(FrozenDict({"prelude": 0}))
        fmt_extra_sym = str(extra_targets)[1:-1] + (", ") if len(extra_targets) != 0 else ""
        with pytest.raises(ParseError) as exc:
            parser.parse("dir/BUILD", "fake", prelude_symbols)
        assert str(exc.value) == (
            f"Name 'fake' is not defined.\n\n{dym}"
            "If you expect to see more symbols activated in the below list,"
            f" refer to {docs_url('enabling-backends')} for all available"
            " backends to activate.\n\n"
            f"All registered symbols: ['caof', {fmt_extra_sym}'obj', 'prelude', 'tgt']"
        )
Esempio n. 8
0
def test_parse_address_family_empty() -> None:
    """Test that parsing an empty BUILD file results in an empty AddressFamily."""
    address_mapper = AddressMapper(parser=Parser(
        target_type_aliases=[], object_aliases=BuildFileAliases()))
    af = run_rule(
        parse_address_family,
        rule_args=[
            address_mapper,
            BuildFilePreludeSymbols(FrozenDict()),
            Dir("/dev/null")
        ],
        mock_gets=[
            MockGet(
                product_type=DigestContents,
                subject_type=PathGlobs,
                mock=lambda _: DigestContents(
                    [FileContent(path="/dev/null/BUILD", content=b"")]),
            ),
        ],
    )
    assert len(af.name_to_target_adaptors) == 0
Esempio n. 9
0
async def evalute_preludes(
        address_mapper: AddressMapper) -> BuildFilePreludeSymbols:
    snapshot = await Get[Snapshot](PathGlobs(
        address_mapper.prelude_glob_patterns,
        glob_match_error_behavior=GlobMatchErrorBehavior.ignore,
    ))
    prelude_files_content = await Get[FilesContent](Digest, snapshot.digest)
    values: Dict[str, Any] = {}
    for file_content in prelude_files_content:
        try:
            file_content_str = file_content.content.decode()
            content = compile(file_content_str, file_content.path, "exec")
            exec(content, values)
        except Exception as e:
            raise Exception(
                f"Error parsing prelude file {file_content.path}: {e}")
        error_on_imports(file_content_str, file_content.path)
    # __builtins__ is a dict, so isn't hashable, and can't be put in a FrozenDict.
    # Fortunately, we don't care about it - preludes should not be able to override builtins, so we just pop it out.
    # TODO: Give a nice error message if a prelude tries to set a expose a non-hashable value.
    values.pop("__builtins__", None)
    return BuildFilePreludeSymbols(FrozenDict(values))
Esempio n. 10
0
async def evaluate_preludes(global_options: GlobalOptions) -> BuildFilePreludeSymbols:
    prelude_digest_contents = await Get(
        DigestContents,
        PathGlobs(
            global_options.options.build_file_prelude_globs,
            glob_match_error_behavior=GlobMatchErrorBehavior.ignore,
        ),
    )
    values: Dict[str, Any] = {}
    for file_content in prelude_digest_contents:
        try:
            file_content_str = file_content.content.decode()
            content = compile(file_content_str, file_content.path, "exec")
            exec(content, values)
        except Exception as e:
            raise Exception(f"Error parsing prelude file {file_content.path}: {e}")
        error_on_imports(file_content_str, file_content.path)
    # __builtins__ is a dict, so isn't hashable, and can't be put in a FrozenDict.
    # Fortunately, we don't care about it - preludes should not be able to override builtins, so we just pop it out.
    # TODO: Give a nice error message if a prelude tries to set a expose a non-hashable value.
    values.pop("__builtins__", None)
    return BuildFilePreludeSymbols(FrozenDict(values))
Esempio n. 11
0
def test_parse_address_family_empty() -> None:
    """Test that parsing an empty BUILD file results in an empty AddressFamily."""
    af = run_rule_with_mocks(
        parse_address_family,
        rule_args=[
            Parser(target_type_aliases=[], object_aliases=BuildFileAliases()),
            create_subsystem(GlobalOptions,
                             build_patterns=["BUILD"],
                             build_ignore=[]),
            BuildFilePreludeSymbols(FrozenDict()),
            Dir("/dev/null"),
        ],
        mock_gets=[
            MockGet(
                product_type=DigestContents,
                subject_type=PathGlobs,
                mock=lambda _: DigestContents(
                    [FileContent(path="/dev/null/BUILD", content=b"")]),
            ),
        ],
    )
    assert len(af.name_to_target_adaptors) == 0
Esempio n. 12
0
def parse(parser, document):
    return parser.parse("/dev/null", document, BuildFilePreludeSymbols(FrozenDict()))
Esempio n. 13
0
    def test_error_presentation(self):
        document = dedent(
            """
            # An example with several Bobs.
        
            # One with hobbies.
              {
                "type_alias": "pants.engine.internals.parsers_test.Bob",
        
                # And internal comment and blank lines.
        
                "hobbies": [1, 2, 3]} {
              # This comment is inside an empty object that started on the prior line!
            }
        
            # Another that is imaginary aged.
            {
              "type_alias": "pants.engine.internals.parsers_test.Bob",
              "age": 42i,
        
              "four": 1,
              "five": 1,
              "six": 1,
              "seven": 1,
              "eight": 1,
              "nine": 1
            }
            """
        ).strip()
        filepath = "/dev/null"
        with self.assertRaises(parser.ParseError) as exc:
            parsers.JsonParser(EMPTY_TABLE).parse(
                filepath, document, BuildFilePreludeSymbols(FrozenDict())
            )

        # Strip trailing whitespace from the message since our expected literal below will have
        # trailing ws stripped via editors and code reviews calling for it.
        actual_lines = [line.rstrip() for line in str(exc.exception).splitlines()]

        # This message from the json stdlib varies between python releases, so fuzz the match a bit.
        self.assertRegex(
            actual_lines[0], r'Expecting (?:,|\',\'|",") delimiter: line 3 column 12 \(char 72\)'
        )

        self.assertEqual(
            dedent(
                """
                In document at {filepath}:
                    # An example with several Bobs.
        
                    # One with hobbies.
                      {{
                        "type_alias": "pants.engine.internals.parsers_test.Bob",
        
                        # And internal comment and blank lines.
        
                        "hobbies": [1, 2, 3]}} {{
                      # This comment is inside an empty object that started on the prior line!
                    }}
        
                    # Another that is imaginary aged.
                 1: {{
                 2:   "type_alias": "pants.engine.internals.parsers_test.Bob",
                 3:   "age": 42i,
        
                 4:   "four": 1,
                 5:   "five": 1,
                 6:   "six": 1,
                 7:   "seven": 1,
                 8:   "eight": 1,
                 9:   "nine": 1
                10: }}
                """.format(
                    filepath=filepath
                )
            ).strip(),
            "\n".join(actual_lines[1:]),
        )
Esempio n. 14
0
 def parse_address_map(self, json):
     path = "/dev/null"
     address_map = AddressMap.parse(path, json, self._parser,
                                    BuildFilePreludeSymbols(FrozenDict()))
     self.assertEqual(path, address_map.path)
     yield address_map