
    (<iSy                     4   U d Z ddlZddlZddl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  ee      j                  Z ej"                  d      Z ed      ZdZ G d	 d
e      Ze	 G d d             Z eg d      Zee   ed<    eg d      Zee   ed<    eg d      Zee   ed<    eg d      Zee   ed<   efdedeeef   fdZdedefdZ dedede!fdZ"dedede!fdZ#dedede!fdZ$ ed      Z%d ed!ededz  fd"Z&d6d#eded$e'd%e'de!f
d&Z(d'ede)e!e*e   f   fd(Z+d)edede!fd*Z,dedede!fd+Z-ej\                  d,ej^                  d-ej`                  d.ejb                  d/iZ2eee)eedz  f   f   ed0<   d1ededede!fd2Z3deded3ed4edef
d5Z4y)7u  이미지 생성 라우터 — 용도별 최적 방법 자동 선택 + fallback 체인.

용도별 라우팅:
- "photorealistic" / "광고" / "포토" → Gemini Pro
- "cardnews" / "카드뉴스" / "배너" / "인포그래픽" → Satori
- "hybrid" / "한글+사진" / "텍스트오버레이" → 하이브리드
- "infographic" / "comparison_table" / "checklist" / "process_flow" / "chart" → Claude CLI HTML→PNG

Fallback 체인:
- Gemini 실패 → 에러 반환 (대체 불가)
- Satori 실패 → 에러 반환 (HTML 기반이라 대체 불가)
- 하이브리드 실패 → Gemini 단독 (텍스트 없이)
- Infographic 실패 → Satori

제약:
- fallback 최대 2회 시도
- 각 시도마다 로그 기록
    N)	dataclass)Enum)Pathimage_routerz/home/jay/workspace/.env.keys   c                        e Zd ZdZdZdZdZdZy)	ImageTypeu   이미지 생성 방법 유형.photorealisticcardnewshybridinfographicN)__name__
__module____qualname____doc__PHOTOREALISTICCARDNEWSHYBRIDINFOGRAPHIC     P/home/jay/workspace/.worktrees/task-2057-dev2/tools/ai-image-gen/image_router.pyr	   r	   3   s    )%NHFKr   r	   c                   d    e Zd ZU dZeed<   edz  ed<   eed<   eed<   eed<   edz  ed<   e	ed	<   y)
GenerationResultu   이미지 생성 결과.successN
image_pathmethod_usedfallback_usedattemptserror_messageelapsed_seconds)
r   r   r   r   bool__annotations__r   strintfloatr   r   r   r   r   <   s5    "MtM:r   r   )r
   u   광고u   포토_PHOTOREALISTIC_KEYWORDS)r   u   카드뉴스u   배너u   인포그래픽_CARDNEWS_KEYWORDS)r   u   한글+사진u   텍스트오버레이_HYBRID_KEYWORDS)r   comparison_table	checklistprocess_flowchart_INFOGRAPHIC_KEYWORDSfilepathreturnc                    i }| j                         rt        | d      5 }|D ]  }|j                         }|r|j                  d      r'|j                  d      r|t	        d      d }d|v sK|j                  d      \  }}}|j                         j                  d      }|||j                         <    	 ddd       dD ])  }t        j                  j                  |      }|s%|||<   + |S # 1 sw Y   9xY w)	u~   환경변수 파일에서 KEY=VALUE 형식의 키를 로드한다.

    환경변수가 파일보다 우선 적용된다.
    utf-8encoding#zexport N=z"')GEMINI_API_KEY)	existsopenstrip
