
    5in                     h   d Z ddlmZmZmZm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mZ dd	lmZmZ d>d
ZeddfdZi Z G d de      Z e	j4                  d      Z e	j4                  d      Z e	j4                  d      Z e	j4                  d      Zd Zd Z d Z!d Z"d Z#d Z$d Z%d Z&d Z'd Z(d Z)d Z*d Z+d  Z,d! Z-d" Z.d# Z/d$ Z0d% Z1d& Z2d?d'Z3d@d(Z4dAd)Z5d* Z6dBd+Z7i fd,Z8	 	 dCd-Z9d. Z:	 	 dDd/Z;dEd0Z<d1 Z=d2 Z>	 	 dFd3Z?d4 Z@d5 ZAd6 ZBd7 ZCd8 ZDd9 ZEd?d:ZFd@d;ZGd?d<ZHd= ZIy)Gz
This file contains all the utilities needed for escaping and parsing
names in the syntax of

    project-ID-or-name:folder/path/to/filename

For more details, see external documentation [TODO: Put link here].
    )print_functionunicode_literalsdivisionabsolute_importN   )get_ls_l_desc   )
basestring)try_callINTERACTIVE_CLI)
DXCLIErrorDXErrorc                    t        |       dk(  rd}|S t        t        |             D ]R  }t        |      dz   }| |   j                  d      }	ddt        |      z  z   }
t	        ||
j                  |	      z          T |rt	        d       t	        d       |,d}|r|d	z  }n|r|d
z  }||dt        |      z   dz   z  }|dz  }	 	 t        |      }||dk(  r|S |r|dk(  r|S |r|dk(  r|S 	 |j                  |      }|S # t        $ r t	        d        t        $ r t	        d        w xY w#  Y nxY w	 t        |      }|t        t        |             vr
t               |S # t        $ r t	        d       Y nw xY w)a  
    :param choices: Strings between which the user will make a choice
    :type choices: list of strings
    :param default: Number the index to be used as the default
    :type default: int or None
    :param str_choices: Strings to be used as aliases for the choices; must be of the same length as choices and each string must be unique
    :type str_choices: list of strings
    :param prompt: A custom prompt to be used
    :type prompt: string
    :param allow_mult: Whether "*" is a valid option to select all choices
    :type allow_mult: boolean
    :param more_choices: Whether "m" is a valid option to ask for more options
    :type more_choices: boolean
    :returns: The user's choice, i.e. one of a numbered index of choices (e.g. 0 for the first item), "*" (only if allow_mult is True), or "m" (only if more_results is True)
    :rtype: int or string
    :raises: :exc:`EOFError` to signify quitting the process

    At most one of allow_mult and more_choices should be set to True.
    r   r   z) 
 zm) More options not shown... zPick a numbered choicez or "*" for allz or "m" for more optionsz []z: *mzNot a valid selection)lenrangestrsplitprintjoininputKeyboardInterruptEOFErrorindexint
IndexError	Exception)choicesdefaultstr_choicesprompt
allow_multmore_choiceschoiceiprefixlinesjoinervalues               u/home/marpiech/ifpan-abm-pgxpred/analysis/marpiech-gwas-test/venv/lib/python3.12/site-packages/dxpy/utils/resolver.pypickr0   $   s   ( 7|q3w<  +Q$
  &c&k))fv{{5))*	+
 ,-	"I~)''F00FdS\)C//F$
	&ME 5B;N%3,LESLL	 &&u-FM ! 	"I 	"I			+ZFU3w<00 l"M 	+)*	+3 s*   C> +D+ >*D(+D/3,E   E76E7
   c                    d}	 g }t        |      |k  rMt        | d       }|n>||j                  |       n ||      r|j                  |       d}t        |      |k  rM|syt        |      dk(  ry	 t        |D cg c]
  } ||       c}fdt        |      |k(  i|}	|	dk(  r||	   S c c}w # t        $ r Y yw xY w)NFTz
none foundr   znone pickedr(   r   )r   nextappendr0   r   )
	generator	render_fn	filter_fnpage_len	pick_optsany_resultsresultspossible_nextresultr)   s
             r/   paginate_and_pickr>   i   s    K
'lX% D1M$ }-]+NN=1K 'lX% \Q 	!7C9V,C '(+G(@'%'F
 S=6?" D ! 	! 	!s$   4	B2 =B-B2 -B2 2	B>=B>c                       e Zd Zd Zd Zy)ResolutionErrorc                     || _         y Nmsg)selfrD   s     r/   __init__zResolutionError.__init__   s	        c                     | j                   S rB   rC   )rE   s    r/   __str__zResolutionError.__str__   s    xxrG   N)__name__
