Skip to content

Commit 63317a8

Browse files
author
Hugo Osvaldo Barrera
committed
Prefer pathlib.Path to plain strings
1 parent 4ed9b5a commit 63317a8

File tree

4 files changed

+66
-65
lines changed

4 files changed

+66
-65
lines changed

tests/test_cli.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import datetime
22
import sys
33
from os.path import exists
4+
from os.path import isdir
5+
from pathlib import Path
46
from unittest import mock
57
from unittest.mock import call
68
from unittest.mock import patch
@@ -11,14 +13,14 @@
1113
from dateutil.tz import tzlocal
1214
from freezegun import freeze_time
1315
from hypothesis import given
14-
15-
from tests.helpers import fs_case_sensitive
16-
from tests.helpers import pyicu_sensitive
1716
from todoman.cli import cli
1817
from todoman.cli import exceptions
1918
from todoman.model import Database
2019
from todoman.model import Todo
2120

21+
from tests.helpers import fs_case_sensitive
22+
from tests.helpers import pyicu_sensitive
23+
2224
# TODO: test --grep
2325

2426

@@ -468,8 +470,8 @@ def test_edit_move(runner, todo_factory, default_database, tmpdir, todos):
468470
tmpdir.mkdir("another_list")
469471

470472
default_database.paths = [
471-
str(tmpdir.join("default")),
472-
str(tmpdir.join("another_list")),
473+
Path(tmpdir.join("default")),
474+
Path(tmpdir.join("another_list")),
473475
]
474476
default_database.update_cache()
475477

tests/test_model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from datetime import date
22
from datetime import datetime
33
from datetime import timedelta
4+
from pathlib import Path
45
from unittest.mock import patch
56

67
import pytest
78
import pytz
89
from dateutil.tz import tzlocal
910
from dateutil.tz.tz import tzoffset
1011
from freezegun import freeze_time
11-
1212
from todoman.exceptions import AlreadyExists
1313
from todoman.model import Database
1414
from todoman.model import Todo
@@ -65,7 +65,7 @@ def test_change_paths(tmpdir, create):
6565

6666
assert {t.summary for t in db.todos()} == old_todos
6767

68-
db.paths = [str(tmpdir.join("3"))]
68+
db.paths = [Path(tmpdir.join("3"))]
6969
db.update_cache()
7070

7171
assert len(list(db.lists())) == 1

tests/test_ui.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from datetime import datetime
2+
from pathlib import Path
23
from unittest import mock
34

45
import pytest
56
import pytz
67
from freezegun import freeze_time
7-
from urwid import ExitMainLoop
8-
98
from todoman.interactive import TodoEditor
9+
from urwid import ExitMainLoop
1010

1111

