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

Commit 43ad165

Browse files
committed
SnapshotReader passing all the test
1 parent 0e340ed commit 43ad165

9 files changed

+242
-157
lines changed

python/selfie-lib/selfie_lib/ArrayMap.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99

1010
class ListBackedSet(Set[T], ABC):
1111
@abstractmethod
12-
def __len__(self) -> int: ...
12+
def __len__(self) -> int:
13+
...
1314

1415
@abstractmethod
15-
def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]: ...
16+
def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]:
17+
...
1618

1719
def __contains__(self, item: object) -> bool:
1820
for i in range(len(self)):

python/selfie-lib/selfie_lib/ConvertToWindowsNewlines.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ def __init__(self, sink):
55
def append(self, value, start_index=None, end_index=None):
66
# If value is a single character
77
if isinstance(value, str) and len(value) == 1:
8-
if value != '\n':
8+
if value != "\n":
99
self.sink.write(value)
1010
else:
11-
self.sink.write('\r\n')
11+
self.sink.write("\r\n")
1212
# If value is a CharSequence (in Python, a str)
1313
elif isinstance(value, str):
1414
# If start_index and end_index are provided, use the slice of the string

python/selfie-lib/selfie_lib/Snapshot.py

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
from .SnapshotValue import SnapshotValue
22
from collections import OrderedDict
3+
import logging
4+
5+
logging.basicConfig(level=logging.DEBUG)
6+
logger = logging.getLogger(__name__)
7+
38

49
class Snapshot:
510
def __init__(self, subject, facet_data):
@@ -10,6 +15,14 @@ def __init__(self, subject, facet_data):
1015
def facets(self):
1116
return OrderedDict(sorted(self._facet_data.items()))
1217

18+
def __eq__(self, other):
19+
if not isinstance(other, Snapshot):
20+
return NotImplemented
21+
return self._subject == other._subject and self._facet_data == other._facet_data
22+
23+
def __hash__(self):
24+
return hash((self._subject, frozenset(self._facet_data.items())))
25+
1326
def plus_facet(self, key, value):
1427
if isinstance(value, bytes):
1528
value = SnapshotValue.of(value)
@@ -47,7 +60,7 @@ def all_entries(self):
4760
entries.extend(self._facet_data.items())
4861
return entries
4962

50-
def __str__(self):
63+
def __bytes__(self):
5164
return f"[{self._subject} {self._facet_data}]"
5265

5366
@staticmethod
@@ -58,8 +71,10 @@ def of(data):
5871
elif isinstance(data, str):
5972
# Handling string data
6073
return Snapshot(SnapshotValue.of(data), {})
74+
elif isinstance(data, SnapshotValue):
75+
return Snapshot(data, {})
6176
else:
62-
raise TypeError("Data must be either binary or string")
77+
raise TypeError("Data must be either binary or string" + data)
6378

6479
@staticmethod
6580
def of_entries(entries):
@@ -68,7 +83,9 @@ def of_entries(entries):
6883
for key, value in entries:
6984
if not key:
7085
if subject is not None:
71-
raise ValueError(f"Duplicate root snapshot.\n first: {subject}\nsecond: {value}")
86+
raise ValueError(
87+
f"Duplicate root snapshot.\n first: {subject}\nsecond: {value}"
88+
)
7289
subject = value
7390
else:
7491
facet_data[key] = value
@@ -78,4 +95,4 @@ def of_entries(entries):
7895