__module____qualname__rF   rI    rG   r/   r@   r@      s    rG   r@   z8^(record|applet|file|workflow|database)-[0-9A-Za-z]{24}$zj^(record|app|applet|workflow|globalworkflow|job|analysis|project|container|file|database)-[0-9A-Za-z]{24}$z$^(user|org|app|globalworkflow|team)-z7^(job|analysis)-[0-9A-Za-z]{24}:[a-zA-Z_][0-9a-zA-Z_]*$c                 0    t         j                  |       d uS rB   )hash_patternmatchstrings    r/   	is_hashidrS          f%T11rG   c                 0    t         j                  |       d uS rB   )data_obj_patternrP   rQ   s    r/   is_data_obj_idrW      s    !!&)55rG   c                 d    t        |       xr$ | j                  d      xs | j                  d      S )Nproject-z
container-rS   
startswithrQ   s    r/   is_container_idr\      s.    Vc&"3"3J"?"b6CTCTUaCbcrG   c                 >    t        |       xr | j                  d      S )NrY   rZ   rQ   s    r/   is_project_idr^      s    V>!2!2:!>>rG   c                 >    t        |       xr | j                  d      S )Nz	analysis-rZ   rQ   s    r/   is_analysis_idr`      s    V?!2!2;!??rG   c                 >    t        |       xr | j                  d      S )Nzjob-rZ   rQ   s    r/   	is_job_idrb      s    V:!2!26!::rG   c                 j    t         j                  j                  d      d u xr | j                  d      S )N	DX_JOB_IDz	localjob-)osenvirongetr[   )things    r/   is_localjob_idri      s*    JJNN;'4/QE4D4D[4QRrG   c                 0    t         j                  |       d uS rB   )nohash_patternrP   rQ   s    r/   is_nohash_idrl      s    't33rG   c                 B    t        d|       dk\  xs t        d|       dk\  S )Nr   r   ?)get_last_pos_of_charrQ   s    r/   is_glob_patternrp      s(     f-2_8LSRX8Y]^8^_rG   c                 0    t         j                  |       d uS rB   )jbor_patternrP   rQ   s    r/   is_jbor_strrs      rT   rG   c                 0    t        |       } t        |        S )zL
    Returns True if the specified path explicitly specifies a project.
    )!_maybe_convert_stringified_dxlinkrS   paths    r/   is_project_explicitrx      s     -T2DrG   c                     | t        d      |t        d      t        |      st        d|d      t        t        j                  d| z   dz   d|id	      d   |k(  S )
z
    :param obj_id: object ID
    :type obj_id: str
    :param proj_id: project ID
    :type proj_id: str

    Returns True if the specified data object can be found in the specified
    project.
    zExpected obj_id to be a stringzExpected proj_id to be a stringz	Expected z to be a container ID/	/describeprojectT)always_retry)
ValueErrorr\   r   dxpyDXHTTPRequest)obj_idproj_ids     r/   object_exists_in_projectr      sy     ~9:::;;7#wHIID&&f{(BYPWDXgklmvw  |C  C  CrG   c                 *    d| j                  d      z   S )N\r   grouprP   s    r/   escaperr      s    %++a.  rG   c                 X    t        j                  dt        | j                  dd            S )Nz([#\?\*: ;&`"'!$\(\)\{\[<>|~])r   \\resubr   replacerQ   s    r/   escape_folder_strr      s"    663WfnnTSY>Z[[rG   c                 X    t        j                  dt        | j                  dd            S )Nz([#\?\*: ;&`"'/!$\(\)\{\[<>|~])r   r   r   rQ   s    r/   escape_name_strr      s"    664gv~~dTZ?[\\rG   c                 *    | j                  d      d   S )Nr   r   r   r   s    r/   	unescaperr      s    ;;q>!rG   c                 X    t        j                  dt        |       j                  dd      S )Nz(\\[#\?*: ;&`"'!$\(\){[<>|~])r   r   r   r   r   r   rQ   s    r/   unescape_folder_strr      s%    663YGOOPVX\]]rG   c                 X    t        j                  dt        |       j                  dd      S )Nz(\\[#\?*: ;&`"'/!$\(\){[<>|~])r   r   r   rQ   s    r/   unescape_name_strr      s%    664iHPPQWY]^^rG   c                     t        |      }|dkD  rV|d| j                  |       }|dk(  ryd}|dz
  }|dk\  r ||   dk(  r|dz  }|dz  }|dk\  r	||   dk(  r|dz  dk(  r|S |dkD  rVy)a  
    :param char: The character to find
    :type char: string
    :param string: The string in which to search for *char*
    :type string: string
    :returns: Index in *string* where *char* last appears (unescaped by a preceding "\"), -1 if not found
    :rtype: int

    Finds the last occurrence of *char* in *string* in which *char* is
    not present as an escaped character.

    r   Nr   r   r	   r   rfind)charrR   posnum_backslashes
test_indexs        r/   ro   ro      s     f+C
'Tcl  &"91W
Ao&"4"<q O!OJ Ao&"4"< Q!#J ' rG   c                     d}t        |      }|dkD  rW|d| j                  |       }|dk(  r|S d}|dz
  }|dk\  r ||   dk(  r|dz  }|dz  }|dk\  r	||   dk(  r|dz  dk(  r|}|dkD  rW|S )a  
    :param char: The character to find
    :type char: string
    :param string: The string in which to search for *char*
    :type string: string
    :returns: Index in *string* where *char* last appears (unescaped by a preceding "\"), -1 if not found
    :rtype: int

    Finds the first occurrence of *char* in *string* in which *char* is
    not present as an escaped character.

    r   r   Nr   r   r	   r   )r   rR   	first_posr   r   r   s         r/   get_first_pos_of_charr     s     I
f+C
'Tcl  &"91W
Ao&"4"<q O!OJ Ao&"4"< Q!#I ' rG   c                     g }t        |      }|}|dk\  r=t        | |d|       }|dk\  r#|dz   |k7  s|r|j                  ||dz   |        |}|dk\  r=|dk7  s|r|j                  |d|        |j                          |S )av  
    :param char: The character on which to split the string
    :type char: string
    :param string: The string to split
    :type string: string
    :returns: List of substrings of *string*
    :rtype: list of strings

    Splits *string* whenever *char* appears without an odd number of
    backslashes ('\') preceding it, discarding any empty string
    elements.

    r   Nr   )r   ro   r4   reverse)r   rR   include_empty_stringswordsr   lastposs         r/   split_unescapedr   '  s     E
