Skip to content

Commit b938679

Browse files
author
Hugo Osvaldo Barrera
committed
Prefer pathlib.Path to plain strings
1 parent 6bf8d19 commit b938679

File tree

4 files changed

+69
-67
lines changed

4 files changed

+69
-67
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 cached_property
1414
from todoman.model import Database
@@ -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: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from datetime import timedelta
1111
from os.path import normpath
1212
from os.path import split
13+
from pathlib import Path
14+
from pathlib import PosixPath
1315
from typing import Dict
1416
from typing import Iterable
1517
from typing import List
@@ -22,7 +24,6 @@
2224
from atomicwrites import AtomicWriter
2325
from dateutil.rrule import rrulestr
2426
from dateutil.tz import tzlocal
25-
2627
from todoman import exceptions
2728

2829
logger = logging.getLogger(name=__name__)
@@ -34,9 +35,12 @@
3435

3536

3637
def register_adapters_and_converters():
38+
sqlite3.register_adapter(Path, str)
39+
sqlite3.register_adapter(PosixPath, str)
40+
41+
sqlite3.register_converter("path", lambda p: Path(p.decode()))
3742
sqlite3.register_converter(
38-
'timestamp',
39-
lambda d: datetime.fromtimestamp(float(d), LOCAL_TIMEZONE)
43+
"timestamp", lambda d: datetime.fromtimestamp(float(d), LOCAL_TIMEZONE)
4044
)
4145

4246

@@ -288,7 +292,7 @@ def path(self) -> str:
288292
if not self.list:
289293
raise ValueError("A todo without a list does not have a path.")
290294

291-
return os.path.join(self.list.path, self.filename)
295+
return self.list.path.joinpath(self.filename)
292296

293297
def cancel(self) -> None:
294298
self.status = "CANCELLED"
@@ -387,7 +391,7 @@ def _read(self, path):
387391
return component
388392

389393
def write(self):
390-
if os.path.exists(self.todo.path):
394+
if self.todo.path.exists():
391395
self._write_existing(self.todo.path)
392396
else:
393397
self._write_new(self.todo.path)
@@ -436,9 +440,10 @@ class Cache:
436440

437441
SCHEMA_VERSION = 7
438442

439-
def __init__(self, path: str):
440-
self.cache_path = str(path)
441-
os.makedirs(os.path.dirname(self.cache_path), exist_ok=True)
443+
def __init__(self, path: Path):
444+
self.cache_path = path
445+
# XXX: Use the below once we drop python3.4
446+
self.cache_path.parent.mkdir(parents=True, exist_ok=True)
442447

