Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
recordCall and CallStack/CallLocation (#35 closes #31)
Browse files Browse the repository at this point in the history
  • Loading branch information
nedtwigg authored Mar 12, 2024
2 parents 2aededc + 336d916 commit 417eaf6
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 0 deletions.
76 changes: 76 additions & 0 deletions python/selfie-lib/selfie_lib/WriteTracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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
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)
50 changes: 50 additions & 0 deletions python/selfie-lib/tests/RecordCall_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from unittest.mock import Mock
from selfie_lib.WriteTracker import CallLocation, CallStack, recordCall
import os


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"

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)
assert (
len(call_stack.rest_of_stack) >= 0
), "Expected the rest of stack to potentially contain only the caller's file location"

0 comments on commit 417eaf6

Please sign in to comment.