f+CG
("4)9:!8Qw'!%:VC!GW56G ( !|,VHW%&	MMOLrG   c                    t        d|       }t        |      dk(  ry|dk(  s+|d   dk(  s#|d   dk(  st        d|       t        |       dz
  k(  rd	}nt        |j	                               }g }|D ]G  }|dk(  r	|dk(  r t        |      dkD  s|j	                          .|j                  t        |             I ddj                  |      z   |fS )
ar  
    :param path: A folder path to sanitize and parse
    :type path: string
    :param expected: Whether a folder ("folder"), a data object ("entity"), or either (None) is expected
    :type expected: string or None
    :returns: *folderpath*, *name*

    Unescape and parse *path* as a folder path to possibly an entity
    name.  Consecutive unescaped forward slashes "/" are collapsed to
    a single forward slash.  If *expected* is "folder", *name* is
    always returned as None.  Otherwise, the string to the right of
    the last unescaped "/" is considered a possible data object name
    and returned as such.

    rz   r   )rz   Nfolderr   .z..r   N)r   r   ro   r   popr4   r   r   )rw   expectedfoldersentity_namesanitized_foldersr   s         r/   clean_folder_pathr   D  s      c4(G
7|q8wr{c1WR[D5HL`adfjLkorswox{|o|L|'6 BS=t^$%)!%%'$$%8%@AB #((,--;;rG   c           
         t        |       }t        |      r|r|gS |S |t        v r|r
t        |   gS t        |   S 	 t        t	        j
                  |dd            }t        |      dk(  r"|d   d   t        |<   |r	|d   d   gS |d   d   S t        |      dk(  r|rt        d|z   d	z         |rg S dS |s`t        rIt        d
|z   d	z          t        |D cg c]  }dj                  |d   |d          c}      }||   d   S t        d
|z   dz         |D cg c]  }|d   	 c}S # t        $ r}t        t        |            d}~ww xY wc c}w c c}w )a  
    :param raw_string: A potential project or container ID or name
    :type raw_string: string
    :param is_error: Whether to raise an exception if the project or
            container ID cannot be resolved
    :type is_error: boolean
    :returns: Project or container ID if found or else None
    :rtype: string or None
    :raises: :exc:`ResolutionError` if *is_error* is True and the
            project or container could not be resolved

    Unescapes and attempts to resolve *raw_string* to a project or
    container ID.

    TVIEW)namedescribelevelNr   r   idz Could not find a project named ""z#Found multiple projects with name "z{id} ({level})r   )r   r   z9"; please use a project ID to specify the desired project)r   r\   cached_project_nameslistr   find_projectsr"   r@   r   r   r   r   r0   format)
raw_stringis_errormultirR   r;   detailsr=   r)   s           r/   resolve_container_id_or_namer   l  s     z*Fv!.v.%%27%f-.Z=QRX=YZ,t))vFST 7|q'.qz$'7V$&+D!"BD1AB	W	!"Dv"MPS"STT&&7&@3FG)02% ,22fTl&QX/2Z 2 3F6?4((!"G&"P  TO  #O  P  P ,33t33)  ,c'l++,2 4s#   !D)  EE)	E2EEc                 t   	 t        j                  |       }t        |t              rd|v rt        |d   t              r|d   S t        |d   t              rZt        |d   j                  dd       t              r7t        |d   j                  dd       t              r|d   d   dz   |d   d   z   S | S #  Y | S xY w)N$dnanexus_linkr|   r   :)jsonloads
isinstancedictr
   rg   )rw   possible_hashs     r/   ru   ru     s    


4(mT*/?=/P-(89:F$%566]+;<dC]+;<@@DQS]^]+;<@@tLjY$%56yACG-XhJijnJooo KKs   A B2 A,B2 2B7c                    dt         j                  v r ddlm}  ||       d   dz    ||       d   z   } | dk(  r|st        d      t	        |       } | dk(  rDt
        j                  t        d
      |rt
        j                  gdd	fS t
        j                  dd	fS | dk(  r_t
        j                  t        d      |rt
        j                  gnt
        j                  t
        j                  j                  dd      d	fS t        |       r|r| gdd	fS | dd	fS t        |       r)|rt
        j                  gd	| fS t
        j                  d	| fS d}d	}d	}t
        j                  j                  dd      }t        d|       }	|	dk\  r%t        d| d	|	       }
|
dk\  rt        d| z   dz         t        d|       }t        |      dk(  rHt        |d         r|r|d   gn|d   d	|d   fS |rt        |d   dd      }nt        |d   d      }d}nt        d|       dk\  rVd}| j!                  d      r0t
        j                  t        d| d      t
        j                  }n4t        |d   d      }d}n!t
        j                  }|t        d| d      |-|d   }t#        |j!                  d      rdn|dz   |z   |      \  }}|r|dk(  r||fS |g||fS |||fS )ab  
    :param path: A path to a data object to attempt to resolve
    :type path: string
    :param expected: one of the following: "folder", "entity", or None
            to indicate whether the expected path is a folder, a data
            object, or either
    :type expected: string or None
    :returns: A tuple of 3 values: container_ID, folderpath, entity_name
    :rtype: string, string, string
    :raises: exc:`ResolutionError` if the project cannot be resolved by
            name or the path is malformed
    :param allow_empty_string: If false, a ResolutionError will be
            raised if *path* is an empty string. Use this when resolving
            the empty string could result in unexpected behavior.
    :type allow_empty_string: boolean

    Attempts to resolve *path* to a project or container ID, a folder
    path, and a data object or folder name.  This method will NOT
    raise an exception if the specified folder or object does not
    exist.  This method is primarily for parsing purposes.

    Returns one of the following:

      (project, folder, maybe_name)
      where
        project is a container ID (non-null)
        folder is a folder path
        maybe_name is a string if the path could represent a folder or an object, or
        maybe_name is None if the path could only represent a folder

    OR

      (maybe_project, None, object_id)
      where
        maybe_project is a container ID or None
        object_id is a dataobject, app, or execution (specified by ID, not name)

    OR

      (job_id, None, output_name)
      where
        job_id and output_name are both non-null

    _DX_FUSEr   )xattrr|   r   r   r   z;Cannot parse ""; expected the path to be a non-empty stringNznCannot resolve ":": expected a project name or ID to the left of the colon, or for a current project to be setrz   zXExpected a project name or ID to the left of a colon, or for a current project to be set	DX_CLI_WDzCannot parse "z7" as a path; at most one unescaped colon can be presentr	   r   T)r   r   )r   zCannot resolve "z]": expected a project name or ID to the left of the colon, or for a current project to be setzo": expected the path to be qualified with a project name or ID, and a colon; or for a current project to be setr   )re   rf   r   r@   ru   r   WORKSPACE_IDconfigrg   r\   rS   ro   r   r   rb   r   r[   r   )rw   r   multi_projectsallow_empty_stringr   r|   
