-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
ref(derived_code_mappings): Pass frame filename to find_roots #85931
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,10 @@ class UnsupportedFrameFilename(Exception): | |
pass | ||
|
||
|
||
class NeedsExtension(Exception): | ||
pass | ||
|
||
|
||
def derive_code_mappings( | ||
organization: Organization, | ||
frame: Mapping[str, Any], | ||
|
@@ -86,7 +90,7 @@ def __init__(self, frame: Mapping[str, Any]) -> None: | |
self.full_path = frame_file_path | ||
self.extension = get_extension(frame_file_path) | ||
if not self.extension: | ||
raise UnsupportedFrameFilename("It needs an extension.") | ||
raise NeedsExtension("It needs an extension.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
# Remove drive letter if it exists | ||
if is_windows_path and frame_file_path[1] == ":": | ||
|
@@ -158,25 +162,19 @@ def list_file_matches(self, frame_filename: FrameFilename) -> list[dict[str, str | |
for file in matches: | ||
stack_path = frame_filename.raw_path | ||
source_path = file | ||
extra = {"stack_path": stack_path, "source_path": source_path} | ||
|
||
try: | ||
stack_root, source_root = find_roots(stack_path, source_path) | ||
stack_root, source_root = find_roots(frame_filename, source_path) | ||
except UnexpectedPathException: | ||
logger.info( | ||
"Unexpected format for stack_path or source_path", | ||
extra={"stack_path": stack_path, "source_path": source_path}, | ||
) | ||
logger.warning("Unexpected format for stack_path or source_path", extra=extra) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changing it to |
||
continue | ||
|
||
extra.update({"stack_root": stack_root, "source_root": source_root}) | ||
if stack_path.replace(stack_root, source_root, 1).replace("\\", "/") != source_path: | ||
logger.info( | ||
logger.warning( | ||
"Unexpected stack_path/source_path found. A code mapping was not generated.", | ||
extra={ | ||
"stack_path": stack_path, | ||
"source_path": source_path, | ||
"stack_root": stack_root, | ||
"source_root": source_root, | ||
}, | ||
extra=extra, | ||
) | ||
else: | ||
file_matches.append( | ||
|
@@ -201,7 +199,7 @@ def _stacktrace_buckets( | |
# Any files without a top directory will be grouped together | ||
buckets[frame_filename.root].append(frame_filename) | ||
except UnsupportedFrameFilename: | ||
logger.info("Frame's filepath not supported: %s", frame.get("filename")) | ||
logger.warning("Frame's filepath not supported: %s", frame.get("filename")) | ||
except Exception: | ||
logger.exception("Unable to split stacktrace path into buckets") | ||
|
||
|
@@ -270,24 +268,18 @@ def _generate_code_mapping_from_tree( | |
stack_path = frame_filename.raw_path | ||
source_path = matched_files[0] | ||
|
||
extra = {"stack_path": stack_path, "source_path": source_path} | ||
try: | ||
stack_root, source_root = find_roots(stack_path, source_path) | ||
stack_root, source_root = find_roots(frame_filename, source_path) | ||
except UnexpectedPathException: | ||
logger.info( | ||
"Unexpected format for stack_path or source_path", | ||
extra={"stack_path": stack_path, "source_path": source_path}, | ||
) | ||
logger.warning("Unexpected format for stack_path or source_path", extra=extra) | ||
return [] | ||
|
||
extra.update({"stack_root": stack_root, "source_root": source_root}) | ||
if stack_path.replace(stack_root, source_root, 1).replace("\\", "/") != source_path: | ||
logger.info( | ||
logger.warning( | ||
"Unexpected stack_path/source_path found. A code mapping was not generated.", | ||
extra={ | ||
"stack_path": stack_path, | ||
"source_path": source_path, | ||
"stack_root": stack_root, | ||
"source_root": source_root, | ||
}, | ||
extra=extra, | ||
) | ||
return [] | ||
|
||
|
@@ -484,11 +476,12 @@ def get_straight_path_prefix_end_index(file_path: str) -> int: | |
return index | ||
|
||
|
||
def find_roots(stack_path: str, source_path: str) -> tuple[str, str]: | ||
def find_roots(frame_filename: FrameFilename, source_path: str) -> tuple[str, str]: | ||
""" | ||
Returns a tuple containing the stack_root, and the source_root. | ||
If there is no overlap, raise an exception since this should not happen | ||
""" | ||
stack_path = frame_filename.raw_path | ||
stack_root = "" | ||
if stack_path[0] == "/" or stack_path[0] == "\\": | ||
stack_root += stack_path[0] | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -15,6 +15,7 @@ | |||||||||||
CodeMapping, | ||||||||||||
CodeMappingTreesHelper, | ||||||||||||
FrameFilename, | ||||||||||||
NeedsExtension, | ||||||||||||
UnexpectedPathException, | ||||||||||||
UnsupportedFrameFilename, | ||||||||||||
convert_stacktrace_frame_path_to_source_path, | ||||||||||||
|
@@ -26,7 +27,7 @@ | |||||||||||
from sentry.testutils.silo import assume_test_silo_mode | ||||||||||||
from sentry.utils.event_frames import EventFrame | ||||||||||||
|
||||||||||||
sentry_files = [ | ||||||||||||
SENTRY_FILES = [ | ||||||||||||
"bin/__init__.py", | ||||||||||||
"bin/example1.py", | ||||||||||||
"bin/example2.py", | ||||||||||||
|
@@ -37,8 +38,6 @@ | |||||||||||
"src/sentry/wsgi.py", | ||||||||||||
"src/sentry_plugins/slack/client.py", | ||||||||||||
] | ||||||||||||
|
||||||||||||
|
||||||||||||
UNSUPPORTED_FRAME_FILENAMES = [ | ||||||||||||
"async https://s1.sentry-cdn.com/_static/dist/sentry/entrypoints/app.js", | ||||||||||||
"/gtm.js", # Top source; starts with backslash | ||||||||||||
|
@@ -47,20 +46,22 @@ | |||||||||||
"[native code]", | ||||||||||||
"O$t", | ||||||||||||
"async https://s1.sentry-cdn.com/_static/dist/sentry/entrypoints/app.js", | ||||||||||||
"/foo/bar/baz", # no extension | ||||||||||||
"README", # no extension | ||||||||||||
"README", # top level file | ||||||||||||
"ssl.py", | ||||||||||||
# XXX: The following will need to be supported | ||||||||||||
"initialization.dart", | ||||||||||||
"backburner.js", | ||||||||||||
] | ||||||||||||
NO_EXTENSION_FRAME_FILENAMES = [ | ||||||||||||
"/foo/bar/baz", # no extension | ||||||||||||
] | ||||||||||||
|
||||||||||||
|
||||||||||||
class TestRepoFiles(TestCase): | ||||||||||||
"""These evaluate which files should be included as part of a repo.""" | ||||||||||||
|
||||||||||||
def test_filter_source_code_files(self) -> None: | ||||||||||||
source_code_files = filter_source_code_files(sentry_files) | ||||||||||||
source_code_files = filter_source_code_files(SENTRY_FILES) | ||||||||||||
|
||||||||||||
assert source_code_files.index("bin/__init__.py") == 0 | ||||||||||||
assert source_code_files.index("docs-ui/.eslintrc.js") == 3 | ||||||||||||
|
@@ -109,19 +110,6 @@ def test_buckets_logic() -> None: | |||||||||||
|
||||||||||||
|
||||||||||||
class TestFrameFilename: | ||||||||||||
def test_frame_filename_package_and_more_than_one_level(self) -> None: | ||||||||||||
pytest.skip("This test is outdated because of refactors have been made to code mappings") | ||||||||||||
# ff = FrameFilename("getsentry/billing/tax/manager.py") | ||||||||||||
# assert f"{ff.root}/{ff.dir_path}/{ff.file_name}" == "getsentry/billing/tax/manager.py" | ||||||||||||
# assert f"{ff.dir_path}/{ff.file_name}" == ff.file_and_dir_path | ||||||||||||
|
||||||||||||
def test_frame_filename_package_and_no_levels(self) -> None: | ||||||||||||
pytest.skip("This test is outdated because of refactors have been made to code mappings") | ||||||||||||
# ff = FrameFilename("root/bar.py") | ||||||||||||
# assert f"{ff.root}/{ff.file_name}" == "root/bar.py" | ||||||||||||
# assert f"{ff.root}/{ff.file_and_dir_path}" == "root/bar.py" | ||||||||||||
# assert ff.dir_path == "" | ||||||||||||
|
||||||||||||
def test_frame_filename_repr(self) -> None: | ||||||||||||
path = "getsentry/billing/tax/manager.py" | ||||||||||||
assert FrameFilename({"filename": path}).__repr__() == f"FrameFilename: {path}" | ||||||||||||
|
@@ -131,6 +119,11 @@ def test_raises_unsupported(self) -> None: | |||||||||||
with pytest.raises(UnsupportedFrameFilename): | ||||||||||||
FrameFilename({"filename": filepath}) | ||||||||||||
|
||||||||||||
def test_raises_no_extension(self) -> None: | ||||||||||||
for filepath in NO_EXTENSION_FRAME_FILENAMES: | ||||||||||||
with pytest.raises(NeedsExtension): | ||||||||||||
FrameFilename({"filename": filepath}) | ||||||||||||
|
||||||||||||
@pytest.mark.parametrize( | ||||||||||||
"frame_filename, prefix", | ||||||||||||
[ | ||||||||||||
|
@@ -167,7 +160,7 @@ def setUp(self) -> None: | |||||||||||
self.bar_repo = RepoAndBranch("Test-Organization/bar", "main") | ||||||||||||
self.code_mapping_helper = CodeMappingTreesHelper( | ||||||||||||
{ | ||||||||||||
self.foo_repo.name: RepoTree(self.foo_repo, files=sentry_files), | ||||||||||||
self.foo_repo.name: RepoTree(self.foo_repo, files=SENTRY_FILES), | ||||||||||||
self.bar_repo.name: RepoTree(self.bar_repo, files=["sentry/web/urls.py"]), | ||||||||||||
} | ||||||||||||
) | ||||||||||||
|
@@ -303,52 +296,76 @@ def test_list_file_matches_multiple(self) -> None: | |||||||||||
assert matches == expected_matches | ||||||||||||
|
||||||||||||
def test_find_roots_starts_with_period_slash(self) -> None: | ||||||||||||
stacktrace_root, source_path = find_roots("./app/", "static/app/") | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know why I originally wrote the tests without a filename at the end of the path. It does not make sense to me. From looking at the calls, it is obvious that two full paths are used rather than base dirs: sentry/src/sentry/issues/auto_source_code_config/code_mapping.py Lines 159 to 163 in ed6ca20
|
||||||||||||
stacktrace_root, source_path = find_roots( | ||||||||||||
FrameFilename({"filename": "./app/foo.tsx"}), "static/app/foo.tsx" | ||||||||||||
) | ||||||||||||
assert stacktrace_root == "./" | ||||||||||||
assert source_path == "static/" | ||||||||||||
|
||||||||||||
def test_find_roots_starts_with_period_slash_no_containing_directory(self) -> None: | ||||||||||||
stacktrace_root, source_path = find_roots("./app/", "app/") | ||||||||||||
stacktrace_root, source_path = find_roots( | ||||||||||||
FrameFilename({"filename": "./app/foo.tsx"}), "app/foo.tsx" | ||||||||||||
) | ||||||||||||
assert stacktrace_root == "./" | ||||||||||||
assert source_path == "" | ||||||||||||
|
||||||||||||
def test_find_roots_not_matching(self) -> None: | ||||||||||||
stacktrace_root, source_path = find_roots("sentry/", "src/sentry/") | ||||||||||||
stacktrace_root, source_path = find_roots( | ||||||||||||
FrameFilename({"filename": "sentry/foo.py"}), "src/sentry/foo.py" | ||||||||||||
) | ||||||||||||
assert stacktrace_root == "sentry/" | ||||||||||||
assert source_path == "src/sentry/" | ||||||||||||
|
||||||||||||
def test_find_roots_equal(self) -> None: | ||||||||||||
stacktrace_root, source_path = find_roots("source/", "source/") | ||||||||||||
stacktrace_root, source_path = find_roots( | ||||||||||||
FrameFilename({"filename": "source/foo.py"}), "source/foo.py" | ||||||||||||
) | ||||||||||||
assert stacktrace_root == "" | ||||||||||||
assert source_path == "" | ||||||||||||
|
||||||||||||
def test_find_roots_starts_with_period_slash_two_levels(self) -> None: | ||||||||||||
stacktrace_root, source_path = find_roots("./app/", "app/foo/app/") | ||||||||||||
stacktrace_root, source_path = find_roots( | ||||||||||||
FrameFilename({"filename": "./app/foo.tsx"}), "app/foo/app/foo.tsx" | ||||||||||||
) | ||||||||||||
assert stacktrace_root == "./" | ||||||||||||
assert source_path == "app/foo/" | ||||||||||||
|
||||||||||||
def test_find_roots_starts_with_app(self) -> None: | ||||||||||||
stacktrace_root, source_path = find_roots("app:///utils/", "utils/") | ||||||||||||
stacktrace_root, source_path = find_roots( | ||||||||||||
FrameFilename({"filename": "app:///utils/foo.tsx"}), "utils/foo.tsx" | ||||||||||||
) | ||||||||||||
assert stacktrace_root == "app:///" | ||||||||||||
assert source_path == "" | ||||||||||||
|
||||||||||||
def test_find_roots_starts_with_multiple_dot_dot_slash(self) -> None: | ||||||||||||
stacktrace_root, source_path = find_roots("../../../../../../packages/", "packages/") | ||||||||||||
stacktrace_root, source_path = find_roots( | ||||||||||||
FrameFilename({"filename": "../../../../../../packages/foo.tsx"}), | ||||||||||||
"packages/foo.tsx", | ||||||||||||
) | ||||||||||||
assert stacktrace_root == "../../../../../../" | ||||||||||||
assert source_path == "" | ||||||||||||
|
||||||||||||
def test_find_roots_starts_with_app_dot_dot_slash(self) -> None: | ||||||||||||
stacktrace_root, source_path = find_roots("app:///../services/", "services/") | ||||||||||||
stacktrace_root, source_path = find_roots( | ||||||||||||
FrameFilename({"filename": "app:///../services/foo.tsx"}), | ||||||||||||
"services/foo.tsx", | ||||||||||||
) | ||||||||||||
assert stacktrace_root == "app:///../" | ||||||||||||
assert source_path == "" | ||||||||||||
|
||||||||||||
def test_find_roots_bad_stack_path(self) -> None: | ||||||||||||
with pytest.raises(UnexpectedPathException): | ||||||||||||
find_roots("https://yrurlsinyourstackpath.com/", "sentry/something.py") | ||||||||||||
find_roots( | ||||||||||||
FrameFilename({"filename": "https://yrurlsinyourstackpath.com/"}), | ||||||||||||
"sentry/something.py", | ||||||||||||
) | ||||||||||||
|
||||||||||||
def test_find_roots_bad_source_path(self) -> None: | ||||||||||||
with pytest.raises(UnexpectedPathException): | ||||||||||||
find_roots("sentry/random.py", "nothing/something.js") | ||||||||||||
find_roots( | ||||||||||||
FrameFilename({"filename": "sentry/random.py"}), | ||||||||||||
"nothing/something.js", | ||||||||||||
) | ||||||||||||
|
||||||||||||
|
||||||||||||
class TestConvertStacktraceFramePathToSourcePath(TestCase): | ||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A
FrameFilename
has more information thanstack_path
, thus, I can use it withinfind_roots
.