startswithlen	partitionosenvironget)	r/   keysflinek_vkeyenv_vals	            r   _load_env_keysrI   `   s    
 D(W- 
	( 	(zz|ts3??9-I 01D$;"nnS1GAq!	.A&'DO	(
	( #  **..%DI  K!
	( 
	(s   AC0.A	C00C9purposec                 T   | j                         j                         }|t        D ch c]  }|j                          c}v rt        j                  S |t
        D ch c]  }|j                          c}v rt        j                  S |t        D ch c]  }|j                          c}v rt        j                  S |t        D ch c]  }|j                          c}v rt        j                  S | j                         t        v rt        j                  S | j                         t
        v rt        j                  S | j                         t        v rt        j                  S | j                         t        v rt        j                  S t        d| d      c c}w c c}w c c}w c c}w )u  용도 문자열을 ImageType으로 매핑한다.

    Args:
        purpose: 이미지 용도 (예: "photorealistic", "카드뉴스", "hybrid")

    Returns:
        ImageType 열거형 값

    Raises:
        ValueError: 알 수 없는 용도인 경우
    u   알 수 없는 용도: u   . 지원 용도: photorealistic/광고/포토, cardnews/카드뉴스/배너/인포그래픽, hybrid/한글+사진/텍스트오버레이, infographic/comparison_table/checklist/process_flow/chart)r:   lowerr'   r	   r   r(   r   r)   r   r.   r   
ValueError)rJ   
normalizedrD   s      r   route_image_typerO      sO    &&(J )ABAaggiBB''');<Aaggi<<!!!)9:Aaggi::)>?Aaggi??$$$ }}22'''}},,!!!}}**}}//$$$

!' -D 	E ' C<:?s   FFF F%promptoutput_pathc                    t         j                  d|j                         t        t        j
                  vr-t        j
                  j                  dt        t                     	 ddl}ddl	}|j                         }|j                  j                  dd       d}| j                         j                  d      s| d|  } n| d	|  } |j                  || |d
      }|j!                         s0|j#                  d      j!                         r|j#                  d      n|}|j!                         xr |j%                         j&                  dkD  S # t(        $ r4}t         j+                  dt-        |      j.                  |       Y d}~yd}~ww xY w)uz   Gemini Pro로 포토리얼리스틱 이미지 생성.

    Returns:
        True: 생성 성공, False: 생성 실패
    u$   [Gemini] 이미지 생성 요청: %sr   NTparentsexist_okzIMPORTANT: Do not render any text, words, letters, or characters in the image. The image must be purely visual with no text overlays.)zcreate z	generate zdesign zK

Generate a high-quality photorealistic image based on this description:

z

router.jpgu   [Gemini] 생성 실패: %s: %sF)loginfoname_AI_IMAGE_GEN_DIRsyspathinsertr$   gcloud_authgemini_pro_generateget_access_tokenparentmkdirrL   r;   generate_image_via_gemini_apir8   with_suffixstatst_size	Exceptionwarningtyper   )	rP   rQ   r_   r`   token	anti_textresultactual_pathexcs	            r   _generate_geminirp      s`    HH3[5E5EF(3012",,.   = \	||~(()LM+lmsltu  "{$vh/F$BB5&R]_gh %%'K,C,CF,K,R,R,T ##F+ 	
 !!#F(8(8(:(B(BQ(FF 4d3i6H6H#Ns   %C)E 	F*FFc           
      <   t         j                  d|j                         t        dz  dz  }|j                  j                  dd       	 t        j                  dt        |      d| dt        |      gddd	
      }|j                  dk7  r$t         j                  d|j                  dd        y|j                         xr |j                         j                  dkD  S # t        $ r4}t         j                  dt!        |      j"                  |       Y d}~yd}~ww xY w)u   Satori HTML→PNG 변환으로 카드뉴스/배너 생성.

    Returns:
        True: 생성 성공, False: 생성 실패
    u%   [Satori] HTML→PNG 변환 시작: %ssatori-testsatori_cli.jsTrS   nodez--prompt--outputx   capture_outputtexttimeoutr   u   [Satori] subprocess 실패: %sN   Fu   [Satori] 생성 실패: %s: %s)rX   rY   rZ   r[   rb   rc   
subprocessrunr$   
returncoderi   stderrr8   rf   rg   rh   rj   r   )rP   rQ   
satori_clirm   ro   s        r   _generate_satorir      s     HH4k6F6FG"]2_DJTD9S_j&*c+FVW	
 !KK8&--:MN!!#F(8(8(:(B(BQ(FF 4d3i6H6H#Ns   A#C /.C 	D'*DDc                    t         j                  d|j                         t        t        j
                  vr-t        j
                  j                  dt        t                     	 ddl}ddl	m
} |j                  dz  }|j                         st         j                  d|       y|j                  j                         s!t         j                  d|j                         yd	| dd
 ddd}|j                  j!                  dd        |       5 }|j"                  j%                         }	 |j'                  ddd      }|j)                  ||j                  |||       |j+                          	 ddd       |j                         xr |j-                         j.                  dkD  S # |j+                          w xY w# 1 sw Y   MxY w# t0        $ r4}	t         j                  dt3        |	      j4                  |	       Y d}	~	yd}	~	ww xY w)u   Gemini 배경 + HTML 텍스트 오버레이 하이브리드 생성.

    Returns:
        True: 생성 성공, False: 생성 실패
    u5   [Hybrid] Gemini 배경 + HTML 오버레이 시작: %sr   Nsync_playwrightzbg_A.jpgu$   [Hybrid] 배경 이미지 없음: %sFu   [Hybrid] 템플릿 없음: %srV   2   zAI Generatedu   더 알아보기)scenarioheadlinesubTextctaTextTrS   i8  widthheightviewportu   [Hybrid] 생성 실패: %s: %s)rX   rY   rZ   r[   r\   r]   r^   r$   generate_hybridplaywright.sync_apir   
OUTPUT_DIRr8   ri   TEMPLATE_PATHrb   rc   chromiumlaunchnew_pagecapture_hybridcloserf   rg   rh   rj   r   )
rP   rQ   _ghr   bg_pathscenario_datapbrowserpagero   s
             r   _generate_hybridr      s    HHDkFVFVW(3012%7..:-~~KK>H  '')KK79J9JK !s%)	)
 	   = 	 !jj'')G ''441P'Q""4):):M7T_`	  !!#F(8(8(:(B(BQ(FF 	  	   4d3i6H6H#NsT   %?G %:G  .G F;*4F&F;/6G &F88F;;G G 	H*G??Hz/home/jay/.local/bin/claudedescriptionimg_typec           
         d| d| d|  d}t         j                  j                         }d|d<   d}d}t        d	|d	z         D ]  }|}|r||k(  r	|d
| dz   }t	        j
                  t        t              d|ddgddd|d      }|j                  dk7  rYt        j                  d|j                  |||j                  r|j                  dd nd       ||k  rt        j                  d	       |j                  j                         }	|	rd|	vr2t        j                  d||       ||k  rt        j                  d	       	 t!        j"                  |	      c S  |r{|d