folderpathr   wd
last_colonlast_last_colon
substringsproject_idss                r/   resolve_pathr     s?   l RZZT{9%+eDk$.??rz,[\\,T2D s{$! #a b b'5""#sTXXX4;L;LsTXXXrz$! #G H H'5""#4;L;Lt{{_jloOprvvvt(S$>>dS$>>	4'5""#tUYYY4;L;LtUYYY GJK	d	+B &c40JQ.sD*4EFa!"2T"9<u"uvv d+J
:! Z]#'5Z]O:a=4Q[\]Q^^^6z!}t[_`K2:a=4PG	c4	(A	- ??3  (%UY'\ ] ]''G 3:a=4PGJ ##?!W[#^ _ _ ^
"3:;P;PQT;UR[]`c[cgq4qs{"|
K&!|ZTT'ZTT
K//rG   c                 T   	 t         j                  j                  |       }|d   }||d<   |d   dk7  rt	        d| z   dz   |d   z   dz         d }d|v r#	 |j                  dd      \  }}t        |      }|}|d	   j                  |d       }	|\t        |	t              st	        d
|z   dz   | z   dz         |dk  s|t        |	      k\  rt	        d
|z   dz   | z   dz   z   dz         |	|   }	g }
|	t        |	t              rt        |	      dkD  rrt        |	d   t              rd|	d   vrt	        d
|z   dz   | z   dz         |	D cg c]  }|d   	 }}	 |D cg c]!  }|t        j                  d|z   dz   |      d# }
}|
S t	        d
|z   dz   | z   dz         t        |	t              r,d|	v r(|	d   }	 |t        j                  d|z   dz   |      dg}
|
S t	        d
|z   dz   | z   dz         t	        d|z   dz   | z   dz   dj                  |d	   j!                               z         # t        $ r}t	        t        |            d }~ww xY w# t        $ r Y w xY wc c}w c c}w # t        $ r}t	        t        |            d }~ww xY w# t        $ r}t	        t        |            d }~ww xY w)Nr|   statedonezthe job z is zA, and it must be in the done state for its outputs to be accessedr   r   outputzFound "z" as an output field name of z., but it is not an array and cannot be indexedr   z, but the specified index z is out of ranger   z(, but it is an array of non-data objectsrz   r{   r   r   z, but it is an empty arrayz&, but it is not of a data object classzCould not find "z; available fields are: z, )r   apijob_describer"   r@   r   rsplitr    r~   rg   r   r   r   r   r   r   keys)job_idr   r   job_descr   r|   r   actual_name	str_indexoutput_fieldr;   linkidsout_idr   s                  r/   resolve_job_refr   6  s   ,88((0 y!G!HYF"j61F:Xg=NN  RU  U  V  	VE
d{	%)[[a%8"K	NED H%))$5L,-!)d"25T"TW]"]"R#S T T19\!22!)d"25T"TW]"]">#?AJ#KM_#` a a#E*GlD)< 1$!,q/48<LT`abTc<c))d*:=\*\_e*e  iS  +S  T  T:FG$t,-GG8twyjp '-,0,>,>sV|k?Y[c,d f yG y" N &i$&69X&X[a&a  eA  'A  B  Bd+0@L0P!"23F4"(d6H6HvXcIcem6nop N	 ")d"25T"TW]"]  aI  #I  J  J047:YY\bbe  CG  CL  CL  MU  V^  M_  Md  Md  Mf  Cg  g  h  	ha  ,c'l++,  		$ Hy  8)#g,778  4%c'l334sq   H! "I  II  &I9I  . J !	I*H>>I	III   	J)I==J	J'J""J'c                 v   |d||dfS t        |      r|j                  d      d   }d}|||vrd}|sy|du ri }d|vr<|t        j                  k7  r||d<   n#t        j                  t        j                  |d<   	 t        j                  d|z   d	z   |      }	t        j
                  |	      }	||	d
}|rd|||gfS d|||fS d|||fS # t        $ rh}
d|v rF|d= 	 t        j                  d|z   d	z   |      }	n9# t        $ r}t        t        |            d}~ww xY wt        t        |
            Y d}