443448
self._conn = sqlite3.connect(
444449
str(self.cache_path),
@@ -485,7 +490,7 @@ def create_tables(self):
485490
"""
486491
CREATE TABLE IF NOT EXISTS lists (
487492
"name" TEXT PRIMARY KEY,
488-
"path" TEXT,
493+
"path" path,
489494
"colour" TEXT,
490495
"mtime" INTEGER,
491496
@@ -497,7 +502,7 @@ def create_tables(self):
497502
self._conn.execute(
498503
"""
499504
CREATE TABLE IF NOT EXISTS files (
500-
"path" TEXT PRIMARY KEY,
505+
"path" path PRIMARY KEY,
501506
"list_name" TEXT,
502507
"mtime" INTEGER,
503508
@@ -510,7 +515,7 @@ def create_tables(self):
510515
self._conn.execute(
511516
"""
512517
CREATE TABLE IF NOT EXISTS todos (
513-
"file_path" TEXT,
518+
"file_path" path,
514519
515520
"id" INTEGER PRIMARY KEY,
516521
"uid" TEXT,
@@ -539,7 +544,7 @@ def create_tables(self):
539544

540545
def clear(self):
541546
self._conn.close()
542-
os.remove(self.cache_path)
547+
self.cache_path.unlink
543548
self._conn = None
544549

545550
def add_list(self, name: str, path: str, colour: str, mtime: int):
@@ -860,24 +865,24 @@ def todos(
860865

861866
def _todo_from_db(self, row: dict) -> Todo:
862867
todo = Todo()
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 = os.path.basename(row['path'])
880-
todo.rrule = row['rrule']
868+
todo.id = row["id"]
869+
todo.uid = row["uid"]
870+
todo.summary = row["summary"]
871+
todo.due = row["due"]
872+
todo.start = row["start"]
873+
todo.priority = row["priority"]
874+
todo.created_at = row["created_at"]
875+
todo.completed_at = row["completed_at"]
876+
todo.dtstamp = row["dtstamp"]
877+
todo.percent_complete = row["percent_complete"]
878+
todo.status = row["status"]
879+
todo.description = row["description"]
880+
todo.location = row["location"]
881+
todo.sequence = row["sequence"]
882+
todo.last_modified = row["last_modified"]
883+
todo.list = self.lists_map[row["list_name"]]
884+
todo.filename = row["path"].name
885+
todo.rrule = row["rrule"]
881886
return todo
882887

883888
def lists(self) -> Iterable[TodoList]:
@@ -957,18 +962,16 @@ def __init__(self, name: str, path: str, colour: str = None):
957962
@staticmethod
958963
def colour_for_path(path: str) -> Optional[str]:
959964
try:
960-
with open(os.path.join(path, "color")) as f:
961-
return f.read().strip()
962-
except OSError:
965+
return path.joinpath("color").read_text().strip()
966+
except (OSError, IOError):
963967
logger.debug("No colour for list %s", path)
964968

965969
@staticmethod
966970
def name_for_path(path: str) -> str:
967971
try:
968-
with open(os.path.join(path, "displayname")) as f:
969-
return f.read().strip()
970-
except OSError:
971-
return split(normpath(path))[1]
972+
return path.joinpath("displayname").read_text().strip()
973+
except (OSError, IOError):
974+
return path.name
972975

973976
@staticmethod
974977
def mtime_for_path(path: str) -> int:
@@ -1005,8 +1008,8 @@ class Database:
10051008
"""
10061009

10071010
def __init__(self, paths, cache_path):
1008-
self.cache = Cache(cache_path)
1009-
self.paths = [str(path) for path in paths]
1011+
self.cache = Cache(Path(cache_path))
1012+
self.paths = [Path(path) for path in paths]
10101013
self.update_cache()
10111014

10121015
def update_cache(self) -> None:
@@ -1023,13 +1026,11 @@ def update_cache(self) -> None:
10231026
TodoList.colour_for_path(path),
10241027
paths[path],
10251028
)
1026-
for entry in os.listdir(path):
1027-
if not entry.endswith(".ics"):
1029+
for entry in path.iterdir():
1030+
if not entry.name.endswith(".ics"):
10281031
continue
1029-
entry_path = os.path.join(path, entry)
1030-
mtime = _getmtime(entry_path)
1031-
paths_to_mtime[entry_path] = mtime
1032-
paths_to_list_name[entry_path] = list_name
1032+
paths_to_mtime[entry] = _getmtime(entry)
1033+
paths_to_list_name[entry] = list_name
10331034

10341035
self.cache.expire_files(paths_to_mtime)
10351036

@@ -1043,11 +1044,11 @@ def update_cache(self) -> None:
10431044
continue
10441045

10451046
try:
1046-
with open(entry_path, "rb") as f:
1047-
cal = icalendar.Calendar.from_ical(f.read())
1048-
for component in cal.walk("VTODO"):
1049-
self.cache.add_vtodo(component, entry_path)
1050-
except Exception:
1047+
data = entry_path.read_bytes()
1048+
cal = icalendar.Calendar.from_ical(data)
1049+
for component in cal.walk("VTODO"):
1050+
self.cache.add_vtodo(component, entry_path)
1051+
except Exception as e:
10511052
logger.exception("Failed to read entry %s.", entry_path)
10521053

10531054
self.cache.save_to_disk()
@@ -1062,17 +1063,16 @@ def lists(self) -> Iterable[TodoList]:
10621063
return self.cache.lists()
10631064

10641065
def move(self, todo: Todo, new_list: TodoList, from_list: TodoList) -> None:
1065-
orig_path = os.path.join(from_list.path, todo.filename)
1066-
dest_path = os.path.join(new_list.path, todo.filename)
1066+
orig_path = from_list.path.joinpath(todo.filename)
1067+
dest_path = new_list.path.joinpath(todo.filename)
10671068

1068-
os.rename(orig_path, dest_path)
1069+
orig_path.rename(dest_path)
10691070

10701071
def delete(self, todo: Todo) -> None:
10711072
if not todo.list:
10721073
raise ValueError("Cannot delete Todo without a list.")
10731074

1074-
path = os.path.join(todo.list.path, todo.filename)
1075-
os.remove(path)
1075+
todo.list.path.joinpath(todo.filename).unlink()
10761076

10771077
def flush(self) -> Iterable[Todo]:
10781078
for todo in self.todos(status=["ANY"]):
@@ -1104,5 +1104,5 @@ def save(self, todo: Todo) -> None:
11041104

11051105

11061106
def _getmtime(path: str) -> int:
1107-
stat = os.stat(path)
1107+
stat = path.stat()
11081108
return getattr(stat, "st_mtime_ns", stat.st_mtime)

0 commit comments

Comments
 (0)