
    sjoR                     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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j                  e      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 G d d      Z  G d d      Z! G d de!      Z" G d d      Z# G d d      Z$d Z%d Z&d"dZ'd#d Z(ed!k(  r e'        yy)$    N)Path)choice)backends   i  i  i  a  
Here is a sample of what a configuration file could look like:

    {
        // ===============================
        // General launcher configuration
        // ===============================

        "configuration": {
            "host" : "localhost",
            "port" : 8080,
            "endpoint": "paraview",                   // SessionManager Endpoint
            "content": "/.../www",                    // Optional: Directory shared over HTTP
            "proxy_file" : "/.../proxy-mapping.txt",  // Proxy-Mapping file for Apache
            "sessionURL" : "ws://${host}:${port}/ws", // ws url used by the client to connect to the started process
            "timeout" : 25,                           // Wait time in second after process start
            "log_dir" : "/.../viz-logs",              // Directory for log files
            "fields" : ["file", "host", "port"]       // List of fields that should be send back to client
                                                             // include "secret" if you provide it as an --authKey to the app
            "sanitize": {                             // Check information coming from the client
                "cmd": {
                    "type": "inList",                 // 'cmd' must be one of the strings in 'list'
                    "list": [
                        "me", "you", "something/else/altogether", "nothing-to-do"
                    ],
                    "default": "nothing-to-do"        // If the string doesn't match, replace it with the default.
                                                      // Include the default in your list
                },
                "cmd2": {                             // 'cmd2' must match the regexp provided, example: not a quote
                    "type": "regexp",
                    "regexp": "^[^"]*$",             // Make sure to include '^' and '$' to match the entire string!
                    "default": "nothing"
                }
            }
        },

        // ===============================
        // Useful session vars for client
        // ===============================

        "sessionData" : { "key": "value" },      // Dictionary of values interesting to the client

        // ===============================
        // Resources list for applications
        // ===============================

        "resources" : [ { "host" : "localhost", "port_range" : [9001, 9003] } ],

        // ===============================
        // Set of properties for cmd line
        // ===============================

        "properties" : {
            "vtkpython" : "/.../VTK/build/bin/vtkpython",
            "pvpython" : "/.../ParaView/build/bin/pvpython",
            "vtk_python_path": "/.../VTK/build/Wrapping/Python/vtk/web",
            "pv_python_path": "/.../ParaView/build/lib/site-packages/paraview/web",
            "plugins_path": "/.../ParaView/build/lib",
            "dataDir": "/.../path/to/data/directory"
        },

        // ===============================
        // Application list with cmd lines
        // ===============================

        "apps" : {
            "cone" : {
                "cmd" : [
                    "${vtkpython}", "${vtk_python_path}/vtk_web_cone.py", "--port", "$port" ],
                "ready_line" : "Starting factory"
            },
            "graph" : {
                "cmd" : [
                    "${vtkpython}", "${vtk_python_path}/vtk_web_graph.py", "--port", "$port",
                    "--vertices", "${numberOfVertices}", "--edges", "${numberOfEdges}" ],
                "ready_line" : "Starting factory"
            },
            "phylotree" : {
                "cmd" : [
                    "${vtkpython}", "${vtk_python_path}/vtk_web_phylogenetic_tree.py", "--port", "$port",
                    "--tree", "${dataDir}/visomics/${treeFile}", "--table", "${dataDir}/visomics/${tableFile}" ],
                "ready_line" : "Starting factory"
            },
            "filebrowser" : {
                "cmd" : [
                    "${vtkpython}", "${vtk_python_path}/vtk_web_filebrowser.py",
                    "--port", "${port}", "--data-dir", "${dataDir}" ],
                "ready_line" : "Starting factory"
            },
            "data_prober": {
                "cmd": [
                    "${pvpython}", "-dr", "${pv_python_path}/pv_web_data_prober.py",
                    "--port", "${port}", "--data-dir", "${dataDir}", "-f" ],
                "ready_line" : "Starting factory"
            },
            "visualizer": {
                "cmd": [
                    "${pvpython}", "-dr", "${pv_python_path}/pv_web_visualizer.py",
                    "--plugins", "${plugins_path}/libPointSprite_Plugin.so", "--port", "${port}",
                    "--data-dir", "${dataDir}", "--load-file", "${dataDir}/${fileToLoad}",
                    "--authKey", "${secret}", "-f" ],  // Use of ${secret} means it needs to be provided to the client, in "fields", above.
                "ready_line" : "Starting factory"
            },
            "loader": {
                "cmd": [
                    "${pvpython}", "-dr", "${pv_python_path}/pv_web_file_loader.py",
                    "--port", "${port}", "--data-dir", "${dataDir}",
                    "--load-file", "${dataDir}/${fileToLoad}", "-f" ],
                "ready_line" : "Starting factory"
            },
            "launcher" : {
                "cmd": [
                    "/.../ParaView/Web/Applications/Parallel/server/launcher.sh",
                    "${port}", "${client}", "${resources}", "${file}" ],
                "ready_line" : "Starting factory"
            },
            "your_app": {
                "cmd": [
                    "your_shell_script.sh", "--resource-host", "${host}", "--resource-port", "${port}",
                    "--session-id", "${id}", "--generated-password", "${secret}",
                    "--application-key", "${application}" ],
                "ready_line": "Output line from your shell script indicating process is ready"
        }
    }