~
d}
~
ww xY w)ac
  
    :param path: Path to the object that required resolution; propagated from
                 command-line
    :type path: string
    :param project: The potential project the entity belongs to
    :type project: string
    :param folderpath: Path to the entity
    :type folderpath: string
    :param entity_name: The name of the entity
    :type entity_name: string
    :param expected_classes: A list of expected classes the entity is allowed
                             to belong to if it is an ID (e.g. "record",
                             "file", "job"); if None, then entity_name may be
                             any data object class
    :type expected_classes: list or None
    :param describe: Dictionary of inputs to the describe API call; if
                     no describe input is provided (default value True), then
                     an empty mapping is passed to the describe API method
    :type describe: dict or True
    :param enclose_in_list: Whether the describe output is to be in the form
                            of a list (if False, the last return value is a
                            dictionary; if True, the last return value is a
                            list of one dictionary); it will only have an
                            effect if entity_name is a DX ID and is described
    :type enclose_in_list: boolean
    :returns: Whether or not the entity needs to be resolved with a more
              general resolution method, the project, the folderpath, and the
              entity name
    :rtype: tuple of 4 elements
    :raises: ResolutionError if the entity fails to be described

    Attempts to resolve the entity to a folder or an object, and describes
    the entity iff it is a DX ID of an expected class in the list
    expected_classes.
    Otherwise, determines whether or not more general resolution may be able
    to resolve the entity.

    If a more general resolution method is needed, then the return values will
    look like:
    (True, <project>, <folderpath>, <entity_name>)

    If the entity is a DX ID, but is not one of the supplied expected
    classes, then the return values will look like:
    (False, None, None, None)

    If the entity can be successfully described, then the return values will
    look like:
    <desc_output> ::= {"id": entity_name, "describe": {...}}
    <desc_or_desc_list> ::= <desc_output> || [<desc_output>]
    (False, <project>, <folderpath>, <desc_or_desc_list>)

    If the entity may be a folder, then the return values will look like:
    (False, <project>, <folderpath>, None)

    TODO: Allow arbitrary flags for the describe mapping.
    NF-r   T)FNNNr|   rz   r{   r   )	rS   r   r   r   r   #append_underlying_workflow_describer"   r@   r   )rw   r|   r   r   expected_classesr   enclose_in_listentity_classfound_valid_classdescr   details2r=   s                r/   _check_resolution_neededr   n  s   t  gz4//	;	"((-a0 'L@P,P % *tH H$$+++&-#"".&*&7&7#	4%%cK&7+&ExPD;;DAD $6':x77':v55 Wj+55%  		4H$Y'9--cK.?+.MxXD  9)#h-889 &c'l33	 		4s<   <1C 	D8D3C54D35	D>DDD33D8c                     d|v r5t        dt        |      z   dz   t        |      z   dz   t        |       z         t        |dz   |z   d      \  }}t        | ||      st        d|z   dz   |z   dz         |S )	a  
    :param project: The project that the folder belongs to
    :type project: string
    :param parent_folder: Full path to the parent folder that contains
                         folder_name
    :type parent_folder: string
    :param folder_name: Name of the folder
    :type folder_name: string
    :returns: The path to folder_name, if it exists, in the form of
              "<parent_folder>/<folder_name>"
    :rtype: string
    :raises: ResolutionError if folder_name is not a folder, or if
             folder_name points to a folder that does not exist

    Attempts to resolve folder_name at location parent_folder in project.
    rz   zObject of name z! could not be resolved in folder z of project ID r   zUnable to resolve "z&" to a data object or folder name in '')r@   r   r   check_folder_exists)r|   parent_folderfolder_namepossible_folder_skips        r/   _resolve_folderr     s    " k/#k2BBEhh!-013DEGJ7|T U 	U.}s/B[/PRZ[OUw{C3kAGHJWXZ]^ _ 	_rG   c           	         t        |      dk(  rt        d      |s|S t        |      dkD  r|r|st        |      r|S t        rt	        d| z   dz          t        |D cg c]  }d|v c}      rt        | d      \  }}}t        |||      }t        |D cg c]  }t        |d          c}|	      }	|r|	d
k(  r|S |r||	   gS ||	   S t        d| z   dz   t        t        |            z   dz         |r|d   gS |d   S c c}w c c}w )am	  
    :param path: Path to the object that required resolution; propagated from
                 command-line
    :type path: string
    :param entity_name: Name of the object
    :type entity_name: string
    :param results: Result of resolution; non-empty list of object
                    specifications (each specification is a dictionary with
                    keys "project" and "id")
    :type results: list of dictionaries
    :param allow_mult: If True, it is okay to choose from multiple results
                       of a single resolved object, or return all results
                       found; if False, raise an error if multiple results
                       are found
    :type allow_mult: boolean
    :param all_mult: If True, return all results if multiple results are
                     found for a single resolved object; if False, user needs
                     to choose a single result if multiple are found; the value
                     of all_mult only has an effect if allow_mult is True)
    :type all_mult: boolean
    :param ask_to_resolve: Whether picking may be necessary (if True, a
                           list is returned; if False, only one result
                           is returned); if specified as True, then all
                           results will be returned, regardless of the
                           values of allow_mult and all_mult
    :type ask_to_resolve: boolean
    :returns: The results of resolving entity_name, expected to be of the
              following form:
              <resolved_object>  # If only one result is present or the user
                                 # is able to select from multiple
              OR
              [<resolved_object>, ...]  # If multiple results are present and
                                        # it is allowed
              where <resolved_object> is of the following form:
              {"project": <project_id>, "id": <object_id>}
    :rtype: dict or list of dicts
    :raises: ValueError if results is empty
    :raises: ResolutionError if too many results are found and the user is
             not in interactive mode and cannot select one

    Precondition: results must be a nonempty list

    Validates length of results.

    If there are multiple results found and the user is in interactive mode,
    then the user will be prompted to select a single result to be returned.
    r   z'results' must be nonempty.r   The given path "z)" resolves to the following data objects:r   entityr   )r'   r   z" resolves to z data objects)r   r~   rp   r   r   anyr   _resolve_global_entityr0   r   r@   r   )
rw   r   r;   r'   all_multask_to_resolver=   r|   r   r)   s
             r/   "_validate_resolution_output_lengthr    s<   b 7|q677 
7|a 8{'CN$t+.YYZ7CJf,CD3?x3X0[0*kR7S=
);<S%/1Ffm,6(KGFOK!"4t";>N"N"%c'l"3#46E#F G G  *
|9wqz9 D Ts   D Dc                     t        |       r|du ri }t        | ||      S 	 t        t        j                  | ||dd||            S # t
        $ r}t        t        |            d}~ww xY w)a=  
    :param project_or_job_id: The project ID to which the entity belongs
                              (then the entity is an existing data object),
                              or the job ID to which the entity belongs
                              (then the entity is a job-based object
                              reference to an object that may not exist yet)
    :type project_or_job_id: string
    :param folderpath: Full path to the object (parsed from command line)
    :type folderpath: string
    :param entity_name: Name of the object
    :type entity_name: string
    :param describe: Input mapping used to describe the job's project if
                     project_or_job_id is a job ID, or True if the input
                     mapping is to be empty
    :type describe: dict or True
    :param visibility: The expected visibility of the entity ("either",
                       "hidden", or "visible"); to be used in resolution
    :type visibility: string
    :returns: The results obtained from attempting to resolve the entity;
              the expected format of the return value is described below
    :rtype: list
    :raises: ResolutionError if dxpy.find_data_objects throws an error

    If project_or_job_id is a job ID, then return value will be like:
        [{"id": ..., "describe": {...}}, ...]

    Otherwise, the return value will be like:
        [{"id": ..., "project": ..., "describe": {...}}, ...]
    Note that if the entity is successfully resolved, then the "describe"
    key will be in the dictionary if and only if a nonempty describe
    mapping was provided.

    TODO: Inspect entity_name and conditionally treat it as a "glob" pattern.

    TODO: Callers should specify exactly what fields they want, and then
    hopefully we can avoid having a default set of fields that may be very
    expensive
    T)r   globF)r|   r   r   	name_moderecurser   