1212
def test_todo_editor_priority(default_database, todo_factory, default_formatter):
@@ -27,8 +27,8 @@ def test_todo_editor_list(default_database, todo_factory, default_formatter, tmp
2727
tmpdir.mkdir("another_list")
2828

2929
default_database.paths = [
30-
str(tmpdir.join("default")),
31-
str(tmpdir.join("another_list")),
30+
Path(tmpdir.join("default")),
31+
Path(tmpdir.join("another_list")),
3232
]
3333
default_database.update_cache()
3434

todoman/model.py

Lines changed: 53 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
from datetime import datetime
99
from datetime import time
1010
from datetime import timedelta
11-
from os.path import normpath
12-
from os.path import split
11+
from pathlib import Path
12+
from pathlib import PosixPath
1313
from typing import Iterable
1414
from uuid import uuid4
1515

@@ -30,9 +30,12 @@
3030

3131

3232
def register_adapters_and_converters():
33+
sqlite3.register_adapter(Path, str)
34+
sqlite3.register_adapter(PosixPath, str)
35+
36+
sqlite3.register_converter("path", lambda p: Path(p.decode()))
3337
sqlite3.register_converter(
34-
'timestamp',
35-
lambda d: datetime.fromtimestamp(float(d), LOCAL_TIMEZONE)
38+
"timestamp", lambda d: datetime.fromtimestamp(float(d), LOCAL_TIMEZONE)
3639
)
3740

3841

@@ -284,7 +287,7 @@ def path(self) -> str:
284287
if not self.list:
285288
raise ValueError("A todo without a list does not have a path.")
286289

287-
return os.path.join(self.list.path, self.filename)
290+
return self.list.path.joinpath(self.filename)
288291

289292
def cancel(self) -> None:
290293
self.status = "CANCELLED"
@@ -383,7 +386,7 @@ def _read(self, path):
383386
return component
384387

385388
def write(self):
386-
if os.path.exists(self.todo.path):
389+
if self.todo.path.exists():
387390
self._write_existing(self.todo.path)
388391
else:
389392
self._write_new(self.todo.path)
@@ -432,9 +435,10 @@ class Cache:
432435

433436
SCHEMA_VERSION = 7
434437

435-
def __init__(self, path: str):
436-
self.cache_path = str(path)
437-
os.makedirs(os.path.dirname(self.cache_path), exist_ok=True)
438+
def __init__(self, path: Path):
439+
self.cache_path = path
440+
# XXX: Use the below once we drop python3.4
441+
self.cache_path.parent.mkdir(parents=True, exist_ok=True)
438442

439443
self._conn = sqlite3.connect(
440444
str(self.cache_path),
@@ -481,7 +485,7 @@ def create_tables(self):
481485
"""
482486
CREATE TABLE IF NOT EXISTS lists (
483487
"name" TEXT PRIMARY KEY,
484-
"path" TEXT,
488+
"path" path,
485489
"colour" TEXT,
486490
"mtime" INTEGER,
487491
@@ -493,7 +497,7 @@ def create_tables(self):
493497
self._conn.execute(
494498
"""
495499
CREATE TABLE IF NOT EXISTS files (
496-
"path" TEXT PRIMARY KEY,
500+
"path" path PRIMARY KEY,
497501
"list_name" TEXT,
498502
"mtime" INTEGER,
499503
@@ -506,7 +510,7 @@ def create_tables(self):
506510
self._conn.execute(
507511
"""
508512
CREATE TABLE IF NOT EXISTS todos (
509-
"file_path" TEXT,
513+
"file_path" path,
510514
511515
"id" INTEGER PRIMARY KEY,
512516
"uid" TEXT,
@@ -535,7 +539,7 @@ def create_tables(self):
535539

536540
def clear(self):
537541
self._conn.close()
538-
os.remove(self.cache_path)
542+
self.cache_path.unlink
539543
self._conn = None
540544

541545
def add_list(self, name: str, path: str, colour: str, mtime: int):
@@ -856,24 +860,24 @@ def todos(
856860

857861
def _todo_from_db(self, row: dict) -> Todo:
858862
todo = Todo()
859-
todo.id = row['id']
860-
todo.uid = row['uid']
861-
todo.summary = row['summary']
862-
todo.due = row['due']
863-
todo.start = row['start']
864-
todo.priority = row['priority']
865-
todo.created_at = row['created_at']
866-
todo.completed_at = row['completed_at']
867-
todo.dtstamp = row['dtstamp']
868-
todo.percent_complete = row['percent_complete']
869-
todo.status = row['status']
870-
todo.description = row['description']
871-
todo.location = row['location']
872-
todo.sequence = row['sequence']
873-
todo.last_modified = row['last_modified']
874-
todo.list = self.lists_map[row['list_name']]
875-
todo.filename = os.path.basename(row['path'])
876-
todo.rrule = row['rrule']
863+
todo.id = row["id"]
864+
todo.uid = row["uid"]
865+
todo.summary = row["summary"]
866+
todo.due = row["due"]
867+
todo.start = row["start"]
868+
todo.priority = row["priority"]
869+
todo.created_at = row["created_at"]
870+
todo.completed_at = row["completed_at"]
871+
todo.dtstamp = row["dtstamp"]
872+
todo.percent_complete = row["percent_complete"]
873+
todo.status = row["status"]
874+
todo.description = row["description"]
875+
todo.location = row["location"]
876+
todo.sequence = row["sequence"]
877+
todo.last_modified = row["last_modified"]
878+
todo.list = self.lists_map[row["list_name"]]
879+
todo.filename = row["path"].name
880+
todo.rrule = row["rrule"]
877881
return todo
878882

879883
def lists(self) -> Iterable[TodoList]:
@@ -953,18 +957,16 @@ def __init__(self, name: str, path: str, colour: str = None):
953957
@staticmethod
954958
def colour_for_path(path: str) -> str | None:
955959
try:
956-
with open(os.path.join(path, "color")) as f:
957-
return f.read().strip()
960+
return path.joinpath("color").read_text().strip()
958961
except OSError:
959962
logger.debug("No colour for list %s", path)
960963

961964
@staticmethod
962965
def name_for_path(path: str) -> str:
963966
try:
964-
with open(os.path.join(path, "displayname")) as f:
965-
return f.read().strip()
967+
return path.joinpath("displayname").read_text().strip()
966968
except OSError:
967-
return split(normpath(path))[1]
969+
return path.name
968970

969971
@staticmethod
970972
def mtime_for_path(path: str) -> int:
@@ -1001,8 +1003,8 @@ class Database:
10011003
"""
10021004

10031005
def __init__(self, paths, cache_path):
1004-
self.cache = Cache(cache_path)
1005-
self.paths = [str(path) for path in paths]
1006+
self.cache = Cache(Path(cache_path))
1007+
self.paths = [Path(path) for path in paths]
10061008
self.update_cache()
10071009

10081010
def update_cache(self) -> None:
@@ -1019,13 +1021,11 @@ def update_cache(self) -> None:
10191021
TodoList.colour_for_path(path),
10201022
paths[path],
10211023
)
1022-
for entry in os.listdir(path):
1023-
if not entry.endswith(".ics"):
1024+
for entry in path.iterdir():
1025+
if not entry.name.endswith(".ics"):
10241026
continue
1025-
entry_path = os.path.join(path, entry)
1026-
mtime = _getmtime(entry_path)
1027-
paths_to_mtime[entry_path] = mtime
1028-
paths_to_list_name[entry_path] = list_name
1027+
paths_to_mtime[entry] = _getmtime(entry)
1028+
paths_to_list_name[entry] = list_name
10291029

10301030
self.cache.expire_files(paths_to_mtime)
10311031

@@ -1039,10 +1039,10 @@ def update_cache(self) -> None:
10391039
continue
10401040

10411041
try:
1042-
with open(entry_path, "rb") as f:
1043-
cal = icalendar.Calendar.from_ical(f.read())
1044-
for component in cal.walk("VTODO"):
1045-
self.cache.add_vtodo(component, entry_path)
1042+
data = entry_path.read_bytes()
1043+
cal = icalendar.Calendar.from_ical(data)
1044+
for component in cal.walk("VTODO"):
1045+
self.cache.add_vtodo(component, entry_path)
10461046
except Exception:
10471047
logger.exception("Failed to read entry %s.", entry_path)
10481048

@@ -1058,17 +1058,16 @@ def lists(self) -> Iterable[TodoList]:
10581058
return self.cache.lists()
10591059

10601060
def move(self, todo: Todo, new_list: TodoList, from_list: TodoList) -> None:
1061-
orig_path = os.path.join(from_list.path, todo.filename)
1062-
dest_path = os.path.join(new_list.path, todo.filename)
1061+
orig_path = from_list.path.joinpath(todo.filename)
1062+
dest_path = new_list.path.joinpath(todo.filename)
10631063

1064-
os.rename(orig_path, dest_path)
1064+
orig_path.rename(dest_path)
10651065

10661066
def delete(self, todo: Todo) -> None:
10671067
if not todo.list:
10681068
raise ValueError("Cannot delete Todo without a list.")
10691069

1070-
path = os.path.join(todo.list.path, todo.filename)
1071-
os.remove(path)
1070+
todo.list.path.joinpath(todo.filename).unlink()
10721071

10731072
def flush(self) -> Iterable[Todo]:
10741073
for todo in self.todos(status=["ANY"]):
@@ -1100,5 +1099,5 @@ def save(self, todo: Todo) -> None:
11001099

11011100

11021101
def _getmtime(path: str) -> int:
1103-
stat = os.stat(path)
1102+
stat = path.stat()
11041103
return getattr(stat, "st_mtime_ns", stat.st_mtime)

0 commit comments

Comments
 (0)