88from datetime import datetime
99from datetime import time
1010from 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
1313from typing import Iterable
1414from uuid import uuid4
1515
3030
3131
3232def 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
11021101def _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