
    Aji                     X   U d Z ddlZddlZddlZddlmZ ddlmZ ddlm	Z	m
Z
mZmZmZmZ  ej                  e      Z e       Z e       Zeeeeef      ed<   defdZed	   Z	 	 d+d
edededee   fdZ ee      j9                         j:                  j:                  j:                  j:                  dz  dz  Zddlm Z   e d      d
ededefd       Z!d
edededefdZ"dZ#dddZ$dddd
edededee   de
ee	f   f
dZ%	 	 d,d
ededee   defdZ&	 	 d,d
ededee   de
ee	f   fd Z'd
edee   fd!Z(d
ededee   fd"Z)de*fd#Z+d
ededed$eddf
d%Z,	 d-d
eded&edeee   ee   ee   f   fd'Z-	 	 d.d
eded&edee   dee   f
d(Z.d/d
edede
ee	f   fd)Z/d/d
ededee   fd*Z0y)0u  프롬프트 로더 — DB 우선, 파일 fallback.

1. DB에서 module+name+is_active=true 조회
2. 없으면 파일에서 최신 버전 로드
3. 새 프롬프트 저장 시 DB + 파일 동시 저장

## Version pack semantics (problems.md #6)

Module 디렉토리는 여러 version 디렉토리를 가질 수 있고 (예: ``shot_extract/
9.x``, ``shot_extract/10.x``, ``shot_extract/11.x``), 각 version 안에 stem
파일이 들어 있다. 이 로더는 stem 별로 ``_version_sort_key`` 기준 가장 최신
version 디렉토리를 독립적으로 탐색하므로, **같은 module 안에서 stem 이 서로
다른 version 에서 로드될 수 있다** (e.g. ``system.md`` 는 v11, ``schema.json``
은 v10 — 각자가 자기 stem 을 가진 가장 최신 version 우선).

이 동작은 backward-compat 으로 유지하되 다음 두 단계 가시화/강제 layer 를
제공한다.

1. **observability** (default): 같은 module 안에서 stem 들의 effective version
   이 일치하지 않으면 ``logger.warning`` (process 당 1회) + ``get_effective
   _source`` response 의 ``module_pack.drift`` 에 stem→version 매핑 노출.
2. **opt-in strict** (``PROMPT_VERSION_PACK_STRICT=true``): module 의 가장 최신
   version 디렉토리 안에 stem 이 없으면 ``RuntimeError`` 발생. 운영자가 prompt
   정리 후 default 로 전환하기 위한 toggle.

   strict 모드 적용 boundary (review I3/I4):
     - **module 디렉토리 자체가 없으면** strict 가 raise 하지 않고 기존
       ``FileNotFoundError`` 가 caller 에서 발생한다 (typo 등은 strict 와 별개
       경로로 진단). 같은 module 의 stem-version 정렬에만 적용.
     - **db= 인자로 DB row 가 hit 되면** ``_load_from_file`` 자체를 호출하지
       않으므로 strict 검증이 우회된다. 거의 모든 caller 가 file-only (db 미전달)
       이지만 ``location_floor_plan_step`` 등 db-aware caller 는 stale DB row
       가 stem 을 살리는 시나리오에서는 strict toggle 영향을 받지 않는다.
    N)Path)Lock)AnyDictListLiteralOptionalTuple_DRIFT_WARNEDnamec                     | j                  dd      }	 t        |d         t        |      dkD  r|d   fS dfS # t        t        f$ r d| fcY S w xY w)u  버전 디렉토리 정렬 키 — '12.202603271830' → (12, '202603271830').

    Primary segment 는 numeric, secondary 는 lexical 비교. 본 프로젝트 컨벤션
    (`버전.YYYYMMDDHHmm`, 12-digit timestamp) 에서는 secondary 길이가 고정이라
    lexical 정렬이 시간 순서와 일치한다.

    **주의 — 비표준 secondary 형식 시 lexical drift 재발**:
      - ``9.99`` vs ``9.100`` 형식은 secondary lexical 비교에서 ``"99" > "100"``
        (잘못된 정렬). 본 프로젝트는 12-digit timestamp 만 사용하므로 안전.
      - 다른 secondary 형식 (예: dotted semver `9.1.2`) 도입 시 추가 numeric
        cast 필요.
    .   r    )splitintlen