c                     t        j                  dt         j                  t         j                  z        }d }|j	                  ||       S )a  
    Removes C-style comments from *json_like* and returns the result.  Example::
        >>> test_json = '''        {
            "foo": "bar", // This is a single-line comment
            "baz": "blah" /* Multi-line
            Comment */
        }'''
        >>> remove_comments('{"foo":"bar","baz":"blah",}')
        '{
    "foo":"bar",
    "baz":"blah"
}'

        From: https://gist.github.com/liftoff/ee7b81659673eca23cd9fc0d8b8e68b7
    z7//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"c                 :    | j                  d      }|d   dk(  ry|S )Nr   / )group)matchss     </DATA/.local/lib/python3.12/site-packages/wslink/launcher.pyreplacerz!remove_comments.<locals>.replacer   s"    KKNQ43;    )recompileDOTALL	MULTILINEsub)	json_likecomments_rer   s      r   remove_commentsr      s=     **B
		BLL K
 ??8Y//r   c                  D    dj                  d t        d      D              S )Nr
   c              3   p   K   | ].  }t        t        j                  t        j                  z          0 y wN)r   stringascii_lettersdigits).0_s     r   	<genexpr>z#generatePassword.<locals>.<genexpr>   s%     SA6&..>?s   46   )joinrange r   r   generatePasswordr&      s    77SrSSSr   c                 J    d}|D ]  }|| vst        d| d| d       d} |S )NTzERROR: z is missing z key.F)print)objexpected_keysobject_nameall_key_foundkeys        r   validateKeySetr.      s<    Mc>GK=SE?@!M  r   c                 Z   |sy |D ]  }|| v s||   }| |   }|d   dk(  r+||d   vs"t         j                  d|| |          |d   | |<   E|d   dk(  sNd|vrt        j                  |d         |d<   |d   j	                  |      t         j                  d|| |          |d   | |<    y )NtypeinListlistz key %s: sanitize %s with defaultdefaultregexpcompiled)loggerwarningr   r   r   )key_pairsanitizer-   	checkItemvalues        r   checkSanitizer<      s    (? ISME H,	& 11NN:C# %.i$8HSM6"h.Y.,.JJy7J,KIj)Z(..u5=NN:C# %.i$8HSM% r   c                     |D ]4  }t        ||       t        j                  |       }|j                  |      } 6 d| v rt        j                  d|        | S )N$z)Some properties could not be resolved: %s)r<   r   Templatesafe_substituter6   error)template_strvariable_listr9   r8   item_templates        r   replaceVariablesrE      sS    !h)5$44X> "
 l@,Or   c                 P    g }| D ]  }|j                  t        |||               |S r   )appendrE   )template_listrC   r9   result_listrB   s        r   replaceListrJ      s/    K%+L-RS &r   c                 X    |j                  g d       i }| D ]  }||v s| |   ||<    |S )N)id
sessionURLsessionManagerURL)extend)r)   public_keysfiltered_outputfields       r   filterResponserS      s>    @AOK%(ZOE"  r   c                 r    | j                   j                  d      }t        |      dk  ry t        |d         S )Nr	         )pathsplitlenstr)requestrW   s     r   extractSessionIdr\     s2    <<c"D
4y1}tAw<r   c                 N    t        j                  | d      j                  d      S )NF)ensure_asciiutf8)jsondumpsencode)payloads    r   jsonResponserd     s    ::gE299&AAr   c                   $    e Zd Zd Zd Zd Zd Zy)SessionManagerc                 n    i | _         || _        t        |d         | _        || _        |d   d   | _        y )N	resourcesconfigurationr9   )sessionsconfigResourceManagerrh   mappingr9   )selfrk   rm   s      r   __init__zSessionManager.__init__  s9    ()<=/
;r   c                    t        t        j                               }| j                  j	                         \  }}|r)||d<   ||d<   ||d<   d|vrt               |d<   t        | j                  d   d   || j                  d   g| j                        |d<   t        | j                  d   |d	      d
   || j                  d   g| j                        |d