7996
@staticmethod
8097
def _unix_newlines(string):
81-
return string.replace("\\r\\n", "\\n")
98+
return string.replace("\\r\\n", "\\n")
+134-121
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,134 @@
1-
import threading
2-
from typing import List
3-
import base64
4-
5-
from .SnapshotValue import SnapshotValue
6-
from .ConvertToWindowsNewlines import ConvertToWindowsNewlines
7-
from .ParseException import ParseException
8-
from .SnapshotReader import SnapshotReader
9-
from .SnapshotValueReader import SnapshotValueReader
10-
11-
class SnapshotFile:
12-
HEADER_PREFIX = "📷 "
13-
END_OF_FILE = "[end of file]"
14-
15-
def __init__(self):
16-
self.unix_newlines = True
17-
self.metadata = None
18-
self._snapshots = {}
19-
self._lock = threading.Lock()
20-
self._was_set_at_test_time = False
21-
22-
@property
23-
def snapshots(self):
24-
return self._snapshots
25-
26-
@snapshots.setter
27-
def snapshots(self, value):
28-
with self._lock:
29-
self._snapshots = value
30-
31-
@property
32-
def was_set_at_test_time(self):
33-
return self._was_set_at_test_time
34-
35-
def set_at_test_time(self, key, snapshot):
36-
with self._lock:
37-
old_snapshots = self._snapshots.copy()
38-
self._snapshots[key] = snapshot
39-
self._was_set_at_test_time = True if self._snapshots != old_snapshots else self._was_set_at_test_time
40-
41-
def serialize(self, value_writer_raw):
42-
value_writer = value_writer_raw if self.unix_newlines else ConvertToWindowsNewlines(value_writer_raw)
43-
if self.metadata:
44-
self.write_entry(value_writer, f"📷 {self.metadata[0]}", None, SnapshotValue.of(self.metadata[1]))
45-
for key, snapshot in self._snapshots.items():
46-
self.write_entry(value_writer, key, None, snapshot.subject)
47-
for facet_key, facet_value in snapshot.facets.items():
48-
self.write_entry(value_writer, key, facet_key, facet_value)
49-
self.write_entry(value_writer, "", "end of file", SnapshotValue.of(""))
50-
51-
def write_entry(value_writer, key, facet, value):
52-
value_writer.write("╔═ ")
53-
value_writer.write(SnapshotValueReader.nameEsc.escape(key))
54-
if facet is not None:
55-
value_writer.write("[")
56-
value_writer.write(SnapshotValueReader.nameEsc.escape(facet))
57-
value_writer.write("]")
58-
value_writer.write(" ═╗")
59-
if value.is_binary:
60-
binary_length = len(value.value_binary())
61-
value_writer.write(f" base64 length {binary_length} bytes")
62-
value_writer.write("\n")
63-
64-
if key == "" and facet == "end of file":
65-
return
66-
67-
if value.is_binary:
68-
# Base64 encoding and replacing \r with an empty string
69-
binary_data = value.value_binary()
70-
encoded = base64.b64encode(binary_data).decode('utf-8')
71-
# Assuming efficientReplace is a more efficient method for replacing characters
72-
# Here, we just use the regular replace method for simplicity
73-
escaped = encoded.replace("\r", "")
74-
value_writer.write(escaped)
75-
else:
76-
# For string values, applying specific escape logic and then replacing "\n╔" with a special sequence
77-
text_data = value.value_string()
78-
escaped = SnapshotValueReader.bodyEsc(text_data).replace("\n╔", "\n\uDF41")
79-
value_writer.write(escaped)
80-
value_writer.write("\n")
81-
82-
@staticmethod
83-
def parse(value_reader):
84-
try:
85-
result = SnapshotFile()
86-
result.unix_newlines = value_reader.unix_newlines
87-
reader = SnapshotReader(value_reader)
88-
89-
# Check if the first value starts with 📷
90-
if reader.peek_key() and reader.peek_key().startswith(SnapshotFile.HEADER_PREFIX):
91-
metadata_name = reader.peek_key()[len(SnapshotFile.HEADER_PREFIX):]
92-
metadata_value = reader.value_reader.next_value().value_string()
93-
# Assuming 'entry' function creates a dictionary entry in Python
94-
result.metadata = (metadata_name, metadata_value)
95-
96-
while reader.peek_key() is not None:
97-
key = reader.peek_key()
98-
snapshot = reader.next_snapshot()
99-
# Update snapshots dictionary with new key-value pair
100-
result.snapshots.update({key: snapshot})
101-
102-
return result
103-
104-
except ValueError as e:
105-
if isinstance(e, ParseException):
106-
raise e
107-
else:
108-
raise ParseException(value_reader.line_reader, e) from None
109-
110-
111-
@staticmethod
112-
def create_empty_with_unix_newlines(unix_newlines):
113-
result = SnapshotFile()
114-
result.unix_newlines = unix_newlines
115-
return result
116-
117-
def remove_all_indices(self, indices: List[int]):
118-
if not indices:
119-
return
120-
self._was_set_at_test_time = True
121-
self.snapshots = self.snapshots.minus_sorted_indices(indices)
1+
# import threading
2+
# from typing import List
3+
# import base64
4+
5+
# from .SnapshotValue import SnapshotValue
6+
# from .ConvertToWindowsNewlines import ConvertToWindowsNewlines
7+
# from .ParseException import ParseException
8+
# from .SnapshotReader import SnapshotReader
9+
# from .SnapshotValueReader import SnapshotValueReader
10+
11+
12+
# class SnapshotFile:
13+
# HEADER_PREFIX = "📷 "
14+
# END_OF_FILE = "[end of file]"
15+
16+
# def __init__(self):
17+
# self.unix_newlines = True
18+
# self.metadata = None
19+
# self._snapshots = {}
20+
# self._lock = threading.Lock()
21+
# self._was_set_at_test_time = False
22+
23+
# @property
24+
# def snapshots(self):
25+
# return self._snapshots
26+
27+
# @snapshots.setter
28+
# def snapshots(self, value):
29+
# with self._lock:
30+
# self._snapshots = value
31+
32+
# @property
33+
# def was_set_at_test_time(self):
34+
# return self._was_set_at_test_time
35+
36+
# def set_at_test_time(self, key, snapshot):
37+
# with self._lock:
38+
# old_snapshots = self._snapshots.copy()
39+
# self._snapshots[key] = snapshot
40+
# self._was_set_at_test_time = (
41+
# True if self._snapshots != old_snapshots else self._was_set_at_test_time
42+
# )
43+
44+
# def serialize(self, value_writer_raw):
45+
# value_writer = (
46+
# value_writer_raw
47+
# if self.unix_newlines
48+
# else ConvertToWindowsNewlines(value_writer_raw)
49+
# )
50+
# if self.metadata:
51+
# self.write_entry(
52+
# value_writer,
53+
# f"📷 {self.metadata[0]}",
54+
# None,
55+
# SnapshotValue.of(self.metadata[1]),
56+
# )
57+
# for key, snapshot in self._snapshots.items():
58+
# self.write_entry(value_writer, key, None, snapshot.subject)
59+
# for facet_key, facet_value in snapshot.facets.items():
60+
# self.write_entry(value_writer, key, facet_key, facet_value)
61+
# self.write_entry(value_writer, "", "end of file", SnapshotValue.of(""))
62+
63+
# def write_entry(value_writer, key, facet, value):
64+
# value_writer.write("╔═ ")
65+
# value_writer.write(SnapshotValueReader.nameEsc.escape(key))
66+
# if facet is not None:
67+
# value_writer.write("[")
68+
# value_writer.write(SnapshotValueReader.nameEsc.escape(facet))
69+
# value_writer.write("]")
70+
# value_writer.write(" ═╗")
71+
# if value.is_binary:
72+
# binary_length = len(value.value_binary())
73+
# value_writer.write(f" base64 length {binary_length} bytes")
74+
# value_writer.write("\n")
75+
76+
# if key == "" and facet == "end of file":
77+
# return
78+
79+
# if value.is_binary:
80+
# # Base64 encoding and replacing \r with an empty string
81+
# binary_data = value.value_binary()
82+
# encoded = base64.b64encode(binary_data).decode("utf-8")
83+
# # Assuming efficientReplace is a more efficient method for replacing characters
84+
# # Here, we just use the regular replace method for simplicity
85+
# escaped = encoded.replace("\r", "")
86+
# value_writer.write(escaped)
87+
# else:
88+
# # For string values, applying specific escape logic and then replacing "\n╔" with a special sequence
89+
# text_data = value.value_string()
90+
# escaped = SnapshotValueReader.bodyEsc(text_data).replace("\n╔", "\n\uDF41")
91+
# value_writer.write(escaped)
92+
# value_writer.write("\n")
93+
94+
# @staticmethod
95+
# def parse(value_reader):
96+
# try:
97+
# result = SnapshotFile()
98+
# result.unix_newlines = value_reader.unix_newlines
99+
# reader = SnapshotReader(value_reader)
100+
101+
# # Check if the first value starts with 📷
102+
# if reader.peek_key() and reader.peek_key().startswith(
103+
# SnapshotFile.HEADER_PREFIX
104+
# ):
105+
# metadata_name = reader.peek_key()[len(SnapshotFile.HEADER_PREFIX) :]
106+
# metadata_value = reader.value_reader.next_value().value_string()
107+
# # Assuming 'entry' function creates a dictionary entry in Python
108+
# result.metadata = (metadata_name, metadata_value)
109+
110+
# while reader.peek_key() is not None:
111+
# key = reader.peek_key()
112+
# snapshot = reader.next_snapshot()
113+
# # Update snapshots dictionary with new key-value pair
114+
# result.snapshots.update({key: snapshot})
115+
116+
# return result
117+
118+
# except ValueError as e:
119+
# if isinstance(e, ParseException):
120+
# raise e
121+
# else:
122+
# raise ParseException(value_reader.line_reader, e) from None
123+
124+
# @staticmethod
125+
# def create_empty_with_unix_newlines(unix_newlines):
126+
# result = SnapshotFile()
127+
# result.unix_newlines = unix_newlines
128+
# return result
129+
130+
# def remove_all_indices(self, indices: List[int]):
131+
# if not indices:
132+
# return
133+
# self._was_set_at_test_time = True
134+
# self.snapshots = self.snapshots.minus_sorted_indices(indices)

