
    I jc                    J    d Z ddlmZ ddlZddlZddlmZ ddZd	dZd
ddZ	y)u  task-2962: .done.notified O_EXCL 원자적 선점 공용 모듈.

성공 알림을 보내는 4개 소비자(done-watcher.sh, activity-watcher.py,
done-watcher.py, notify-completion.py)가 동일한 `{task_id}.done.notified`
마커를 놓고 경쟁하던 문제(체크 → 전송 → 마커 순서로는 TOCTOU 경쟁창이 생김)를
해소하기 위한 공용 원자적 선점 유틸리티.

사용 패턴 (모든 소비자 공통):
    if claim_notification(marker_path):
        ok = send_telegram(...)
        if ok:
            finalize_notification(marker_path, payload={...})
        else:
            release_notification(marker_path)
    else:
        # 이미 다른 프로세스가 선점(또는 이미 전송 완료) → 스킵
        pass
    )annotationsN)Pathc                P   t        |       }	 |j                  j                  dd       t        j                  t        |      t        j                  t        j                  z  t        j                  z        }t        j                  |       y# t        $ r Y yt        $ r Y yw xY w)u  O_EXCL 로 마커를 원자적 선점한다.

    부모 디렉토리는 존재하지 않으면 자동 생성한다.

    Returns:
        선점에 성공하면 True. 마커가 이미 존재하면(다른 프로세스가 선점했거나
        이미 알림 완료됨) False. FileExistsError 이외의 OSError 가 발생하면
        전송을 금지하는 쪽(fail-closed)으로 False 를 반환한다.
    Tparentsexist_okF)r   parentmkdirosopenstrO_CREATO_EXCLO_WRONLYcloseFileExistsErrorOSError)marker_pathpathfds      scripts/lib/notify_claim.pyclaim_notificationr      s     D
$6WWSY

RYY 6 DE
   s   BB 	B%B%$B%c                n    t        |       }	 |j                          y# t        $ r Y yt        $ r Y yw xY w)u   전송 실패 시 선점을 롤백한다(마커 삭제).

    마커가 없으면 조용히 무시한다. 삭제 중 예외는 전파하지 않는다.
    N)r   unlinkFileNotFoundErrorr   )r   r   s     r   release_notificationr   3   s9    
 D  s    	444c                    t        |       }	 |j                  j                  dd       ||ni }|j                  t	        j
                  |dt              d       y# t        $ r Y yw xY w)ul  전송 성공을 확정한다. payload 를 JSON 으로 마커에 기록한다.

    marker_path 는 claim_notification 으로 이미 생성돼 있는 것을 전제로 하지만,
    없더라도(레이스 등으로 유실된 경우) 새로 생성한다. 기록 실패는 예외를
    전파하지 않는다(알림 전송 자체는 이미 성공했으므로).
    Tr   NF)ensure_asciidefaultzutf-8)encoding)r   r	   r
   
write_textjsondumpsr   r   )r   payloadr   datas       r   finalize_notificationr&   A   sf     D$6!-w2

4eSIT[\ s   AA 	A*)A*)r   str | os.PathLikereturnbool)r   r'   r(   None)N)r   r'   r$   zdict | Noner(   r*   )
__doc__
__future__r   r"   r   pathlibr   r   r   r&        r   <module>r0      s'   & #  	 0r/   