Skip to content

Commit 967cd75

Browse files
Matt D'Souzafacebook-github-bot
authored andcommitted
Introduce specific exceptions for buck project builder
Summary: Isolating the number of exceptions to catch should simplify the interface when using the builder. Also makes exceptions more debuggable. Reviewed By: dkgi Differential Revision: D14759548 fbshipit-source-id: 34cd00412c2f18b4d60c340ca14dbfc8e29284b4
1 parent cd88e17 commit 967cd75

File tree

4 files changed

+54
-20
lines changed

4 files changed

+54
-20
lines changed

client/buck_project_builder/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
Target = NamedTuple("Target", [("build_file_directory", str), ("name", str)])
1818

1919

20+
class BuilderException(Exception):
21+
pass
22+
23+
2024
class Builder(object):
2125
def __init__(self, buck_root: str, build_file_name: str = "TARGETS") -> None:
2226
self.buck_root = buck_root
@@ -53,14 +57,16 @@ def compute_targets_to_build(self, target: str) -> List[BuildTarget]:
5357
targets_seen.update(new_targets)
5458

5559
if targets_not_found:
56-
raise ValueError(
60+
raise BuilderException(
5761
"Target(s) not found: {}".format(", ".join(targets_not_found))
5862
)
5963
return build_targets
6064

6165
def _parse_target(self, target: str) -> Target:
6266
if target.endswith("...") or target.endswith(":"):
63-
raise ValueError("Target {} is not an absolute target.".format(target))
67+
raise BuilderException(
68+
"Target {} is not an absolute target.".format(target)
69+
)
6470
split = _strip(target, left="//").split(":")
6571
return Target(split[0], split[1])
6672

client/buck_project_builder/parser/__init__.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
)
1818

1919

20+
class ParserException(Exception):
21+
pass
22+
23+
2024
class Parser(object):
2125
def __init__(self, buck_root: str, build_file_name: str = "TARGETS") -> None:
2226
self.buck_root = buck_root
@@ -27,17 +31,15 @@ def parse_file(self, build_file_directory: str) -> BuildFile:
2731
if build_file_directory in self.build_files:
2832
return self.build_files[build_file_directory]
2933

30-
build_file_path = os.path.join(
31-
self.buck_root, build_file_directory, self.build_file_name
32-
)
34+
build_file_path = self._build_file_path(build_file_directory)
3335
try:
3436
with open(build_file_path, "r") as build_file:
3537
file_contents = build_file.read()
3638
tree = ast.parse(file_contents)
3739
except FileNotFoundError:
38-
raise ValueError("No build file found at {}.".format(build_file_path))
40+
raise ParserException("No build file found at {}.".format(build_file_path))
3941
except SyntaxError as error:
40-
raise ValueError(
42+
raise ParserException(
4143
"Could not parse build file at {}. Reason: {}".format(
4244
build_file_path, error
4345
)
@@ -48,6 +50,9 @@ def parse_file(self, build_file_directory: str) -> BuildFile:
4850
self.build_files[build_file_directory] = build_file
4951
return build_file
5052

53+
def _build_file_path(self, build_file_directory: str) -> str:
54+
return os.path.join(self.buck_root, build_file_directory, self.build_file_name)
55+
5156
def _parse_targets(
5257
self, tree: ast.AST, build_file_directory: str
5358
) -> Dict[str, BuildTarget]:
@@ -56,14 +61,25 @@ def _parse_targets(
5661

5762
targets = {}
5863
for expression in expressions:
59-
assert isinstance(expression, ast.Expr)
60-
call = expression.value
61-
assert isinstance(call, ast.Call)
62-
named = call.func
63-
assert isinstance(named, ast.Name)
64-
rule = named.id
65-
if rule in SUPPORTED_RULES:
66-
target = SUPPORTED_RULES[rule].parse(call, build_file_directory)
67-
targets[target.name] = target
64+
try:
65+
assert isinstance(expression, ast.Expr)
66+
call = expression.value
67+
assert isinstance(call, ast.Call)
68+
named = call.func
69+
assert isinstance(named, ast.Name)
70+
rule = named.id
71+
if rule in SUPPORTED_RULES:
72+
target = SUPPORTED_RULES[rule].parse(call, build_file_directory)
73+
targets[target.name] = target
74+
except (AssertionError, ValueError) as error:
75+
raise ParserException(
76+
"Error occurred parsing targets in file {}, "
77+
"at line {}, column {}: {}".format(
78+
self._build_file_path(build_file_directory),
79+
expression.lineno,
80+
expression.col_offset,
81+
error,
82+
)
83+
)
6884

6985
return targets

client/buck_project_builder/parser/tests/parser_test.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import unittest
77
from unittest.mock import mock_open, patch
88

9-
from .. import Parser
9+
from .. import Parser, ParserException
1010
from ..build_target import PythonBinary, PythonLibrary, PythonUnitTest
1111

1212

@@ -47,6 +47,12 @@
4747
)
4848
"""
4949

50+
TARGETS_FILE_2 = """
51+
python_binary(
52+
name = 1234,
53+
)
54+
"""
55+
5056

5157
class ParserTest(unittest.TestCase):
5258
def test_parse_file(self):
@@ -92,3 +98,9 @@ def test_parse_file(self):
9298
# The parser should cache files it has already parsed.
9399
parser.parse_file("my/module")
94100
mocked_open.assert_not_called()
101+
102+
with patch("builtins.open", mock_open(read_data=TARGETS_FILE_2)) as mocked_open:
103+
self.assertRaises(ParserException, parser.parse_file, "my/other_module")
104+
mocked_open.assert_called_once_with(
105+
"/buck_root/my/other_module/TARGETS", "r"
106+
)

client/buck_project_builder/tests/builder_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import List
99
from unittest.mock import MagicMock, patch
1010

11-
from .. import Builder, parser
11+
from .. import Builder, BuilderException, parser
1212
from ..parser.build_target import BuildTarget, PythonBinary, PythonLibrary
1313

1414

@@ -62,7 +62,7 @@ def test_compute_targets_to_build_simple(self):
6262
self.assert_targets_equal(targets, ["//project:e"])
6363

6464
self.assertRaises(
65-
ValueError, builder.compute_targets_to_build, "//project:f"
65+
BuilderException, builder.compute_targets_to_build, "//project:f"
6666
)
6767

6868
def test_compute_targets_to_build_complex(self):
@@ -116,7 +116,7 @@ def test_compute_targets_to_build_complex(self):
116116
self.assert_targets_equal(targets, ["//project2:e", "//project2:d"])
117117

118118
self.assertRaises(
119-
ValueError, builder.compute_targets_to_build, "//project1:f"
119+
BuilderException, builder.compute_targets_to_build, "//project1:f"
120120
)
121121

122122
def test_targets_to_build_file_wildcard(self):

0 commit comments

Comments
 (0)