<   d| j                  v rM| j                  d   D ];  }t        | j                  d   |   || j                  d   g| j                        ||<   = || j                  |<   | j                  j                  | j                         |S y )NrL   hostportsecretri   rM   
propertiesappsapplicationcmdsessionData)rZ   uuiduuid1rh   getNextResourcer&   rE   rk   r9   rJ   rj   rm   update)rn   optionsrL   rq   rr   r-   s         r   createSessionzSessionManager.createSession  s`    ^^335
d GDM"GFO"GFOw&$4$6!$4O,\:$++l34%GL!
 )F#GM$:;EB$++l34GEN +;;}5C#3M237 $++l";<$GCL 6 !(DMM"LL.Nr   c                     | j                   |   d   }| j                   |   d   }| j                  j                  ||       | j                   |= | j                  j	                  | j                          y )Nrq   rr   )rj   rh   freeResourcerm   r|   )rn   rL   rq   rr   s       r   deleteSessionzSessionManager.deleteSessionF  s^    }}R (}}R (##D$/MM"DMM*r   c                 >    || j                   v r| j                   |   S y r   rj   rn   rL   s     r   
getSessionzSessionManager.getSessionM  s     ==$$r   N)__name__
__module____qualname__ro   r~   r   r   r%   r   r   rf   rf     s    <%N+r   rf   c                       e Zd Zd Zy)ProxyMappingManagerc                      y r   r%   r   s    r   r|   zProxyMappingManager.updateY  s    r   N)r   r   r   r|   r%   r   r   r   r   X  s    r   r   c                       e Zd ZddZd Zy)ProxyMappingManagerTXTc                      || _         || _        y r   )	file_pathpattern)rn   r   r   s      r   ro   zProxyMappingManagerTXT.__init__^  s    "r   c                      t         j                        j                  dj                   fdD              d       y )Nr
   c              3   Z   K   | ]"  }j                   ||   d    |   d   fz   $ yw)rq   rr   N)r   )r   rL   rn   rj   s     r   r!   z0ProxyMappingManagerTXT.update.<locals>.<genexpr>d  s;      "B HRL$8(2,v:NOO"s   (+utf-8encoding)r   r   
write_textr#   )rn   rj   s   ``r   r|   zProxyMappingManagerTXT.updateb  s=    T^^''GG "   	( 	
r   N)z	%s %s:%d
)r   r   r   ro   r|   r%   r   r   r   r   ]  s    
r   r   c                   "    e Zd ZdZd Zd Zd Zy)rl   zV
    Class that provides methods to keep track on available resources (host/port)
    c                     i | _         |D ]m  }|d   }t        t        |d   d   |d   d   dz               }|| j                   v r"| j                   |   d   j                  |       \|g d| j                   |<   o y )Nrq   
port_ranger      	available)r   used)rh   r2   r$   rO   )rn   resourceListresourcerq   portLists        r   ro   zResourceManager.__init__v  s    $HF#Dh|,Q/,1G1JQ1NOH t~~%t$[188B5=r'Jt$ %r   c                 4   d}d}| j                   D ]>  }|t        | j                   |   d         k  s"t        | j                   |   d         }|}@ |rE| j                   |   d   j                         }| j                   |   d   j                  |       ||fS y)zX
        Return a (host, port) pair if any available otherwise will return None
        Nr   r   r   )NN)rh   rY   poprG   )rn   winneravailibilityCountrq   rr   s        r   r{   zResourceManager.getNextResource  s    
 NND 3t~~d';K'H#II$'t(<[(I$J! #
 >>&)+6::<DNN6"6*11$7D>!r   c                     || j                   v rX|| j                   |   d   v rC| j                   |   d   j                  |       | j                   |   d   j                  |       yyy)z5
        Free a previously reserved resource
        r   r   N)rh   removerG   )rn   rq   rr   s      r   r   zResourceManager.freeResource  sf     4>>!ddnnT.B6.J&JNN4 (//5NN4 -44T: 'K!r   N)r   r   r   __doc__ro   r{   r   r%   r   r   rl   rl   q  s    
K&;r   rl   c                   >    e Zd Zd Zd Zd Zd Zd Zd Zd Z	d
dZ
y	)ProcessManagerc                 :    || _         |d   d   | _        i | _        y )Nri   log_dir)rk   r   	processes)rn   ri   s     r   ro   zProcessManager.__init__  s!    #$_5i@r   c                 `    | j                   D ]  }| j                   |   j                          ! y r   )r   	terminater   s     r   __del__zProcessManager.__del__  s%    ..BNN2((* !r   c                 8    t        | j                        | dz  S )Nz.txt)r   r   r   s     r   _getLogFilePathzProcessManager._getLogFilePath  s    DLL!rd$K//r   c                    d }| j                  |d         }|j                  ddd      5 }	 t        j                  |d   ||      }|| j                  |d   <   	 d d d        |S #  t
        j                  d       t
        j                  d	j                  t        t        |d                      Y d d d        y xY w# 1 sw Y   |S xY w)