| dz   }t	        j
                  t        t              d|ddgddd|d      }|j                  dk(  r6|j                  j                         }	|	rd|	v r	 t!        j"                  |	      S t        j                  d|d	z          y# t         j$                  $ rH}
t        |
      }t        j                  d|||
       ||k  rt        j                  d	       Y d}
~
d}
~
ww xY w# t         j$                  $ r }
t        j                  d|
       Y d}
~
d}
~
ww xY w)u   Claude CLI로 한국어 설명에서 구조화된 JSON 데이터를 추출한다.

    Returns:
        추출된 JSON dict 또는 실패 시 None
    u   아래 설명을 분석하여 uX    유형의 구조화된 JSON 데이터를 추출하세요.

## JSON 스키마
{"type": "u1  ", "title": "제목", "subtitle": "부제목(선택)", "items": [...]}

## 유형별 items 구조:
- infographic: [{"heading": "...", "description": "..."}]
- comparison_table: [{"label": "...", "features": ["...", "..."]}]
- checklist: ["항목1", "항목2", ...]
- process_flow: ["단계1", "단계2", ...]
- chart: [{"label": "...", "value": 숫자}]

## 규칙
- 순수 JSON만 출력 (```json 태그, 설명 텍스트 없이)
- 한국어 텍스트 사용
- items 최대 개수: comparison=8, checklist=10, process_flow=6, chart=8, infographic=6

## 내용
u   

JSON만 출력:1(CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC   N   u%   

[이전 시도 JSON 파싱 오류: u%   . 올바른 JSON만 출력하세요.]z-pz--modelhaikuT<   z/tmp)rx   ry   rz   envcwdr   uH   [Infographic] Claude CLI 비정상 종료 (code=%d), 재시도 %d/%d: %sr{   z	no stderr{u>   [Infographic] Claude CLI 빈/비정상 응답, 재시도 %d/%du5   [Infographic] JSON 파싱 실패, 재시도 %d/%d: %su5   [Infographic] JSON 파싱 최종 실패 (4회차): %su3   [Infographic] Claude CLI %d회 시도 모두 실패)r>   r?   copyranger|   r}   r$   _CLAUDE_CLIr~   rX   ri   r   timesleepstdoutr:   _jsonloadsJSONDecodeError)r   r   rP   r   max_attemptslast_parse_errorattemptcurrent_promptrm   rawro   retry_prompts               r   _extract_structured_jsonr     s    )
 3Z   "] #	 " **//
C69C23L#'L1,- 0< 7#(OP`Oa  bG  'H  HNt^YH
 !KKZ!!'-}}ds#+ %

1mm!!#cnKKP
 %

1	;;s##M0f "IJZI[  \A  !B  Bt\9gF
 !--%%'Cscz^ ;;s++ KKE|VWGWX? $$ 		"3xKKG	 %

1		6 ,, ^KK WY\]]^s0   G0I 0I=IIJ!I<<Jhtml_contentr   r   c                    	 ddl m} |j                  d      }|j                  j                  dd       d| d	| d
|  d}|j                  |d       	  |       5 }|j                  j                         }	 |j                  ||d      }	|	j                  d| d       |	j                  t        |      d       |j                          	 ddd       |j!                         xr |j#                         j$                  dkD  	 |j!                         r|j'                  d       S S # t        $ r t        j	                  d       Y yw xY w# |j                          w xY w# 1 sw Y   xY w# t(        $ rW}
t        j	                  dt+        |
      j,                  |
       Y d}
