!"2h] NddlZddlZddlZddlZddlZddlZddlZddlZddlm Z m Z m Z m Z m Z mZddlmZddlZddlmZmZmZmZmZeddlmZdZdZdZGd d ZGd d ZGd dZGddZ eeefde!de!de"ddfdZ#ddZ$e%dk(r e$yy)N)DictListAnyCallableTupleTextIO)ArgumentParser)import_custom_nodes find_path!add_comfyui_directory_to_sys_pathadd_extra_model_pathsget_value_at_index)NODE_CLASS_MAPPINGSzworkflow_api.jsonzworkflow_api.py cZeZdZdZed deezdedefdZedeezdeddfdZ y) FileHandlerzHandles reading and writing files. This class provides methods to read JSON data from an input file and write code to an output file. file_pathencodingreturnct|drtj|St|dd5}tj|}ddd|S#1swYSxYw)a Reads a JSON file and returns its contents as a dictionary. Args: file_path (str): The path to the JSON file. Returns: dict: The contents of the JSON file as a dictionary. Raises: FileNotFoundError: If the file is not found, it lists all JSON files in the directory of the file path. ValueError: If the file is not a valid JSON. readrutf-8rN)hasattrjsonloadopen)rrfiledatas 8D:\ComfyUI_windows_portable\ComfyUI\comfyui_to_python.pyread_json_filezFileHandler.read_json_file&sM 9f %99Y' ' )S7 3t99T?D4 4 s AAcodeNcZt|tr~tjj |}|r4tjj |stj |t|dd5}|j|dddy|j|y#1swYyxYw)zWrite the specified code to a Python file. Args: file_path (str): The path to the Python file. code (str): The code to write to the file. Returns: None wrrN) isinstancestrospathdirnameexistsmakedirsrwrite)rr# directoryrs r!write_code_to_filezFileHandler.write_code_to_file<s{ i % 2I !: I&iw74 4 87 OOD !87s 4B!!B*)r) __name__ __module__ __qualname____doc__ staticmethodr'rdictr"r/r!rr sa #,#D*"cFl"#"$""r7rcVeZdZdZdedefdZdeeeee ffdZ deddfd Z d d Z y) LoadOrderDetermineraDetermine the load order of each key in the provided dictionary. This class places the nodes without node dependencies first, then ensures that any node whose result is used in another node will be added to the list in the order it should be executed. Attributes: data (Dict): The dictionary for which to determine the load order. node_class_mappings (Dict): Mappings of node classes. r node_class_mappingscJ||_||_i|_g|_d|_y)zInitialize the LoadOrderDeterminer with the given data and node class mappings. Args: data (Dict): The dictionary for which to determine the load order. node_class_mappings (Dict): Mappings of node classes. FN)r r:visited load_orderis_special_function)selfr r:s r!__init__zLoadOrderDeterminer.__init__as) #6  #( r7rc|jd|_|jD]"}||jvs|j |$|j S)zDetermine the load order for the given data. Returns: List[Tuple[str, Dict, bool]]: A list of tuples representing the load order. F)_load_special_functions_firstr>r r<_dfsr=)r?keys r!determine_load_orderz(LoadOrderDeterminer.determine_load_ordernsI **,#( 99C$,,& #r7rDNcNd|j|<|j|d}|jD]<\}}t|ts|d|jvs)|j |d>|j j||j||jfy)zDepth-First Search function to determine the load order. Args: key (str): The key from which to start the DFS. Returns: None TinputsrN) r<r itemsr&listrCr=appendr>)r?rDrG input_keyvals r!rCzLoadOrderDeterminer._dfs{s! S3)$llnNIs#t$Qt||)C #a&! - TYYs^T5M5MNOr7cX|jD]}|j|j|d}|jdk(s?|jdvs1t d|j|dj Drud|_||jvs|j|y)znLoad functions without dependencies, loaderes, and encoders first. Returns: None class_typeloaders)encodec3<K|]}t|tywN)r&rI).0rLs r! zDLoadOrderDeterminer._load_special_functions_first..s5VcJsD)5VsrGTN) r r:CATEGORYFUNCTIONanyvaluesr>r<rC)r?rD class_defs r!rBz1LoadOrderDeterminer._load_special_functions_firsts99CN003 1MNPI""i/%%359YYs^H5M5T5T5V,0(dll*IIcNr7rN) r0r1r2r3rr@rrr'boolrErCrBr6r7r!r9r9VsQ )T ) ) d5dD+A&B PPP*#r7r9c eZdZdZdedefdZ ddededefdZ d ed ed ed e def d Z dede defdZ ddedeedeededef dZdedeeeeffdZededefdZd edefdZdededefdZy) CodeGeneratorzGenerates Python code for a workflow based on the load order. Attributes: node_class_mappings (Dict): Mappings of node classes. base_node_class_mappings (Dict): Base mappings of node classes. r:base_node_class_mappingsc ||_||_y)zInitialize the CodeGenerator with given node class mappings. Args: node_class_mappings (Dict): Mappings of node classes. base_node_class_mappings (Dict): Base mappings of node classes. N)r:r^)r?r:r^s r!r@zCodeGenerator.__init__s$7 (@%r7r= queue_sizerc tdgiggf\}}}}i}d}|D]R\} } } | d| d} } |j| j}|j| }d}d|jvr|dD]}|| jvsd}|ry| |vr| dk(r|j | \} }}|j | || <| |j jvr|j|| |j jvrd}|j||jt||j}|du}| jDcic]\}}|s||vr||} }}d |jvr/d |d jvrtjd d | d <n|d |vrtjd d | d <|j | d | || <|j| |} | r7|j|j || |j|| | fi| |j|j || |j|| | fi| U|j#|||||}|Scc}}w)a9Generate the execution code based on the load order. Args: load_order (List): A list of tuples representing the load order. queue_size (int): The number of photos that will be created by the script. Returns: str: Generated execution code as a string. rFrGrNrequiredT PreviewImageNhidden unique_idl_)setr: INPUT_TYPESkeysget_class_infoclean_variable_namer^addrJget_function_parametersgetattrrVrHrandomrandint update_inputscreate_function_call_codeassemble_python_code)r?r=r`import_statementsexecuted_variablesspecial_functions_coder#initialized_objects custom_nodesidxr r>rGrN input_typesrYmissing_required_variablerbimport_statement class_codeclass_def_params no_paramsrDvalue final_codes r!generate_workflowzCodeGenerator.generate_workflows& &' (    O K-/Et! .8 *C*!%hl1CJF22:>JJLK<00<>I). %[--// +J 7Hv{{}4481!8)!44/;?;N;N<8 ,j372J2J:2V#J/!>!>!C!C!EE%))*:;T%B%B%G%G%II#'L&--j9 $;; 9#5#56  )D0I #),,."0JC'7 7U "0 K,,..;x#8#=#=#??&,nnQ&>{#!-"22*0..E*BF;'*.)A)A*)M(NaPSu&U s #''0BCF"&--2D22+J7!***3/+  !  2D22+J7!***3/+  ! I/9^.. 5tZ [s J obj_namefunc variable_namer>c djfd|jD}|d|d|d|d}|sd|}|S)aGenerate Python code for a function call. Args: obj_name (str): The name of the initialized object. func (str): The function to be called. variable_name (str): The name of the variable that the function result should be assigned to. is_special_function (bool): Determines the code indentation. **kwargs: The keyword arguments for the function. Returns: str: The generated Python code. , c3HK|]\}}j||ywrR) format_arg)rSrDrr?s r!rTz:CodeGenerator.create_function_call_code..<s!V~ee4~s" = .(z)  )joinrH)r?rrrr>kwargsargsr#s` r!rsz'CodeGenerator.create_function_call_code(sT(yyVv||~VV H:QtfAdV3?#v;D r7rDrc|dk(s|dk(r|dSt|tr*|jddjdd}|d|dSt|trd |vr |d |d S|d |S) zFormats arguments based on key and value. Args: key (str): Argument key. value (any): Argument value. Returns: str: Formatted argument as a string. noise_seedseedz=random.randint(1, 2**64) z\n"'z="r=)r&r'replacer5)r?rDrs r!rzCodeGenerator.format_argHs , #-U34 4 s #MM$.66sC@EU"UG1% % t $E)AU!E/234 4awr7ruspeical_functions_coder#cJg}ttttfD])}|j dt j |+gd|zdgz}|r/|j dt j tdd}nd}ddj|D cgc]} | c} g} d|d zd j|zd |d zd j|z} dj|| zd| dd dgz} tj| tj} | Scc} w)aGenerates the final code string. Args: import_statements (set): A set of unique import statements. speical_functions_code (List[str]): A list of special functions code strings. code (List[str]): A list of code strings. queue_size (int): Number of photos that will be generated by the script. custom_nodes (bool): Whether to include custom nodes in the code. Returns: str: Generated final code as a string. r)z import osz import randomz import sysz0from typing import Sequence, Mapping, Any, Unionz import torchz> add_comfyui_directory_to_sys_path() add_extra_model_paths() zimport_custom_nodes() zfrom nodes import rz def main(): zwith torch.inference_mode(): z z for q in range(z): zif __name__ == "__main__":z main())mode) rr r r rJinspect getsourcer rblack format_strMode) r?rurr#r`ry func_stringsrstatic_imports class_name imports_codemain_function_coders r!rtz"CodeGenerator.assemble_python_code[ss*   - !  D   "W%6%6t%<$= > ?    TT U    ! !Bw'8'89L'M&Nb"Q R6LL!IZ+[IZ:JIZ+[!\ ] ^ @A Bmm23 4( |8< =mmD!  " YY  %r+GT U %%juzz|D ',\s D rNc|}|j|}||jjvr|d|jd}n|d|d}|||fS)zGenerates and returns necessary information about class type. Args: class_type (str): Class type. Returns: Tuple[str, str, str]: Updated class type, import statement string, class initialization code. rz()z = NODE_CLASS_MAPPINGS["z"]())rlr^rjstrip)r?rNr}rr~s r!rkzCodeGenerator.get_class_infosn&00< 66;;= =)?#j.>.>.@-ADJ)?*B:,dSJ+Z77r7c|jjjddjdd}tjdd|}|dj rd|z}|S)a Remove any characters from variable name that could cause errors running the Python script. Args: class_type (str): Class type. Returns: str: Cleaned variable name with no special characters or spaces -rg z [^a-z0-9_]rr)lowerrrresubisdigit)rN clean_names r!rlz!CodeGenerator.clean_variable_namesk %%'--/77SAII#sS VVM2z:  a= "z)Jr7cjtj|}|jjDcic]-\}}||j|j k7r |jnd/}}}t d|jjD}|st|jSdScc}}w)zGet the names of a function's parameters. Args: func (Callable): The function whose parameters we want to inspect. Returns: List: A list containing the names of the function's parameters. Nc3jK|]+}|jtjjk(-ywrR)kindr Parameter VAR_KEYWORD)rSparams r!rTz8CodeGenerator.get_function_parameters..s- 6 JJ'++77 76s13) r signature parametersrHdefaultemptyrWrXrIrj)r?rrnamerr catch_alls r!rnz%CodeGenerator.get_function_parameterss%%d+  )3399; ; e 5==EKK#?%--T I;   "--446  /8tJOO%&ATA s2B/rGrvc|jD]L}t||ts||d|jvs0dd|||dd||ddi||<N|S)aUpdate inputs based on the executed variables. Args: inputs (Dict): Inputs dictionary to update. executed_variables (Dict): Dictionary storing executed variable names. Returns: Dict: Updated inputs dictionary. rrzget_value_at_index(rrf))rjr&rI)r?rGrvrDs r!rrzCodeGenerator.update_inputss;;=C6#;-3KN&8&=&=&??$':;MfUXkZ[n;]:^^`aghkalmnao`ppq%rs ! r7N)r)F)r0r1r2r3rr@rintr'rr[rsrWrrhrtrrkr4rlrrnrrr6r7r!r]r]sQADADAmmm m^  "  @ c # # 2 DD!%S D3i D  D DL88sC}1E8$,BHBB(Ddtr7r]c JeZdZdZddddedfdededeezded ed e f d Z d Z y )ComfyUItoPythonaMain workflow to generate Python code from a workflow_api.json file. Attributes: input_file (str): Path to the input JSON file. output_file (str): Path to the output Python file. queue_size (int): The number of photos that will be created by the script. node_class_mappings (Dict): Mappings of node classes. base_node_class_mappings (Dict): Base mappings of node classes. rrfFworkflow input_file output_filer`r:needs_init_custom_nodesc|r |r td|s |s td|s td||_||_||_||_||_||_tj|j |_ |jy)aInitialize the ComfyUItoPython class with the given parameters. Exactly one of workflow or input_file must be specified. Args: workflow (str): The workflow's JSON. input_file (str): Path to the input JSON file. output_file (str | TextIO): Path to the output file or a file-like object. queue_size (int): The number of times a workflow will be executed by the script. Defaults to 1. node_class_mappings (Dict): Mappings of node classes. Defaults to NODE_CLASS_MAPPINGS. needs_init_custom_nodes (bool): Whether to initialize custom nodes. Defaults to False. z*Can't provide both input_file and workflowzNeeds input_file or workflowzNeeds output_fileN) ValueErrorrrrr`r:rcopydeepcopyr^execute)r?rrrr`r:rs r!r@zComfyUItoPython.__init__s$ (IJ JH;< <01 1  $&$#6 '>$(, d6N6N(O% r7c |jr tni|_|jr tj |j}nt j|j}t||j}|j}t|j|j}|j||j}tj|j |t#d|j y)z^Execute the main workflow to generate Python code. Returns: None )r`z+Code successfully generated and written to N)rr r^rrr"rloadsrr9r:rEr]rr`r/rprint)r?r load_order_determinerr=code_generatorgenerated_codes r!rzComfyUItoPython.execute"s  ' '  !-/D ) ??--doo>D::dmm,D!4D$:R:R S*??A '  $ $d&C&C (99 4??:  &&t'7'7H ;DGenerate Python code from a ComfyUI workflow_api.json file.) descriptionz-fz --input_filezpath to the input JSON file)typehelprz-oz --output_filezpath to the output Python filez-qz --queue_sizez8number of times the workflow will be executed by defaultzDone.Nr6) r add_argumentr'DEFAULT_INPUT_FILEDEFAULT_OUTPUT_FILErDEFAULT_QUEUE_SIZE parse_argsrvarsr)parserpargss r!mainr`s TF    *"     -#     G"     E$u+ 'Nr7__main__rZ)&rglobrrr(rpsysrtypingrrrrrrargparser rcomfyui_to_python_utilsr r r r rnodesrrrrrr9r]rr'rrrr0r6r7r!rs  ;;# "#%)'3"3"lN#N#bIIX QPQPj)*( 2> z Fr7