From e81cf0b169194d5a0364fdf4c85996e79aebde92 Mon Sep 17 00:00:00 2001 From: Harvir Sahota Date: Sat, 23 Mar 2024 14:17:14 -0700 Subject: [PATCH 1/3] Implement InlineWriteTracker --- python/selfie-lib/selfie_lib/ArrayMap.py | 6 ++- .../selfie-lib/selfie_lib/CommentTracker.py | 46 +++++++++++++++---- python/selfie-lib/selfie_lib/WriteTracker.py | 39 +++++++++++++++- 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/python/selfie-lib/selfie_lib/ArrayMap.py b/python/selfie-lib/selfie_lib/ArrayMap.py index 49d317b1..485fb296 100644 --- a/python/selfie-lib/selfie_lib/ArrayMap.py +++ b/python/selfie-lib/selfie_lib/ArrayMap.py @@ -9,10 +9,12 @@ class ListBackedSet(Set[T], ABC): @abstractmethod - def __len__(self) -> int: ... + def __len__(self) -> int: + ... @abstractmethod - def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]: ... + def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]: + ... def __contains__(self, item: object) -> bool: for i in range(len(self)): diff --git a/python/selfie-lib/selfie_lib/CommentTracker.py b/python/selfie-lib/selfie_lib/CommentTracker.py index e8ee1bf0..7631c7cb 100644 --- a/python/selfie-lib/selfie_lib/CommentTracker.py +++ b/python/selfie-lib/selfie_lib/CommentTracker.py @@ -1,18 +1,46 @@ -from typing import Dict, Iterable, Tuple +from typing import Dict, Iterable, Tuple, Sequence, TypeVar, Callable +from abc import ABC, abstractmethod from enum import Enum, auto import threading -from selfie_lib.TypedPath import TypedPath + from selfie_lib.Slice import Slice +from selfie_lib.TypedPath import TypedPath + +T = TypeVar("T") + + +class FS(ABC): + @abstractmethod + def file_walk(self, typed_path, walk: Callable[[Sequence["TypedPath"]], T]) -> T: + pass -# Placeholder implementations for CallStack, SnapshotFileLayout, and FS -class CallStack: - pass + @abstractmethod + def file_read(self, typed_path) -> str: + return self.file_read_binary(typed_path).decode() + + @abstractmethod + def file_write(self, typed_path, content: str): + self.file_write_binary(typed_path, content.encode()) + + @abstractmethod + def file_read_binary(self, typed_path) -> bytes: + pass + + @abstractmethod + def file_write_binary(self, typed_path, content: bytes): + pass + + @abstractmethod + def assert_failed(self, message: str, expected=None, actual=None) -> Exception: + pass class SnapshotFileLayout: + def __init__(self, fs: FS): + self.fs = fs + def sourcePathForCall(self, location) -> "TypedPath": - # Placeholder return or raise NotImplementedError raise NotImplementedError("sourcePathForCall is not implemented") @@ -39,8 +67,10 @@ def pathsWithOnce(self) -> Iterable[TypedPath]: if comment == WritableComment.ONCE ] - def hasWritableComment(self, call: CallStack, layout: SnapshotFileLayout) -> bool: - path = layout.sourcePathForCall(call) + def hasWritableComment(self, call, layout): + from selfie_lib.WriteTracker import CallStack + + path = layout.sourcePathForCall(call.location) with self.lock: if path in self.cache: comment = self.cache[path] diff --git a/python/selfie-lib/selfie_lib/WriteTracker.py b/python/selfie-lib/selfie_lib/WriteTracker.py index 37e2cd9b..b3c35e88 100644 --- a/python/selfie-lib/selfie_lib/WriteTracker.py +++ b/python/selfie-lib/selfie_lib/WriteTracker.py @@ -1,9 +1,13 @@ -from typing import List, Optional, Generic, TypeVar, Dict -from selfie_lib.CommentTracker import SnapshotFileLayout +from typing import List, Optional, Generic, TypeVar, Dict, cast from abc import ABC, abstractmethod import inspect, threading from functools import total_ordering +from selfie_lib.CommentTracker import SnapshotFileLayout +from selfie_lib.SourceFile import SourceFile +from selfie_lib.Literals import LiteralValue + + T = TypeVar("T") U = TypeVar("U") @@ -132,3 +136,34 @@ def recordInternal( class DiskWriteTracker(WriteTracker[T, U]): def record(self, key: T, snapshot: U, call: CallStack, layout: SnapshotFileLayout): super().recordInternal(key, snapshot, call, layout) + + +class InlineWriteTracker(WriteTracker[CallLocation, LiteralValue]): + def record( + self, + key: CallLocation, + snapshot: LiteralValue, + call: CallStack, + layout: SnapshotFileLayout, + ): + super().recordInternal(key, snapshot, call, layout) + + file = layout.sourcePathForCall(key) + if snapshot.expected is not None: + content = SourceFile(file.name, layout.fs.file_read(file)) + try: + snapshot = cast(LiteralValue, snapshot) + parsed_value = content.parse_to_be_like(key.line).parse_literal( + snapshot.format + ) + except Exception as e: + raise AssertionError( + f"Error while parsing the literal at {key.ide_link(layout)}. Please report this error at https://github.com/diffplug/selfie", + e, + ) + if parsed_value != snapshot.expected: + raise layout.fs.assert_failed( + f"Selfie cannot modify the literal at {key.ide_link(layout)} because Selfie has a parsing bug. Please report this error at https://github.com/diffplug/selfie", + snapshot.expected, + parsed_value, + ) From 5b4bd9e41f71abfd8a7bce889dfd388c45b710e3 Mon Sep 17 00:00:00 2001 From: Harvir Sahota Date: Sat, 23 Mar 2024 14:21:46 -0700 Subject: [PATCH 2/3] Update ArrayMap --- python/selfie-lib/selfie_lib/ArrayMap.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/python/selfie-lib/selfie_lib/ArrayMap.py b/python/selfie-lib/selfie_lib/ArrayMap.py index 485fb296..49d317b1 100644 --- a/python/selfie-lib/selfie_lib/ArrayMap.py +++ b/python/selfie-lib/selfie_lib/ArrayMap.py @@ -9,12 +9,10 @@ class ListBackedSet(Set[T], ABC): @abstractmethod - def __len__(self) -> int: - ... + def __len__(self) -> int: ... @abstractmethod - def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]: - ... + def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]: ... def __contains__(self, item: object) -> bool: for i in range(len(self)): From 4d6d4c4bdb397b71529ccb6fe4cb62cc381a55d3 Mon Sep 17 00:00:00 2001 From: Harvir Sahota Date: Sun, 24 Mar 2024 13:09:08 -0700 Subject: [PATCH 3/3] Update WriteTracker and CommentTracker --- .../selfie-lib/selfie_lib/CommentTracker.py | 44 ++----------------- python/selfie-lib/selfie_lib/WriteTracker.py | 36 ++++++++++++++- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/python/selfie-lib/selfie_lib/CommentTracker.py b/python/selfie-lib/selfie_lib/CommentTracker.py index 7631c7cb..8e23e0c5 100644 --- a/python/selfie-lib/selfie_lib/CommentTracker.py +++ b/python/selfie-lib/selfie_lib/CommentTracker.py @@ -4,44 +4,8 @@ import threading from selfie_lib.Slice import Slice - from selfie_lib.TypedPath import TypedPath - -T = TypeVar("T") - - -class FS(ABC): - @abstractmethod - def file_walk(self, typed_path, walk: Callable[[Sequence["TypedPath"]], T]) -> T: - pass - - @abstractmethod - def file_read(self, typed_path) -> str: - return self.file_read_binary(typed_path).decode() - - @abstractmethod - def file_write(self, typed_path, content: str): - self.file_write_binary(typed_path, content.encode()) - - @abstractmethod - def file_read_binary(self, typed_path) -> bytes: - pass - - @abstractmethod - def file_write_binary(self, typed_path, content: bytes): - pass - - @abstractmethod - def assert_failed(self, message: str, expected=None, actual=None) -> Exception: - pass - - -class SnapshotFileLayout: - def __init__(self, fs: FS): - self.fs = fs - - def sourcePathForCall(self, location) -> "TypedPath": - raise NotImplementedError("sourcePathForCall is not implemented") +from selfie_lib.WriteTracker import CallStack, SnapshotFileLayout class WritableComment(Enum): @@ -67,10 +31,8 @@ def pathsWithOnce(self) -> Iterable[TypedPath]: if comment == WritableComment.ONCE ] - def hasWritableComment(self, call, layout): - from selfie_lib.WriteTracker import CallStack - - path = layout.sourcePathForCall(call.location) + def hasWritableComment(self, call: CallStack, layout: SnapshotFileLayout) -> bool: + path = layout.sourcePathForCall(call) with self.lock: if path in self.cache: comment = self.cache[path] diff --git a/python/selfie-lib/selfie_lib/WriteTracker.py b/python/selfie-lib/selfie_lib/WriteTracker.py index b3c35e88..579a2afd 100644 --- a/python/selfie-lib/selfie_lib/WriteTracker.py +++ b/python/selfie-lib/selfie_lib/WriteTracker.py @@ -1,17 +1,49 @@ -from typing import List, Optional, Generic, TypeVar, Dict, cast +from typing import List, Optional, Generic, TypeVar, Dict, cast, Callable, Sequence from abc import ABC, abstractmethod import inspect, threading from functools import total_ordering -from selfie_lib.CommentTracker import SnapshotFileLayout from selfie_lib.SourceFile import SourceFile from selfie_lib.Literals import LiteralValue +from selfie_lib.TypedPath import TypedPath T = TypeVar("T") U = TypeVar("U") +class FS(ABC): + @abstractmethod + def file_walk(self, typed_path, walk: Callable[[Sequence["TypedPath"]], T]) -> T: + pass + + def file_read(self, typed_path) -> str: + return self.file_read_binary(typed_path).decode() + + def file_write(self, typed_path, content: str): + self.file_write_binary(typed_path, content.encode()) + + @abstractmethod + def file_read_binary(self, typed_path) -> bytes: + pass + + @abstractmethod + def file_write_binary(self, typed_path, content: bytes): + pass + + @abstractmethod + def assert_failed(self, message: str, expected=None, actual=None) -> Exception: + pass + + +class SnapshotFileLayout: + def __init__(self, fs: FS): + self.fs = fs + + def sourcePathForCall(self, location) -> "TypedPath": + raise NotImplementedError("sourcePathForCall is not implemented") + + @total_ordering class CallLocation: def __init__(self, file_name: Optional[str], line: int):