~
|j!                         r|j'                  d       yyd}
~
ww xY w# |j!                         r|j'                  d       w w xY w)u-   Playwright로 HTML을 PNG로 렌더링한다.r   r   u+   [Playwright] playwright 패키지 미설치Fz.htmlTrS   z<!DOCTYPE html>
<html><head><meta charset="utf-8">
<style>
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css');
* { margin: 0; padding: 0; box-sizing: border-box; }
body { width: zpx; min-height: z/px; overflow: visible; }
</style></head>
<body>z</body></html>r2   r3   r   r   zfile://networkidle)
wait_until)r]   	full_pageN)
missing_oku%   [Playwright] 렌더링 실패: %s: %s)r   r   ImportErrorrX   ri   re   rb   rc   
write_textr   r   r   goto
screenshotr$   r   r8   rf   rg   unlinkrh   rj   r   )r   rQ   r   r   r   	html_path	full_htmlr   r   r   ro   s              r   _render_html_to_pngr     s   7
 ''0ITD9
 w&vh /nN&I W5. 	 !jj'')G ''5F1S'T		GI;/M	JS%5F	  !!#F(8(8(:(B(BQ(FF
 - A  AB4 	  	   ;T#Y=O=OQTU- 	 - sl   D, E1 E%9AE E%6E1 ,EEE""E%%E.*E1 1	G:*G$G GG %G9r   c                    g }| j                         sddgfS | j                         j                  dz  }|dk  r
dd|ddgfS 	 dd	l}dd
lm} |j                  |       }|j                  \  }}|dk  r|j                  d| d       |dk  r|j                  d| d       |j                  |j                  d            }|j                         }	|	dk  r|j                  d|	dd       n|	dkD  r|j                  d|	dd       |d	d	dd	d	df   }
t        |j                  |
j                  dd      d            }|dk  r|j                  d| d       |j                  |d      }|j                  |      }|d k  r||d!z   kD  j!                         }n||d!z
  k  j!                         }||j                  z  }|d"kD  r|j                  d#|d$d%       t        |D cg c]  }d(|v sd)|v s| c}      dk(  }||fS # t"        $ r t$        j'                  d&       Y It(        $ r }t$        j'                  d'|       Y d	}~ld	}~ww xY wc c}w )*uo   블로그 이미지 품질을 검증한다.

    Returns:
        (통과 여부, 경고 메시지 목록)
    Fu-   이미지 파일이 존재하지 않습니다i      u   파일 크기 z.1fu'   KB < 최소 15KB (빈 이미지 의심)r   N)Imagei   u   너비 u   px < 최소 800pxi  u   높이 u   px < 최소 400pxRGB   u   평균 밝기 z.0fu,   /255 — 이미지가 너무 어둡습니다   u@   /255 — 이미지가 너무 밝습니다 (빈 이미지 의심)   r   )axisr   u   고유 색상 u.   개 — 이미지가 너무 단조롭습니다r      r   g?u   텍스트 영역 비율 추정 z.0%u    > 40% 제한u1   [QC] PIL/numpy 미설치 — 고급 검증 스킵u    [QC] 이미지 분석 실패: %su   최소u   빈 이미지)r8   rf   rg   numpyPILr   r9   sizeappendarrayconvertmeanr<   uniquereshapemediansumr   rX   ri   rh   )r   warningsfile_size_kbnpr   imgwharr
