@@ -49,47 +49,108 @@ async def new(
4949 owner : str ,
5050 path : DAVPath ,
5151 depth : DAVDepth = DAVDepth .INFINITY ,
52- scope : DAVLockScope = DAVLockScope .exclusive ,
52+ scope : DAVLockScope = DAVLockScope .EXCLUSIVE ,
5353 timeout : int = DAVLockTimeoutMaxValue ,
54+ lock_objs_of_path : list [DAVLockObj ] | None = None ,
5455 ) -> DAVLockObj | None :
5556 """return None if create lock failed"""
56- # TODO: check with Depth.INFINITY
5757 async with self ._asyncio_lock :
58- lock_obj = DAVLockObj (
58+ new_lock_obj = DAVLockObj (
5959 owner = owner ,
6060 path = path ,
6161 depth = depth ,
6262 token = uuid4 (),
6363 scope = scope ,
6464 timeout = timeout ,
6565 )
66- success = self ._new_path2lock_obj_set ( path , scope , lock_obj )
66+ success = self ._new ( new_lock_obj , lock_objs_of_path )
6767 if not success :
6868 return None
6969
70- self ._token2lock_obj [lock_obj .token ] = lock_obj
71- return lock_obj
70+ return new_lock_obj
7271
73- def _new_path2lock_obj_set (
74- self , path : DAVPath , lock_scope : DAVLockScope , lock_obj : DAVLockObj
72+ def _new (
73+ self ,
74+ new_lock_obj : DAVLockObj ,
75+ lock_objs_of_path : list [DAVLockObj ] | None = None ,
7576 ) -> bool :
76- lock_obj_set = self ._path2lock_obj_set .get (path )
77- if lock_obj_set is None :
78- # new lock for path
79- self ._path2lock_obj_set [path ] = DAVLockObjSet (lock_scope , {lock_obj })
77+ """
78+ - 资源无锁
79+ - next
80+ - 资源有锁
81+ - 新锁的 scope 为 DAVLockScope.EXCLUSIVE
82+ - 锁定失败
83+ - 新锁的 scope 为 DAVLockScope.SHARED
84+ - 任意锁的 scope 为 DAVLockScope.EXCLUSIVE
85+ - 锁定失败
86+ - 所有锁的 scope 为 DAVLockScope.SHARED
87+ - next
88+
89+ - 新锁的 depth 为 DAVDepth.ZERO
90+ - Success
91+ - 新锁的 depth 为 DAVDepth.INFINITY
92+ - next
93+
94+ - 资源子目录无锁
95+ - Success
96+ - 资源子目录有锁
97+ - 新锁的 scope 为 DAVLockScope.EXCLUSIVE
98+ - Failed
99+ - 新锁的 scope 为 DAVLockScope.SHARED
100+ - 任意子目录锁的 scope 为 DAVLockScope.EXCLUSIVE
101+ - Failed
102+ - 所有子目录锁的 scope 为 DAVLockScope.SHARED
103+ - Success
104+
105+ resource has any lock:
106+ - resource's path has any lock
107+ - path's parent path's has any lock AND lock is DAVDepth.INFINITY
108+ """
109+ if lock_objs_of_path is None :
110+ lock_objs_of_path = self ._get_lock_objs_from_path (new_lock_obj .path )
111+
112+ if len (lock_objs_of_path ) > 0 :
113+ if new_lock_obj .scope == DAVLockScope .EXCLUSIVE :
114+ return False
115+
116+ for lock_obj in lock_objs_of_path :
117+ if lock_obj .scope == DAVLockScope .EXCLUSIVE :
118+ return False
119+
120+ if new_lock_obj .depth == DAVDepth .ZERO :
121+ self ._new_just_do_it (new_lock_obj )
80122 return True
81123
82- if lock_scope == DAVLockScope .exclusive :
83- # can not lock some path with exclusive scope
84- return False
124+ lock_objs_of_child_path = self ._get_lock_objs_of_child_path_from_path (
125+ new_lock_obj .path
126+ )
127+ if len (lock_objs_of_child_path ) == 0 :
128+ self ._new_just_do_it (new_lock_obj )
129+ return True
85130
86- if lock_obj_set .lock_scope == DAVLockScope .exclusive :
87- # can not lock some path with exclusive scope, even if the old lock's scope is shared
131+ if new_lock_obj .scope == DAVLockScope .EXCLUSIVE :
88132 return False
89133
90- lock_obj_set .add (lock_obj )
134+ for lock_obj in lock_objs_of_child_path :
135+ if lock_obj .scope == DAVLockScope .EXCLUSIVE :
136+ return False
137+
138+ self ._new_just_do_it (new_lock_obj )
91139 return True
92140
141+ def _new_just_do_it (self , new_lock_obj : DAVLockObj ) -> None :
142+ lock_obj_set = self ._path2lock_obj_set .get (new_lock_obj .path )
143+ if lock_obj_set is None :
144+ self ._path2lock_obj_set [new_lock_obj .path ] = DAVLockObjSet (
145+ new_lock_obj .scope , {new_lock_obj }
146+ )
147+ self ._token2lock_obj [new_lock_obj .token ] = new_lock_obj
148+ return
149+
150+ lock_obj_set .add (new_lock_obj )
151+ self ._token2lock_obj [new_lock_obj .token ] = new_lock_obj
152+ return
153+
93154 async def refresh (
94155 self , lock_obj : DAVLockObj , timeout : int | None = None
95156 ) -> DAVLockObj :
@@ -114,14 +175,6 @@ async def release(self, token: UUID) -> bool:
114175 return self ._release (lock_obj )
115176
116177 def _release (self , lock_obj : DAVLockObj ) -> bool :
117- success = self ._release_path2lock_set (lock_obj )
118- if not success :
119- return False
120-
121- self ._token2lock_obj .pop (lock_obj .token )
122- return True
123-
124- def _release_path2lock_set (self , lock_obj : DAVLockObj ) -> bool :
125178 path = lock_obj .path
126179
127180 if path not in self ._path2lock_obj_set :
@@ -135,6 +188,7 @@ def _release_path2lock_set(self, lock_obj: DAVLockObj) -> bool:
135188 if lock_obj_set .is_empty ():
136189 self ._path2lock_obj_set .pop (path )
137190
191+ self ._token2lock_obj .pop (lock_obj .token )
138192 return True
139193
140194 async def is_valid_lock_token (self , token : UUID , path : DAVPath ) -> bool :
@@ -143,7 +197,7 @@ async def is_valid_lock_token(self, token: UUID, path: DAVPath) -> bool:
143197 if lock_obj is None :
144198 return False
145199
146- if not lock_obj .check_path (path ):
200+ if not lock_obj .is_locking_path (path ):
147201 return False
148202
149203 if lock_obj .is_expired ():
@@ -155,31 +209,49 @@ async def is_valid_lock_token(self, token: UUID, path: DAVPath) -> bool:
155209 return True
156210
157211 async def get_lock_objs_from_path (self , path : DAVPath ) -> list [DAVLockObj ]:
158- now = time ()
212+ async with self ._asyncio_lock :
213+ return self ._get_lock_objs_from_path (path )
159214
215+ def _get_lock_objs_from_path (self , path : DAVPath ) -> list [DAVLockObj ]:
216+ """get lock_objs from path
217+ - path is locking by any lock
218+ - path's parent path's lock is DAVDepth.INFINITY
219+ """
220+ now = time ()
160221 lock_obj_expired = set ()
161- result : list [DAVLockObj ] = list ()
162- async with self ._asyncio_lock :
163- for lock_path , lock_obj_set in self ._path2lock_obj_set .items ():
164- if not lock_path .is_parent_of_or_is_self (path ):
165- continue
222+ lock_objs : list [DAVLockObj ] = list ()
166223
167- for lock_obj in lock_obj_set .data :
168- if lock_obj .is_expired (now = now ):
169- lock_obj_expired .add (lock_obj )
170- continue
224+ for lock_path , lock_obj_set in self ._path2lock_obj_set .items ():
225+ if not lock_path .is_parent_of_or_is_self (path ):
226+ continue
171227
172- if not lock_obj .check_path (path ):
173- continue
228+ for lock_obj in list (lock_obj_set .data ):
229+ if lock_obj .is_expired (now = now ):
230+ lock_obj_expired .add (lock_obj )
231+ continue
174232
175- result .append (lock_obj )
233+ if not lock_obj .is_locking_path (path ):
234+ continue
176235
177- for lock_obj in lock_obj_expired :
178- if not self ._release (lock_obj ):
179- raise DAVCodingError # pragma: no cover
236+ lock_objs .append (lock_obj )
180237
181- return result
238+ for lock_obj in lock_obj_expired :
239+ if not self ._release (lock_obj ):
240+ raise DAVCodingError # pragma: no cover
241+
242+ return lock_objs
182243
183244 async def has_lock (self , path : DAVPath ) -> bool :
184245 """res path is locking by any lock"""
185- return len (await self .get_lock_objs_from_path (path )) > 0
246+ async with self ._asyncio_lock :
247+ return len (self ._get_lock_objs_from_path (path )) > 0
248+
249+ def _get_lock_objs_of_child_path_from_path (self , path : DAVPath ) -> list [DAVLockObj ]:
250+ """get lock_objs of child path from path"""
251+ result : list [DAVLockObj ] = list ()
252+ for lock_obj_set in self ._path2lock_obj_set .values ():
253+ for lock_obj in list (lock_obj_set .data ):
254+ if path .is_parent_of (lock_obj .path ):
255+ result .append (lock_obj )
256+
257+ return result
0 commit comments