visibilityN)rb   r   r   r   find_data_objectsr"   r@   r   )project_or_job_idr   r   r   r  r   s         r/   r  r  D  s    N "#tH
 0+QQ		0..7H6@4?9?7<8@:DF G G  	0!#g,//	0s   $A 	A(A##A(c                     	 t        |      dk(  rt        |||      }||ddS t        | ||      }t        |      rdn|d|dS # t        $ r	 ddddcY S w xY w)a  
    :param path: Path to the object that required resolution; propagated from
                 command-line
    :type path: string
    :param project: The potential project the entity belongs to
    :type project: string
    :param folderpath: Path to the entity
    :type folderpath: string
    :param entity_name: The name of the entity
    :type entity_name: string
    :param result: The result of resolving entity_name
    :type result: list of dictionaries
    :returns: The validated resolution output
    :rtype: dictionary

    Formats the output from the resolution of entity_name based on the number
    of resolved entities.

    If no results are found and entity_name can be resolved to a folder, then
    the return value will look like:
    {"project": <project>, "folder": <folder>, "name": None}

    If exactly one result is found, then the return value will look like:
    {"project": <project>, "folder": <folder>, "name": {"id": <id>,
                                                        "project": <project>}}
    OR
    {"project": None, "folder": <folder>, "name": {"id": <id>,
                                                   "project": <project>}}

    Else, the return value will look like:
    {"project": None, "folder": None, "name": None}
    r   Nr|   r   r   )r   r   r  rb   r@   )rw   r|   r   r   r=   r   validated_resultss          r/   _format_resolution_outputr    sw    B	?v;!$Wj+FF&&$GG B4V\ ]'0'9tw",=? ? ?4>>?s    A A AAc                    i }g }g }| D ]  }t        |d      \  }}}	 t        ||||      \  }}}}|rSt        |      r!	 t        |||      }	t	        |||||	      ||<   V|j                  |       |j                  |||d       }|||d||<    t        j                  |      }
t        |||
      D ]!  \  }}}t	        ||d   |d   |d   |      ||<   # |S #  d}Y xY w# t
        $ r dddd||<   Y w xY w)	a  
    :param paths: A list of paths to items that need to be resolved
    :type paths: list
    :returns: A dictionary mapping a specified path to either its resolved
              object or Nones, if the object could not be resolved
    :rtype: dict

    For each input given in paths, attempts to resolve the path, and returns
    the resolved object in a dictionary.

    The return value will look like:
    {<path1>: <resolved_object1>, <path2>: <resolved_object2>,...}

    If entity_id is a DX ID that can be described,
        <resolved_object*> ::= {"project": None,
                                "folder": None,
                                "name": {"id": <id>,
                                         "describe": <describe_output>}}

    Else if a general resolution (or search) method will be used to resolve
    the entity,
        <resolved_object*> ::= {"project": <project>,
                                "folder": None,
                                "name": {"project": <project>,
                                         "id": <resolved_id>}}

    Else if <project> is a job ID,
        <resolved_object*> ::= {"project": None,
                                "folder": None,
                                "name": {"project": <project>,
                                         "id": <resolved_id>}}

    Else if the path refers to a folder instead of a data object,
        <resolved_object*> ::= {"project": <project>,
                                "folder": <folder>,
                                "name": None}

    Else if description or resolution fails,
        <resolved_object*> ::= {"project": None, "folder": None, "name": None}
    r  r  FNr  r|   r   r   )
r   r   rp   r  r  r@   r4   r   resolve_data_objectszip)pathsdone_objectsto_resolve_in_batch_pathsto_resolve_in_batch_inputsrw   r|   r   r   must_resolvefind_resultsresolution_resultsinputsr=   s                r/   resolve_multiple_existing_pathsr    sq   R L "!# a+7x+P([	!=Ugz;>8:L':{
 {+Y#9':{#[L)B4R\^iCO*QL& *006*11gQ[ep2qr .5
T_!`L1a6 223MN #$=?Y$6!8 ?ff6tVI=NPVW_P`bhiobp7=?T? 7	! L ' Y59TSW)XL&Ys   CC"C"C76C7c	           
         t        | ||      \  }	}
}t        | |	|
|||| xs |      \  }}	}
}|rVt        |	|
|||      }t        |      dk(  rt	        |	|
|      }|	|dfS t        | |||||      }t        |	      rdd|fS |	d|fS |	|
|fS )a
	  
    :param expected: one of the following: "folder", "entity", or None to indicate
                     whether the expected path is a folder, a data object, or either
    :type expected: string or None
    :param ask_to_resolve: Whether picking may be necessary (if true, a list is returned; if false, only one result is returned)
    :type ask_to_resolve: boolean
    :param expected_classes: A list of expected classes the entity is allowed
                             to belong to if it is an ID (e.g. "record",
                             "file", "job"); if None, then entity_name may be
                             any data object class
    :type expected_classes: list or None
    :param allow_mult: Whether to allow the user to select multiple results from the same path
    :type allow_mult: boolean
    :param describe: Input hash to describe call for the results, or True if no describe input
                     is to be provided
    :type describe: dict or True
    :param all_mult: Whether to return all matching results without prompting (only applicable if allow_mult == True)
    :type all_mult: boolean
    :returns: A LIST of results when ask_to_resolve is False or allow_mult is True
    :raises: :exc:`ResolutionError` if the request path was invalid, or a single result was requested and input is not a TTY
    :param allow_empty_string: If false, a ResolutionError will be raised if *path* is an empty string. Use this when resolving the empty string could result in unexpected behavior.
    :type allow_empty_string: boolean
    :param visibility: The visibility expected ("either", "hidden", or "visible")
    :type visibility: string

    Returns either a list of results or a single result (depending on
    how many is expected; if only one, then an interactive picking of
    a choice will be initiated if input is a tty, or else throw an error).

    TODO: Always treats the path as a glob pattern.

    Output is of the form {"id": id, "describe": describe hash} a list
    of those

    TODO: Callers should specify exactly what fields they want, and then
    hopefully we can avoid having a default set of fields that may be very
    expensive

    NOTE: if expected_classes is provided and conflicts with the class
    of the hash ID, it will return None for all fields.
    )r   r   )r   r   r   )r   r  r   N)r'   r  r  )r   r   r  r   r   r  rb   )rw   r   r  r   r'   r   r  r   r  r|   r   r   r  r;   r   r  s                   r/   resolve_existing_pathr     s    V (4D8`r's$GZ5MdNUNXNY_oW_cq_q `j_i6l2L':{ (*kT\istw<1$Wj+FFFD(( B4CNCJNXLTR`!b !T#444D"333J++rG   c                 8   ||y	 t         j                  j                  | |dd      }|dz   |z   }t        |d      \  }}||d   v S # t         j                  j                  $ r4}|j
                  dk(  rt        t        |j                              |d}~ww xY w)a  
    :param project: project id
    :type project: string
    :param path: path to where we should look for the folder in question
    :type path: string
    :param folder_name: name of the folder in question
    :type folder_name: string
    :returns: A boolean True or False whether the folder exists at the specified path
    :type: boolean
    :raises: :exc:'ResolutionError' if dxpy.api.container_list_folder raises an exception

    This function returns a boolean value that indicates whether a folder of the
    specified name exists at the specified path

    Note: this function will NOT work on the root folder case, i.e. '/'
    NFr   )r   onlyResourceNotFoundrz   r   )