brightnessdownsampledunique_colorsgraybg_brightnesstext_pixels
text_ratioro   passeds                     r   _validate_image_qualityr     sx    HFGGG??$,,t3LbS'99`abbb)=jj$xx1s7OOgaS(9:;s7OOgaS(9:;hhs{{5)* XXZ
?OOnZ,<<hij#OOnZ,<<|}~ #A#ss(mBIIk&9&9"a&@qIIJ2OOn]O;ijk wwsw#		$3-""4499;K-""4499;K 499,
OO=j=M][\ XOQ/Q:N!OPTUUF8  IGH =6<<= Ps+   FH &I4II&I.I		I	json_datac           
      *   t         dz  dz  }|j                  j                  dd       	 t        j                  | d      }t        j                  dt        |      d|d	t        |      gddd
      }|j                  dk7  r$t        j                  d|j                  dd        y|j                         xr |j                         j                  dkD  S # t        $ r4}t        j                  dt!        |      j"                  |       Y d}~yd}~ww xY w)uA   Satori --json 모드로 JSON 데이터를 PNG로 렌더링한다.rr   rs   TrS   F)ensure_asciirt   z--jsonru   rv   rw   r   u#   [Satori-JSON] subprocess 실패: %sNr{   u&   [Satori-JSON] 렌더링 실패: %s: %s)r[   rb   rc   r   dumpsr|   r}   r$   r~   rX   ri   r   r8   rf   rg   rh   rj   r   )r   rQ   r   json_strrm   ro   s         r   _render_json_to_pngr     s    "]2_DJTD9;;yu=S_h*c+FVW	
 !KK=v}}Tc?RS!!#F(8(8(:(B(BQ(FF <d3i>P>PRUVs   A:C &.C 	D*DDc                    t         j                  d|j                         ddl}|j	                  d| |j
                        }|r|j                  d      nd}|r|j                  d      n| }	 t        ||      }|t         j                  d       y	t        ||      S # t        $ r4}t         j                  d
t        |      j                  |       Y d}~y	d}~ww xY w)u_   인포그래픽 유형 이미지 생성: Claude CLI JSON 추출 → Satori 템플릿 렌더링.u-   [Infographic] JSON→Satori 변환 시작: %sr   Nz\[(\w+)\]\s*(.*)r   r   r   u    [Infographic] JSON 추출 실패Fu#   [Infographic] 생성 실패: %s: %s)rX   rY   rZ   rematchDOTALLgroupr   ri   r   rh   rj   r   )rP   rQ   r   mr   r   r   ro   s           r   _generate_infographicr     s    HH<k>N>NO
$fbii8AqwwqzMH !!''!*vK,[(C	KK:;"9k:: 949;M;MsSs   -#B B 	C&*CC)geminiN)satoriN)r   r   )r   r   _FALLBACK_CHAINmethod_namec                     ddl }|j                  |j                  |j                  |j                  d}||    }ddlm}  |||      S )u   method_name에 해당하는 생성 함수를 호출한다.

    모듈 전역을 통해 간접 호출하므로 unittest.mock.patch가 올바르게 동작한다.
    r   N)r   r   r   r   )Callable)r   rp   r   r   r   typingr   )r   rP   rQ   _selfdispatchfuncr   s          r   _call_methodr    sP    
 ! ((((((22	#H K D&+&&r   brand
output_dirc           	      x   t        j                         }t        |       }t        |   \  }}t        j                  d| ||       |j                  dd       |j                  dd      j                  dd      }|| d| dz  }	d}
|}d	}d
}	 t        |||	      }|r|	j                         s0|	j                  d      j                         r|	j                  d      }	t        j                         |z
  }t        j                  d||       	 dd
l}|j!                  |	|       t        j                  d|	j"                         t%        |	      \  }}|r%t        j                  ddj'                  |             |st        j                  d       t)        d|	|d	|
d
|      S |It        j                         |z
  }|r|n| d}t        j+                  d||       t)        d	d
|d	|
||      S |t        j                  d||       d}
d}|}|| d| dz  }		 t        |||	      }t        j                         |z
  }|rt        j                  d||       	 dd
