!"2h] xddlZddlZddlZddlZddlZddlZddlZddlZddlm Z m Z m Z m Z m Z mZddlmZddlZddlmZmZmZmZmZeddlmZdZdZdZGd d ZGd d ZGd dZGddZ eeefde!de!de"ddfdZ#ddZ$e%dkr e$dSdS)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 cheZdZdZed deezdedefdZedeezdeddfd Z dS) FileHandlerzHandles reading and writing files. This class provides methods to read JSON data from an input file and write code to an output file. utf-8 file_pathencodingreturnct|drtj|St|dd5}tj|}dddn #1swxYwY|S)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. readrrrN)hasattrjsonloadopen)rrfiledatas 8D:\ComfyUI_windows_portable\ComfyUI\comfyui_to_python.pyread_json_filezFileHandler.read_json_file&s 9f % % (9Y'' ' )S7 3 3 3 #t9T??D # # # # # # # # # # # # # # # sAAAcodeNct|trtj|}|r3tj|stj|t|dd5}||ddddS#1swxYwYdS||dS)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 % % " 22I ' !:!: ' I&&&iw777 !4 4    ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! OOD ! ! ! ! !s<BB#&B#)r) __name__ __module__ __qualname____doc__ staticmethodr'rdictr"r/r!rr s #,#D\*"cFl"#"$"""\"""r7rcdeZdZdZdedefdZdeeeee ffdZ deddfd Z d d Z dS) 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_mappingscL||_||_i|_g|_d|_dS)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|d|_|jD] }||jvr||!|jS)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_ordernsT **,,,#( 9  C$,&& #r7rDNcLd|j|<|j|d}|D]D\}}t|tr*|d|jvr||dE|j||j||jfdS)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)$llnn " "NIs#t$$ "Qt|)C)C #a&!!! TYs^T5MNOOOOOr7cP|jD]}|j|j|d}|jdksE|jdvszDLoadOrderDeterminer._load_special_functions_first..s=.1JsD))r7rGTN) r r:CATEGORYFUNCTIONanyvaluesr>r<rC)r?rD class_defs r!rBz1LoadOrderDeterminer._load_special_functions_firsts9 # #CN03 1MNPPI"i//%3359Ys^H5M5T5T5V5V4 ,0(dl**IIcNNN # #r7rN) r0r1r2r3rr@rrr'boolrErCrBr6r7r!r9r9Vs )T ) ) ) ) ) d5dD+A&B    PPPPPP*######r7r9c eZdZdZdedefdZ ddededefd Z d ed ed ed e def dZ dede defdZ d dedeedeededef dZdedeeeeffdZededefdZd edefdZdededefdZdS)! 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"||_||_dS)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 (@%%%r7rr= queue_sizerc ztdgiggf\}}}}i}d}|D]~\} } } | d| d} } |j| }|j| }d}d|vr#|dD]}|| vrd}|r| |vr| dkr|| \} }}|| || <| |jvr||| |jvrd}||| t||j dufd | D} d |vr5d |d vrtjd d | d <nd vrtjd d | d <|| d| || <|| |} | r7||j|| |j || | fi| I||j|| |j || | fi| ||||||}|S)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 PreviewImageNc(i|]\}}s|v ||Sr6r6)rSrDvalueclass_def_params no_paramss r! z3CodeGenerator.generate_workflow..s?C!$'7 7 7U 7 7 7r7hidden 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_code final_coderfrgs @@r!generate_workflowzCodeGenerator.generate_workflows &' ( (    O K-/Et! .8L L  *C*!%hl1CJF2:>JJLLK<0<>>I). %[--//// +J 799Hv{{}}44481( !444//;?;N;N<<8 ,j372J2J:2V2V#J/!>!C!C!E!EEE%))*:;;;T%B%G%G%I%III#'L&--j999 $;; 9#566   )D0I"(,,..FK,,....;x#8#=#=#?#???&,nQ&>&>{##!-"222*0.E*B*BF;'*.)A)A*)M)M&U&UPS&U&U s #''0BCCF" &--2D2+J7!**3/+  !  2D2+J7!**3/+  ! .. 5tZ  r7obj_namefunc variable_namer>c dfd|D}|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. , c3JK|]\}}||VdSrR) format_arg)rSrDrer?s r!rTz:CodeGenerator.create_function_call_code..<s5VVee44VVVVVVr7 = .(z)  )joinrH)r?rrrr>kwargsargsr#s` r!rxz'CodeGenerator.create_function_call_code(su(yyVVVVv||~~VVVVV ??H??t??d???# ;;D r7rDrec |dks|dkr|dSt|tr2|dddd}|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?rDres r!rzCodeGenerator.format_argHs ,  #--444 4 s # # 5MM$..66sC@@E%%U%%% % t $ $ 5E)A)A44E/244 4r7Frzspeical_functions_coder#cfg}ttttfD],}|dt j|-gd|zdgz}|r3|dt jtdd}nd}ddd|Dg} d |d zd |zd |d zd |z} d|| zd| dddgz} tj | tj } | S)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 rcg|]}|Sr6r6)rS class_names r! z6CodeGenerator.assemble_python_code..s+[+[+[:J+[+[+[r7z 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?rzrr#r`r~ func_stringsrstatic_imports imports_codemain_function_coders r!ryz"CodeGenerator.assemble_python_code[s*   - !   @ @D    >W%6t%<%< > > ? ? ? ?    TT U     ! !"Qw'89L'M'M"Q"Q"Q R R R6LLL _+[+[IZ+[+[+[!\!\ ^ ^ AAA Bmm233 4= <<< =mmD!!  " YY  %r+GT U  %juz||DDD r7rNc|}||}||jvr|d|d}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"]())rqr^rostrip)r?rNrrrs r!rpzCodeGenerator.get_class_infos&00<< 6;;== = =)DDj.>.>.@.@DDDJJ)SS:SSSJ+Z77r7c |dddd}tjdd|}|drd|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 -rl z [^a-z0-9_]rr)lowerrrresubisdigit)rN clean_names r!rqz!CodeGenerator.clean_variable_names %%''--//77SAAII#sSS VM2z::  a= " " *z)Jr7ctj|}d|jD}t d|jD}|s!t |ndS)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. cHi|]\}}||j|jkr|jnd SrR)defaultempty)rSnameparams r!rhz9CodeGenerator.get_function_parameters..sB   e 5=EK#?#?%--T   r7c3JK|]}|jtjjkVdSrR)kindr Parameter VAR_KEYWORD)rSrs r!rTz8CodeGenerator.get_function_parameters..sC   J'+7 7      r7N)r signature parametersrHrWrXrIro)r?rrr catch_alls r!rsz%CodeGenerator.get_function_parameterss%d++   (399;;     "-4466     /8AtJOO%%&&&TAr7rGr{c|D]k}t||trN||d|vr,dd|||dd||ddi||<l|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(rrk))ror&rI)r?rGr{rDs r!rwzCodeGenerator.update_inputss;;==  C6#;-- 3KN&8&=&=&?&???$%r;MfUXkZ[n;]%r%raghkalmnao%r%r%rs  r7N)r)F)r0r1r2r3rr@rintr'rr[rxrWrrmryrrpr4rqrrsrwr6r7r!r]r]sADADAAAAmmmm mmmm^  "  @ c # #    2 DDD!%S D3i D  D DDDDL88sC}1E8888$\,BHBBBBB(Ddtr7r]c LeZdZdZddddedfdededeezded ed e f d Z d Z d S)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. rrkFworkflow input_file output_filer`r:needs_init_custom_nodesc,|r|rtd|s|std|std||_||_||_||_||_||_tj|j|_ | dS)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$  =( =IJJ J =H =;<< < 2011 1  $&$#6 '>$(, d6N(O(O% r7c|jrtni|_|jr t|j}nt j|j}t||j }| }t|j |j}| ||j}t|j|t#d|jdS)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 ) ? ---do>>DD:dm,,D!4D$:R S S*??AA '  $d&C  (99 4?:   &&t'7HHH NDGenerate 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++ 'NNNNNr7__main__rZ)&rglobrrr(rusysrtypingrrrrrrargparser rcomfyui_to_python_utilsr r r r rnodesrrrrrr9r]rr'rrrr0r6r7r!rs\  ;;;;;;;;;;;;;;;;###### "!###%%%%%%)'3"3"3"3"3"3"3"3"lN#N#N#N#N#N#N#N#bIIIIIIIIX QPQPQPQPQPQPQPQPj)*( 2> z DFFFFFr7