r   r   container_list_folder
exceptions
DXAPIErrorr   r@   r   rD   r   )r|   rw   r   folder_listetarget_folderr   s          r/   r   r   D  s    " dlhh44WW`>ab 3J,M,]HEM5 K	222 ??%% 66''!#aee*--G	s   #A B%/BBc                    d}| j                  d      sd| z   } d| v r+| | j                  d      dz   d }| d| j                  d       } 	 t        j                  j	                  | |      S # t        j
                  $ r Y yw xY w)a  
    :param path: A string to attempt to resolve to an app object
    :type path: string
    :returns: The describe hash of the app object if found, or None otherwise
    :rtype: dict or None

    This method parses a string that is expected to perhaps refer to
    an app object.  If found, its describe hash will be returned.  For
    more information on the contents of this hash, see the API
    documentation. [TODO: external link here]

    Napp-rz   r   alias)r[   findr   r   app_describer&  rw   r-  s     r/   get_app_from_pathr1  e  s     E??6"}
d{TYYs^a'()OTYYs^$xx$$T$77??    	 A* *B ?B c                    d}| j                  d      sd| z   } d| v r+| | j                  d      dz   d }| d| j                  d       } 	 t        j                  j	                  | |      S # t        j
                  $ r Y yw xY w)a  
    :param path: A string to attempt to resolve to a global workflow object
    :type path: string
    :returns: The describe hash of the global workflow object if found, or None otherwise
    :rtype: dict or None

    This method parses a string that is expected to perhaps refer to
    a global workflow object.  If found, its describe hash will be returned.
    For more information on the contents of this hash, see the API
    documentation. [TODO: external link here]

    Nglobalworkflow-rz   r   r,  )r[   r.  r   r   global_workflow_describer&  r0  s     r/   get_global_workflow_from_pathr6  }  s     E??,- 4'
d{TYYs^a'()OTYYs^$xx00U0CC?? r2  c                     | j                  d      rt        |       S | j                  d      rt        |       S t        |       }|st        |       }|S )Nr+  r4  )r[   r1  r6  rw   r   s     r/   get_global_exec_from_pathr9    sO    v &&	*	+,T22 T"D,T2KrG   c                 B    t        |       }|t        d| z   dz         |S )a  
    :param path: A string which is supposed to identify an app
    :type path: string
    :returns: The describe hash of the app object
    :raises: :exc:`ResolutionError` if it cannot be found

    *path* is expected to have one of the following forms:

    - hash ID, e.g. "app-B8GZ8bQ0xky1PKY6FjGQ000J"
    - named ID, e.g. "app-myapp"
    - named ID with alias (version or tag), e.g. "app-myapp/1.2.0"
    r   z," could not be resolved to an accessible app)r1  r@   r8  s     r/   resolve_appr;    s/     T"D|047:hhiirG   c                 B    t        |       }|t        d| z   dz         |S )a  
    :param path: A string which is supposed to identify a global workflow
    :type path: string
    :returns: The describe hash of the global workflow object
    :raises: :exc:`ResolutionError` if it cannot be found

    *path* is expected to have one of the following forms:

    - hash ID, e.g. "globalworkflow-F85Z6bQ0xku1PKY6FjGQ011J"
    - named ID, e.g. "globalworkflow-myworkflow"
    - named ID with alias (version or tag), e.g. "globalworkflow-myworkflow/1.2.0"
    r   z8" could not be resolved to an accessible global workflow)r6  r@   r8  s     r/   resolve_global_workflowr=    s/     ).D|047:ttuurG   c                     t        |       s|rd| vrt        dj                               | j                  d      rt	        |       S | j                  d      rt        |       S t        |       }|st        |       }|t        d| z   dz         |S )aD  
    :param path: A string which is supposed to identify a global executable (app or workflow)
    :type path: string
    :param is_version_required: If set to True, the path has to specify a specific version/alias, e.g. "myapp/1.0.0"
    :type is_version_required: boolean
    :returns: The describe hash of the global executable object (app or workflow)
    :raises: :exc:`ResolutionError` if it cannot be found

    *path* is expected to have one of the following forms:

    - hash ID, e.g. "globalworkflow-F85Z6bQ0xku1PKY6FjGQ011J", "app-FBZ3f200yfzkKYyp9JkFVQ97"
    - named ID, e.g. "app-myapp", "globalworkflow-myworkflow"
    - named ID with alias (version or tag), e.g. "myapp/1.2.0", "myworkflow/1.2.0"
    - named ID with prefix and with alias (version or tag), e.g. "app-myapp/1.2.0", "globalworkflow-myworkflow/1.2.0"
    rz   z(Version is required, e.g. "myexec/1.0.0"r+  r4  r   zL" could not be resolved to an accessible global executable (app or workflow))rS   r@   r   r[   r;  r=  r1  r6  )rw   is_version_requiredr   s      r/   resolve_global_executabler@    s      T?2s$HOOQRR v4  	*	+&t,, T"D,T2|%(vvx 	xKrG   c                    d }d }d }d } ||       st        |       }|"	 t        | ddg dd      \  }}}d }	|(|D 
