From 32651b685428ad846b1aa12c15e7bfbaefc25287 Mon Sep 17 00:00:00 2001 From: Harvir Sahota Date: Mon, 11 Mar 2024 11:13:59 -0700 Subject: [PATCH 1/2] Push WriteTracker and RecordCall_test --- python/selfie-lib/selfie_lib/WriteTracker.py | 74 ++++++++++++++++++++ python/selfie-lib/tests/RecordCall_test.py | 34 +++++++++ 2 files changed, 108 insertions(+) create mode 100644 python/selfie-lib/selfie_lib/WriteTracker.py create mode 100644 python/selfie-lib/tests/RecordCall_test.py diff --git a/python/selfie-lib/selfie_lib/WriteTracker.py b/python/selfie-lib/selfie_lib/WriteTracker.py new file mode 100644 index 00000000..0e703462 --- /dev/null +++ b/python/selfie-lib/selfie_lib/WriteTracker.py @@ -0,0 +1,74 @@ +from typing import List, Optional +from selfie_lib.CommentTracker import SnapshotFileLayout +import inspect + + +class CallLocation: + def __init__(self, file_name: Optional[str], line: int): + self._file_name = file_name + self._line = line + + @property + def file_name(self) -> Optional[str]: + return self._file_name + + @property + def line(self) -> int: + return self._line + + def with_line(self, line: int) -> "CallLocation": + return CallLocation(self._file_name, line) + + def ide_link(self, layout: "SnapshotFileLayout") -> str: + return f"File: {self._file_name}, Line: {self._line}" + + def same_path_as(self, other: "CallLocation") -> bool: + if not isinstance(other, CallLocation): + return False + return self._file_name == other.file_name + + def source_filename_without_extension(self) -> str: + if self._file_name is not None: + return self._file_name.rsplit(".", 1)[0] + return "" + + def __lt__(self, other) -> bool: + if not isinstance(other, CallLocation): + return NotImplemented + return (self._file_name, self._line) < (other.file_name, other.line) + + def __eq__(self, other) -> bool: + if not isinstance(other, CallLocation): + return NotImplemented + return (self._file_name, self._line) == (other.file_name, other.line) + + +class CallStack: + def __init__(self, location: CallLocation, rest_of_stack: List[CallLocation]): + self.location = location + self.rest_of_stack = rest_of_stack + + def ide_link(self, layout: "SnapshotFileLayout") -> str: + links = [self.location.ide_link(layout)] + [ + loc.ide_link(layout) for loc in self.rest_of_stack + ] + return "\n".join(links) + + +def recordCall(callerFileOnly: bool = False) -> CallStack: + stack_frames = inspect.stack()[1:] + + if callerFileOnly: + caller_file = stack_frames[0].filename + stack_frames = [ + frame for frame in stack_frames if frame.filename == caller_file + ] + + call_locations = [ + CallLocation(frame.filename, frame.lineno) for frame in stack_frames + ] + + location = call_locations[0] + rest_of_stack = call_locations[1:] + + return CallStack(location, rest_of_stack) diff --git a/python/selfie-lib/tests/RecordCall_test.py b/python/selfie-lib/tests/RecordCall_test.py new file mode 100644 index 00000000..83ae8fdf --- /dev/null +++ b/python/selfie-lib/tests/RecordCall_test.py @@ -0,0 +1,34 @@ +from unittest.mock import Mock +from selfie_lib.WriteTracker import CallLocation, CallStack, recordCall + + +def test_call_location_ide_link(): + layout = Mock() + location = CallLocation(file_name="example.py", line=10) + expected_link = "File: example.py, Line: 10" + + assert location.ide_link(layout) == expected_link + + +def test_call_stack_ide_link(): + layout = Mock() + location1 = CallLocation(file_name="example1.py", line=10) + location2 = CallLocation(file_name="example2.py", line=20) + call_stack = CallStack(location=location1, rest_of_stack=[location2]) + + expected_links = "File: example1.py, Line: 10\nFile: example2.py, Line: 20" + assert call_stack.ide_link(layout) == expected_links + + +def test_record_call_with_caller_file_only_false(): + call_stack = recordCall(False) + assert ( + len(call_stack.rest_of_stack) > 0 + ), "Expected the rest of stack to contain more than one CallLocation" + + +def test_record_call_with_caller_file_only_true(): + call_stack = recordCall(True) + assert ( + len(call_stack.rest_of_stack) >= 0 + ), "Expected the rest of stack to potentially contain only the caller's file location" From 336d9169f8e9b95c10646551b2c1e5edec69795b Mon Sep 17 00:00:00 2001 From: Harvir Sahota Date: Mon, 11 Mar 2024 16:23:51 -0700 Subject: [PATCH 2/2] Fix changes --- python/selfie-lib/selfie_lib/WriteTracker.py | 2 ++ python/selfie-lib/tests/RecordCall_test.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/python/selfie-lib/selfie_lib/WriteTracker.py b/python/selfie-lib/selfie_lib/WriteTracker.py index 0e703462..def334a8 100644 --- a/python/selfie-lib/selfie_lib/WriteTracker.py +++ b/python/selfie-lib/selfie_lib/WriteTracker.py @@ -1,8 +1,10 @@ from typing import List, Optional from selfie_lib.CommentTracker import SnapshotFileLayout import inspect +from functools import total_ordering +@total_ordering class CallLocation: def __init__(self, file_name: Optional[str], line: int): self._file_name = file_name diff --git a/python/selfie-lib/tests/RecordCall_test.py b/python/selfie-lib/tests/RecordCall_test.py index 83ae8fdf..cb9b6980 100644 --- a/python/selfie-lib/tests/RecordCall_test.py +++ b/python/selfie-lib/tests/RecordCall_test.py @@ -1,5 +1,6 @@ from unittest.mock import Mock from selfie_lib.WriteTracker import CallLocation, CallStack, recordCall +import os def test_call_location_ide_link(): @@ -22,10 +23,25 @@ def test_call_stack_ide_link(): def test_record_call_with_caller_file_only_false(): call_stack = recordCall(False) + assert ( len(call_stack.rest_of_stack) > 0 ), "Expected the rest of stack to contain more than one CallLocation" + expected_call_location_str = "File: RecordCall_test.py, Line: 25" + + if call_stack.location.file_name is not None: + actual_file_name = os.path.basename(call_stack.location.file_name) + actual_call_location_str = ( + f"File: {actual_file_name}, Line: {call_stack.location.line}" + ) + else: + actual_call_location_str = "File name is None" + + assert ( + actual_call_location_str == expected_call_location_str + ), f"Expected call location to be '{expected_call_location_str}' but was '{actual_call_location_str}'" + def test_record_call_with_caller_file_only_true(): call_stack = recordCall(True)