Skip to content

Commit 7c8993f

Browse files
committed
remove cursor files
1 parent 8f92eb5 commit 7c8993f

10 files changed

Lines changed: 118 additions & 53 deletions

File tree

src/flareio_cli/commands/export_identifier_credentials.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from flareio.api_client import FlareApiClient
88

99
from flareio_cli.api.client import get_api_client
10-
from flareio_cli.cursor import CursorFile
10+
from flareio_cli.cursor import Cursor
1111
from flareio_cli.exporters.credentials import export_credentials
1212

1313

@@ -26,7 +26,7 @@ def export_identifier_credentials(
2626
api_client: FlareApiClient = get_api_client()
2727

2828
# Load existing cursor if it exists.
29-
cursor: CursorFile = CursorFile(path=cursor_file)
29+
cursor: Cursor = Cursor.from_csv(path=output_file)
3030
if cursor.value():
3131
typer.echo(f"Found existing cursor. Will resume from cursor={cursor.value()}")
3232

src/flareio_cli/commands/export_tenant_credentials.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from flareio.api_client import FlareApiClient
88

99
from flareio_cli.api.client import get_api_client
10-
from flareio_cli.cursor import CursorFile
10+
from flareio_cli.cursor import Cursor
1111
from flareio_cli.exporters.credentials import export_credentials
1212

1313

@@ -17,15 +17,14 @@
1717
@app.command()
1818
def export_tenant_credentials(
1919
*,
20-
cursor_file: t.Annotated[pathlib.Path, typer.Option()],
2120
output_file: t.Annotated[pathlib.Path, typer.Option()],
2221
format: t.Literal["csv"] = "csv",
2322
) -> None:
2423
# Setup API client
2524
api_client: FlareApiClient = get_api_client()
2625

2726
# Load existing cursor if it exists.
28-
cursor: CursorFile = CursorFile(path=cursor_file)
27+
cursor: Cursor = Cursor.from_csv(path=output_file)
2928
if cursor.value():
3029
typer.echo(f"Found existing cursor. Will resume from cursor={cursor.value()}")
3130

src/flareio_cli/commands/export_tenant_events.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from flareio.models import ScrollEventsResult
1010

1111
from flareio_cli.api.client import get_api_client
12-
from flareio_cli.cursor import CursorFile
12+
from flareio_cli.cursor import Cursor
1313
from flareio_cli.exporters.events import export_events
1414

1515

@@ -28,7 +28,7 @@ def export_tenant_events(
2828
api_client: FlareApiClient = get_api_client()
2929

3030
# Load existing cursor if it exists.
31-
cursor: CursorFile = CursorFile(path=cursor_file)
31+
cursor: Cursor = Cursor.from_csv(path=cursor_file)
3232
if cursor.value():
3333
typer.echo(f"Found existing cursor. Will resume from cursor={cursor.value()}")
3434

src/flareio_cli/csv.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import typing as t
66

7+
from flareio_cli.cursor import Cursor
8+
79

810
CsvModel = t.TypeVar("CsvModel", bound=BaseModel)
911

@@ -15,21 +17,29 @@ def __init__(
1517
file: t.TextIO,
1618
model: t.Type[CsvModel],
1719
) -> None:
20+
fieldnames = [
21+
field_info.serialization_alias or field_name
22+
for field_name, field_info in model.model_fields.items()
23+
]
24+
fieldnames.append("next")
1825
self.dict_writer = csv.DictWriter(
1926
file,
20-
fieldnames=[
21-
field_info.serialization_alias or field_name
22-
for field_name, field_info in model.model_fields.items()
23-
],
27+
fieldnames=fieldnames,
2428
lineterminator="\n",
2529
)
2630
self.file = file
2731

2832
def writeheader(self) -> None:
2933
self.dict_writer.writeheader()
3034

31-
def writerow(self, row: CsvModel) -> None:
32-
dumped = row.model_dump(by_alias=True)
35+
def writerow(
36+
self,
37+
*,
38+
row: CsvModel,
39+
cursor: Cursor,
40+
) -> None:
41+
dumped: dict = row.model_dump(by_alias=True)
42+
dumped["next"] = cursor.value()
3343
self.dict_writer.writerow(dumped)
3444

3545
def flush(self) -> None:

src/flareio_cli/cursor.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
1+
import csv
12
import pathlib
23

34

4-
class CursorFile:
5+
class Cursor:
56
def __init__(
67
self,
78
*,
8-
path: pathlib.Path,
9+
value: str | None,
910
) -> None:
10-
self._path = path
11-
self._cached_value: str | None = None
11+
self._value: str | None = value
1212

1313
def value(self) -> str | None:
14-
if self._cached_value:
15-
return self._cached_value
16-
if not self._path.exists():
17-
return None
18-
cursor = self._path.read_text().strip() or None
19-
self._cached_value = cursor
20-
return cursor
14+
return self._value
2115

2216
def save(self, value: str | None) -> None:
2317
if not value:
2418
return
25-
self._cached_value = value
26-
self._path.write_text(value)
19+
self._value = value
20+
21+
@classmethod
22+
def from_csv(
23+
cls,
24+
*,
25+
path: pathlib.Path,
26+
) -> "Cursor":
27+
cursor = cls(value=None)
28+
29+
if not path.exists():
30+
return cursor
31+
32+
with open(path, "r", encoding="utf-8") as f:
33+
csv_reader = csv.DictReader(f)
34+
for row in csv_reader:
35+
next_ = row.get("next")
36+
cursor.save(next_)
37+
38+
return cursor

src/flareio_cli/exporters/base.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
import typing as t
88

99
from flareio_cli.csv import PydanticCsvWriter
10-
from flareio_cli.cursor import CursorFile
10+
from flareio_cli.cursor import Cursor
1111
from flareio_cli.progress import export_progress
1212

1313

14-
ExportItem = t.TypeVar("ExportItem", bound=pydantic.BaseModel)
14+
ExportItem = t.TypeVar(
15+
"ExportItem",
16+
bound=pydantic.BaseModel,
17+
)
1518

1619

1720
@dataclasses.dataclass(frozen=True)
@@ -24,7 +27,7 @@ def export_to_csv(
2427
*,
2528
output_file: pathlib.Path,
2629
pages: t.Iterator[ExportPage[ExportItem]],
27-
cursor: CursorFile,
30+
cursor: Cursor,
2831
object_name: str = "items",
2932
item_model: type[ExportItem],
3033
) -> None:
@@ -45,7 +48,7 @@ def _export_to_csv(
4548
*,
4649
output_file: pathlib.Path,
4750
pages: t.Iterator[ExportPage[ExportItem]],
48-
cursor: CursorFile,
51+
cursor: Cursor,
4952
object_name: str = "items",
5053
item_model: type[ExportItem],
5154
) -> None:
@@ -65,12 +68,15 @@ def _export_to_csv(
6568
writer.writeheader()
6669

6770
for page in pages:
71+
cursor.save(page.next)
72+
6873
for row in page.items:
69-
writer.writerow(row)
74+
writer.writerow(
75+
row=row,
76+
cursor=cursor,
77+
)
7078
writer.flush()
7179

72-
cursor.save(page.next)
73-
7480
progress_manager.update_progress(
7581
incr_completed=len(page.items),
7682
new_cursor=cursor.value(),

src/flareio_cli/exporters/credentials.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from flareio.ratelimit import Limiter
1111

1212
from flareio_cli.api.models.credentials import CredentialItem
13-
from flareio_cli.cursor import CursorFile
13+
from flareio_cli.cursor import Cursor
1414
from flareio_cli.exporters.base import ExportPage
1515
from flareio_cli.exporters.base import export_to_csv
1616

@@ -60,7 +60,7 @@ def export_credentials(
6060
*,
6161
output_file: pathlib.Path,
6262
resp_iterator: t.Iterator[requests.Response],
63-
cursor: CursorFile,
63+
cursor: Cursor,
6464
) -> None:
6565
export_to_csv(
6666
output_file=output_file,

src/flareio_cli/exporters/events.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from flareio.models import ScrollEventsResult
99

1010
from flareio_cli.api.models.events import EventItem
11-
from flareio_cli.cursor import CursorFile
11+
from flareio_cli.cursor import Cursor
1212
from flareio_cli.exporters.base import ExportPage
1313
from flareio_cli.exporters.base import export_to_csv
1414

@@ -45,7 +45,7 @@ def export_events(
4545
*,
4646
output_file: pathlib.Path,
4747
events_iterator: t.Iterator[ScrollEventsResult],
48-
cursor: CursorFile,
48+
cursor: Cursor,
4949
) -> None:
5050
export_to_csv(
5151
output_file=output_file,

src/flareio_cli/test_csv.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pydantic
44

55
from flareio_cli.csv import PydanticCsvWriter
6+
from flareio_cli.cursor import Cursor
67

78

89
def test_pydantic_csv_writer() -> None:
@@ -18,13 +19,19 @@ class TestModel(pydantic.BaseModel):
1819
)
1920

2021
writer.writeheader()
21-
writer.writerow(TestModel(first_field=1, second_field="Alice"))
22-
writer.writerow(TestModel(first_field=2, second_field="Bob"))
22+
writer.writerow(
23+
row=TestModel(first_field=1, second_field="Alice"),
24+
cursor=Cursor(value=None),
25+
)
26+
writer.writerow(
27+
row=TestModel(first_field=2, second_field="Bob"),
28+
cursor=Cursor(value=None),
29+
)
2330

2431
assert (
2532
csv_file_str.getvalue()
26-
== """first_field,second.field
27-
1,Alice
28-
2,Bob
33+
== """first_field,second.field,next
34+
1,Alice,
35+
2,Bob,
2936
"""
3037
)

src/flareio_cli/test_cursor.py

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,49 @@
1-
import pathlib
21
import tempfile
32

4-
from flareio_cli.cursor import CursorFile
3+
from pathlib import Path
54

5+
from flareio_cli.cursor import Cursor
66

7-
def test_cursor_file() -> None:
8-
with tempfile.NamedTemporaryFile() as f:
9-
cursor_path = pathlib.Path(f.name)
10-
cursor = CursorFile(path=cursor_path)
117

12-
assert cursor.value() is None
8+
def test_cursor() -> None:
9+
cursor = Cursor(value=None)
10+
11+
assert cursor.value() is None
12+
13+
cursor.save("123")
14+
assert cursor.value() == "123"
15+
16+
cursor.save(None)
17+
assert cursor.value() == "123"
1318

14-
cursor.save("123")
15-
assert cursor.value() == "123"
1619

17-
cursor.save(None)
18-
assert cursor.value() == "123"
20+
def test_cursor_from_csv() -> None:
21+
with tempfile.NamedTemporaryFile() as f:
22+
f.write(
23+
"""name,next
24+
bob,first
25+
bob,second
26+
bob,
27+
bob,third
28+
bob,
29+
""".encode()
30+
)
31+
f.flush()
32+
cursor = Cursor.from_csv(
33+
path=Path(f.name),
34+
)
35+
assert cursor.value() == "third"
36+
37+
38+
def test_cursor_from_csv_none() -> None:
39+
with tempfile.NamedTemporaryFile() as f:
40+
f.write(
41+
"""name,next
42+
bob,
43+
""".encode()
44+
)
45+
f.flush()
46+
cursor = Cursor.from_csv(
47+
path=Path(f.name),
48+
)
49+
assert cursor.value() is None

0 commit comments

Comments
 (0)