NrL   za+r   r   )mode	bufferingr   rw   )stdoutstderrzThe command line failed )
r   open
subprocessPopenr   r6   rA   r#   maprZ   )rn   sessionproclogFilePathlog_files        r   startProcesszProcessManager.startProcess  s     **74=941wG8!''EN8H 15wt}- H 67SXXc#wu~&>?@ HGG s#   B>-A%%AB;1B>;B>>Cc                     | j                   |   }| j                   |= t        j                  t              5  |j	                          d d d        y # 1 sw Y   y xY wr   )r   
contextlibsuppress	Exceptionr   )rn   rL   r   s      r   stopProcesszProcessManager.stopProcess  s@    ~~b!NN2  +NN ,++s   AAc                     g }| j                   D ]1  }| j                   |   j                         !|j                  |       3 |S r   )r   pollrG   )rn   session_to_releaserL   s      r   listEndedProcesszProcessManager.listEndedProcess  sD    ..B~~b!&&(4"))"- ! "!r   c                 @    | j                   |   j                         d u S r   )r   r   r   s     r   	isRunningzProcessManager.isRunning  s    ~~b!&&(D00r   c                 l   |d   }| j                  |      s|dk  ry| j                  |      sy| j                  d   |d      }|j                  dd       }|syd}| j                  |d         }|j	                  d	      5 }|j                         D ]
  }	||	v sd} n d d d        |S # 1 sw Y   |S xY w)
NrL   <   FTru   rv   
ready_liner   r   )r   rk   getr   r   	readlines)
rn   r   countrL   rv   r   readyr   r   lines
             r   isReadyzProcessManager.isReady  s    T] ~~b!ebj ~~b!kk&)'-*@A __\48
  **74=9w/8 **,% E - 0  0 s   B)B))B3N)r   )r   r   r   ro   r   r   r   r   r   r   r   r%   r   r   r   r     s*    
+0$"1r   r   c                    	 t        t        | j                  d         j                  d            }t	        j
                  |      }g d}t        |d	      s$t        t               t        j                  d       g d
}t        |d   |d      s$t        t               t        j                  d       d|d   vrd|d   d<   d|d   vri |d   d<   |S #  d}|t        t        j                         d         dz   t        t        j                         d         z   z  }t        |       t        t               t        j                  d       Y 
xY w)Nr   r   r   z#ERROR: Unable to read config file.
r   
rV   )ri   ru   rt   rh   zConfig file)endpointrq   rr   
proxy_filerM   timeoutr   fieldsri   zfile.configurationcontentr
   r9   )r   r   rk   	read_textr`   loadsrZ   sysexc_infor(   sample_config_fileexitr.   )r}   config_commentsrk   messager*   s        r   parseConfigr     s/   
)"#--w-?
 O, IM&-? !	M &1=BVW !//-/	*00.0
+M?83s||~a()D03s||~a7H3IIIg !s   AC A:Ec                     | j                  dt        dd       | j                  dddd	       | j                  d
dd       | S )Nrk   r   z#configuration file for the launcher)r0   nargshelpz-dz--debugz log debugging messages to stdout
store_true)r   actionz	--backendz&Server implementation to use (aiohttp)aiohttp)r   r3   )add_argumentrZ   )parsers    r   add_argumentsr   -  sb    
s!*O   i@   BI   Mr   c                     t        j                  |t         j                  t              }t	        |       |j                  |       }t        |      }t        j                  |||j                         y )N)descriptionformatter_classepilogbackend)
argparseArgumentParserRawDescriptionHelpFormatterr   r   
parse_argsr   r   launcher_startr   )argvr   r   argsrk   s        r   startr   @  s[    $$ <<!F
 &T"DFD&$,,?r   c                 2    t        j                  | ||      S )Nr   )r   r   )r   rk   r   s      r   startWebServerr   O  s    ""4AAr   __main__)Nzwslink Web Launcher)r   ))r   r   r`   loggingr   r   r   r   ry   pathlibr   randomr   wslinkr   	getLoggerr   r6   	STATUS_OKSTATUS_BAD_REQUESTSTATUS_NOT_FOUNDSTATUS_SERVICE_UNAVAILABLEr   r   r&   r.   r<   rE   rJ   rS   r\   rd   rf   r   r   rl   r   r   r   r   r   r%   r   r   <module>r     s        	   
    			8	$	    | F08T9:	B9 9B 

0 
(*; *;dR Rt&`&	@B z	G r   