l}|j!                  |	|       t        j                  d|	j"                         t%        |	      \  }}|r%t        j                  ddj'                  |             |st        j                  d       t)        d|	|d|
d
|      S |r|n| d| d}t        j+                  d ||||       t)        d	d
|d|
||      S # t        $ rW}t        j                  d||r|ndt        |      j                  |       d	}t        |      j                   d| }Y d
}~d
}~ww xY w# t        $ r!}t        j                  d|       Y d
}~fd
}~ww xY w# t        $ rR}t        |      j                   d| }d	}t        j                  d|t        |      j                  |       Y d
}~d
}~ww xY w# t        $ r!}t        j                  d|       Y d
}~d
}~ww xY w)!u  이미지를 생성한다. 실패 시 fallback 체인을 실행한다.

    Args:
        purpose: 이미지 용도 문자열 (라우팅 기준)
        prompt: 이미지 생성 프롬프트
        brand: 브랜드명 (파일명에 사용)
        output_dir: 출력 디렉토리

    Returns:
        GenerationResult: 생성 결과 (성공/실패, 사용 방법, fallback 여부 등)
    uD   [ImageRouter] 생성 시작 | 용도=%s → 방법=%s | 브랜드=%sTrS    rE   /z.pngr   FNu@   [FALLBACK] %s 실패 → %s 시도 (시도 1/2) | 원인: %s: %su   없음z: rW   u6   [ImageRouter] 생성 성공 | 방법=%s | 소요=%.2fsr   )titleu(   [IPTC] 메타데이터 삽입 완료: %su1   [IPTC] 메타데이터 삽입 실패 (무시): %su    [QC] 이미지 품질 경고: %sz; uH   [QC] QC 게이트 실패 → 프롬프트 단순화 후 재생성 시도)r   r   r   r   r   r    r!   u0    생성 실패 (fallback 불가 — HTML 기반)uH   [ImageRouter] 생성 실패 | 방법=%s | fallback 없음 | 소요=%.2fsu/   [FALLBACK] %s 실패 → %s 시도 (시도 1/2)r   u5   [FALLBACK] %s도 실패 (시도 2/2) | 원인: %s: %su8   [ImageRouter] fallback 성공 | 방법=%s | 소요=%.2fsu    및 u    모두 실패uI   [ImageRouter] 최종 실패 | primary=%s, fallback=%s | 소요=%.2fs | %s)r   	monotonicrO   r   rX   rY   rc   replacer  rh   ri   rj   r   r8   re   iptc_tagger	tag_imagerZ   r   joinr   error)rJ   rP   r  r  
start_time
image_typeprimary_methodfallback_method
safe_brandrQ   r   r   r   
last_errorr   ro   elapsed_tagger_tag_exc	qc_passedqc_warnings	error_msgfinal_errors                          r   generate_imager  6  sr   " !J!'*J&5j&A#NOHHN	 TD1sC(00c:J*Q~.>dCCK H KM!J4~v{C !!#(?(?(G(N(N(P%11&9K.."Z/D	
	W)k7HH?AQAQR "9!E	;HH7;9OPKKbc"##
 	
 .."Z/",J^4DDt2u			V	

  &##
 	
 =	
 HM!K*Q.?tDDK

D nn+GF	
	W)k7HH?AQAQR "9!E	;HH7;9OPKKbc"##
 	
 !+*>2B%GXXf0gKIIS ! E  	4N.OHI	
 S	**+2cU3
	44  	WKKKXVV	Wj  
S	**+2cU3
CI		
 	

0  	WKKKXVV	Wsb   L! 7N N1 7P !	N*AM<<N	N.N))N.1	P:APP	P9P44P9)i  i  )5r   jsonr   loggingr>   r|   r\   r   dataclassesr   enumr   pathlibr   __file__rb   r[   	getLoggerrX   ENV_KEYS_FILEMAX_FALLBACK_ATTEMPTSr	   r   	frozensetr'   r$   r#   r(   r)   r.   dictrI   rO   r"   rp   r   r   r   r   r%   r   tuplelistr   r   r   r   r   r   r   r   r  r  r   r   r   <module>r-     s  &   	  
  !  N))  g' 45     	 	 	  ,55[+\ )C. \%./h%i IcN i#,-a#b )C. b(1) y~   %2 T d38n >'c 'i '^ S  t    FS t  2&S &t & &Z 01c# c c cL%.c %. %.S %.X[ %.fj %.P; ;tT#Y1G ;|4 d t *# D T 8 .(*4	<isC$J!778 'c '3 'T 'd '0sss s 	s
 sr   