ValueError
IndexError)r   partss     O/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/prompt_loader.py_version_sort_keyr   2   s`     JJsAEE!H3u:>uQxBBrBB
# 4ys    8 8 AA)r   AND schema_json IS NOT NULLmodulewhere_extraversionc                 R   ddl m} d| }	 | j                   ||      ||d      j                         }|sy|"|D cg c]  }|j                  |k(  s| }}|sy|j                  d d       |d   S # t        $ r" 	 | j                           # t        $ r Y  w xY ww xY wc c}w )	uF  prompt_template 중 (module, name, is_active=true) + extra where 매칭 row
    들을 모두 가져와 ``_version_sort_key`` 기준 최댓값 row 반환. 없으면 None.

    SQL ``ORDER BY version DESC`` 는 lexical 이라 ``9.x`` > ``10.x`` 로 잘못된
    정렬이 가능 (problems.md #7). Python in-memory 정렬로 numeric-aware.

    where_extra: ``_AllowedWhereExtra`` 닫힌 enum 으로 좁힘 — 신규 절을 추가
    하려면 enum 에 명시 필요 (정적 보호). 직접 concat 방식이라 user-supplied
    값을 받지 못하게 type-level 차단.

    W19A versioned-load (review §3.6): ``version`` 이 주어지면 그 version 과
    정확히 일치하는 row 만 후보로 두고 sort. 없으면 None (latest 로 silent
    fallback 하지 않음 — caller 의 명시적 pin 을 깨지 않기 위해).
    r   )textzSSELECT * FROM prompt_template WHERE module = :m AND name = :n AND is_active = true )mnNc                 ,    t        | j                        S N)r   r   )rs    r   <lambda>z+_select_latest_active_row.<locals>.<lambda>~   s    -aii8    Tkeyreverse)
sqlalchemyr   executefetchall	Exceptionrollbackr   sort)	dbr   r   r   r   sql_textsqlrowsr#   s	            r   _select_latest_active_rowr3   I   s    * ,	@-	 
zz(3-vD)ABKKM& 84a199#748II8$IG7N3  	KKM 	  		* 9s:   )A6 B$B$6	B! BB!	BB!BB!prompts_base)	lru_cache@   )maxsizeresolvedreturnc                 
   ddl }t        | z  |z  }|j                         r!t        d |j	                  d      D              ng }|st        |  d|       |j                         }|D ]  }|j                  t        |j                  |            j                  d             |j                  d       |j                  |j                  |j                               j                                 |j                         dd S )	u  해석된 프롬프트 팩 디렉토리의 파일 bytes 해시 16 hex (#77 공용).

    버전 '문자열' 스탬프는 셀렉터 상수·해석 코드·서버 메모리 중 어느 층이
    낡아도 안 움직인다 — 이 해시는 **이 프로세스가 실제 로드하는 디렉토리의
    bytes** 를 지문에 접는다(2026-08-08 최종 스틸 253/311 재사용 사고의
    수정을 multiroll_judge 전용에서 모든 팩 공용으로 승격).

    팩 디렉토리는 불변 관례(덮어쓰기 금지·새 버전 디렉토리)라 프로세스
    수명 동안 캐시한다. 부재·빈 디렉토리는 fail-closed — 없는 팩을 빈
    해시로 접으면 "팩이 사라져도 지문이 안 움직이는" 같은 부류의 구멍이
    다시 생긴다.
    r   Nc              3   B   K   | ]  }|j                         s|  y wr"   )is_file).0ps     r   	<genexpr>z(pack_dir_content_hash.<locals>.<genexpr>   s     >!!))+As   *u.    팩 디렉토리가 없거나 비어 있다: utf-8       )hashlibPROMPTS_BASEis_dirsortedrglobr   sha256updatestrrelative_toencode
read_bytesdigest	hexdigest)r   r9   rE   rootfileshr?   s          r   pack_dir_content_hashrU      s     & 8+D >tzz#>>#% 
hDTFKM 	MA	Q]]4()009:		/6689  ;;="r%   stemc                     ddl }t        | z  |z  | dz  }|j                         st        |  d|       |j	                  |j                               j                         dd S )u  해석된 팩 디렉토리 안 **한 스템 파일**의 bytes 해시 16 hex.

    (2026-08-13 Codex R2): 소비 범위가 스템 하나인 절을 디렉토리 전체
    해시로 지문에 접으면, 무관한 동거 스템 개정·selector 이동까지 전량
    유료 재생성을 일으킨다 — 그 스템 bytes 만 접는다. 부재=fail-closed
    (pack_dir_content_hash 와 같은 이유).
    r   N.mdu    팩 스템이 없다: rD   )rE   rF   r=   r   rJ   rO   rQ   )r   r9   rV   rE   r?   s        r   pack_stem_content_hashrY      sf     v(dV3<7A99;F8#:1#>??>>!,,.)335cr::r%   r   rX   .json)promptschema)r   r/   kindc                   ddl }|t        vrt        dt        t               d|      d}d}d}i }	|}
|h	 t	        || ||dk(  rdnd|      }|N|dk(  r|j
                  n|j                  }|/|j                  d	      }d
}dt        |dd      i}	t        |d|      }
|t        |   }|8t        | z  |z  | | z  }|j                         r|j                         nd}|}n&t        | ||      \  }}}||j                         nd}||j!                  d	      j#                         }|d}dt%        |      i}	|}
|||t'        d|  d| d| d|d|dnd d      t(        || |||	|
|j+                  |      j-                         dd |dk(  rt/        j0                  |      d	S |d	S # t        $ r$}t        j                  d| |||       Y d}~8d}~ww xY w)u#  실효 source 하나를 **content + provenance 와 함께** 돌려준다 (GROUNDING-V2 §8).

    왜 필요한가 — ``load_prompt`` / ``load_schema`` 는 각자 따로 source 를 고르고,
    ``pack_dir_content_hash`` 는 **파일 디렉토리 bytes 만** 해시한다. 그래서
    같은 version 의 활성 DB row 가 생기면 **실제 prompt 는 바뀌는데 지문은 안 움직인다**.
    이 함수는 loader 와 지문이 **같은 반환값 하나**를 소비하게 해서 그 구멍을 막는다.

    ``kind`` 가 ``.md``/``.json`` 과 ``content``/``schema_json`` 중 무엇을 고를지 정한다.
    ``locator`` 는 **어느 DB row · 어느 파일**인지를 남긴다.

    ★``raw_content_hash`` 는 **로드한 원본 source bytes** 의 해시다 — ``.strip()`` 이나
    파싱 **전**이다. 파일 팩은 ``published_packs`` 래칫도 같은 bytes 를 해시하므로
    두 값이 어긋나면 「팩이 바뀌었다」를 두 도구가 다르게 말하게 된다. provider 에 실제로
    나간 payload 는 prompt 의 ``.format(**kwargs)`` 뒤 · schema 의 재직렬화 뒤라
    **원리상 다르다** — 그쪽은 호출부가 ``payload_hash`` 로 따로 남긴다. 둘을 한 칸으로
    합치면 format 인자가 바뀌어도 지문이 안 움직인다.

    Returns:
        {
          "contract_version": int,
          "kind": "prompt" | "schema",
          "module": str, "stem": str,
          "source": "db" | "file",
          "locator": {"db_row_id": ...} | {"file_path": ...},
          "version": str | None,
          "raw_content_hash": str,      # sha256 앞 16 hex
          "content": str | dict,        # prompt=text, schema=parsed
        }

    Raises:
        FileNotFoundError: 어느 source 에서도 못 찾음.
        ValueError: kind 가 enum 밖.
    r   Nz'resolve_effective: kind must be one of z, got r\   r   r   r   r   rB   r/   	db_row_ididr   z7resolve_effective DB lookup failed (%s/%s, kind=%s): %sextfile	file_pathz!resolve_effective: no source for /z (kind=z
, version=z, db=yesno)rD   )	contract_versionr]   r   rV   sourcelocatorr   raw_content_hashcontent)rE   	_KIND_EXTr   rH   r3   schema_jsonrn   rN   getattrr,   loggerwarningrF   existsrO   _resolve_stem_in_packdecodestriprL   FileNotFoundError"RESOLVE_EFFECTIVE_CONTRACT_VERSIONrJ   rQ   jsonloads)r   rV   r]   r   r/   rE   raw	raw_bytesrk   rl   resolved_versionrowexcrc   fpathfound_version	_versionss                    r   resolve_effectiver      sh   R 95fY6G5HthWY 	Y C!%I F G&-	~	+FD=AX=M9SUC
 )-)9coos{{? #

7 3I!F*GCt,DEG'.sIw'G$ {o 6)G3senDE.3lln((*$I#M.CFDVY.Z+E=).3.?((*TI ""7+113C?F"CJ/G,
{i'6>/xq ?F*WKubnURV4WWXZ
 	
 ?##NN95??A#2F&*h&64::c?  =@ 9  	NNIdC 	s   A'F/ /	G8GGc                 6   d}|r	 t        || ||      }|r|j                  }|t        | ||      }|"t        d|  d| |d| dz         dz         |r |j                  d	i |S |S # t        $ r"}t        j	                  d| ||       Y d}~pd}~ww xY w)
u  프롬프트 텍스트 로드 (DB 우선, 파일 fallback).

    Args:
        module: 모듈명 (scene_extractor_v2, outlook_extractor 등)
        name: 파일명 stem (system, turn_scene_detail 등)
        db: SQLAlchemy session (없으면 파일만 사용)
        version: pack 디렉토리 / DB row version 정확히 일치하는 source 만
            허용. None 이면 기존 latest-pack 동작 (numeric desc) 유지.
            W19A 가 floor_plan_prompt 의 v5↔v6 selector 를 안전하게 분기하기
            위해 도입 (review §3.6). 다른 module 은 caller 가 version 을
            전달하지 않는 한 동작 변경 없음.
        **kwargs: 프롬프트 텍스트의 format 변수

    Returns: 포맷된 프롬프트 텍스트
    N)r   z!DB prompt load failed (%s/%s): %szPrompt not found: rf   
 (version=ri   r    )r3   rn   r,   rr   rs   _load_from_filerx   format)r   r   r/   r   kwargsrn   r   r   s           r   load_promptr   +  s    , G 
	S+BgNC++
 !&$@ $0*1*=G9A&G
 	
CEG
 	

 (.>7>>#F#:7:  	SNN>cRR	Ss   A- -	B6BBc                 n   |r>	 t        || |d|      }|r+|j                  rt        j                  |j                        S t        | |d|      }|rt        j                  |      S t        d|  d| |d	| d
z         dz         # t        $ r"}t
        j                  d| ||       Y d}~nd}~ww xY w)u{  JSON 스키마 로드 (DB 우선, 파일 fallback).

    Args:
        module: 모듈명
        name: 스키마 파일명 stem (extract_schema 등, .json 확장자 제외)
        version: pack 디렉토리 / DB row version 정확히 일치하는 source 만
            허용. None 이면 기존 latest-pack 동작 유지. ``load_prompt`` 와
            동일한 의미.
    r   r_   z!DB schema load failed (%s/%s): %sNrZ   )rc   r   zSchema not found: rf   r   ri   r   )	r3   rp   rz   r{   r,   rr   rs   r   rx   )r   r   r/   r   r   r   rn   s          r   load_schemar   Y  s      
		S+FD9C
 szz#//22
 fdIGzz'""

VHAdV,&-&9Zy"	C ?A	C   	SNN>cRR	Ss   <B	 		B4B//B4c                    t         | z  }|j                         sg S t        |j                         D cg c]9  }|j	                         r'|j
                  j                  d      s|j
                  ; c}t        d      S c c}w )u   module 디렉토리의 version 디렉토리 이름들 (numeric desc 정렬). 없으면 [].

    같은 정렬을 ``_load_from_file`` / ``get_effective_source`` /
    ``_resolve_stem_version_in_pack`` 가 공유한다.
    r   Tr&   )rF   rt   rH   iterdirrG   r   
startswithr   )r   
module_dirds      r   _list_module_versionsr     s{     &J	 #++- 	7-AHHJqvv005 
- 	7	 	7s   >A?c                     t         | z  }t        |       D cg c]  }||z  | dz  j                         r| c}S c c}w )u  이 스템 파일을 **실제로 가진** 판 목록 (`_list_module_versions` 정렬 그대로).

    부르는 쪽이 판 번호를 제 코드에 다시 적지 않게 하려고 있다 — 같은 목록을
    두 자리에 적으면 한쪽만 고쳐진다.
    rX   )rF   r   rt   )r   rV   r   vs       r   pack_versions_with_stemr     sS     &J,V4 94!QD6-557 4 9 9 9s   "<c                      t         j                  j                  d      } |  | j                         j	                         dv S 	 ddlm} t        t        |dd            S # t        $ r Y yw xY w)up  version pack strict 모드 평가 (review I1).

    우선순위: ``PROMPT_VERSION_PACK_STRICT`` ENV (있으면 그대로 사용) →
    ``settings.prompt_version_pack_strict`` (Pydantic, .env 자동 로드).
    ENV 직접 평가 분기는 import-cycle/legacy 호환을 위해 보존한다 — 운영자
    instant-toggle (export 후 reload 없이) 도 가능.
    PROMPT_VERSION_PACK_STRICT)1truerg   onr   )settingsprompt_version_pack_strictF)
osenvirongetrw   lowerapp.core.configr   boolrq   r,   )r|   r   s     r   _is_version_pack_strictr     sf     **..5
6C
yy{  "&@@@,GH&BEJKK s   A 	A+*A+latest_versionc                     | ||f}t         5  |t        v r
	 ddd       yt        j                  |       ddd       t        j	                  d| |||       y# 1 sw Y   #xY w)uS   동일 (module, stem, version) 조합에 대해 process 당 1회만 warning 발행.NzPrompt version pack drift: module=%s stem=%s loaded_from=%s (latest_in_module=%s). To enforce single-pack semantics, set PROMPT_VERSION_PACK_STRICT=true and align stem files within the latest version directory.)_DRIFT_WARNED_LOCKr   addrr   rs   )r   rV   r   r   r'   s        r   _emit_pack_drift_warningr     sb     4
!C	- 
	 	# 
 NN	 	g~	 
	s   
AAArc   c                     t         | z  }|j                         sddg fS t        |       }|D ]%  }||z  | | z  }|j                         s |||fc S  dd|fS )u  stem 파일을 numeric-desc version 순회로 찾고 (path, found_version, all_versions) 반환.

    - found_version 이 ``all_versions[0]`` (latest) 와 다르면 drift 후보.
    - strict 모드면 latest 에 stem 없을 때 caller 가 RuntimeError 발생시킨다.
    N)rF   rt   r   )r   r   rc   r   versionsverr   s          r   ru   ru     sv     &JT2~$V,HS dVC5>1<<>#x''  xr%   c                    |Dt         | z  |z  | | z  }|j                         sy|j                  d      j                         S t	        | ||      \  }}}|'t               r|rt        d| | d|  d|d    d	      y|r|d   nd}|r;||k7  r6t               rt        d| | d
| d|  d| d
      t        | | | ||       |j                  d      j                         S )uF  파일에서 stem 의 최신 매칭 version 을 읽어 반환.

    같은 module 안에서 stem 이 서로 다른 version 에서 로드될 수 있다
    (backward-compat). drift 시 logger.warning + ``module_pack.drift`` 가시화.
    ``PROMPT_VERSION_PACK_STRICT`` 가 truthy 면 latest pack 에 stem 이 없을 때
    RuntimeError 를 raise 하여 운영자가 prompt 정리 후 single-pack 으로 전환할
    수 있게 한다.

    W19A versioned-load (review §3.6): ``version`` 이 주어지면 그 pack
    디렉토리 안에서만 stem 을 찾고, 없으면 None 반환. latest 로 silent
    fallback 하지 않는다 (caller 의 명시적 pin 을 깨지 않음). 이 모드는
    strict drift 검사를 적용하지 않는다 — 명시적 version 선택은 운영자가
    이미 pack 을 알고 있다는 신호.
    NrB   )encodingrb   z"Prompt version pack strict: stem 'z&' not found in any version of module 'z'. Latest pack='r   zh'. Add the stem to the latest version directory or unset PROMPT_VERSION_PACK_STRICT to use lenient mode.z' missing in latest pack 'z' of module 'z' (resolved from 'zh' under lenient mode). Add the stem to the latest version directory or unset PROMPT_VERSION_PACK_STRICT.)rF   rt   	read_textrw   ru   r   RuntimeErrorr   )r   r   rc   r   r   	found_verr   
latest_vers           r   r   r     s<   ( v%/TF3%.@||~06688!6vt!ME9h}"$4TF3% @&&,X-=hqk] KBB   (!dJi:-"$4TF3% @#M& :; II  	!D6#JO??G?,2244r%   c           
         d}d}|@	 t        || |      }|r0t        |dd      |j                  dt        t        |dd            d}g }t        | z  }|j                         r>	 t        |       }|D ]-  }	||	z  | dz  }
|
j                         s|	t        |
      d} n |d
dd}n|
d|dndd}nddd}|ddd}nddd}|r|d   nd}t        |duxr |duxr |j                  d      |k7        }| |||d||d||t               |ddS # t        $ r"}t
        j                  d| ||       Y d}~d}~ww xY w# t        $ r"}t
        j                  d	| ||       Y d}~d}~ww xY w)u  ``load_prompt`` 가 실제로 사용할 source 의 provenance 표시 (problems.md #8).

    admin UI 가 prompt_template DB row 만 보고 active prompt 판단하던 문제를
    해소하기 위한 effective view. **caller 별 db 전달 여부에 따라 winner 가
    다르므로 두 mode 를 동시에 표시** — db-aware caller (db 전달, 예: location
    _floor_plan_step) 와 file-only caller (db 미 전달, 예: scene_extractor_v2,
    background_master_plan 등 대부분의 pipeline module).

    Returns:
        {
          "module": str, "name": str,
          "candidates": {
            "db":   {"id", "version", "is_active", "has_schema"} | None,
            "file": {"version", "path"} | None,
          },
          "effective": {
            "with_db":    {"winner": "db"|"file"|None, "fallback_reason": ...},
            "without_db": {"winner": "file"|None, "fallback_reason": ...},
          },
          "module_pack": {
            "versions": [str],          # numeric desc 정렬, 모든 version 디렉토리
            "latest": str | None,
            "strict_enabled": bool,     # PROMPT_VERSION_PACK_STRICT 평가 결과
            "stem_drift": bool,         # file_candidate.version != latest 면 True
          },
        }

    winner 결정:
      - with_db: DB 후보 존재 → "db", 없으면 file → "file", 둘 다 없으면 None
      - without_db: DB 무시, file 만 — file → "file", 없으면 None

    ``module_pack`` (problems.md #6) 는 admin UI 가 stem-version drift 와 strict
    toggle 상태를 한눈에 볼 수 있게 한다. 이 view 는 ``.md`` 파일 후보만
    고려하므로, schema (``.json``) drift 는 별도 ``get_effective_schema_source``
    로 확인한다.
    Nra   Trp   )ra   r   	is_active
has_schemaz-DB prompt effective lookup failed (%s/%s): %srX   )r   pathz/File prompt effective lookup failed (%s/%s): %sr/   )winnerfallback_reasonrd   no_db_sessionno_active_db_rowno_filer   r   )r/   rd   )with_db
without_db)r   lateststrict_enabled
stem_drift)r   r   
candidates	effectivemodule_pack)r3   rq   r   r   r,   rr   rs   rF   rt   r   rL   r   r   )r   r   r/   db_candidatefile_candidater   r   pack_versionsr   r   r   r   r   pack_latestr   s                  r   get_effective_sourcer     s   J .2L/3N 
~	+B=C!#tT2"{{!%"&wsM4'H"I	   "M&J	1&9M$"S(dV3<7<<>#& #E
&N  % !d;		#24*BT

 "i@ ! &4@
 $C
&3-"Kd" 	9t#	9y)[8J "

 $

 &!57$	
 a  	NN?s 	&  	NNA64QT 	s5   ?D #+E E 	D>D99D>	E,
E''E,c                     |r	 t        || |      }|r|j                  S 	 t        |       }|r|d   S dS # t        $ r"}t        j	                  d| ||       Y d}~:d}~ww xY w)u   현재 활성 버전 반환.uB   prompt_template DB lookup failed for %s/%s: %s — 파일 fallbackNr   )r3   r   r,   rr   rs   r   )r   r   r/   r   r   r   s         r   get_active_versionr     su    		t+B=C{{"  %V,H"8A;,,  	tNN_agimorss	ts   4 	AAA)r   N)NN)rX   )rX   Nr"   )1__doc__rz   loggingr   pathlibr   	threadingr   typingr   r   r   r   r	   r
   	getLogger__name__rr   r   setr   tuplerL   __annotations__r   _AllowedWhereExtrar3   __file__resolveparentrF   	functoolsr6   rU   rY   ry   ro   r   r   r   r   r   r   r   r   ru   r   r   r   r   r%   r   <module>r      sL  !F   	   < <			8	$ V +.5s5c3'( 0C ( >?  ')!66 6 $	6
 c]6t 	N##**1188    2#    :;3 ;# ;S ;S ;  &' "0	 "jj
j 	j
 c]j 
#s(^j` !	+;+;
+; c]	+; 	+;b !	$$
$ c]	$
 
#s(^$N# $s) ,9C 9s 9tCy 9 $%(:=	& (-   !$ 
8D>8C=$s)34 , !	0505
05 
05 c]	05
 c]05ft tC tT#s(^ tn-s -# -8C= -r%   