python/selfie-lib/selfie_lib/SnapshotReader.py

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .Snapshot import Snapshot
22

3+
34
class SnapshotReader:
45
def __init__(self, value_reader):
56
self.value_reader = value_reader
@@ -8,8 +9,10 @@ def peek_key(self):
89
next_key = self.value_reader.peek_key()
910
if next_key is None or next_key == "[end of file]":
1011
return None
11-
if '[' in next_key:
12-
raise ValueError(f"Missing root snapshot, square brackets not allowed: '{next_key}'")
12+
if "[" in next_key:
13+
raise ValueError(
14+
f"Missing root snapshot, square brackets not allowed: '{next_key}'"
15+
)
1316
return next_key
1417

1518
def next_snapshot(self):
@@ -19,18 +22,20 @@ def next_snapshot(self):
1922
next_key = self.value_reader.peek_key()
2023
if next_key is None:
2124
return snapshot
22-
facet_idx = next_key.find('[')
25+
facet_idx = next_key.find("[")
2326
if facet_idx == -1 or (facet_idx == 0 and next_key == "[end of file]"):
2427
return snapshot
2528
facet_root = next_key[:facet_idx]
2629
if facet_root != root_name:
27-
raise ValueError(f"Expected '{next_key}' to come after '{facet_root}', not '{root_name}'")
28-
facet_end_idx = next_key.find(']', facet_idx + 1)
30+
raise ValueError(
31+
f"Expected '{next_key}' to come after '{facet_root}', not '{root_name}'"
32+
)
33+
facet_end_idx = next_key.find("]", facet_idx + 1)
2934
if facet_end_idx == -1:
3035
raise ValueError(f"Missing ] in {next_key}")
31-
facet_name = next_key[facet_idx + 1:facet_end_idx]
36+
facet_name = next_key[facet_idx + 1 : facet_end_idx]
3237
snapshot = snapshot.plus_facet(facet_name, self.value_reader.next_value())
33-
38+
3439
def skip_snapshot(self):
3540
root_name = self.peek_key()
3641
if root_name is None:

0 commit comments

Comments
 (0)