10
10
from datetime import timedelta
11
11
from os .path import normpath
12
12
from os .path import split
13
+ from pathlib import Path
14
+ from pathlib import PosixPath
13
15
from typing import Dict
14
16
from typing import Iterable
15
17
from typing import List
22
24
from atomicwrites import AtomicWriter
23
25
from dateutil .rrule import rrulestr
24
26
from dateutil .tz import tzlocal
25
-
26
27
from todoman import exceptions
27
28
28
29
logger = logging .getLogger (name = __name__ )
34
35
35
36
36
37
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 ()))
37
42
sqlite3 .register_converter (
38
- 'timestamp' ,
39
- lambda d : datetime .fromtimestamp (float (d ), LOCAL_TIMEZONE )
43
+ "timestamp" , lambda d : datetime .fromtimestamp (float (d ), LOCAL_TIMEZONE )
40
44
)
41
45
42
46
@@ -288,7 +292,7 @@ def path(self) -> str:
288
292
if not self .list :
289
293
raise ValueError ("A todo without a list does not have a path." )
290
294
291
- return os . path . join ( self .list .path , self .filename )
295
+ return self .list .path . joinpath ( self .filename )
292
296
293
297
def cancel (self ) -> None :
294
298
self .status = "CANCELLED"
@@ -387,7 +391,7 @@ def _read(self, path):
387
391
return component
388
392
389
393
def write (self ):
390
- if os . path . exists ( self .todo .path ):
394
+ if self .todo .path . exists ( ):
391
395
self ._write_existing (self .todo .path )
392
396
else :
393
397
self ._write_new (self .todo .path )
@@ -436,9 +440,10 @@ class Cache:
436
440
437
441
SCHEMA_VERSION = 7
438
442
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 )
442
447
443
448
self ._conn = sqlite3 .connect (
444
449
str (self .cache_path ),
@@ -485,7 +490,7 @@ def create_tables(self):
485
490
"""
486
491
CREATE TABLE IF NOT EXISTS lists (
487
492
"name" TEXT PRIMARY KEY,
488
- "path" TEXT ,
493
+ "path" path ,
489
494
"colour" TEXT,
490
495
"mtime" INTEGER,
491
496
@@ -497,7 +502,7 @@ def create_tables(self):
497
502
self ._conn .execute (
498
503
"""
499
504
CREATE TABLE IF NOT EXISTS files (
500
- "path" TEXT PRIMARY KEY,
505
+ "path" path PRIMARY KEY,
501
506
"list_name" TEXT,
502
507
"mtime" INTEGER,
503
508
@@ -510,7 +515,7 @@ def create_tables(self):
510
515
self ._conn .execute (
511
516
"""
512
517
CREATE TABLE IF NOT EXISTS todos (
513
- "file_path" TEXT ,
518
+ "file_path" path ,
514
519
515
520
"id" INTEGER PRIMARY KEY,
516
521
"uid" TEXT,
@@ -539,7 +544,7 @@ def create_tables(self):
539
544
540
545
def clear (self ):
541
546
self ._conn .close ()
542
- os . remove ( self .cache_path )
547
+ self .cache_path . unlink
543
548
self ._conn = None
544
549
545
550
def add_list (self , name : str , path : str , colour : str , mtime : int ):
@@ -860,24 +865,24 @@ def todos(
860
865
861
866
def _todo_from_db (self , row : dict ) -> Todo :
862
867
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" ]
881
886
return todo
882
887
883
888
def lists (self ) -> Iterable [TodoList ]:
@@ -957,18 +962,16 @@ def __init__(self, name: str, path: str, colour: str = None):
957
962
@staticmethod
958
963
def colour_for_path (path : str ) -> Optional [str ]:
959
964
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 ):
963
967
logger .debug ("No colour for list %s" , path )
964
968
965
969
@staticmethod
966
970
def name_for_path (path : str ) -> str :
967
971
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
972
975
973
976
@staticmethod
974
977
def mtime_for_path (path : str ) -> int :
@@ -1005,8 +1008,8 @@ class Database:
1005
1008
"""
1006
1009
1007
1010
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 ]
1010
1013
self .update_cache ()
1011
1014
1012
1015
def update_cache (self ) -> None :
@@ -1023,13 +1026,11 @@ def update_cache(self) -> None:
1023
1026
TodoList .colour_for_path (path ),
1024
1027
paths [path ],
1025
1028
)
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" ):
1028
1031
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
1033
1034
1034
1035
self .cache .expire_files (paths_to_mtime )
1035
1036
@@ -1043,11 +1044,11 @@ def update_cache(self) -> None:
1043
1044
continue
1044
1045
1045
1046
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 :
1051
1052
logger .exception ("Failed to read entry %s." , entry_path )
1052
1053
1053
1054
self .cache .save_to_disk ()
@@ -1062,17 +1063,16 @@ def lists(self) -> Iterable[TodoList]:
1062
1063
return self .cache .lists ()
1063
1064
1064
1065
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 )
1067
1068
1068
- os .rename (orig_path , dest_path )
1069
+ orig_path .rename (dest_path )
1069
1070
1070
1071
def delete (self , todo : Todo ) -> None :
1071
1072
if not todo .list :
1072
1073
raise ValueError ("Cannot delete Todo without a list." )
1073
1074
1074
- path = os .path .join (todo .list .path , todo .filename )
1075
- os .remove (path )
1075
+ todo .list .path .joinpath (todo .filename ).unlink ()
1076
1076
1077
1077
def flush (self ) -> Iterable [Todo ]:
1078
1078
for todo in self .todos (status = ["ANY" ]):
@@ -1104,5 +1104,5 @@ def save(self, todo: Todo) -> None:
1104
1104
1105
1105
1106
1106
def _getmtime (path : str ) -> int :
1107
- stat = os .stat (path )
1107
+ stat = path .stat ()
1108
1108
return getattr (stat , "st_mtime_ns" , stat .st_mtime )
0 commit comments