cg c]  }
 |	|
      s|
 }}
t        |      d	k(  rd }| t        |      d
k(  r| ||d	   d         }|S ||
 ||      }|S |t        st        d| z         t        d| z          |D cg c]  }t        |d          }}|,|j                  dj                  |d   |d   |d                t        |      }|t        |      k  r |||   d         }|S  ||      }|S t        d| z          ||      }|S c c}
w # t        $ r | d }Y w xY wc c}w )Nc                 T   | d   dk(  rt        j                  | d   | d         S | d   dk(  rt        j                  | d         S | d   dk(  rt        j                  | d   | d         S | d   d	k(  rt        j                  | d         S t        d
j                  | d               )Nclassappletr   r|   )r|   app)dxidworkflowglobalworkflowz(The executable class {} is not supported)r   DXAppletDXApp
DXWorkflowDXGlobalWorkflowr   r   )r   s    r/   get_handler_from_descz/get_exec_handler.<locals>.get_handler_from_desc  s    =H$==dT)_EE']e#::4:..']j(??4:tIGG']..((d4j99DKKDQXMZ[[rG   c                 J    | j                  d      xs | j                  d      S )Nz	workflow-zapplet-)r[   rv   s    r/   "path_starts_with_non_global_prefixz<get_exec_handler.<locals>.path_starts_with_non_global_prefix  s    {+Ity/IIrG   r  F)rD  recordrG  visible)r   r  r   r  c                     | d   d   dv S )Nr   rC  )rD  rG  rM   )r*   s    r/   is_applet_or_workflowz/get_exec_handler.<locals>.is_applet_or_workflow
  s    *g.2HHIrG   r   r   r   z)Found multiple executables with the path z"{prefix}-{name}, version {version}rC  r   version)r+   r   rT  zNo matches found for )
r9  r   r   r@   r   r   r   r4   r   r0   )rw   r-  handlerrM  rO  global_exec_desc_project_folderpathentity_resultsrS  r*   rchoice_descriptionsr)   s                 r/   get_exec_handlerr\    s   G
\J
 -d34T:}	&4I$S[Y^[{U^	5`1Hk>
J)-;!X?TUV?W!!X!X~&!+%)N %#n*=*BGWG_+N1,=j,IJG2 N1 #(8(D+,<=G. N- '"%&QTX&XYY=DEIW"XA=:#?"X"X+#**8??/8-f5 0 ; @ =>
 -.FN++ 0v0Fz0RS N 00@A
 N ""9D"@AA'(89NG "Y  	&'!%		& #Ys/   E EEE =E&E E#"E#c                     t        | dd|      \  }}}|&t        |       s|dk7  r|dk7  rt        d| z   dz         |||fS )a3  
    :param path: Path to resolve
    :type path: string
    :param all_matching_results: Whether to return a list of all matching results
    :type all_matching_results: boolean

    A thin wrapper over :meth:`resolve_existing_path` which throws an
    error if the path does not look like a project and doesn't match a
    data object path.

    Returns either a list of results or a single result (depending on
    how many is expected; if only one, then an interactive picking of
    a choice will be initiated if input is a tty, or else throw an error).
    r  T)r   r'   r  Nrz   zCould not resolve "z" to an existing data object or to only a project;
                                  if you were attempting to refer to a project by name,
                                  please append a colon ":" to indicate that it is a project.)r   r\   r@   )rw   all_matching_resultsr|   r   rY  s        r/   resolve_to_objects_or_projectr_  2  sr      +@IQKOI]+_'GZ od&;*"3!"7$">#a#a b b J..rG   c                 ~    	 t        d|       }|dk(  r t        d|       d   }| |dz   d  }||fS #  t        d      xY w)N=r   r   r   zSAn input was found that did not conform to the syntax: -i<input name>=<input value>)r   r   r   )keyeqvalfirst_eq_posr   r.   s       r/   parse_input_keyvalrd  P  s`    p,S(;2sH-a0)*+e}pnoos   ,/ <)NNNFF)FrB   )FF)NFT)NTF)FFT)Teither)NTNFTFTre  )J__doc__
__future__r   r   r   r   re   sysr   r   r   r   r   compatr
   clir   r   r%  r   r   r0   r   r>   r   r@   compilerV   rO   rk   rr   rS   rW   r\   r^   r`   rb   ri   rl   rp   rs   rx   r   r   r   r   r   r   r   ro   r   r   r   r   ru   r   r   r   r   r  r  r  r  r   r   r1  r6  r9  r;  r=  r@  r\  r_  rd  rM   rG   r/   <module>rl     s  " S R    #  + ,C+J ,/$ #@  g  2::XY rzz  G  HBCrzzST26d?@;S4`2"C(!\]^_68:%<P-4`I0V ,. 5p fj-2e6P: _d6:N:b90x*?ZL^ gl]eE,P3B02
&&!FEN/<	prG   