Compare commits
57 Commits
Author | SHA1 | Date |
---|---|---|
Иван Маслов | 793d353c48 | 2 years ago |
Ivan Maslov | 28078f8b41 | 2 years ago |
Ivan Maslov | 5b9274570f | 2 years ago |
Ivan Maslov | b5996fad45 | 2 years ago |
Ivan Maslov | ab959bce2e | 2 years ago |
Ivan Maslov | c4e7dcba43 | 2 years ago |
Ivan Maslov | 0e5c0521de | 2 years ago |
Ivan Maslov | 0741fa69fb | 2 years ago |
Ivan Maslov | 5b7c24d855 | 2 years ago |
Ivan Maslov | e8a0cd1b7b | 2 years ago |
Ivan Maslov | 9ad4d7a026 | 2 years ago |
Ivan Maslov | 7b7f246dab | 2 years ago |
Ivan Maslov | 136c7d87a5 | 2 years ago |
Ivan Maslov | da97936b1d | 2 years ago |
Ivan Maslov | 99911bf643 | 2 years ago |
Ivan Maslov | 0d97ec4ff2 | 2 years ago |
Ivan Maslov | 20988db521 | 2 years ago |
Ivan Maslov | acc59de61b | 2 years ago |
Ivan Maslov | 297e3ba9fa | 2 years ago |
Ivan Maslov | 5cea74b940 | 2 years ago |
Ivan Maslov | f55bd55003 | 2 years ago |
Ivan Maslov | c8d8ea2620 | 2 years ago |
Ivan Maslov | 6efc358b99 | 2 years ago |
Ivan Maslov | 6bfa44d291 | 2 years ago |
Ivan Maslov | 1078c8ee2c | 2 years ago |
Ivan Maslov | 3dad37a1bf | 2 years ago |
Ivan Maslov | 60ca4e5bcb | 2 years ago |
Ivan Maslov | 285262ad77 | 2 years ago |
robo-bo | 67323432b7 | 2 years ago |
robo-bo | 4a674f7068 | 2 years ago |
robo-bo | 1999fa03e4 | 2 years ago |
robo-bo | 02694add30 | 2 years ago |
Иван Маслов | b5dfd586db | 2 years ago |
robo-bo | 4115f461f9 | 2 years ago |
robo-bo | c79749096e | 2 years ago |
Alex Kuralesov | f3ef7145cb | 2 years ago |
robo-bo | cb9af1f6e8 | 2 years ago |
Vladislav Klychkov | 1043e44ca4 | 2 years ago |
Vladislav Klychkov | f34d675f90 | 2 years ago |
Vladislav Klychkov | aebcace966 | 2 years ago |
Ivan Maslov | c37d714814 | 2 years ago |
Ivan Maslov | 6c04e81bdd | 2 years ago |
Ivan Maslov | 1c77c8d078 | 2 years ago |
Ivan Maslov | 8830d6eba3 | 2 years ago |
Ivan Maslov | b3331d3782 | 2 years ago |
Ivan Maslov | cce7f1ebe3 | 2 years ago |
Ivan Maslov | fec135e16f | 2 years ago |
robo-bo | b3d04a6393 | 2 years ago |
robo-bo | cfbdefd161 | 2 years ago |
robo-bo | 1baacac24e | 2 years ago |
robo-bo | 1c175f109e | 2 years ago |
robo-bo | 77c27f526b | 2 years ago |
Ivan Maslov | d15cec2277 | 2 years ago |
Ivan Maslov | 23b17e0d1f | 2 years ago |
Ivan Maslov | 5ece90166f | 2 years ago |
Ivan Maslov | a12de50131 | 2 years ago |
robo-bo | 2b06170730 | 2 years ago |
@ -1,3 +1,3 @@
|
|||||||
cd %~dp0
|
cd %~dp0
|
||||||
taskkill /F /FI "USERNAME eq %username%" /IM orpa-agent.exe
|
taskkill /F /FI "USERNAME eq %username%" /IM orpa-agent.exe
|
||||||
pause >nul
|
pause >nul
|
@ -1,5 +1,6 @@
|
|||||||
cd %~dp0
|
chcp 65001
|
||||||
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
|
cd /d "%~dp0"
|
||||||
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\pythonw.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
|
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
|
||||||
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
|
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\pythonw.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
|
||||||
|
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
|
||||||
pause >nul
|
pause >nul
|
@ -1,5 +1,6 @@
|
|||||||
cd %~dp0
|
chcp 65001
|
||||||
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
|
cd /d "%~dp0"
|
||||||
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
|
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
|
||||||
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
|
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
|
||||||
|
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
|
||||||
pause >nul
|
pause >nul
|
@ -1,2 +0,0 @@
|
|||||||
git pull
|
|
||||||
pause>nul
|
|
@ -1,9 +1,9 @@
|
|||||||
cd %~dp0
|
cd %~dp0
|
||||||
git push origin devIvan
|
git push origin devIvan
|
||||||
git checkout master
|
git checkout master
|
||||||
git reset --hard
|
git reset --hard
|
||||||
git pull
|
git pull
|
||||||
git merge devIvan
|
git merge devIvan
|
||||||
git push origin master
|
git push origin master
|
||||||
git checkout devIvan
|
git checkout devIvan
|
||||||
pause >nul
|
pause >nul
|
@ -1,3 +1,3 @@
|
|||||||
git clean -f -d
|
git clean -f -d
|
||||||
git reset --hard
|
git reset --hard
|
||||||
pause>nul
|
pause>nul
|
@ -0,0 +1,2 @@
|
|||||||
|
git pull
|
||||||
|
pause>nul
|
@ -1,4 +1,4 @@
|
|||||||
cd %~dp0
|
cd %~dp0
|
||||||
git reset --hard
|
git reset --hard
|
||||||
git pull
|
git pull
|
||||||
pause >nul
|
pause >nul
|
@ -1,57 +0,0 @@
|
|||||||
# !ATTENTION - Current Control panel works only from pyOpenRPA v1.2.0!
|
|
||||||
from pyOpenRPA import Orchestrator
|
|
||||||
def CPRender(inGSettings):
|
|
||||||
|
|
||||||
lJSCheckVersion="""
|
|
||||||
lT = 9;
|
|
||||||
lY="123";
|
|
||||||
console.log(lT+1);
|
|
||||||
if (lT==9) {
|
|
||||||
alert(123)
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
lResultDict={
|
|
||||||
"HeaderLeftText":"CP_TEST",
|
|
||||||
"HeaderRightText":"NAME",
|
|
||||||
"DataStorageKey":"", #Use key for set current dict in mGlobal.DataStorage["DataStorageKey"] on client side
|
|
||||||
"SubheaderText":"",
|
|
||||||
"BodyKeyValueList":[
|
|
||||||
{"Key": "HTMLLinkURL", "Value": Orchestrator.Web.Basic.HTMLLinkURL(inURLStr="test",inColorStr="orange")},
|
|
||||||
{"Key": "HTMLLinkJSOnClick", "Value": Orchestrator.Web.Basic.HTMLLinkJSOnClick(inJSOnClickStr=lJSCheckVersion, inTitleStr="!Click me!",inColorStr="green")},
|
|
||||||
],
|
|
||||||
"FooterText":"",
|
|
||||||
"FooterButtonX2List":[],
|
|
||||||
"FooterButtonX1List":[],
|
|
||||||
"GlobalStorage": "" # UNCOMMENT FOR DEBUG PURPOSE TO WATCH inGSettings on client side
|
|
||||||
}
|
|
||||||
return lResultDict
|
|
||||||
# Check in control panel, that process is runnning
|
|
||||||
|
|
||||||
# Test JSON generator when page init
|
|
||||||
def JSONGenerator():
|
|
||||||
lJSONDict=[1,2,2,4,2,2,2]
|
|
||||||
return lJSONDict
|
|
||||||
|
|
||||||
# Test JS when page init
|
|
||||||
def JSInitGenerator():
|
|
||||||
lJSCheckVersion="""
|
|
||||||
lT = 9;
|
|
||||||
lY="123";
|
|
||||||
//console.log(lT+1);
|
|
||||||
if (lT==9) {
|
|
||||||
//alert(123)
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
return lJSCheckVersion
|
|
||||||
|
|
||||||
def test():
|
|
||||||
#Orchestrator.WebRequestGet()
|
|
||||||
Orchestrator.WebRequestResponseSend("Hello my friend!")
|
|
||||||
|
|
||||||
#Orchestrator settings
|
|
||||||
def SettingsUpdate(inGSettings):
|
|
||||||
# New way to add CP defs in orchestrator - no gSettings..
|
|
||||||
Orchestrator.WebCPUpdate(inGSettings=inGSettings, inCPKeyStr="TEST", inHTMLRenderDef=CPRender, inJSONGeneratorDef=JSONGenerator, inJSInitGeneratorDef=JSInitGenerator)
|
|
||||||
Orchestrator.WebURLConnectDef(inMethodStr="GET", inURLStr="/test", inMatchTypeStr="Equal", inDef=test, inUACBool=False)
|
|
||||||
return inGSettings
|
|
@ -1,54 +0,0 @@
|
|||||||
# !ATTENTION - Current Control panel works only from pyOpenRPA v1.2.0!
|
|
||||||
from pyOpenRPA import Orchestrator
|
|
||||||
from pyOpenRPA.Orchestrator import Managers
|
|
||||||
def ControlPanelRenderDict(inGSettings):
|
|
||||||
# Example of the JS code in Python code
|
|
||||||
lJSCheckVersion = f"""
|
|
||||||
if (!('VersionStr' in mGlobal)) {{
|
|
||||||
window.location.reload(true);
|
|
||||||
}} else {{
|
|
||||||
if (mGlobal.VersionStr != "{inGSettings["VersionStr"]}") {{
|
|
||||||
window.location.reload(true);
|
|
||||||
}} else {{
|
|
||||||
$('div.orchestrator-version').html(mGlobal['VersionStr']);
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
"""
|
|
||||||
lResultDict={
|
|
||||||
"HeaderLeftText":"Version check",
|
|
||||||
"HeaderRightText":"Orchestrator",
|
|
||||||
"DataStorageKey":"", #Use key for set current dict in mGlobal.DataStorage["DataStorageKey"] on client side
|
|
||||||
"SubheaderText":f"<script>{lJSCheckVersion}</script>",
|
|
||||||
"BodyKeyValueList":[
|
|
||||||
{"Key": "Client", "Value": '<div class="orchestrator-version" style="display:inline;"></div>'},
|
|
||||||
{"Key": "Server", "Value": inGSettings["VersionStr"]},
|
|
||||||
],
|
|
||||||
"FooterText":"",
|
|
||||||
"FooterButtonX2List":[],
|
|
||||||
"FooterButtonX1List":[],
|
|
||||||
"GlobalStorage": "" # UNCOMMENT FOR DEBUG PURPOSE TO WATCH inGSettings on client side
|
|
||||||
}
|
|
||||||
return lResultDict
|
|
||||||
# Check in control panel, that process is runnning
|
|
||||||
|
|
||||||
#Orchestrator settings
|
|
||||||
def SettingsUpdate(inGSettings):
|
|
||||||
#Add RobotRDPActive in control panel
|
|
||||||
Orchestrator.WebCPUpdate(inCPKeyStr="VersionCheck", inHTMLRenderDef=ControlPanelRenderDict)
|
|
||||||
|
|
||||||
#Orchestrator.Managers.ControlPanel(inControlPanelNameStr="TestTTT",inRefreshHTMLJinja2TemplatePathStr="ControlPanel\\test.html", inJinja2TemplateRefreshBool = True)
|
|
||||||
lProcess = Orchestrator.Managers.ProcessInitSafe(inAgentHostNameStr="IVANMASLOV-DESKTOP",inAgentUserNameStr="ND",
|
|
||||||
inProcessNameWOExeStr="notepad",inStartCMDStr="notepad",inStopSafeTimeoutSecFloat=3)
|
|
||||||
# Some test
|
|
||||||
Orchestrator.OrchestratorInitWait()
|
|
||||||
#lProcess.ScheduleStatusCheckEverySeconds(inIntervalSecondsInt=5)
|
|
||||||
#lProcess.Start(inIsManualBool=False)
|
|
||||||
lProcess.StartCheck()
|
|
||||||
#Orchestrator.OrchestratorScheduleGet().every(2).seconds.do(Orchestrator.OrchestratorThreadStart,
|
|
||||||
# lProcess.StartCheck)
|
|
||||||
#Orchestrator.OrchestratorScheduleGet().every(5).seconds.do(Orchestrator.OrchestratorThreadStart,lProcess.StatusCheckStart)
|
|
||||||
#lProcess.Start()
|
|
||||||
lGit = Orchestrator.Managers.Git(inAgentHostNameStr="IVANMASLOV-DESKTOP",inAgentUserNameStr="ND",inGitPathStr="C:\Abs\Archive\scopeSrcUL\pyStore")
|
|
||||||
lGit.BranchRevLastGetInterval(inBranchLocalStr="prd", inBranchRemoteStr="origin/prd", inPreviousBranchRestoreBool=False,inIntervalSecFloat=10.0)
|
|
||||||
lGit.ProcessConnect(inProcess=lProcess)
|
|
||||||
return inGSettings
|
|
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
from pyOpenRPA import Orchestrator
|
||||||
|
from pyOpenRPA.Orchestrator.Managers import ControlPanel
|
||||||
|
from pyOpenRPA.Orchestrator.Managers import Process
|
||||||
|
from pyOpenRPA.Tools import CrossOS
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import datetime
|
||||||
|
import urllib.parse
|
||||||
|
import time
|
||||||
|
g_cp_name_str = "HELLO"
|
||||||
|
g_repo_name_str = os.path.abspath(__file__).split("\\")[-5]
|
||||||
|
g_repo_package_path_str = os.path.abspath("\\".join(os.path.abspath(__file__).split("\\")[:-1]))
|
||||||
|
|
||||||
|
|
||||||
|
# User settings
|
||||||
|
g_host_str = socket.gethostname().upper() # Identify PC
|
||||||
|
|
||||||
|
|
||||||
|
g_control_panel = ControlPanel(inControlPanelNameStr=g_cp_name_str,
|
||||||
|
inRefreshHTMLJinja2TemplatePathStr=os.path.join(g_repo_package_path_str, "html_jinja2.xhtml"),
|
||||||
|
inJinja2TemplateRefreshBool=True,inRobotNameStr=g_repo_name_str)
|
||||||
|
g_jinja_context_dict = {"settings": sys.modules[__name__],
|
||||||
|
"urllib_parse_quote_plus": urllib.parse.quote_plus, "g_host_str": g_host_str, "g_repo_name_str": g_repo_name_str}
|
||||||
|
g_control_panel.Jinja2DataUpdateDictSet(inJinja2DataUpdateDict=g_jinja_context_dict)
|
||||||
|
g_control_panel.InitJSJinja2TemplatePathSet(inJinja2TemplatePathStr=os.path.join(g_repo_package_path_str, "js.js"))
|
@ -0,0 +1,4 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
$('#test8-progress').progress();
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,39 @@
|
|||||||
|
Button {
|
||||||
|
color: white;
|
||||||
|
background: rgb(82, 105, 95);
|
||||||
|
border-radius: 5px;
|
||||||
|
border-color: #FFFFFF #FFFFFF #FFFFFF;
|
||||||
|
border-width: 2px 2px;
|
||||||
|
padding: .5em 2em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button:hover { background: rgb(53, 167, 110); }
|
||||||
|
|
||||||
|
|
||||||
|
i#first {
|
||||||
|
position: absolute;
|
||||||
|
top: 280px;
|
||||||
|
left: 135px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i#second {
|
||||||
|
position: absolute;
|
||||||
|
top: 340px;
|
||||||
|
left: 135px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 5px solid teal;
|
||||||
|
line-height: 0.1em;
|
||||||
|
margin: 10px 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 span {
|
||||||
|
background:#fff;
|
||||||
|
padding:0 7px;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
|||||||
|
$('.ui.selection.dropdown')
|
||||||
|
.dropdown("clear")
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
var switchFlag = "1,";
|
||||||
|
document.getElementById("Vacancy1Status").innerHTML = "Робот на вакансию 1 оффлайн"
|
||||||
|
document.getElementById("Vacancy2Status").innerHTML = "Робот на вакансию 2 оффлайн"
|
||||||
|
|
||||||
|
var btn1 = document.createElement("Button")
|
||||||
|
btn1.innerHTML = "Старт"
|
||||||
|
btn1.onclick = function() {
|
||||||
|
if (switchFlag == "1,"){
|
||||||
|
document.getElementById("Vacancy1Status").innerHTML = "Робот на вакансию 1 онлайн"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.getElementById("Vacancy2Status").innerHTML = "Робот на вакансию 2 онлайн"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
var btn2 = document.createElement("Button")
|
||||||
|
btn2.innerHTML = "Стоп"
|
||||||
|
btn2.onclick = function() {
|
||||||
|
if (switchFlag == "1,"){
|
||||||
|
document.getElementById("Vacancy1Status").innerHTML = "Робот на вакансию 1 оффлайн"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.getElementById("Vacancy2Status").innerHTML = "Робот на вакансию 2 оффлайн"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function get_data(i)
|
||||||
|
{
|
||||||
|
var link = document.createElement('a');
|
||||||
|
if (i == 'question') {
|
||||||
|
link.setAttribute('href', '/HR_officer01/questions');
|
||||||
|
link.setAttribute('download', 'Questions.csv');
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
} else if (i == "results") {
|
||||||
|
link.setAttribute('href', '/HR_officer01/results');
|
||||||
|
link.setAttribute('download', 'All_results.csv');
|
||||||
|
link.click();
|
||||||
|
} else if (i == "logs"){
|
||||||
|
link.setAttribute('href', '/HR_officer01/logs');
|
||||||
|
link.setAttribute('download', 'log.txt');
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function bot_change() {
|
||||||
|
switchFlag = $('.ui.selection.dropdown').dropdown("get value")
|
||||||
|
if ($('.ui.selection.dropdown').dropdown("get value") == "1,") {
|
||||||
|
document.getElementById("conteiner").appendChild(btn1)
|
||||||
|
document.getElementById("conteiner").appendChild(btn2)
|
||||||
|
}
|
||||||
|
else if ($('.ui.selection.dropdown').dropdown("get value") == "0,"){
|
||||||
|
document.getElementById("conteiner").appendChild(btn1)
|
||||||
|
document.getElementById("conteiner").appendChild(btn2)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
|||||||
|
Это лог
|
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
from pyOpenRPA import Orchestrator
|
||||||
|
from pyOpenRPA.Orchestrator.Managers import ControlPanel
|
||||||
|
from pyOpenRPA.Orchestrator.Managers import Process
|
||||||
|
from pyOpenRPA.Tools import CrossOS
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import datetime
|
||||||
|
import urllib.parse
|
||||||
|
import time
|
||||||
|
g_cp_name_str = "CP_JUPYTER"
|
||||||
|
g_repo_name_str = os.path.abspath(__file__).split("\\")[-5]
|
||||||
|
g_repo_package_path_str = os.path.abspath("\\".join(os.path.abspath(__file__).split("\\")[:-1]))
|
||||||
|
#g_repo_package_path_str = os.path.join(g_repo_path_str, "Orchestrator","Demo","JUPITER_01")
|
||||||
|
#g_repo_package_path_str = os.path.join(g_repo_package_path_str, g_cp_name_str)
|
||||||
|
|
||||||
|
|
||||||
|
# User settings
|
||||||
|
g_host_str = socket.gethostname().upper() # Identify PC
|
||||||
|
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
g_control_panel = ControlPanel(inControlPanelNameStr=g_cp_name_str,
|
||||||
|
inRefreshHTMLJinja2TemplatePathStr=os.path.join(g_repo_package_path_str, "html_jinja2.xhtml"),
|
||||||
|
inJinja2TemplateRefreshBool=True,inRobotNameStr=g_repo_name_str)
|
||||||
|
g_jinja_context_dict = {"settings": sys.modules[__name__],
|
||||||
|
"urllib_parse_quote_plus": urllib.parse.quote_plus, "g_host_str": g_host_str, "g_repo_name_str": g_repo_name_str}
|
||||||
|
g_control_panel.Jinja2DataUpdateDictSet(inJinja2DataUpdateDict=g_jinja_context_dict)
|
||||||
|
g_control_panel.InitJSJinja2TemplatePathSet(inJinja2TemplatePathStr=os.path.join(g_repo_package_path_str, "js.js"))
|
@ -0,0 +1,33 @@
|
|||||||
|
<div class="card" style=""">
|
||||||
|
<div class="content">
|
||||||
|
<div class="right floated mini ui ">
|
||||||
|
{{settings.g_cp_name_str}}
|
||||||
|
</div>
|
||||||
|
<div class="header">
|
||||||
|
ИТ - JUPYTER
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="meta">
|
||||||
|
<div>Рабочая область PYTHON JUPYTER. <br> Сотрудник: {{UserInfoDict["UserNameUpperStr"]}}</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="description">
|
||||||
|
<button class="massive ui button" >
|
||||||
|
Открыть в новой вкладке
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="extra content">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
@ -0,0 +1,4 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
$('#test8-progress').progress();
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
from pyOpenRPA import Orchestrator
|
||||||
|
from pyOpenRPA.Orchestrator.Managers import ControlPanel
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import datetime
|
||||||
|
import urllib.parse
|
||||||
|
time.sleep(1.5)
|
||||||
|
g_cp_name_str = "OPER_00"
|
||||||
|
g_repo_name_str = os.path.abspath(__file__).split("\\")[-5]
|
||||||
|
g_repo_package_cp_path_str = os.path.abspath("\\".join(os.path.abspath(__file__).split("\\")[:-1]))
|
||||||
|
|
||||||
|
# User settings
|
||||||
|
g_host_str = socket.gethostname().upper() # Identify PC
|
||||||
|
|
||||||
|
g_control_panel = ControlPanel(inControlPanelNameStr=g_cp_name_str,
|
||||||
|
inRefreshHTMLJinja2TemplatePathStr=os.path.join(g_repo_package_cp_path_str, "html_jinja2.xhtml"),
|
||||||
|
inJinja2TemplateRefreshBool=True,inRobotNameStr=g_repo_name_str)
|
||||||
|
g_jinja_context_dict = {"settings": sys.modules[__name__],
|
||||||
|
"urllib_parse_quote_plus": urllib.parse.quote_plus, "g_host_str": g_host_str, "g_repo_name_str": g_repo_name_str}
|
||||||
|
g_control_panel.Jinja2DataUpdateDictSet(inJinja2DataUpdateDict=g_jinja_context_dict)
|
@ -0,0 +1,7 @@
|
|||||||
|
$('.ui.dropdown')
|
||||||
|
.dropdown('clear')
|
||||||
|
;
|
||||||
|
|
||||||
|
$('.ui.dropdown')
|
||||||
|
.dropdown('clear')
|
||||||
|
;
|
@ -0,0 +1,24 @@
|
|||||||
|
chcp 65001
|
||||||
|
@echo off
|
||||||
|
echo Формат использования init-python-env.cmd [имя запускаемого процесса.exe] [имя убиваемого процесса.exe]
|
||||||
|
echo Пример использования init-python-env.cmd orpa-rbt.exe orpa-rbt.exe
|
||||||
|
|
||||||
|
if [%1]==[] goto :python-env
|
||||||
|
goto create-exe
|
||||||
|
:create-exe
|
||||||
|
copy /Y "%~dp0..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe" "%~dp0..\Resources\WPy64-3720\python-3.7.2.amd64\%1"
|
||||||
|
if [%2]==[] goto :python-env
|
||||||
|
goto taskkill
|
||||||
|
:taskkill
|
||||||
|
taskkill /im "%2" /F /fi "username eq %username%"
|
||||||
|
goto :python-env
|
||||||
|
:python-env
|
||||||
|
set CD_PREV=%cd%
|
||||||
|
cd /d "%~dp0..\Resources\WPy64-3720\python-3.7.2.amd64"
|
||||||
|
set PATH=%cd%;%cd%\Scripts;%PATH%
|
||||||
|
cd /d "%~dp0..\Sources"
|
||||||
|
set PYTHONPATH=%cd%;%PYTHONPATH%
|
||||||
|
cd %CD_PREV%
|
||||||
|
:eof
|
||||||
|
echo Инициализация Python окружения прошла успешно!
|
||||||
|
@echo on
|
@ -0,0 +1,4 @@
|
|||||||
|
chcp 65001
|
||||||
|
cd /d "%~dp0"
|
||||||
|
call init-python-env.cmd orpa-rbt.exe orpa-rbt.exe
|
||||||
|
cmd
|
@ -1,5 +1,5 @@
|
|||||||
cd %~dp0
|
chcp 65001
|
||||||
taskkill /im "orpa-orc.exe" /F /fi "username eq %username%"
|
cd /d "%~dp0"
|
||||||
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-orc.exe
|
call init-python-env.cmd orpa-orc.exe orpa-orc.exe
|
||||||
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-orc.exe "config.py"
|
orpa-orc.exe "config.py"
|
||||||
pause>nul
|
pause>nul
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
https://github.com/intxcc/pyaudio_portaudio
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -0,0 +1,172 @@
|
|||||||
|
#pyaudio loopback
|
||||||
|
import pyaudio
|
||||||
|
import wave
|
||||||
|
import os
|
||||||
|
|
||||||
|
defaultframes = 512
|
||||||
|
|
||||||
|
class textcolors:
|
||||||
|
if not os.name == 'nt':
|
||||||
|
blue = '\033[94m'
|
||||||
|
green = '\033[92m'
|
||||||
|
warning = '\033[93m'
|
||||||
|
fail = '\033[91m'
|
||||||
|
end = '\033[0m'
|
||||||
|
else:
|
||||||
|
blue = ''
|
||||||
|
green = ''
|
||||||
|
warning = ''
|
||||||
|
fail = ''
|
||||||
|
end = ''
|
||||||
|
|
||||||
|
recorded_frames = []
|
||||||
|
device_info = {}
|
||||||
|
useloopback = False
|
||||||
|
recordtime = 5
|
||||||
|
|
||||||
|
#Use module
|
||||||
|
p = pyaudio.PyAudio()
|
||||||
|
|
||||||
|
#Set default to first in list or ask Windows
|
||||||
|
try:
|
||||||
|
default_device_index = p.get_default_input_device_info()
|
||||||
|
except IOError:
|
||||||
|
default_device_index = -1
|
||||||
|
|
||||||
|
#Select Device
|
||||||
|
print (textcolors.blue + "Available devices:\n" + textcolors.end)
|
||||||
|
for i in range(0, p.get_device_count()):
|
||||||
|
info = p.get_device_info_by_index(i)
|
||||||
|
print (textcolors.green + str(info["index"]) + textcolors.end + ": \t %s \n \t %s \n" % (info["name"], p.get_host_api_info_by_index(info["hostApi"])["name"]))
|
||||||
|
|
||||||
|
if default_device_index == -1:
|
||||||
|
default_device_index = info["index"]
|
||||||
|
|
||||||
|
#Handle no devices available
|
||||||
|
if default_device_index == -1:
|
||||||
|
print (textcolors.fail + "No device available. Quitting." + textcolors.end)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
#Get input or default
|
||||||
|
device_id = int(input("Choose device [" + textcolors.blue + str(default_device_index) + textcolors.end + "]: ") or default_device_index)
|
||||||
|
print ("")
|
||||||
|
|
||||||
|
#Get device info
|
||||||
|
try:
|
||||||
|
device_info = p.get_device_info_by_index(device_id)
|
||||||
|
except IOError:
|
||||||
|
device_info = p.get_device_info_by_index(default_device_index)
|
||||||
|
print (textcolors.warning + "Selection not available, using default." + textcolors.end)
|
||||||
|
|
||||||
|
#Choose between loopback or standard mode
|
||||||
|
is_input = device_info["maxInputChannels"] > 0
|
||||||
|
is_wasapi = (p.get_host_api_info_by_index(device_info["hostApi"])["name"]).find("WASAPI") != -1
|
||||||
|
if is_input:
|
||||||
|
print (textcolors.blue + "Selection is input using standard mode.\n" + textcolors.end)
|
||||||
|
else:
|
||||||
|
if is_wasapi:
|
||||||
|
useloopback = True;
|
||||||
|
print (textcolors.green + "Selection is output. Using loopback mode.\n" + textcolors.end)
|
||||||
|
else:
|
||||||
|
print (textcolors.fail + "Selection is input and does not support loopback mode. Quitting.\n" + textcolors.end)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
recordtime = int(input("Record time in seconds [" + textcolors.blue + str(recordtime) + textcolors.end + "]: ") or recordtime)
|
||||||
|
|
||||||
|
#Open stream
|
||||||
|
channelcount = device_info["maxInputChannels"] if (device_info["maxOutputChannels"] < device_info["maxInputChannels"]) else device_info["maxOutputChannels"]
|
||||||
|
stream = p.open(format = pyaudio.paInt16,
|
||||||
|
channels = channelcount,
|
||||||
|
rate = int(device_info["defaultSampleRate"]),
|
||||||
|
input = True,
|
||||||
|
frames_per_buffer = defaultframes,
|
||||||
|
input_device_index = device_info["index"],
|
||||||
|
as_loopback = useloopback)
|
||||||
|
|
||||||
|
#Start Recording
|
||||||
|
print (textcolors.blue + "Starting..." + textcolors.end)
|
||||||
|
|
||||||
|
for i in range(0, int(int(device_info["defaultSampleRate"]) / defaultframes * recordtime)):
|
||||||
|
recorded_frames.append(stream.read(defaultframes))
|
||||||
|
print (".")
|
||||||
|
|
||||||
|
print (textcolors.blue + "End." + textcolors.end)
|
||||||
|
#Stop Recording
|
||||||
|
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
|
||||||
|
#Close module
|
||||||
|
p.terminate()
|
||||||
|
|
||||||
|
filename = input("Save as [" + textcolors.blue + "out.wav" + textcolors.end + "]: ") or "out.wav"
|
||||||
|
from pydub import AudioSegment
|
||||||
|
# Advanced usage, if you have raw audio data:
|
||||||
|
sound = AudioSegment(
|
||||||
|
# raw audio data (bytes)
|
||||||
|
data=b''.join(recorded_frames),
|
||||||
|
# 2 byte (16 bit) samples
|
||||||
|
sample_width=p.get_sample_size(pyaudio.paInt16),
|
||||||
|
# 44.1 kHz frame rate
|
||||||
|
frame_rate=int(device_info["defaultSampleRate"]),
|
||||||
|
# stereo
|
||||||
|
channels=channelcount
|
||||||
|
)
|
||||||
|
sound.export("out.mp3", format="mp3")
|
||||||
|
#waveFile = wave.open(filename, 'wb')
|
||||||
|
#waveFile.setnchannels(channelcount)
|
||||||
|
#waveFile.setsampwidth(p.get_sample_size(pyaudio.paInt16))
|
||||||
|
#waveFile.setframerate(int(device_info["defaultSampleRate"]))
|
||||||
|
#waveFile.writeframes(b''.join(recorded_frames))
|
||||||
|
#waveFile.close()
|
||||||
|
|
||||||
|
|
||||||
|
#pyaudio classic
|
||||||
|
|
||||||
|
from sys import byteorder
|
||||||
|
from array import array
|
||||||
|
from struct import pack
|
||||||
|
import pyaudio
|
||||||
|
import wave
|
||||||
|
THRESHOLD = 500
|
||||||
|
CHUNK_SIZE = 1024
|
||||||
|
FORMAT = pyaudio.paInt16
|
||||||
|
RATE = 44100
|
||||||
|
|
||||||
|
STOP_BOOL = False
|
||||||
|
import time
|
||||||
|
STOP_SEC_INT = 10
|
||||||
|
TIME_LAST = time.time()
|
||||||
|
|
||||||
|
def is_silent(snd_data):
|
||||||
|
"Returns 'True' if below the 'silent' threshold"
|
||||||
|
return max(snd_data) < THRESHOLD
|
||||||
|
|
||||||
|
def normalize(snd_data):
|
||||||
|
"Average the volume out"
|
||||||
|
MAXIMUM = 16384
|
||||||
|
times = float(MAXIMUM)/max(abs(i) for i in snd_data)
|
||||||
|
r = array('h')
|
||||||
|
for i in snd_data:
|
||||||
|
r.append(int(i*times))
|
||||||
|
return r
|
||||||
|
def trim(snd_data):
|
||||||
|
"Trim the blank spots at the start and end"
|
||||||
|
def _trim(snd_data):
|
||||||
|
snd_started = False
|
||||||
|
r = array('h')
|
||||||
|
for i in snd_data:
|
||||||
|
if not snd_started and abs(i)>THRESHOLD:
|
||||||
|
snd_started = True
|
||||||
|
r.append(i)
|
||||||
|
elif snd_started:
|
||||||
|
r.append(i)
|
||||||
|
return r
|
||||||
|
# Trim to the left
|
||||||
|
snd_data = _trim(snd_data)
|
||||||
|
# Trim to the right
|
||||||
|
snd_data.reverse()
|
||||||
|
snd_data = _trim(snd_data)
|
||||||
|
snd_data.reverse()
|
||||||
|
return snd_data
|
@ -0,0 +1,424 @@
|
|||||||
|
import inspect
|
||||||
|
from pyOpenRPA.Tools import CrossOS
|
||||||
|
import urllib.parse # decode URL in string
|
||||||
|
import os #for path operations
|
||||||
|
from . import __Orchestrator__
|
||||||
|
import mimetypes
|
||||||
|
mimetypes.add_type("font/woff2",".woff2")
|
||||||
|
mimetypes.add_type("application/javascript",".js")
|
||||||
|
|
||||||
|
# объявление import
|
||||||
|
from fastapi import FastAPI, Form, Request, HTTPException, Depends, Header, Response, Body
|
||||||
|
|
||||||
|
gCacheDict = {}
|
||||||
|
|
||||||
|
|
||||||
|
# Tool to merge complex dictionaries
|
||||||
|
def __ComplexDictMerge2to1__(in1Dict, in2Dict):
|
||||||
|
lPathList=None
|
||||||
|
if lPathList is None: lPathList = []
|
||||||
|
for lKeyStr in in2Dict:
|
||||||
|
if lKeyStr in in1Dict:
|
||||||
|
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
|
||||||
|
__ComplexDictMerge2to1__(in1Dict[lKeyStr], in2Dict[lKeyStr])
|
||||||
|
elif in1Dict[lKeyStr] == in2Dict[lKeyStr]:
|
||||||
|
pass # same leaf value
|
||||||
|
else:
|
||||||
|
raise Exception('Conflict at %s' % '.'.join(lPathList + [str(lKeyStr)]))
|
||||||
|
else:
|
||||||
|
in1Dict[lKeyStr] = in2Dict[lKeyStr]
|
||||||
|
return in1Dict
|
||||||
|
|
||||||
|
# Tool to merge complex dictionaries - no exceptions, just overwrite dict 2 in dict 1
|
||||||
|
def __ComplexDictMerge2to1Overwrite__(in1Dict, in2Dict):
|
||||||
|
"""
|
||||||
|
Merge in2Dict in in1Dict. In conflict override and get value from dict 2
|
||||||
|
|
||||||
|
:param in1Dict: Source dict. Save the link (structure)
|
||||||
|
:param in2Dict: New data dict
|
||||||
|
:return: Merged dict 1
|
||||||
|
"""
|
||||||
|
lPathList=None
|
||||||
|
if lPathList is None: lPathList = []
|
||||||
|
for lKeyStr in in2Dict:
|
||||||
|
if lKeyStr in in1Dict:
|
||||||
|
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
|
||||||
|
__ComplexDictMerge2to1Overwrite__(in1Dict[lKeyStr], in2Dict[lKeyStr])
|
||||||
|
else:
|
||||||
|
in1Dict[lKeyStr] = in2Dict[lKeyStr]
|
||||||
|
else:
|
||||||
|
in1Dict[lKeyStr] = in2Dict[lKeyStr]
|
||||||
|
return in1Dict
|
||||||
|
|
||||||
|
|
||||||
|
def AuthenticateBlock(inRequest):
|
||||||
|
raise HTTPException(status_code=401, detail="here is the details", headers={'Content-type':'text/html', 'WWW-Authenticate':'Basic'})
|
||||||
|
|
||||||
|
#Check access before execute the action
|
||||||
|
#return bool True - go execute, False - dont execute
|
||||||
|
def UserAccessCheckBefore(inMethod, inRequest):
|
||||||
|
# Help def - Get access flag from dict
|
||||||
|
#pdb.set_trace()
|
||||||
|
def HelpGetFlag(inAccessRuleItem, inRequest, inGlobalDict, inAuthenticateDict):
|
||||||
|
if "FlagAccess" in inAccessRuleItem:
|
||||||
|
return inAccessRuleItem["FlagAccess"]
|
||||||
|
elif "FlagAccessDefRequestGlobalAuthenticate" in inAccessRuleItem:
|
||||||
|
return inAccessRuleItem["FlagAccessDefRequestGlobalAuthenticate"](inRequest, inGlobalDict,
|
||||||
|
inAuthenticateDict)
|
||||||
|
##########################################
|
||||||
|
inMethod=inMethod.upper()
|
||||||
|
#Prepare result false
|
||||||
|
lResult = False
|
||||||
|
lAuthToken = inRequest.OpenRPA["AuthToken"]
|
||||||
|
#go next if user is identified
|
||||||
|
lUserDict = None
|
||||||
|
#print(f"lAuthToken: {lAuthToken}")
|
||||||
|
if lAuthToken:
|
||||||
|
lUserDict = __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]
|
||||||
|
#print(f"lUserDict: {lUserDict}")
|
||||||
|
#pdb.set_trace()
|
||||||
|
########################################
|
||||||
|
########################################
|
||||||
|
#Check general before rule (without User domain)
|
||||||
|
#Check rules
|
||||||
|
inRuleMatchURLList = __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("RuleMethodMatchURLBeforeList", [])
|
||||||
|
for lAccessRuleItem in inRuleMatchURLList:
|
||||||
|
#Go next execution if flag is false
|
||||||
|
if not lResult:
|
||||||
|
#Check if Method is identical
|
||||||
|
if lAccessRuleItem["Method"].upper() == inMethod:
|
||||||
|
#check Match type variant: BeginWith
|
||||||
|
if lAccessRuleItem["MatchType"].upper() == "BEGINWITH":
|
||||||
|
lURLPath = inRequest.path
|
||||||
|
lURLPath = lURLPath.upper()
|
||||||
|
if lURLPath.startswith(lAccessRuleItem["URL"].upper()):
|
||||||
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
|
||||||
|
# check Match type variant: Contains
|
||||||
|
elif lAccessRuleItem["MatchType"].upper() == "CONTAINS":
|
||||||
|
lURLPath = inRequest.path
|
||||||
|
lURLPath = lURLPath.upper()
|
||||||
|
if lURLPath.contains(lAccessRuleItem["URL"].upper()):
|
||||||
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
|
||||||
|
# check Match type variant: Equal
|
||||||
|
elif lAccessRuleItem["MatchType"].upper() == "EQUAL":
|
||||||
|
if lAccessRuleItem["URL"].upper() == inRequest.path.upper():
|
||||||
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
|
||||||
|
# check Match type variant: EqualCase
|
||||||
|
elif lAccessRuleItem["MatchType"].upper() == "EQUALCASE":
|
||||||
|
if lAccessRuleItem["URL"] == inRequest.path:
|
||||||
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
|
||||||
|
#########################################
|
||||||
|
#########################################
|
||||||
|
#Do check if lResult is false
|
||||||
|
if not lResult:
|
||||||
|
#Check access by User Domain
|
||||||
|
#Check rules to find first appicable
|
||||||
|
#Check rules
|
||||||
|
lMethodMatchURLList = __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lUserDict["Domain"].upper(), lUserDict["User"].upper()), {}).get("MethodMatchURLBeforeList", [])
|
||||||
|
if len(lMethodMatchURLList) > 0:
|
||||||
|
for lAccessRuleItem in lMethodMatchURLList:
|
||||||
|
#Go next execution if flag is false
|
||||||
|
if not lResult:
|
||||||
|
#Check if Method is identical
|
||||||
|
if lAccessRuleItem["Method"].upper() == inMethod:
|
||||||
|
#check Match type variant: BeginWith
|
||||||
|
if lAccessRuleItem["MatchType"].upper() == "BEGINWITH":
|
||||||
|
lURLPath = inRequest.path
|
||||||
|
lURLPath = lURLPath.upper()
|
||||||
|
if lURLPath.startswith(lAccessRuleItem["URL"].upper()):
|
||||||
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
|
||||||
|
#check Match type variant: Contains
|
||||||
|
elif lAccessRuleItem["MatchType"].upper() == "CONTAINS":
|
||||||
|
lURLPath = inRequest.path
|
||||||
|
lURLPath = lURLPath.upper()
|
||||||
|
if lURLPath.contains(lAccessRuleItem["URL"].upper()):
|
||||||
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
|
||||||
|
# check Match type variant: Equal
|
||||||
|
elif lAccessRuleItem["MatchType"].upper() == "EQUAL":
|
||||||
|
if lAccessRuleItem["URL"].upper() == inRequest.path.upper():
|
||||||
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
|
||||||
|
# check Match type variant: EqualCase
|
||||||
|
elif lAccessRuleItem["MatchType"].upper() == "EQUALCASE":
|
||||||
|
if lAccessRuleItem["URL"] == inRequest.path:
|
||||||
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
#####################################
|
||||||
|
#####################################
|
||||||
|
#Return lResult
|
||||||
|
return lResult
|
||||||
|
|
||||||
|
class HTTPRequestOld():
|
||||||
|
mRequest:Request = None
|
||||||
|
mResponse:Response = None
|
||||||
|
OpenRPA: dict = {}
|
||||||
|
headers={}
|
||||||
|
|
||||||
|
def __init__(self,inRequest,inResponse,inAuthTokenStr):
|
||||||
|
self.mRequest = inRequest
|
||||||
|
self.mResponse = inResponse
|
||||||
|
if inAuthTokenStr != None:
|
||||||
|
self.OpenRPA = {}
|
||||||
|
self.OpenRPA["IsSuperToken"] = __Orchestrator__.WebUserIsSuperToken(inAuthTokenStr=inAuthTokenStr)
|
||||||
|
self.OpenRPA["AuthToken"] = inAuthTokenStr
|
||||||
|
self.OpenRPA["Domain"] = __Orchestrator__.WebUserDomainGet(inAuthTokenStr=inAuthTokenStr)
|
||||||
|
self.OpenRPA["User"] = __Orchestrator__.WebUserLoginGet(inAuthTokenStr=inAuthTokenStr)
|
||||||
|
else: self.OpenRPA = {"IsSuperToken":False, "AuthToken":None, "Domain":None, "User":None}
|
||||||
|
self.headers=inRequest.headers
|
||||||
|
|
||||||
|
# Def to check User Role access grants
|
||||||
|
def UACClientCheck(self, inRoleKeyList): # Alias
|
||||||
|
return self.UserRoleAccessAsk(inRoleKeyList=inRoleKeyList)
|
||||||
|
def UserRoleAccessAsk(self, inRoleKeyList):
|
||||||
|
lResult = True # Init flag
|
||||||
|
lRoleHierarchyDict = self.UserRoleHierarchyGet() # get the Hierarchy
|
||||||
|
# Try to get value from key list
|
||||||
|
lKeyValue = lRoleHierarchyDict # Init the base
|
||||||
|
for lItem in inRoleKeyList:
|
||||||
|
if type(lKeyValue) is dict:
|
||||||
|
if lItem in lKeyValue: # Has key
|
||||||
|
lKeyValue = lKeyValue[lItem] # Get the value and go to the next loop iteration
|
||||||
|
else: # Else branch - true or false
|
||||||
|
if len(lKeyValue)>0: # False - if Dict has some elements
|
||||||
|
lResult = False # Set the False Flag
|
||||||
|
else:
|
||||||
|
lResult = True # Set the True flag
|
||||||
|
break # Stop the loop
|
||||||
|
else: # Has element with no detalization - return True
|
||||||
|
lResult = True # Set the flag
|
||||||
|
break # Close the loop
|
||||||
|
return lResult # Return the result
|
||||||
|
|
||||||
|
# Def to get hierarchy of the current user roles
|
||||||
|
# if return {} - all is available
|
||||||
|
def UserRoleHierarchyGet(self):
|
||||||
|
try:
|
||||||
|
lDomainUpperStr = self.OpenRPA["Domain"].upper()
|
||||||
|
lUserUpperStr = self.OpenRPA["User"].upper()
|
||||||
|
return __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lDomainUpperStr, lUserUpperStr), {}).get("RoleHierarchyAllowedDict", {})
|
||||||
|
except Exception as e:
|
||||||
|
return {}
|
||||||
|
#Tech def
|
||||||
|
#return {"headers":[],"body":"","statuscode":111}
|
||||||
|
def URLItemCheckDo(self, inURLItem, inMethod, inOnlyFlagUACBool = False):
|
||||||
|
###############################
|
||||||
|
#Tech sub def - do item
|
||||||
|
################################
|
||||||
|
def URLItemDo(inURLItem,inRequest,inGlobalDict):
|
||||||
|
global gCacheDict
|
||||||
|
inResponseDict = inRequest.OpenRPAResponseDict
|
||||||
|
inResponseDict["Headers"]["Content-type"]= None
|
||||||
|
#Set status code 200
|
||||||
|
inResponseDict["StatusCode"] = 200
|
||||||
|
#Content-type
|
||||||
|
if "ResponseContentType" in inURLItem:
|
||||||
|
inResponseDict["Headers"]["Content-type"] = inURLItem["ResponseContentType"]
|
||||||
|
#If file path is set
|
||||||
|
if "ResponseFilePath" in inURLItem:
|
||||||
|
# Check cache
|
||||||
|
if inURLItem.get("UseCacheBool",False) == True:
|
||||||
|
if inURLItem["ResponseFilePath"] in gCacheDict:
|
||||||
|
# Write content as utf-8 data
|
||||||
|
inResponseDict["Body"] = gCacheDict[inURLItem["ResponseFilePath"]]
|
||||||
|
else:
|
||||||
|
if os.path.exists(inURLItem["ResponseFilePath"]) and os.path.isfile(inURLItem["ResponseFilePath"]):
|
||||||
|
lFileObject = open(CrossOS.PathStr(inURLItem["ResponseFilePath"]), "rb")
|
||||||
|
# Write content as utf-8 data
|
||||||
|
gCacheDict[inURLItem["ResponseFilePath"]] = lFileObject.read()
|
||||||
|
inResponseDict["Body"] = gCacheDict[inURLItem["ResponseFilePath"]]
|
||||||
|
# Закрыть файловый объект
|
||||||
|
lFileObject.close()
|
||||||
|
else: inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
|
||||||
|
else:
|
||||||
|
if os.path.exists(inURLItem["ResponseFilePath"]) and os.path.isfile(inURLItem["ResponseFilePath"]):
|
||||||
|
lFileObject = open(CrossOS.PathStr(inURLItem["ResponseFilePath"]), "rb")
|
||||||
|
# Write content as utf-8 data
|
||||||
|
inResponseDict["Body"] = lFileObject.read()
|
||||||
|
# Закрыть файловый объект
|
||||||
|
lFileObject.close()
|
||||||
|
else: inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
|
||||||
|
# detect MIME type if none
|
||||||
|
if inResponseDict["Headers"]["Content-type"] is None:
|
||||||
|
inResponseDict["Headers"]["Content-type"]= mimetypes.guess_type(inURLItem["ResponseFilePath"])[0]
|
||||||
|
#If function is set
|
||||||
|
if "ResponseDefRequestGlobal" in inURLItem:
|
||||||
|
lDef = inURLItem["ResponseDefRequestGlobal"]
|
||||||
|
lDefSignature = inspect.signature(lDef)
|
||||||
|
if len(lDefSignature.parameters) == 2:
|
||||||
|
inURLItem["ResponseDefRequestGlobal"](inRequest, inGlobalDict)
|
||||||
|
elif len(lDefSignature.parameters) == 1:
|
||||||
|
inURLItem["ResponseDefRequestGlobal"](inRequest)
|
||||||
|
else:
|
||||||
|
inURLItem["ResponseDefRequestGlobal"]()
|
||||||
|
if "ResponseFolderPath" in inURLItem:
|
||||||
|
#lRequestPath = inRequest.path
|
||||||
|
lRequestPath = urllib.parse.unquote(inRequest.path)
|
||||||
|
if inURLItem["URL"][-1]!="/": inURLItem["URL"]+= "/" # Fix for settings
|
||||||
|
lFilePathSecondPart = lRequestPath.replace(inURLItem["URL"],"")
|
||||||
|
lFilePathSecondPart = lFilePathSecondPart.split("?")[0]
|
||||||
|
lFilePath = CrossOS.PathStr(os.path.join(inURLItem["ResponseFolderPath"],lFilePathSecondPart))
|
||||||
|
#print(f"File full path {lFilePath}")
|
||||||
|
#Check if file exist
|
||||||
|
if os.path.exists(lFilePath) and os.path.isfile(lFilePath):
|
||||||
|
# Check cache
|
||||||
|
if inURLItem.get("UseCacheBool",False) == True:
|
||||||
|
if lFilePath in gCacheDict:
|
||||||
|
# Write content as utf-8 data
|
||||||
|
inResponseDict["Body"] = gCacheDict[lFilePath]
|
||||||
|
else:
|
||||||
|
lFileObject = open(lFilePath, "rb")
|
||||||
|
# Write content as utf-8 data
|
||||||
|
gCacheDict[lFilePath] = lFileObject.read()
|
||||||
|
inResponseDict["Body"] = gCacheDict[lFilePath]
|
||||||
|
# Закрыть файловый объект
|
||||||
|
lFileObject.close()
|
||||||
|
else:
|
||||||
|
lFileObject = open(lFilePath, "rb")
|
||||||
|
# Write content as utf-8 data
|
||||||
|
inResponseDict["Body"] = lFileObject.read()
|
||||||
|
# Закрыть файловый объект
|
||||||
|
lFileObject.close()
|
||||||
|
# detect MIME type if none
|
||||||
|
if inResponseDict["Headers"]["Content-type"] is None:
|
||||||
|
inResponseDict["Headers"]["Content-type"]= mimetypes.guess_type(lFilePath)[0]
|
||||||
|
else:
|
||||||
|
inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
|
||||||
|
# If No content-type
|
||||||
|
if inResponseDict["Headers"]["Content-type"] is None:
|
||||||
|
inResponseDict["Headers"]["Content-type"]= "application/octet-stream"
|
||||||
|
##############################################
|
||||||
|
# UAC Check
|
||||||
|
if inOnlyFlagUACBool == True and inURLItem.get("UACBool",None) in [None, True]:
|
||||||
|
return False
|
||||||
|
if inURLItem["Method"].upper() == inMethod.upper():
|
||||||
|
# check Match type variant: BeginWith
|
||||||
|
if inURLItem["MatchType"].upper() == "BEGINWITH":
|
||||||
|
lURLPath = urllib.parse.unquote(self.path)
|
||||||
|
lURLPath = lURLPath.upper()
|
||||||
|
if lURLPath.startswith(inURLItem["URL"].upper()):
|
||||||
|
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
|
||||||
|
return True
|
||||||
|
# check Match type variant: Contains
|
||||||
|
elif inURLItem["MatchType"].upper() == "CONTAINS":
|
||||||
|
lURLPath = urllib.parse.unquote(self.path)
|
||||||
|
lURLPath = lURLPath.upper()
|
||||||
|
if lURLPath.contains(inURLItem["URL"].upper()):
|
||||||
|
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
|
||||||
|
return True
|
||||||
|
# check Match type variant: Equal
|
||||||
|
elif inURLItem["MatchType"].upper() == "EQUAL":
|
||||||
|
if inURLItem["URL"].upper() == urllib.parse.unquote(self.path).upper():
|
||||||
|
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
|
||||||
|
return True
|
||||||
|
# check Match type variant: EqualNoParam
|
||||||
|
elif inURLItem["MatchType"].upper() == "EQUALNOPARAM":
|
||||||
|
if inURLItem["URL"].upper() == urllib.parse.unquote(self.path).upper().split("?")[0]:
|
||||||
|
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
|
||||||
|
return True
|
||||||
|
# check Match type variant: EqualCase
|
||||||
|
elif inURLItem["MatchType"].upper() == "EQUALCASE":
|
||||||
|
if inURLItem["URL"] == urllib.parse.unquote(self.path):
|
||||||
|
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
#ResponseContentTypeFile
|
||||||
|
def SendResponseContentTypeFile(self, inContentType, inFilePath):
|
||||||
|
inResponseDict = self.OpenRPAResponseDict
|
||||||
|
self.mResponse.status_code = 200
|
||||||
|
# Send headers
|
||||||
|
self.mResponse.headers["Content-type"]=inContentType
|
||||||
|
#Check if var exist
|
||||||
|
if hasattr(self, "OpenRPASetCookie"):
|
||||||
|
self.mResponse.set_cookie(key='AuthToken',value=self.OpenRPA['AuthToken'])
|
||||||
|
lFileObject = open(inFilePath, "rb")
|
||||||
|
# Write content as utf-8 data
|
||||||
|
lFileBytes = lFileObject.read()
|
||||||
|
#Закрыть файловый объект
|
||||||
|
lFileObject.close()
|
||||||
|
return lFileBytes
|
||||||
|
# ResponseContentTypeFile
|
||||||
|
def ResponseDictSend(self):
|
||||||
|
inResponseDict = self.OpenRPAResponseDict
|
||||||
|
self.mResponse.status_code = inResponseDict["StatusCode"]
|
||||||
|
# Send headers
|
||||||
|
for lItemKey, lItemValue in inResponseDict["Headers"].items():
|
||||||
|
self.mResponse.headers[lItemKey]=lItemValue
|
||||||
|
# Send headers: Set-Cookie
|
||||||
|
for lItemKey, lItemValue in inResponseDict["SetCookies"].items():
|
||||||
|
self.mResponse.set_cookie(key=lItemKey,value=lItemValue)
|
||||||
|
self.send_header("Set-Cookie", f"{lItemKey}={lItemValue}")
|
||||||
|
return inResponseDict["Body"]
|
||||||
|
|
||||||
|
def do_GET(self, inBodyStr):
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
self.OpenRPA["DefUserRoleAccessAsk"]=self.UserRoleAccessAsk # Alias for def
|
||||||
|
self.OpenRPA["DefUserRoleHierarchyGet"]=self.UserRoleHierarchyGet # Alias for def
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
# Prepare result dict
|
||||||
|
lResponseDict = {"Headers": {}, "SetCookies": {}, "Body": b"", "StatusCode": None, "BodyIsText":True}
|
||||||
|
self.OpenRPAResponseDict = lResponseDict
|
||||||
|
#Check the user access (if flag, UAC)
|
||||||
|
####################################
|
||||||
|
lFlagUserAccess = True
|
||||||
|
#If need user authentication
|
||||||
|
if __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
|
||||||
|
if self.OpenRPA["AuthToken"] != None:
|
||||||
|
lFlagUserAccess = UserAccessCheckBefore("GET", self)
|
||||||
|
######################################
|
||||||
|
if lFlagUserAccess:
|
||||||
|
if CrossOS.IS_WINDOWS_BOOL: lOrchestratorFolder = "\\".join(__file__.split("\\")[:-1])
|
||||||
|
if CrossOS.IS_LINUX_BOOL: lOrchestratorFolder = "/".join(__file__.split("/")[:-1])
|
||||||
|
############################
|
||||||
|
#New server engine (url from global dict (URLList))
|
||||||
|
############################
|
||||||
|
for lURLItem in __Orchestrator__.GSettingsGet()["ServerDict"]["URLList"]:
|
||||||
|
#Check if all condition are applied
|
||||||
|
lFlagURLIsApplied=False
|
||||||
|
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "GET")
|
||||||
|
if lFlagURLIsApplied:
|
||||||
|
return self.ResponseDictSend()
|
||||||
|
else:
|
||||||
|
raise HTTPException(status_code=403, detail="here is the details", headers={})
|
||||||
|
except Exception as e:
|
||||||
|
lL = __Orchestrator__.OrchestratorLoggerGet()
|
||||||
|
if lL: lL.exception(f"Сервер (do_GET): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
|
||||||
|
# POST
|
||||||
|
def do_POST(self, inBodyStr):
|
||||||
|
try:
|
||||||
|
lL = __Orchestrator__.OrchestratorLoggerGet()
|
||||||
|
try:
|
||||||
|
self.OpenRPA["DefUserRoleAccessAsk"]=self.UserRoleAccessAsk # Alias for def
|
||||||
|
self.OpenRPA["DefUserRoleHierarchyGet"]=self.UserRoleHierarchyGet # Alias for def
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
# Prepare result dict
|
||||||
|
#pdb.set_trace()
|
||||||
|
lResponseDict = {"Headers": {}, "SetCookies": {}, "Body": b"", "StatusCode": None, "BodyIsText":True}
|
||||||
|
self.OpenRPAResponseDict = lResponseDict
|
||||||
|
#Check the user access (if flag)
|
||||||
|
####################################
|
||||||
|
lFlagUserAccess = True
|
||||||
|
#If need user authentication
|
||||||
|
if __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
|
||||||
|
if self.OpenRPA["AuthToken"] != None:
|
||||||
|
lFlagUserAccess = UserAccessCheckBefore("POST", self)
|
||||||
|
######################################
|
||||||
|
if lFlagUserAccess:
|
||||||
|
lOrchestratorFolder = "\\".join(__file__.split("\\")[:-1])
|
||||||
|
############################
|
||||||
|
#New server engine (url from global dict (URLList))
|
||||||
|
############################
|
||||||
|
for lURLItem in __Orchestrator__.GSettingsGet()["ServerDict"]["URLList"]:
|
||||||
|
#Check if all condition are applied
|
||||||
|
lFlagURLIsApplied=False
|
||||||
|
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "POST")
|
||||||
|
if lFlagURLIsApplied:
|
||||||
|
return self.ResponseDictSend()
|
||||||
|
else:
|
||||||
|
raise HTTPException(status_code=403, detail="here is the details", headers={})
|
||||||
|
except Exception as e:
|
||||||
|
lL = __Orchestrator__.OrchestratorLoggerGet()
|
||||||
|
if lL: lL.exception(f"Сервер, обратная совместимость (do_POST): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
|
||||||
|
|
@ -1,992 +0,0 @@
|
|||||||
var mGlobal={}
|
|
||||||
mGlobal.pyOpenRPA = {}
|
|
||||||
window.onload=function() {
|
|
||||||
//document.cookie = "SessionGUIDStr=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
|
||||||
//Render existing data
|
|
||||||
//mGlobal.Monitor.fControlPanelRefresh_TechnicalRender()
|
|
||||||
}
|
|
||||||
$(document).ready(function() {
|
|
||||||
document.cookie = "SessionGUIDStr=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
|
||||||
console.log("Cookie is deleted")
|
|
||||||
// fix main menu to page on passing
|
|
||||||
$('.main.menu').visibility({
|
|
||||||
type: 'fixed'
|
|
||||||
});
|
|
||||||
$('.overlay').visibility({
|
|
||||||
type: 'fixed',
|
|
||||||
offset: 80
|
|
||||||
});
|
|
||||||
|
|
||||||
// lazy load images
|
|
||||||
$('.image').visibility({
|
|
||||||
type: 'image',
|
|
||||||
transition: 'vertical flip in',
|
|
||||||
duration: 500
|
|
||||||
});
|
|
||||||
|
|
||||||
// show dropdown on hover
|
|
||||||
$('.main.menu .ui.dropdown').dropdown({
|
|
||||||
on: 'hover'
|
|
||||||
});
|
|
||||||
function clone(obj) {
|
|
||||||
var copy;
|
|
||||||
|
|
||||||
// Handle the 3 simple types, and null or undefined
|
|
||||||
if (null == obj || "object" != typeof obj) return obj;
|
|
||||||
|
|
||||||
// Handle Date
|
|
||||||
if (obj instanceof Date) {
|
|
||||||
copy = new Date();
|
|
||||||
copy.setTime(obj.getTime());
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Array
|
|
||||||
if (obj instanceof Array) {
|
|
||||||
copy = [];
|
|
||||||
for (var i = 0, len = obj.length; i < len; i++) {
|
|
||||||
copy[i] = clone(obj[i]);
|
|
||||||
}
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Object
|
|
||||||
if (obj instanceof Object) {
|
|
||||||
copy = {};
|
|
||||||
for (var attr in obj) {
|
|
||||||
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
|
|
||||||
}
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
throw new Error("Unable to copy obj! Its type isn't supported.");
|
|
||||||
}
|
|
||||||
//For data storage key
|
|
||||||
mGlobal["DataStorage"] = {}
|
|
||||||
// Clear the session cookie
|
|
||||||
|
|
||||||
String.prototype.replaceAll = function(search, replace){
|
|
||||||
return this.split(search).join(replace);
|
|
||||||
}
|
|
||||||
mGlobal.GeneralGenerateHTMLCodeHandlebars=function(inInnerTemplateSelector,inData) {
|
|
||||||
lHTMLTemplate=$(inInnerTemplateSelector)[0].innerHTML
|
|
||||||
//console.log(lHTMLTemplate)
|
|
||||||
//Компиляция
|
|
||||||
var template = Handlebars.compile(lHTMLTemplate);
|
|
||||||
//Вставка данных
|
|
||||||
var lHTMLResult = template(inData);
|
|
||||||
return lHTMLResult
|
|
||||||
}
|
|
||||||
mGlobal.GeneralGenerateHTMLCode=function(inTemplateHTMLSelector,inItemDictionary,inKeywordPrefix="::",inKeywordPostfix="::") {
|
|
||||||
///Получить заготовку
|
|
||||||
lTemplateHTMLCode=$(inTemplateHTMLSelector)[0].outerHTML
|
|
||||||
///Определить ключь экранирования специальных ключевых слов
|
|
||||||
///Выполнить циклические замены, если там есть пожходящие ключи
|
|
||||||
lResultHTMLCode=lTemplateHTMLCode
|
|
||||||
for(var lKey in inItemDictionary) {
|
|
||||||
lHTMLKey=inKeywordPrefix+lKey+inKeywordPostfix;
|
|
||||||
lResultHTMLCode=lResultHTMLCode.replaceAll(lHTMLKey,inItemDictionary[lKey])
|
|
||||||
}
|
|
||||||
///Вернуть результат
|
|
||||||
return lResultHTMLCode
|
|
||||||
}
|
|
||||||
//////////////////////////
|
|
||||||
/////Info JS module
|
|
||||||
//////////////////////////
|
|
||||||
mGlobal.Info={};
|
|
||||||
|
|
||||||
mGlobal.Info.TableActivityLogScheduleListRefresh=function() {
|
|
||||||
|
|
||||||
}
|
|
||||||
//////////////////////////
|
|
||||||
/////Controller JS module
|
|
||||||
//////////////////////////
|
|
||||||
mGlobal.Controller={};
|
|
||||||
|
|
||||||
mGlobal.Controller.CMDRunText=function(inCMDText) {
|
|
||||||
///Подготовить конфигурацию
|
|
||||||
lData = [
|
|
||||||
{"Type":"CMDStart", "Command": inCMDText}
|
|
||||||
]
|
|
||||||
///Обнулить таблицу
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: 'Utils/Processor',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3){},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mGlobal.Controller.CMDRun=function() {
|
|
||||||
///Обнулить таблицу
|
|
||||||
lCMDCode=$(".openrpa-controller-cmd-run-input")[0].value
|
|
||||||
///Подготовить конфигурацию
|
|
||||||
lData = [
|
|
||||||
{
|
|
||||||
"Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
||||||
"ArgList":[], // Args list
|
|
||||||
"ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
|
|
||||||
"ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
"ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: '/pyOpenRPA/ActivityListExecute',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
///Отправить запрос на формирование таблицы
|
|
||||||
//lHTMLCode=console.log("CMDRun result: "+lResponseJSON[0].result)
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mGlobal.Controller.CMDRunGUILogout=function() {
|
|
||||||
///Обнулить таблицу
|
|
||||||
lCMDCode="for /f \"skip=1 tokens=2\" %s in ('query user %USERNAME%') do (tscon \\dest:console)"
|
|
||||||
//lCMDCode = lCMDCode.replace(/\\n/g, "\\n")
|
|
||||||
// .replace(/\\'/g, "\\'")
|
|
||||||
// .replace(/\\"/g, '\\"')
|
|
||||||
// .replace(/\\&/g, "\\&")
|
|
||||||
// .replace(/\\r/g, "\\r")
|
|
||||||
// .replace(/\\t/g, "\\t")
|
|
||||||
// .replace(/\\b/g, "\\b")
|
|
||||||
// .replace(/\\f/g, "\\f")
|
|
||||||
// .replace('"', "\\\"");
|
|
||||||
///Подготовить конфигурацию
|
|
||||||
lData = [
|
|
||||||
{"Type":"CMDStart", "Command": lCMDCode }
|
|
||||||
]
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: 'Utils/Processor',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
///Отправить запрос на формирование таблицы
|
|
||||||
//lHTMLCode=console.log("CMDRun result: "+lResponseJSON[0].result)
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
///Restart PC
|
|
||||||
mGlobal.Controller.PCRestart = function () {
|
|
||||||
mGlobal.Controller.OrchestratorSessionSave()
|
|
||||||
mGlobal.Controller.CMDRunText("shutdown -r")
|
|
||||||
}
|
|
||||||
///Orchestrator save session
|
|
||||||
mGlobal.Controller.OrchestratorSessionSave=function() {
|
|
||||||
///Подготовить конфигурацию
|
|
||||||
lData = [
|
|
||||||
{"Type":"OrchestratorSessionSave"}
|
|
||||||
]
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: 'Utils/Processor',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
///Перезагрузить Orchestrator
|
|
||||||
mGlobal.Controller.OrchestratorRestart=function() {
|
|
||||||
///Подготовить конфигурацию
|
|
||||||
lData = [
|
|
||||||
{"Type":"OrchestratorRestart"}
|
|
||||||
]
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: 'Utils/Processor',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mGlobal.Controller.OrchestratorGITPullRestart = function() {
|
|
||||||
mGlobal.Controller.OrchestratorSessionSave() //Save current RDP list session
|
|
||||||
mGlobal.Controller.CMDRunText("timeout 3 & taskkill /f /im OpenRPA_Orchestrator.exe & timeout 2 & cd "+mGlobal.WorkingDirectoryPathStr+" & git reset --hard & git pull & pyOpenRPA.Orchestrator_x64_administrator_startup.cmd");
|
|
||||||
}
|
|
||||||
//////////////////////////
|
|
||||||
/////Monitor JS module
|
|
||||||
//////////////////////////
|
|
||||||
mGlobal.Monitor={};
|
|
||||||
mGlobal.Monitor.ScreenshotModal={};
|
|
||||||
mGlobal.Monitor.GenerateUniqueID=function(inPrefix="tempUID=") {
|
|
||||||
return inPrefix+Math.round(Math.random()*1000)+"-"+Math.round(Math.random()*10000)+"-"+Math.round(Math.random()*1000)
|
|
||||||
}
|
|
||||||
//inHostURI: http://localhost:8081
|
|
||||||
mGlobal.Monitor.ScreenshotModal.Show=function(inHostURI=" ") {
|
|
||||||
$('.ui.modal.daemon-screenshot').modal({'onHide':function (inElement) {mGlobal.Monitor.ScreenshotModal.Close();} }).modal('show');
|
|
||||||
|
|
||||||
//Функция обновления картинки
|
|
||||||
lScreenshotUpdate=function() {
|
|
||||||
lScreenshotSrc=inHostURI+"/GetScreenshot?"+mGlobal.Monitor.GenerateUniqueID()
|
|
||||||
$(".daemon-screenshot img").attr('src', lScreenshotSrc);
|
|
||||||
}
|
|
||||||
|
|
||||||
mGlobal.Monitor.ScreenshotModal.timerId=setInterval(lScreenshotUpdate,1500)
|
|
||||||
}
|
|
||||||
mGlobal.Monitor.ScreenshotModal.Close=function() {
|
|
||||||
clearInterval(mGlobal.Monitor.ScreenshotModal.timerId);
|
|
||||||
}
|
|
||||||
///Monitor
|
|
||||||
mGlobal.Monitor.DaemonList={}
|
|
||||||
mGlobal.Monitor.DaemonList.fRefreshTable=function() {
|
|
||||||
///Загрузка данных
|
|
||||||
$.ajax({
|
|
||||||
type: "GET",
|
|
||||||
url: 'Monitor/JSONDaemonListGet',
|
|
||||||
data: '',
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
///Сформировать HTML код новой таблицы
|
|
||||||
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-monitor-table-general",lResponseJSON)
|
|
||||||
///Очистить дерево
|
|
||||||
//mGlobal.ElementTree.fClear();
|
|
||||||
///Прогрузить новую таблицу
|
|
||||||
$(".openrpa-monitor").html(lHTMLCode)
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
////////////////////////////////
|
|
||||||
///////Control panel
|
|
||||||
///////////////////////////////
|
|
||||||
///Refresh control panel
|
|
||||||
function sleep(ms) {
|
|
||||||
ms += new Date().getTime();
|
|
||||||
while (new Date() < ms){}
|
|
||||||
}
|
|
||||||
function uuidv4() {
|
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
||||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mGlobal.SessionGUIDStr = uuidv4() // Generate uuid4 of the session
|
|
||||||
//console.log(uuidv4());
|
|
||||||
mGlobal.RobotRDPActive = {}
|
|
||||||
mGlobal.Monitor.fControlPanelRefresh_TechnicalRender = function()
|
|
||||||
{
|
|
||||||
lResponseJSON = mGlobal.Monitor.mDatasetLast
|
|
||||||
|
|
||||||
if (lResponseJSON!= null) {
|
|
||||||
/// New version of control panels
|
|
||||||
for (var lKeyStr in lResponseJSON){
|
|
||||||
if (lKeyStr != "RenderRobotList") { /// Check if not "RenderRobotList"
|
|
||||||
lCPDict = lResponseJSON[lKeyStr]
|
|
||||||
/// Render HTML
|
|
||||||
if ("HTMLStr" in lCPDict) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// v1.2.0 Backward compatibility - support old control panels
|
|
||||||
if ("RenderRobotList" in lResponseJSON) {
|
|
||||||
///Escape onclick
|
|
||||||
/// RenderRobotList
|
|
||||||
lResponseJSON["RenderRobotList"].forEach(
|
|
||||||
function(lItem){
|
|
||||||
if ('FooterButtonX2List' in lItem) {
|
|
||||||
/// FooterButtonX2List
|
|
||||||
lItem["FooterButtonX2List"].forEach(
|
|
||||||
function(lItem2){
|
|
||||||
if ('OnClick' in lItem) {
|
|
||||||
lOnClickEscaped = lItem["OnClick"];
|
|
||||||
lOnClickEscaped = lOnClickEscaped.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
||||||
lItem["OnClick"] = lOnClickEscaped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
/// FooterButtonX1List
|
|
||||||
lItem["FooterButtonX1List"].forEach(
|
|
||||||
function(lItem2){
|
|
||||||
if ('OnClick' in lItem) {
|
|
||||||
lOnClickEscaped = lItem["OnClick"];
|
|
||||||
lOnClickEscaped = lOnClickEscaped.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
||||||
lItem["OnClick"] = lOnClickEscaped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
//////////////////////////////////////////////////////////
|
|
||||||
///Сформировать HTML код новой таблицы - контрольная панель
|
|
||||||
lHTMLCode+=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-control-panel",lResponseJSON)
|
|
||||||
//Присвоить ответ в mGlobal.Monitor.mResponseList
|
|
||||||
mGlobal.Monitor.mResponseList = lResponseJSON
|
|
||||||
///Set result in mGlobal.DataStorage
|
|
||||||
lResponseJSON["RenderRobotList"].forEach(
|
|
||||||
function(lItem){
|
|
||||||
if ('DataStorageKey' in lItem) {
|
|
||||||
mGlobal["DataStorage"][lItem['DataStorageKey']]=lItem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
///Прогрузить новую таблицу
|
|
||||||
$(".openrpa-control-panel").html(lHTMLCode)
|
|
||||||
////////////////////////////////////////////////////
|
|
||||||
/// !RDP List ! Сформировать HTML код новой таблицы - список RDP
|
|
||||||
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-robotrdpactive-control-panel",lResponseJSON)
|
|
||||||
//Присвоить ответ в mGlobal.RobotRDPActive.mResponseList
|
|
||||||
mGlobal.RobotRDPActive.mResponseList = lResponseJSON
|
|
||||||
///Прогрузить новую таблицу
|
|
||||||
$(".openrpa-robotrdpactive-control-panel").html(lHTMLCode)
|
|
||||||
///Очистить дерево
|
|
||||||
//mGlobal.ElementTree.fClear();
|
|
||||||
////////////////////////////////////////////////////
|
|
||||||
/// !UserAgent List ! Сформировать HTML код новой таблицы - список RDP
|
|
||||||
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".pyOpenRPA-Agent-ListTemplate",lResponseJSON)
|
|
||||||
//Присвоить ответ в mGlobal.RobotRDPActive.mResponseList
|
|
||||||
mGlobal.RobotRDPActive.mResponseList = lResponseJSON
|
|
||||||
///Прогрузить новую таблицу
|
|
||||||
$(".pyOpenRPA-Agent-List").html(lHTMLCode)
|
|
||||||
///Очистить дерево
|
|
||||||
//mGlobal.ElementTree.fClear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///v 1.2.0 pyOpenRPA
|
|
||||||
/// Execute ActivityItem
|
|
||||||
mGlobal.pyOpenRPA.ActivityItemExecute=function(inActivityItem) {
|
|
||||||
///EXAMPLE
|
|
||||||
// {
|
|
||||||
// "Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
||||||
// "ArgList":[], // Args list
|
|
||||||
// "ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
|
|
||||||
// "ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
// "ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
// }
|
|
||||||
///Подготовить конфигурацию
|
|
||||||
lData = [inActivityItem]
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: '/pyOpenRPA/ActivityListExecute',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
console.log(lResponseJSON)
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/// Execute ActivityList
|
|
||||||
mGlobal.pyOpenRPA.ActivityListExecute=function(inActivityList) {
|
|
||||||
///EXAMPLE
|
|
||||||
// [{
|
|
||||||
// "Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
||||||
// "ArgList":[], // Args list
|
|
||||||
// "ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
|
|
||||||
// "ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
// "ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
// }]
|
|
||||||
///Подготовить конфигурацию
|
|
||||||
lData = inActivityList
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: '/pyOpenRPA/ActivityListExecute',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
console.log(lResponseJSON)
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/// Add ActivityList in processor queue
|
|
||||||
mGlobal.pyOpenRPA.ProcessorQueueAdd=function(inActivityList) {
|
|
||||||
///EXAMPLE
|
|
||||||
// [{
|
|
||||||
// "Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
||||||
// "ArgList":[], // Args list
|
|
||||||
// "ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
|
|
||||||
// "ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
// "ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
// }]
|
|
||||||
///Подготовить конфигурацию
|
|
||||||
lData = inActivityList
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: '/pyOpenRPA/ProcessorQueueAdd',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
console.log(lResponseJSON)
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// v1.2.0 pyOpenRPA ServerJSInit
|
|
||||||
mGlobal.pyOpenRPA.ServerJSInitDef=function() {
|
|
||||||
try {
|
|
||||||
$.ajax({
|
|
||||||
type: "GET",
|
|
||||||
headers: {},
|
|
||||||
url: 'pyOpenRPA/ServerJSInit',
|
|
||||||
data: mGlobal.pyOpenRPA.ServerDataHashStr,
|
|
||||||
async: false,
|
|
||||||
success: function(lJSText) {
|
|
||||||
try {
|
|
||||||
eval(lJSText)
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dataType: "text",
|
|
||||||
error: function(jqXHR, textStatus, errorThrown ) {
|
|
||||||
console.log(textStatus)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// v1.2.0 pyOpenRPA ServerData
|
|
||||||
mGlobal.pyOpenRPA.ServerDataDict = null
|
|
||||||
mGlobal.pyOpenRPA.ServerDataHashStr = ""
|
|
||||||
mGlobal.pyOpenRPA.ServerDataRefreshDef_TechnicalRender = function()
|
|
||||||
{
|
|
||||||
lResponseJSON = mGlobal.pyOpenRPA.ServerDataDict
|
|
||||||
if (lResponseJSON!= null) {
|
|
||||||
/// New version of control panels
|
|
||||||
lHTMLCode = '<div class="ui cards">'
|
|
||||||
for (var lKeyStr in lResponseJSON["CPDict"]){
|
|
||||||
lCPDict = lResponseJSON["CPDict"][lKeyStr]
|
|
||||||
/// Render HTML
|
|
||||||
if ("HTMLStr" in lCPDict) {
|
|
||||||
lHTMLCode+=lCPDict["HTMLStr"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lHTMLCode += '</div>'
|
|
||||||
///Прогрузить новую таблицу
|
|
||||||
$(".openrpa-control-panel").html(lHTMLCode)
|
|
||||||
////////////////////////////////////////////////////
|
|
||||||
/// !RDP List ! Сформировать HTML код новой таблицы - список RDP
|
|
||||||
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-robotrdpactive-control-panel",lResponseJSON["RDPDict"])
|
|
||||||
//Присвоить ответ в mGlobal.RobotRDPActive.mResponseList
|
|
||||||
mGlobal.RobotRDPActive.mResponseList = lResponseJSON["RDPDict"]
|
|
||||||
///Прогрузить новую таблицу
|
|
||||||
$(".openrpa-robotrdpactive-control-panel").html(lHTMLCode)
|
|
||||||
///Очистить дерево
|
|
||||||
//mGlobal.ElementTree.fClear();
|
|
||||||
////////////////////////////////////////////////////
|
|
||||||
/// !UserAgent List ! Сформировать HTML код новой таблицы - список RDP
|
|
||||||
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".pyOpenRPA-Agent-ListTemplate",lResponseJSON["AgentDict"])
|
|
||||||
///Прогрузить новую таблицу
|
|
||||||
$(".pyOpenRPA-Agent-List").html(lHTMLCode)
|
|
||||||
///Очистить дерево
|
|
||||||
//mGlobal.ElementTree.fClear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mGlobal.pyOpenRPA.ServerDataRefreshDef=function() {
|
|
||||||
try {
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
headers: {},
|
|
||||||
url: 'pyOpenRPA/ServerData',
|
|
||||||
data: mGlobal.pyOpenRPA.ServerDataHashStr,
|
|
||||||
success: function(lData,l2,l3) {
|
|
||||||
try {
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
mGlobal.VersionStr = lResponseJSON["ServerDataDict"]["UserDict"]["VersionStr"]
|
|
||||||
mGlobal.pyOpenRPA.ServerDataDict = lResponseJSON["ServerDataDict"]
|
|
||||||
mGlobal.pyOpenRPA.ServerDataHashStr = lResponseJSON["HashStr"]
|
|
||||||
mGlobal.pyOpenRPA.ServerDataRefreshDef_TechnicalRender()
|
|
||||||
mGlobal.UserRoleUpdate();
|
|
||||||
setTimeout(mGlobal.pyOpenRPA.ServerDataRefreshDef,600) // If LOGS are update every ms - set some limit in ms on the client side
|
|
||||||
//mGlobal.pyOpenRPA.ServerDataRefreshDef() // Go to the next call
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
console.log(error)
|
|
||||||
setTimeout(mGlobal.pyOpenRPA.ServerDataRefreshDef,3000)
|
|
||||||
}
|
|
||||||
//mGlobal.pyOpenRPA.ServerDataRefreshDef() // recursive
|
|
||||||
},
|
|
||||||
dataType: "text",
|
|
||||||
error: function(jqXHR, textStatus, errorThrown ) {
|
|
||||||
setTimeout(mGlobal.pyOpenRPA.ServerDataRefreshDef,3000)
|
|
||||||
//sleep(3000)
|
|
||||||
//mGlobal.pyOpenRPA.ServerDataRefreshDef() // recursive
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
setTimeout(mGlobal.pyOpenRPA.ServerDataRefreshDef,3000)
|
|
||||||
//sleep(3000)
|
|
||||||
//mGlobal.pyOpenRPA.ServerDataRefreshDef() // recursive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
/// v1.2.0 pyOpenRPA ServerLogs
|
|
||||||
mGlobal.pyOpenRPA.ServerLogList = null
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListHashStr = ""
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListDoRenderBool = true
|
|
||||||
///Turn OFF rendering
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListDoRenderFalse = function() {
|
|
||||||
///Set unfreeze button
|
|
||||||
$("a.mGlobal-pyOpenRPA-ServerLogListDoRender").html("Unfreeze textarea")
|
|
||||||
$("a.mGlobal-pyOpenRPA-ServerLogListDoRender").attr("onclick","mGlobal.pyOpenRPA.ServerLogListDoRenderTrue()")
|
|
||||||
$("textarea.mGlobal-pyOpenRPA-ServerLogList").css("background-color","#b9e2e8")
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListDoRenderBool = false
|
|
||||||
}
|
|
||||||
///Turn ON rendering
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListDoRenderTrue = function() {
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListDoRenderBool = true
|
|
||||||
///Render last data
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListRefreshDef_TechnicalRender()
|
|
||||||
///Set unfreeze button
|
|
||||||
$("a.mGlobal-pyOpenRPA-ServerLogListDoRender").html("Freeze textarea")
|
|
||||||
$("a.mGlobal-pyOpenRPA-ServerLogListDoRender").attr("onclick","mGlobal.pyOpenRPA.ServerLogListDoRenderFalse()")
|
|
||||||
$("textarea.mGlobal-pyOpenRPA-ServerLogList").css("background-color","")
|
|
||||||
|
|
||||||
}
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListScrollBottomDef = function() {
|
|
||||||
var lTA = $("textarea.mGlobal-pyOpenRPA-ServerLogList")[0];
|
|
||||||
lTA.scrollTop = lTA.scrollHeight;
|
|
||||||
}
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListRefreshDef_TechnicalRender = function()
|
|
||||||
{
|
|
||||||
lResponseJSON = mGlobal.pyOpenRPA.ServerLogList
|
|
||||||
if (lResponseJSON!= null && mGlobal.pyOpenRPA.ServerLogListDoRenderBool==true) {
|
|
||||||
lText = lResponseJSON.join("\n") /// Code for the processing the text
|
|
||||||
$("textarea.mGlobal-pyOpenRPA-ServerLogList")[0].value= lText ///Прогрузить новую таблицу
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListScrollBottomDef() //Scroll to the bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListRefreshDef=function() {
|
|
||||||
try {
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
headers: {},
|
|
||||||
url: 'pyOpenRPA/ServerLog',
|
|
||||||
data: mGlobal.pyOpenRPA.ServerLogListHashStr,
|
|
||||||
success: function(lData,l2,l3) {
|
|
||||||
try {
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
mGlobal.pyOpenRPA.ServerLogList = lResponseJSON["ServerLogList"]
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListHashStr = lResponseJSON["HashStr"]
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListRefreshDef_TechnicalRender()
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
}
|
|
||||||
setTimeout(mGlobal.pyOpenRPA.ServerLogListRefreshDef,600) // If LOGS are update every ms - set some limit in ms on the client side
|
|
||||||
//mGlobal.pyOpenRPA.ServerLogListRefreshDef() // recursive
|
|
||||||
},
|
|
||||||
dataType: "text",
|
|
||||||
error: function(jqXHR, textStatus, errorThrown ) {
|
|
||||||
setTimeout(mGlobal.pyOpenRPA.ServerLogListRefreshDef,3000)
|
|
||||||
//sleep(3000)
|
|
||||||
//mGlobal.pyOpenRPA.ServerLogListRefreshDef() // recursive
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
setTimeout(mGlobal.pyOpenRPA.ServerLogListRefreshDef,3000)
|
|
||||||
//sleep(3000)
|
|
||||||
//mGlobal.pyOpenRPA.ServerLogListRefreshDef() // recursive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
mGlobal.Monitor.mDatasetLast = null
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
///Processor functions
|
|
||||||
///////////////////////////////
|
|
||||||
mGlobal.Processor = {}
|
|
||||||
mGlobal.Processor.ServerValueAppend = function(inKeyList,inValue) {
|
|
||||||
lData = [
|
|
||||||
{
|
|
||||||
"Type":"GlobalDictKeyListValueAppend",
|
|
||||||
"KeyList": inKeyList,
|
|
||||||
"Value": inValue
|
|
||||||
}
|
|
||||||
]
|
|
||||||
///Обнулить таблицу
|
|
||||||
$('.ui.modal.basic .content').html("");
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: 'Utils/Processor',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
///TODO Show error if exist error
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mGlobal.Processor.ServerValueSet = function(inKeyList,inValue) {
|
|
||||||
lData = [
|
|
||||||
{
|
|
||||||
"Type":"GlobalDictKeyListValueSet",
|
|
||||||
"KeyList": inKeyList,
|
|
||||||
"Value": inValue
|
|
||||||
}
|
|
||||||
]
|
|
||||||
///Обнулить таблицу
|
|
||||||
$('.ui.modal.basic .content').html("");
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: 'Utils/Processor',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
///TODO Show error if exist error
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mGlobal.Processor.ServerValueOperatorPlus = function(inKeyList,inValue) {
|
|
||||||
lData = [
|
|
||||||
{
|
|
||||||
"Type":"GlobalDictKeyListValueOperator+",
|
|
||||||
"KeyList": inKeyList,
|
|
||||||
"Value": inValue
|
|
||||||
}
|
|
||||||
]
|
|
||||||
///Обнулить таблицу
|
|
||||||
$('.ui.modal.basic .content').html("");
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: 'Utils/Processor',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
///TODO Show error if exist error
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mGlobal.Processor.Send = function(inData) {
|
|
||||||
lData = inData
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: 'Utils/Processor',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
///TODO Show error if exist error
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mGlobal.Server= {}
|
|
||||||
mGlobal.Server.JSONGet=function(inMethod, inURL, inDataJSON, inCallback)
|
|
||||||
{
|
|
||||||
$.ajax({
|
|
||||||
type: inMethod,
|
|
||||||
url: inURL,
|
|
||||||
data: JSON.stringify(inDataJSON),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
inCallback(lResponseJSON)
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////
|
|
||||||
///Modal
|
|
||||||
///////////////////
|
|
||||||
mGlobal.Modal={}
|
|
||||||
/////////////////////////////////////////////////////
|
|
||||||
mGlobal.Modal.TableFilter={}
|
|
||||||
mGlobal.Modal.TableFilter.Show=function(inJSON) {
|
|
||||||
//{
|
|
||||||
// "Title":"",
|
|
||||||
// "Headers":["Header1","Header2"],
|
|
||||||
// "Rows": [["Cell1","Cell2"],["Cell2-1","Cell2-2"]],
|
|
||||||
// "FilterOnKeyUp": "<JS Code>" //Fill here in function
|
|
||||||
//}
|
|
||||||
//Set js handler to Search field
|
|
||||||
inJSON["FilterOnKeyUp"]="mGlobal.Modal.TableFilter.FilterUpdate(this.value);"
|
|
||||||
///Set value
|
|
||||||
mGlobal.Modal.TableFilter.mDataJSON = inJSON
|
|
||||||
//Render HTML
|
|
||||||
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-handlebar-template-table-filter",inJSON);
|
|
||||||
///Установить HTML код
|
|
||||||
$('.ui.modal.basic .content').html(lHTMLCode);
|
|
||||||
$('.ui.modal.basic').modal('show');
|
|
||||||
//DO widest modal for table with scroll x
|
|
||||||
$("div.ui.basic.modal.transition.visible.active.scrolling")[0].style["width"]="1300px"
|
|
||||||
$("div.ui.basic.modal.transition.visible.active.scrolling")[0].style["overflow"]="scroll"
|
|
||||||
}
|
|
||||||
//Service function
|
|
||||||
mGlobal.Modal.TableFilter.FilterUpdate=function(inFilterValue) {
|
|
||||||
//Get JSON, apply filter, clone data
|
|
||||||
lDataJSON = clone(mGlobal.Modal.TableFilter.mDataJSON)
|
|
||||||
delete lDataJSON["Rows"]
|
|
||||||
lDataJSON["Rows"]=[]
|
|
||||||
//Filter code [any occurence in the row is ok for push! ]
|
|
||||||
mGlobal.Modal.TableFilter.mDataJSON["Rows"].forEach(
|
|
||||||
function(inElement) {
|
|
||||||
lFlagElementAppend = false
|
|
||||||
inElement.forEach(
|
|
||||||
function(inElement2) {
|
|
||||||
if (String(inElement2).includes(inFilterValue)) {
|
|
||||||
lFlagElementAppend = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (lFlagElementAppend) {
|
|
||||||
lDataJSON["Rows"].push(inElement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
//Clear Filter Title property (fixed in html)
|
|
||||||
delete lDataJSON["FilterOnKeyUp"]
|
|
||||||
delete lDataJSON["Title"]
|
|
||||||
//Search the table element [replace only table html]
|
|
||||||
lElement = $('.ui.modals.active .content table.table')[0]
|
|
||||||
lElementParentElement = lElement.parentNode
|
|
||||||
lElement.parentNode.removeChild(lElement);
|
|
||||||
//Render HTML
|
|
||||||
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-handlebar-template-table-filter",lDataJSON);
|
|
||||||
///Установить HTML код
|
|
||||||
lElementParentElement.insertAdjacentHTML("beforeend",lHTMLCode);
|
|
||||||
}
|
|
||||||
/////////////////////////////////////////////////////////////
|
|
||||||
mGlobal.Modal.ListFilter={}
|
|
||||||
mGlobal.Modal.ListFilter.Show=function(inJSON) {
|
|
||||||
//{
|
|
||||||
// "Title":"",
|
|
||||||
// "List":[{"Header":"","Description":""}],
|
|
||||||
// "FilterOnKeyUp": "<JS Code>" //Fill here in function
|
|
||||||
//}
|
|
||||||
//Set js handler to Search field
|
|
||||||
inJSON["FilterOnKeyUp"]="mGlobal.Modal.ListFilter.FilterUpdate(this.value);"
|
|
||||||
///Set value
|
|
||||||
mGlobal.Modal.ListFilter.mDataJSON = inJSON
|
|
||||||
//Render HTML
|
|
||||||
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-handlebar-template-list-filter",inJSON);
|
|
||||||
///Установить HTML код
|
|
||||||
$('.ui.modal.basic .content').html(lHTMLCode);
|
|
||||||
$('.ui.modal.basic').modal('show');
|
|
||||||
}
|
|
||||||
//Service function
|
|
||||||
mGlobal.Modal.ListFilter.FilterUpdate=function(inFilterValue) {
|
|
||||||
//Get JSON, apply filter, clone data
|
|
||||||
lDataJSON = clone(mGlobal.Modal.ListFilter.mDataJSON)
|
|
||||||
delete lDataJSON["List"]
|
|
||||||
lDataJSON["List"]=[]
|
|
||||||
//Filter code [any occurence in the row is ok for push! ]
|
|
||||||
mGlobal.Modal.ListFilter.mDataJSON["List"].forEach(
|
|
||||||
function(inElement) {
|
|
||||||
lFlagElementAppend = false
|
|
||||||
if (String(inElement["Header"]).includes(inFilterValue)) {
|
|
||||||
lFlagElementAppend = true
|
|
||||||
}
|
|
||||||
if (String(inElement["Description"]).includes(inFilterValue)) {
|
|
||||||
lFlagElementAppend = true
|
|
||||||
}
|
|
||||||
if (lFlagElementAppend) {
|
|
||||||
lDataJSON["List"].push(inElement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
//Clear Filter Title property (fixed in html)
|
|
||||||
delete lDataJSON["FilterOnKeyUp"]
|
|
||||||
delete lDataJSON["Title"]
|
|
||||||
//Search the table element [replace only table html]
|
|
||||||
lElement = $('.ui.modals.active .content div.ui.inverted.segment')[0]
|
|
||||||
lElementParentElement = lElement.parentNode
|
|
||||||
lElement.parentNode.removeChild(lElement);
|
|
||||||
//Render HTML
|
|
||||||
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-handlebar-template-list-filter",lDataJSON);
|
|
||||||
///Установить HTML код
|
|
||||||
lElementParentElement.insertAdjacentHTML("beforeend",lHTMLCode);
|
|
||||||
}
|
|
||||||
mGlobal.UserRoleHierarchyDict = null // Put here the user role hierarchy
|
|
||||||
// UAC Ask
|
|
||||||
mGlobal.UserRoleAsk=function(inList) {
|
|
||||||
var lResult = true; // Init flag
|
|
||||||
var lRoleHierarchyDict = mGlobal.pyOpenRPA.ServerDataDict.UserDict.UACClientDict; // get the Hierarchy
|
|
||||||
// Try to get value from key list
|
|
||||||
var lKeyValue = lRoleHierarchyDict; // Init the base
|
|
||||||
var lListLength = inList.length;
|
|
||||||
for (var i = 0; i<lListLength; i++) {
|
|
||||||
var lItem = inList[i]; // get the item
|
|
||||||
if (typeof lKeyValue == "object") {
|
|
||||||
if (lItem in lKeyValue) { // Has key
|
|
||||||
lKeyValue = lKeyValue[lItem]; // Get the value and go to the next loop iteration
|
|
||||||
} else { // Else branch - true or false
|
|
||||||
if (Object.keys(lKeyValue).length > 0) { // false - if Dict has some elements
|
|
||||||
lResult = false; // Set the False Flag
|
|
||||||
} else {
|
|
||||||
lResult = true; // Set the true flag
|
|
||||||
}
|
|
||||||
break; // Stop the loop
|
|
||||||
}
|
|
||||||
} else { // Has element with no detalization - return true
|
|
||||||
lResult = true; // Set the flag
|
|
||||||
break; // Close the loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lResult; // Return the result
|
|
||||||
}
|
|
||||||
// Check user roles and update the Orchestrator UI
|
|
||||||
mGlobal.UserRoleUpdate=function() {
|
|
||||||
var lUACAsk = mGlobal.UserRoleAsk // Alias
|
|
||||||
//CPKeyDict
|
|
||||||
if (lUACAsk(["pyOpenRPADict","CPKeyDict"])) { $(".UACClient-pyOpenRPADict-CPKeyDict").show(); }
|
|
||||||
|
|
||||||
//RDPKeyDict
|
|
||||||
if (lUACAsk(["pyOpenRPADict","RDPKeyDict"])) { $(".UACClient-pyOpenRPADict-RDPKeyDict").show(); }
|
|
||||||
|
|
||||||
//AgentKeyDict
|
|
||||||
if (lUACAsk(["pyOpenRPADict","AgentKeyDict"])) { $(".UACClient-pyOpenRPADict-AgentKeyDict").show(); }
|
|
||||||
|
|
||||||
// AdminDict
|
|
||||||
if (lUACAsk(["pyOpenRPADict","AdminDict","LogViewerBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-LogViewerBool").show(); }
|
|
||||||
if (lUACAsk(["pyOpenRPADict","AdminDict","CMDInputBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-CMDInputBool").show(); }
|
|
||||||
if (lUACAsk(["pyOpenRPADict","AdminDict","ScreenshotViewerBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-ScreenshotViewerBool").show(); }
|
|
||||||
if (lUACAsk(["pyOpenRPADict","AdminDict","RestartOrchestratorBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorBool").show(); }
|
|
||||||
if (lUACAsk(["pyOpenRPADict","AdminDict","RestartOrchestratorGITPullBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorGITPullBool").show(); }
|
|
||||||
if (lUACAsk(["pyOpenRPADict","AdminDict","RestartPCBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-RestartPCBool").show(); }
|
|
||||||
if (lUACAsk(["pyOpenRPADict","AdminDict","Debugging"])) { $(".UACClient-pyOpenRPADict-AdminDict-Debugging").show(); }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// v1.2.0 pyOpenRPA Init defs
|
|
||||||
mGlobal.pyOpenRPA.ServerDataRefreshDef(); // Init the refresh data def from server side
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListRefreshDef(); // Init the refresh data def from the log window
|
|
||||||
mGlobal.pyOpenRPA.ServerLogListDoRenderTrue(); // Init button to freeze/unfreeze textare with logs
|
|
||||||
mGlobal.pyOpenRPA.ServerJSInitDef(); // Recieve JS from server (if exist) and then call anothe url ServerData
|
|
||||||
//$('.ui.dropdown').dropdown();
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
|
||||||
// 1.2.7 Debugging
|
|
||||||
/// Execute ActivityItem
|
|
||||||
|
|
||||||
// Debugging onchange def autofill init
|
|
||||||
var lDropdownOnChange = function(inEvent){
|
|
||||||
//lValueStr = inEvent.target.value
|
|
||||||
lValueStr = inEvent
|
|
||||||
$.ajax({
|
|
||||||
type: "GET",
|
|
||||||
url: '/pyOpenRPA/Debugging/HelperDefAutofill/'+lValueStr,
|
|
||||||
data: null,
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
console.log("HelperDefAutofill:")
|
|
||||||
console.log(lResponseJSON)
|
|
||||||
//ArgDict merge
|
|
||||||
var lArgDictTargetDict = lResponseJSON["ArgDict"]
|
|
||||||
var lArgDictStr = $(".mGlobal-pyOpenRPA-Debugging-ArgDict")[0].value
|
|
||||||
if (lArgDictStr !="" && lArgDictStr !=null) {
|
|
||||||
lArgDictLastDict = JSON.parse(lArgDictStr)
|
|
||||||
lArgDictTargetDict = mGlobal.pyOpenRPA.DebuggingAutofillMerge(lArgDictTargetDict, lArgDictLastDict)
|
|
||||||
}
|
|
||||||
|
|
||||||
$(".mGlobal-pyOpenRPA-Debugging-ArgList")[0].value = JSON.stringify(lResponseJSON["ArgList"])
|
|
||||||
$(".mGlobal-pyOpenRPA-Debugging-ArgDict")[0].value = JSON.stringify(lArgDictTargetDict)
|
|
||||||
$(".mGlobal-pyOpenRPA-Debugging-ArgGSettingsStr")[0].value = JSON.stringify(lResponseJSON["ArgGSettingsStr"])
|
|
||||||
$(".mGlobal-pyOpenRPA-Debugging-ArgLoggerStr")[0].value = JSON.stringify(lResponseJSON["ArgLoggerStr"])
|
|
||||||
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//$('.ui.dropdown.mGlobal-pyOpenRPA-Debugging-Def-Dropdown')[0].onchange=lDropdownOnChange
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mGlobal.pyOpenRPA.DebuggingExecute=function() {
|
|
||||||
///EXAMPLE
|
|
||||||
// {
|
|
||||||
// "Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
||||||
// "ArgList":[], // Args list
|
|
||||||
// "ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
|
|
||||||
// "ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
// "ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
// }
|
|
||||||
///Подготовить конфигурацию
|
|
||||||
lArgListStr = $(".mGlobal-pyOpenRPA-Debugging-ArgList")[0].value
|
|
||||||
lArgDictStr = $(".mGlobal-pyOpenRPA-Debugging-ArgDict")[0].value
|
|
||||||
lArgGSettingsStr = $(".mGlobal-pyOpenRPA-Debugging-ArgGSettingsStr")[0].value
|
|
||||||
lArgLoggerStr = $(".mGlobal-pyOpenRPA-Debugging-ArgLoggerStr")[0].value
|
|
||||||
lActivityItem = {
|
|
||||||
"Def":$(".mGlobal-pyOpenRPA-Debugging-Def")[0].value, // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
||||||
"ArgList":(lArgListStr == "" ? [] : JSON.parse(lArgListStr)), // Args list
|
|
||||||
"ArgDict":(lArgDictStr == "" ? {} : JSON.parse(lArgDictStr)), // Args dictionary
|
|
||||||
"ArgGSettingsStr": (lArgGSettingsStr == "" ? null : lArgGSettingsStr), // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
"ArgLoggerStr": (lArgLoggerStr == "" ? null : lArgLoggerStr) // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
||||||
}
|
|
||||||
lData = [lActivityItem]
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: '/pyOpenRPA/ActivityListExecute',
|
|
||||||
data: JSON.stringify(lData),
|
|
||||||
success:
|
|
||||||
function(lData,l2,l3)
|
|
||||||
{
|
|
||||||
var lResponseJSON=JSON.parse(lData)
|
|
||||||
console.log(lResponseJSON)
|
|
||||||
$(".mGlobal-pyOpenRPA-Debugging-Output")[0].value = JSON.stringify(lResponseJSON[0])
|
|
||||||
},
|
|
||||||
dataType: "text"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mGlobal.pyOpenRPA.DebuggingAutofillMerge=function(inTargetDict, inLastDict) {
|
|
||||||
// Merge 2 dict (get values from Last dict if key exists in new dict
|
|
||||||
for (const [lKeyStr, lValue] of Object.entries(inTargetDict)) {
|
|
||||||
//Check if key exists in LastDict
|
|
||||||
if (lKeyStr in inLastDict) {
|
|
||||||
inTargetDict[lKeyStr] = inLastDict[lKeyStr]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inTargetDict
|
|
||||||
}
|
|
||||||
// 1.2.7 Debugging toolbox init
|
|
||||||
$('.ui.dropdown.mGlobal-pyOpenRPA-Debugging-Def-Dropdown')
|
|
||||||
.dropdown({
|
|
||||||
apiSettings: {
|
|
||||||
// this url parses query server side and returns filtered results
|
|
||||||
url: '/pyOpenRPA/Debugging/HelperDefList/{query}'
|
|
||||||
},
|
|
||||||
onChange: lDropdownOnChange
|
|
||||||
})
|
|
||||||
;
|
|
||||||
});
|
|
@ -1,499 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" >
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>pyOpenRPA Orchestrator</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="3rdParty/Semantic-UI-CSS-master/semantic.min.css">
|
|
||||||
<script
|
|
||||||
src="3rdParty/jQuery/jquery-3.1.1.min.js"
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<script src="3rdParty/Semantic-UI-CSS-master/semantic.min.js"></script>
|
|
||||||
<script src="3rdParty/Handlebars/handlebars-v4.1.2.js"></script>
|
|
||||||
<script src = "Index.js"></script>
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
}
|
|
||||||
.main.container {
|
|
||||||
margin-top: 2em;
|
|
||||||
}
|
|
||||||
.overlay {
|
|
||||||
float: left;
|
|
||||||
margin: 0em 3em 1em 0em;
|
|
||||||
}
|
|
||||||
.overlay .menu {
|
|
||||||
position: relative;
|
|
||||||
left: 0;
|
|
||||||
transition: left 0.5s ease;
|
|
||||||
}
|
|
||||||
.main.menu.fixed {
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
border: 1px solid #DDD;
|
|
||||||
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
.overlay.fixed .menu {
|
|
||||||
left: 800px;
|
|
||||||
}
|
|
||||||
.text.container .left.floated.image {
|
|
||||||
margin: 2em 2em 2em -4em;
|
|
||||||
}
|
|
||||||
.text.container .right.floated.image {
|
|
||||||
margin: 2em -4em 2em 2em;
|
|
||||||
}
|
|
||||||
.ui.footer.segment {
|
|
||||||
margin: 5em 0em 0em;
|
|
||||||
padding: 5em 0em;
|
|
||||||
}
|
|
||||||
.ui.search.dropdown>input.search {
|
|
||||||
width:100%;
|
|
||||||
font-family:monospace;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.ui.search.dropdown>.text {
|
|
||||||
width:100%;
|
|
||||||
font-family:monospace;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="ui internally celled grid">
|
|
||||||
<div class="row black">
|
|
||||||
<div class="sixteen wide column" style="display: flex;">
|
|
||||||
<img src="pyOpenRPA_logo.png" width="70px;" height="70px"></img>
|
|
||||||
|
|
||||||
<h1 class="ui header inverted" style="cursor: pointer" onclick="window.open('https://gitlab.com/UnicodeLabs/OpenRPA','_blank');">pyOpenRPA</h1>
|
|
||||||
<h5 style="cursor: pointer" onclick="window.open('https://www.facebook.com/RU.IT4Business','_blank');">by Ivan Maslov</h5>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1 class="ui header inverted">ORCHESTRATOR WEB GUI</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="sixteen wide column openrpa-control-panel-general UACClient-pyOpenRPADict-CPKeyDict" style="display:none;" >
|
|
||||||
<h4 class="ui horizontal divider header">
|
|
||||||
<i class="clipboard list icon"></i>
|
|
||||||
Dashboard (Robot control panel)
|
|
||||||
</h4>
|
|
||||||
<div class="openrpa-control-panel"></div>
|
|
||||||
<script class="openrpa-hidden-control-panel" style="display:none" type="text/x-handlebars-template">
|
|
||||||
<div class="ui cards">
|
|
||||||
{{#RenderRobotList}}
|
|
||||||
<div class="card">
|
|
||||||
<div class="content">
|
|
||||||
<div class="right floated mini ui ">
|
|
||||||
{{{HeaderRightText}}}
|
|
||||||
</div>
|
|
||||||
<div class="header">
|
|
||||||
{{{HeaderLeftText}}}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="meta">
|
|
||||||
{{{SubheaderText}}}
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
<ul style="padding-inline-start:16px;margin:0px">
|
|
||||||
{{#BodyKeyValueList}}
|
|
||||||
<li>{{{Key}}}: {{{Value}}}</li>
|
|
||||||
{{/BodyKeyValueList}}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="extra content">
|
|
||||||
{{{FooterText}}}
|
|
||||||
</div>
|
|
||||||
<div class="extra content">
|
|
||||||
<div class="ui two buttons">
|
|
||||||
{{#FooterButtonX2List}}
|
|
||||||
<div class="ui basic {{Color}} button" onclick="{{OnClick}}">{{{Text}}}</div>
|
|
||||||
{{/FooterButtonX2List}}
|
|
||||||
</div>
|
|
||||||
<div class="ui horizontal divider">Add. controls</div>
|
|
||||||
<div class="ui one buttons">
|
|
||||||
{{#FooterButtonX1List}}
|
|
||||||
<div class="ui basic {{Color}} button" onclick="{{OnClick}}">{{{Text}}}</div>
|
|
||||||
{{/FooterButtonX1List}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/RenderRobotList}}
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<script class="openrpa-hidden-monitor-table-general" style="display:none" type="text/x-handlebars-template">
|
|
||||||
<table class="ui celled table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Machine name</th>
|
|
||||||
<th>Machihe host</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Actions,length: {{childs.length}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#ListenURLList}}
|
|
||||||
<tr><td>{{Description}}</td><td>{{URL}}</td><td class="negative">None</td></tr>
|
|
||||||
{{/ListenURLList}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script class="openrpa-handlebar-template-table-filter" style="display:none" type="text/x-handlebars-template">
|
|
||||||
{{#if Title}}
|
|
||||||
<h1>{{{Title}}}</h1>
|
|
||||||
{{/if}}
|
|
||||||
{{#if FilterOnKeyUp}}
|
|
||||||
<div class="ui icon input search" style="width:500px;">
|
|
||||||
<input type="text" onkeyup="{{#if FilterOnKeyUp}}{{{FilterOnKeyUp}}}{{/if}}" placeholder="Search...">
|
|
||||||
<i class="inverted circular search link icon"></i>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
<table class="ui celled table selectable inverted">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{{#Columns}}
|
|
||||||
<th>{{{this}}}</th>
|
|
||||||
{{/Columns}}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#Rows}}
|
|
||||||
<tr>
|
|
||||||
{{#this}}
|
|
||||||
<td>
|
|
||||||
{{{this}}}
|
|
||||||
</td>
|
|
||||||
{{/this}}
|
|
||||||
</tr>
|
|
||||||
{{/Rows}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</script>
|
|
||||||
<script class="openrpa-handlebar-template-list-filter" style="display:none" type="text/x-handlebars-template">
|
|
||||||
{{#if Title}}
|
|
||||||
<h1>{{{Title}}}</h1>
|
|
||||||
{{/if}}
|
|
||||||
{{#if FilterOnKeyUp}}
|
|
||||||
<div class="ui icon input search" style="width:500px;">
|
|
||||||
<input type="text" onkeyup="{{#if FilterOnKeyUp}}{{{FilterOnKeyUp}}}{{/if}}" placeholder="Search...">
|
|
||||||
<i class="inverted circular search link icon"></i>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
<div class="ui inverted segment">
|
|
||||||
<div class="ui inverted relaxed divided list">
|
|
||||||
{{#List}}
|
|
||||||
<div class="item">
|
|
||||||
<i class="map marker icon"></i>
|
|
||||||
<div class="content">
|
|
||||||
<a class="header">{{{Header}}}</a>
|
|
||||||
<div class="description">{{{Description}}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/List}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
<div class="eight wide column openrpa-robotrdpactive-control-panel-general UACClient-pyOpenRPADict-RDPKeyDict" style="display:none;">
|
|
||||||
<h2 class="ui header openrpa-rdpactive-title">
|
|
||||||
<i class="desktop icon"></i>
|
|
||||||
<div class="content">
|
|
||||||
RDP active list
|
|
||||||
</div>
|
|
||||||
</h2>
|
|
||||||
<div class="openrpa-robotrdpactive-control-panel"></div>
|
|
||||||
<script class="openrpa-hidden-robotrdpactive-control-panel" style="display:none" type="text/x-handlebars-template">
|
|
||||||
<div class="ui inverted segment" style="background: #368279">
|
|
||||||
<div class="ui inverted relaxed divided list">
|
|
||||||
{{#HandlebarsList}}
|
|
||||||
<div class="item">
|
|
||||||
<div class="right floated content">
|
|
||||||
<div class="ui button" onclick="mGlobal.Processor.ServerValueAppend(['RobotRDPActive','ActivityList'],{'DefNameStr': 'RDPSessionReconnect', 'ArgList': [], 'ArgDict': {'inRDPSessionKeyStr': '{{{SessionKeyStr}}}'} })" >Reconnect</div>
|
|
||||||
</div>
|
|
||||||
<div class="right floated content">
|
|
||||||
{{#if IsIgnoredBool}}
|
|
||||||
<div class="ui button red" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','RDPList','{{{SessionKeyStr}}}','SessionIsIgnoredBool'],false);">Ignore</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="ui button" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','RDPList','{{{SessionKeyStr}}}','SessionIsIgnoredBool'],true);">Ignore</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="right floated content">
|
|
||||||
{{#if IsFullScreenBool}}
|
|
||||||
<div class="ui button green" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','FullScreenRDPSessionKeyStr'],null);">Full screen</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="ui button" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','FullScreenRDPSessionKeyStr'],'{{{SessionKeyStr}}}');">Full screen</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="header">Session key: {{{SessionKeyStr}}}</div>
|
|
||||||
{{{SessionHexStr}}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/HandlebarsList}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
<div class="eight wide column UACClient-pyOpenRPADict-AgentKeyDict" style="display:none">
|
|
||||||
<h2 class="ui header " style="">
|
|
||||||
<i class="bug icon"></i>
|
|
||||||
<div class="content">
|
|
||||||
Agent active list
|
|
||||||
</div>
|
|
||||||
</h2>
|
|
||||||
<div class="pyOpenRPA-Agent-List"></div>
|
|
||||||
<script class="pyOpenRPA-Agent-ListTemplate" style="display:none" type="text/x-handlebars-template">
|
|
||||||
<div class="ui inverted segment" style="background: #368279">
|
|
||||||
<div class="ui inverted relaxed divided list">
|
|
||||||
{{#HandlebarsList}}
|
|
||||||
<div class="item">
|
|
||||||
<div class="right floated content">
|
|
||||||
{{#if IsListenBool}}
|
|
||||||
<i class="circle icon green"></i>
|
|
||||||
Online
|
|
||||||
{{else}}
|
|
||||||
<i class="circle icon red"></i>
|
|
||||||
Offline
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="header">Hostname: {{{HostnameUpperStr}}}, User: {{{UserUpperStr}}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/HandlebarsList}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="row openrpa-monitor">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="sixteen wide column" style="">
|
|
||||||
<h2 class="ui header">
|
|
||||||
<i class="settings icon"></i>
|
|
||||||
<div class="content">
|
|
||||||
Administration
|
|
||||||
</div>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="sixteen wide column" style="">
|
|
||||||
<h4 class="ui horizontal divider header" >
|
|
||||||
<i class="clipboard list icon"></i>
|
|
||||||
Logs
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<textarea class="mGlobal-pyOpenRPA-ServerLogList UACClient-pyOpenRPADict-AdminDict-LogViewerBool" readonly="readonly" style="width:100%; display:none; resize: none; font-family:monospace; font-weight: bold;" id="textarea_id" rows="20">
|
|
||||||
|
|
||||||
</textarea>
|
|
||||||
<a class="mGlobal-pyOpenRPA-ServerLogListDoRender" onclick="" style="cursor: pointer;">Freeze textarea</a>
|
|
||||||
<div class="ui fluid action input UACClient-pyOpenRPADict-AdminDict-CMDInputBool" style="display:none;">
|
|
||||||
<input class="openrpa-controller-cmd-run-input" type="text" placeholder="CMD Code...">
|
|
||||||
<div class="ui button" onclick="mGlobal.Controller.CMDRun();">Run!</div>
|
|
||||||
<div class="ui button" onclick="mGlobal.Controller.CMDRunGUILogout();">GUI Logout</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row UACClient-pyOpenRPADict-AdminDict-Debugging" style= "display:none;">
|
|
||||||
<div class="twelve wide column">
|
|
||||||
<h4 class="ui horizontal divider header" >
|
|
||||||
<i class="bug icon"></i>
|
|
||||||
Debugging - Send
|
|
||||||
</h4>
|
|
||||||
<div class="ui labeled input">
|
|
||||||
<div class="ui label">Def</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui fluid search selection dropdown mGlobal-pyOpenRPA-Debugging-Def-Dropdown" style="margin-bottom:10px;">
|
|
||||||
<input class="mGlobal-pyOpenRPA-Debugging-Def" type="hidden" name="country" style="width:100%; font-family:monospace; font-weight: bold;">
|
|
||||||
<i class="dropdown icon"></i>
|
|
||||||
<div class="default text">Def</div>
|
|
||||||
<div class="menu">
|
|
||||||
<div class="item" data-value="eh">pyOpenRPA... sys.. os.. </div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui fluid labeled input" style="margin-bottom:10px;">
|
|
||||||
<div class="ui label">
|
|
||||||
ArgList
|
|
||||||
</div>
|
|
||||||
<input type="text" placeholder="[1,2,3]" class="mGlobal-pyOpenRPA-Debugging-ArgList" style="width:100%; font-family:monospace; font-weight: bold;">
|
|
||||||
</div>
|
|
||||||
<div class="ui fluid labeled input" style="margin-bottom:10px;">
|
|
||||||
<div class="ui label">
|
|
||||||
ArgDict
|
|
||||||
</div>
|
|
||||||
<input type="text" placeholder="{"Key1":"Value1"}" class="mGlobal-pyOpenRPA-Debugging-ArgDict" style="width:100%; font-family:monospace; font-weight: bold;">
|
|
||||||
</div>
|
|
||||||
<div class="ui fluid labeled input" style="margin-bottom:10px;">
|
|
||||||
<div class="ui label">
|
|
||||||
ArgGSettingsStr
|
|
||||||
</div>
|
|
||||||
<input type="text" placeholder="inGSettings" class="mGlobal-pyOpenRPA-Debugging-ArgGSettingsStr" style="width:100%; font-family:monospace; font-weight: bold;">
|
|
||||||
</div>
|
|
||||||
<div class="ui fluid labeled input" style="margin-bottom:10px;">
|
|
||||||
<div class="ui label">
|
|
||||||
ArgLoggerStr
|
|
||||||
</div>
|
|
||||||
<input type="text" placeholder="inLogger" class="mGlobal-pyOpenRPA-Debugging-ArgLoggerStr" style="width:100%; font-family:monospace; font-weight: bold;">
|
|
||||||
</div>
|
|
||||||
<div class="ui fluid button" onclick="mGlobal.pyOpenRPA.DebuggingExecute();">Execute</div>
|
|
||||||
</div>
|
|
||||||
<div class="four wide column">
|
|
||||||
<h4 class="ui horizontal divider header" >
|
|
||||||
<i class="bug icon"></i>
|
|
||||||
Debugging - Output
|
|
||||||
</h4>
|
|
||||||
<p><textarea class="mGlobal-pyOpenRPA-Debugging-Output" readonly="readonly" style="width:100%; font-family:monospace; font-weight: bold;" rows="16" cols="60"></textarea></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h4 class="ui horizontal divider header">
|
|
||||||
<i class="clipboard list icon"></i>
|
|
||||||
Controls
|
|
||||||
</h4>
|
|
||||||
<div class="four ui buttons">
|
|
||||||
<div class="ui animated button openrpa-control-lookmachinescreenshot green UACClient-pyOpenRPADict-AdminDict-ScreenshotViewerBool" onclick="mGlobal.Monitor.ScreenshotModal.Show();" style="display: none; margin-top: 5px;">
|
|
||||||
<div class="visible content">Show live screenshots</div>
|
|
||||||
<div class="hidden content">
|
|
||||||
<i class="right arrow icon"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui animated button openrpa-control-restartorchestrator orange UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorBool" onclick="mGlobal.Controller.OrchestratorRestart();" style="display: none; margin-top: 5px;">
|
|
||||||
<div class="visible content">Restart orchestrator</div>
|
|
||||||
<div class="hidden content">
|
|
||||||
<i class="right arrow icon"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui animated button openrpa-control-gitrestartorchestrator teal UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorGITPullBool" onclick="mGlobal.Controller.OrchestratorGITPullRestart();" style="display: none; margin-top: 5px;">
|
|
||||||
<div class="visible content">Git pull, restart orchestrator</div>
|
|
||||||
<div class="hidden content">
|
|
||||||
<i class="right arrow icon"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui animated button openrpa-control-restartpc red UACClient-pyOpenRPADict-AdminDict-RestartPCBool" onclick="mGlobal.Controller.PCRestart();" style="display: none; margin-top: 5px;">
|
|
||||||
<div class="visible content">Restart PC</div>
|
|
||||||
<div class="hidden content">
|
|
||||||
<i class="right arrow icon"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row black">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui inverted vertical footer segment">
|
|
||||||
<div class="ui center aligned container">
|
|
||||||
<div class="ui stackable inverted divided grid">
|
|
||||||
<div class="three wide column">
|
|
||||||
<h4 class="ui inverted header">Pywinauto</h4>
|
|
||||||
<div class="ui inverted link list">
|
|
||||||
<a href="https://pywinauto.readthedocs.io/en/latest/code/pywinauto.findwindows.html" class="item" target="_blank">Specification manual</a>
|
|
||||||
<a href="https://pywinauto.readthedocs.io/en/latest/code/code.html#main-user-modules" class="item" target="_blank">Classes manual</a>
|
|
||||||
<a href="https://pywinauto.readthedocs.io/en/latest/code/code.html#main-user-modules" class="item" target="_blank">How to use manual</a>
|
|
||||||
<a href="https://pywinauto.readthedocs.io/en/latest/code/pywinauto.base_wrapper.html" class="item" target="_blank">Base wrapper manual</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="three wide column">
|
|
||||||
<h4 class="ui inverted header">Semantic UI</h4>
|
|
||||||
<div class="ui inverted link list">
|
|
||||||
<a href="https://semantic-ui.com/usage/theming.html" class="item" target="_blank">Color manual</a>
|
|
||||||
<a href="https://semantic-ui.com/elements/input.html" class="item" target="_blank">Input manual</a>
|
|
||||||
<a href="https://semantic-ui.com/elements/icon.html" class="item" target="_blank">Icon list</a>
|
|
||||||
<a href="https://semantic-ui.com/elements/button.html" class="item" target="_blank">Button manual</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="three wide column">
|
|
||||||
<h4 class="ui inverted header">GitLab</h4>
|
|
||||||
<div class="ui inverted link list">
|
|
||||||
<a href="https://gitlab.com/UnicodeLabs/OpenRPA" class="item" target="_blank">pyOpenRPA repository</a>
|
|
||||||
<a href="https://www.facebook.com/RU.IT4Business" class="item" target="_blank">Ivan Maslov</a>
|
|
||||||
<a href="#" class="item">Link -</a>
|
|
||||||
<a href="#" class="item">Link -</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="seven wide column">
|
|
||||||
<h4 class="ui inverted header">pyOpenRPA</h4>
|
|
||||||
<p>Open source Robotic Process Automation software by the pyOpenRPA LLC (Created by Ivan Maslov in Russia). Licensed under LICENSE.PDF or https://pyopenrpa.ru/license/oferta.pdf #IT4Business</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui inverted section divider"></div>
|
|
||||||
<div class="ui horizontal inverted small divided link list">
|
|
||||||
<a class="item" href="#">Site Map</a>
|
|
||||||
<a class="item" href="#">Contact Us</a>
|
|
||||||
<a class="item" href="#">Terms and Conditions</a>
|
|
||||||
<a class="item" href="#">Privacy Policy</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui basic modal">
|
|
||||||
<div class="ui icon header">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<p>Here is the message text!</p>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="ui red basic cancel inverted button">
|
|
||||||
<i class="remove icon"></i>
|
|
||||||
No
|
|
||||||
</div>
|
|
||||||
<div class="ui green ok inverted button">
|
|
||||||
<i class="checkmark icon"></i>
|
|
||||||
Yes
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ui daemon-screenshot modal">
|
|
||||||
<div class="ui icon header">
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<img src="GetScreenshot" class="ui fluid image">
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="ui green ok inverted button" onclick="mGlobal.Monitor.ScreenshotModal.Close()">
|
|
||||||
<i class="checkmark icon"></i>
|
|
||||||
Close
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ui modal openrpa-code-list-gui-import-modal">
|
|
||||||
<i class="close icon"></i>
|
|
||||||
<div class="header">
|
|
||||||
Code list import
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="description">
|
|
||||||
<div class="ui header">Insert your JSON specification here.</div>
|
|
||||||
<p><textarea style="width:100%" rows="6" cols="60"></textarea></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="ui black deny button">
|
|
||||||
Cancel
|
|
||||||
</div>
|
|
||||||
<div class="ui positive right labeled icon button" onclick="mGlobal.CodeList.fActionSpecificationImportFromJSON();">
|
|
||||||
Parse
|
|
||||||
<i class="checkmark icon"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
Before Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 110 KiB |
@ -0,0 +1,11 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
def CheckFile(inDstPathStr, inTmplPathStr):
|
||||||
|
if os.path.exists(inDstPathStr) == False:
|
||||||
|
shutil.copy(inTmplPathStr, inDstPathStr)
|
||||||
|
|
||||||
|
def CheckFolder(inDstPathStr):
|
||||||
|
# проверка наличия всех файлов/каталогов
|
||||||
|
if not os.path.exists(os.path.abspath(inDstPathStr)):
|
||||||
|
os.mkdir(inDstPathStr)
|
@ -0,0 +1,17 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
def RequestPrettyPrint(inRequest):
|
||||||
|
"""
|
||||||
|
At this point it is completely built and ready
|
||||||
|
to be fired; it is "prepared".
|
||||||
|
However pay attention at the formatting used in
|
||||||
|
this function because it is programmed to be pretty
|
||||||
|
printed and may differ from the actual request.
|
||||||
|
"""
|
||||||
|
prepared = inRequest.prepare()
|
||||||
|
print('{}\n{}\r\n{}\r\n\r\n{}'.format(
|
||||||
|
'-----------START-----------',
|
||||||
|
prepared.method + ' ' + prepared.url,
|
||||||
|
'\r\n'.join('{}: {}'.format(k, v) for k, v in prepared.headers.items()),
|
||||||
|
prepared.body,
|
||||||
|
))
|
@ -0,0 +1,7 @@
|
|||||||
|
import difflib
|
||||||
|
|
||||||
|
def SimilarityNoCase(in1Str, in2Str):
|
||||||
|
normalized1 = in1Str.lower()
|
||||||
|
normalized2 = in2Str.lower()
|
||||||
|
matcher = difflib.SequenceMatcher(None, normalized1, normalized2)
|
||||||
|
return matcher.ratio()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 Alex Grönholm
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -0,0 +1,82 @@
|
|||||||
|
anyio-3.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
anyio-3.6.1.dist-info/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081
|
||||||
|
anyio-3.6.1.dist-info/METADATA,sha256=cXu3CLppFqT_rBl4Eo2HOb0J1zm6Ltu_tMFuzjuQnew,4654
|
||||||
|
anyio-3.6.1.dist-info/RECORD,,
|
||||||
|
anyio-3.6.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
||||||
|
anyio-3.6.1.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39
|
||||||
|
anyio-3.6.1.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6
|
||||||
|
anyio/__init__.py,sha256=M2R8dk6L5gL5lXHArzpSfEn2oH5jMyUKhzyrkRiv2AM,4037
|
||||||
|
anyio/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
anyio/__pycache__/from_thread.cpython-37.pyc,,
|
||||||
|
anyio/__pycache__/lowlevel.cpython-37.pyc,,
|
||||||
|
anyio/__pycache__/pytest_plugin.cpython-37.pyc,,
|
||||||
|
anyio/__pycache__/to_process.cpython-37.pyc,,
|
||||||
|
anyio/__pycache__/to_thread.cpython-37.pyc,,
|
||||||
|
anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
anyio/_backends/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
anyio/_backends/__pycache__/_asyncio.cpython-37.pyc,,
|
||||||
|
anyio/_backends/__pycache__/_trio.cpython-37.pyc,,
|
||||||
|
anyio/_backends/_asyncio.py,sha256=ZJDvRwfS4wv9WWcqWledNJyl8hx8A8-m9-gSKAJ6nBM,69238
|
||||||
|
anyio/_backends/_trio.py,sha256=CebCaqr8Szi6uCnUzwtBRLfUitR5OnDT_wfH-KiqvBQ,29696
|
||||||
|
anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
anyio/_core/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_compat.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_eventloop.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_exceptions.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_fileio.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_resources.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_signals.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_sockets.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_streams.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_subprocesses.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_synchronization.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_tasks.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_testing.cpython-37.pyc,,
|
||||||
|
anyio/_core/__pycache__/_typedattr.cpython-37.pyc,,
|
||||||
|
anyio/_core/_compat.py,sha256=X99W70r-O-JLdkKNtbddcIY5H2Nyg3Nk34oUYE9WZRs,5790
|
||||||
|
anyio/_core/_eventloop.py,sha256=DRn_hy679LtsJFsPX7dXjDv72bLtSFkTnWY9WVVfgCQ,4108
|
||||||
|
anyio/_core/_exceptions.py,sha256=1wqraNldZroYkoyB0HZStAruz_7yDCBaW-4zYwsKj8s,2904
|
||||||
|
anyio/_core/_fileio.py,sha256=au82uZXZX4fia8EoZq_E-JDwZFKe6ZtI0J6IkxK8FmQ,18298
|
||||||
|
anyio/_core/_resources.py,sha256=M_uN-90N8eSsWuvo-0xluWU_OG2BTyccAgsQ7XtHxzs,399
|
||||||
|
anyio/_core/_signals.py,sha256=D4btJN527tAADspKBeNKaCds-ZcEZJP8LWM_MjVuQRA,827
|
||||||
|
anyio/_core/_sockets.py,sha256=fW_Cbg6kfw4xgYuVuWbcWrAYspOcDSEjwxVATMzf2fo,19820
|
||||||
|
anyio/_core/_streams.py,sha256=gjT5xChJ1OoV8nNinljSv1yW4nqUS-QzZzIydQz3exQ,1494
|
||||||
|
anyio/_core/_subprocesses.py,sha256=pcchMI2OII0QSjiVxRiTEz4M0B7TlQPzGurfCuka-xc,5049
|
||||||
|
anyio/_core/_synchronization.py,sha256=xOOG4hF9783N6E2IcD3YKiukguA5bPrj6BodDsKNaJY,16822
|
||||||
|
anyio/_core/_tasks.py,sha256=ebGLjHvwL6I9aGyPwvCig1drebSVYFzvY3pnN3TsB4o,5273
|
||||||
|
anyio/_core/_testing.py,sha256=VZka_yebIhJ6mJ6Vo_ilO3Nbz53ieqg0WBijwciMwdY,2196
|
||||||
|
anyio/_core/_typedattr.py,sha256=k5-wBvMlDlKHIpn18INVnXAlGwI3CrAvPmWoceHjnOQ,2534
|
||||||
|
anyio/abc/__init__.py,sha256=hMa47CMs5O1twC2bBcSbzwX-3Q08BAgAPTRekQobb3E,2123
|
||||||
|
anyio/abc/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
anyio/abc/__pycache__/_resources.cpython-37.pyc,,
|
||||||
|
anyio/abc/__pycache__/_sockets.cpython-37.pyc,,
|
||||||
|
anyio/abc/__pycache__/_streams.cpython-37.pyc,,
|
||||||
|
anyio/abc/__pycache__/_subprocesses.cpython-37.pyc,,
|
||||||
|
anyio/abc/__pycache__/_tasks.cpython-37.pyc,,
|
||||||
|
anyio/abc/__pycache__/_testing.cpython-37.pyc,,
|
||||||
|
anyio/abc/_resources.py,sha256=js737mWPG6IW0fH8W4Tz9eNWLztse7dKxEC61z934Vk,752
|
||||||
|
anyio/abc/_sockets.py,sha256=i1VdcJTLAuRlYeZoL6s5RBSWbX62Cu6ln5YZBL2YrWk,5754
|
||||||
|
anyio/abc/_streams.py,sha256=0g70fhKAzbnK0KKmWwRgwmKdApBwduAcVj4TpjSzjzU,6501
|
||||||
|
anyio/abc/_subprocesses.py,sha256=iREP_YQ91it88lDU4XIcI3HZ9HUvV5UmjQk_sSPonrw,2071
|
||||||
|
anyio/abc/_tasks.py,sha256=mQQd1DANqpySKyehVVPdMfi_UEG49zZUJpt5blunOjg,3119
|
||||||
|
anyio/abc/_testing.py,sha256=ifKCUPzcQdHAEGO-weu2GQvzjMQPPIWO24mQ0z6zkdU,1928
|
||||||
|
anyio/from_thread.py,sha256=nSq6mafYMqwxKmzdJyISg8cp-AyBj9rxZPMt_b7klSM,16497
|
||||||
|
anyio/lowlevel.py,sha256=W4ydshns7f86YuSESFc2igTf46AWMXnGPQGsY_Esl2E,4679
|
||||||
|
anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
anyio/pytest_plugin.py,sha256=kWj2B8BJehePJd1sztRBmJBRh8O4hk1oGSYQRlX5Gr8,5134
|
||||||
|
anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
anyio/streams/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
anyio/streams/__pycache__/buffered.cpython-37.pyc,,
|
||||||
|
anyio/streams/__pycache__/file.cpython-37.pyc,,
|
||||||
|
anyio/streams/__pycache__/memory.cpython-37.pyc,,
|
||||||
|
anyio/streams/__pycache__/stapled.cpython-37.pyc,,
|
||||||
|
anyio/streams/__pycache__/text.cpython-37.pyc,,
|
||||||
|
anyio/streams/__pycache__/tls.cpython-37.pyc,,
|
||||||
|
anyio/streams/buffered.py,sha256=FegOSO4Xcxa5SaDfU1A3ZkTTxaPrv6G435Y_giZ8k44,4437
|
||||||
|
anyio/streams/file.py,sha256=pujJ-m6BX-gOLnVoZwkE5kh-YDs5Vx9eJFVkvliQ0S4,4353
|
||||||
|
anyio/streams/memory.py,sha256=3RGeZoevoGIgBWfD2_X1cqxIPOz-BqQkRf6lUcOnBYc,9209
|
||||||
|
anyio/streams/stapled.py,sha256=0E0V15v8M5GVelpHe5RT0S33tQ9hGe4ZCXo_KJEjtt4,4258
|
||||||
|
anyio/streams/text.py,sha256=WRFyjsRpBjQKdCmR4ZuzYTEAJqGx2s5oTJmGI1C6Ng0,5014
|
||||||
|
anyio/streams/tls.py,sha256=-WXGsMV14XHXAxc38WpBvGusjuY7e449g4UCEHIlnWw,12040
|
||||||
|
anyio/to_process.py,sha256=hu0ES3HJC-VEjcdPJMzAzjyTaekaCNToO3coj3jvnus,9247
|
||||||
|
anyio/to_thread.py,sha256=VeMQoo8Va2zz0WFk2p123QikDpqk2wYZGw20COC3wqw,2124
|
@ -0,0 +1,5 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: bdist_wheel (0.37.1)
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
@ -0,0 +1,2 @@
|
|||||||
|
[pytest11]
|
||||||
|
anyio = anyio.pytest_plugin
|
@ -0,0 +1 @@
|
|||||||
|
anyio
|
@ -0,0 +1,167 @@
|
|||||||
|
__all__ = (
|
||||||
|
"maybe_async",
|
||||||
|
"maybe_async_cm",
|
||||||
|
"run",
|
||||||
|
"sleep",
|
||||||
|
"sleep_forever",
|
||||||
|
"sleep_until",
|
||||||
|
"current_time",
|
||||||
|
"get_all_backends",
|
||||||
|
"get_cancelled_exc_class",
|
||||||
|
"BrokenResourceError",
|
||||||
|
"BrokenWorkerProcess",
|
||||||
|
"BusyResourceError",
|
||||||
|
"ClosedResourceError",
|
||||||
|
"DelimiterNotFound",
|
||||||
|
"EndOfStream",
|
||||||
|
"ExceptionGroup",
|
||||||
|
"IncompleteRead",
|
||||||
|
"TypedAttributeLookupError",
|
||||||
|
"WouldBlock",
|
||||||
|
"AsyncFile",
|
||||||
|
"Path",
|
||||||
|
"open_file",
|
||||||
|
"wrap_file",
|
||||||
|
"aclose_forcefully",
|
||||||
|
"open_signal_receiver",
|
||||||
|
"connect_tcp",
|
||||||
|
"connect_unix",
|
||||||
|
"create_tcp_listener",
|
||||||
|
"create_unix_listener",
|
||||||
|
"create_udp_socket",
|
||||||
|
"create_connected_udp_socket",
|
||||||
|
"getaddrinfo",
|
||||||
|
"getnameinfo",
|
||||||
|
"wait_socket_readable",
|
||||||
|
"wait_socket_writable",
|
||||||
|
"create_memory_object_stream",
|
||||||
|
"run_process",
|
||||||
|
"open_process",
|
||||||
|
"create_lock",
|
||||||
|
"CapacityLimiter",
|
||||||
|
"CapacityLimiterStatistics",
|
||||||
|
"Condition",
|
||||||
|
"ConditionStatistics",
|
||||||
|
"Event",
|
||||||
|
"EventStatistics",
|
||||||
|
"Lock",
|
||||||
|
"LockStatistics",
|
||||||
|
"Semaphore",
|
||||||
|
"SemaphoreStatistics",
|
||||||
|
"create_condition",
|
||||||
|
"create_event",
|
||||||
|
"create_semaphore",
|
||||||
|
"create_capacity_limiter",
|
||||||
|
"open_cancel_scope",
|
||||||
|
"fail_after",
|
||||||
|
"move_on_after",
|
||||||
|
"current_effective_deadline",
|
||||||
|
"TASK_STATUS_IGNORED",
|
||||||
|
"CancelScope",
|
||||||
|
"create_task_group",
|
||||||
|
"TaskInfo",
|
||||||
|
"get_current_task",
|
||||||
|
"get_running_tasks",
|
||||||
|
"wait_all_tasks_blocked",
|
||||||
|
"run_sync_in_worker_thread",
|
||||||
|
"run_async_from_thread",
|
||||||
|
"run_sync_from_thread",
|
||||||
|
"current_default_worker_thread_limiter",
|
||||||
|
"create_blocking_portal",
|
||||||
|
"start_blocking_portal",
|
||||||
|
"typed_attribute",
|
||||||
|
"TypedAttributeSet",
|
||||||
|
"TypedAttributeProvider",
|
||||||
|
)
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ._core._compat import maybe_async, maybe_async_cm
|
||||||
|
from ._core._eventloop import (
|
||||||
|
current_time,
|
||||||
|
get_all_backends,
|
||||||
|
get_cancelled_exc_class,
|
||||||
|
run,
|
||||||
|
sleep,
|
||||||
|
sleep_forever,
|
||||||
|
sleep_until,
|
||||||
|
)
|
||||||
|
from ._core._exceptions import (
|
||||||
|
BrokenResourceError,
|
||||||
|
BrokenWorkerProcess,
|
||||||
|
BusyResourceError,
|
||||||
|
ClosedResourceError,
|
||||||
|
DelimiterNotFound,
|
||||||
|
EndOfStream,
|
||||||
|
ExceptionGroup,
|
||||||
|
IncompleteRead,
|
||||||
|
TypedAttributeLookupError,
|
||||||
|
WouldBlock,
|
||||||
|
)
|
||||||
|
from ._core._fileio import AsyncFile, Path, open_file, wrap_file
|
||||||
|
from ._core._resources import aclose_forcefully
|
||||||
|
from ._core._signals import open_signal_receiver
|
||||||
|
from ._core._sockets import (
|
||||||
|
connect_tcp,
|
||||||
|
connect_unix,
|
||||||
|
create_connected_udp_socket,
|
||||||
|
create_tcp_listener,
|
||||||
|
create_udp_socket,
|
||||||
|
create_unix_listener,
|
||||||
|
getaddrinfo,
|
||||||
|
getnameinfo,
|
||||||
|
wait_socket_readable,
|
||||||
|
wait_socket_writable,
|
||||||
|
)
|
||||||
|
from ._core._streams import create_memory_object_stream
|
||||||
|
from ._core._subprocesses import open_process, run_process
|
||||||
|
from ._core._synchronization import (
|
||||||
|
CapacityLimiter,
|
||||||
|
CapacityLimiterStatistics,
|
||||||
|
Condition,
|
||||||
|
ConditionStatistics,
|
||||||
|
Event,
|
||||||
|
EventStatistics,
|
||||||
|
Lock,
|
||||||
|
LockStatistics,
|
||||||
|
Semaphore,
|
||||||
|
SemaphoreStatistics,
|
||||||
|
create_capacity_limiter,
|
||||||
|
create_condition,
|
||||||
|
create_event,
|
||||||
|
create_lock,
|
||||||
|
create_semaphore,
|
||||||
|
)
|
||||||
|
from ._core._tasks import (
|
||||||
|
TASK_STATUS_IGNORED,
|
||||||
|
CancelScope,
|
||||||
|
create_task_group,
|
||||||
|
current_effective_deadline,
|
||||||
|
fail_after,
|
||||||
|
move_on_after,
|
||||||
|
open_cancel_scope,
|
||||||
|
)
|
||||||
|
from ._core._testing import (
|
||||||
|
TaskInfo,
|
||||||
|
get_current_task,
|
||||||
|
get_running_tasks,
|
||||||
|
wait_all_tasks_blocked,
|
||||||
|
)
|
||||||
|
from ._core._typedattr import TypedAttributeProvider, TypedAttributeSet, typed_attribute
|
||||||
|
|
||||||
|
# Re-exported here, for backwards compatibility
|
||||||
|
# isort: off
|
||||||
|
from .to_thread import current_default_worker_thread_limiter, run_sync_in_worker_thread
|
||||||
|
from .from_thread import (
|
||||||
|
create_blocking_portal,
|
||||||
|
run_async_from_thread,
|
||||||
|
run_sync_from_thread,
|
||||||
|
start_blocking_portal,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Re-export imports so they look like they live directly in this package
|
||||||
|
key: str
|
||||||
|
value: Any
|
||||||
|
for key, value in list(locals().items()):
|
||||||
|
if getattr(value, "__module__", "").startswith("anyio."):
|
||||||
|
value.__module__ = __name__
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,988 @@
|
|||||||
|
import array
|
||||||
|
import math
|
||||||
|
import socket
|
||||||
|
from concurrent.futures import Future
|
||||||
|
from contextvars import copy_context
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from functools import partial
|
||||||
|
from io import IOBase
|
||||||
|
from os import PathLike
|
||||||
|
from signal import Signals
|
||||||
|
from types import TracebackType
|
||||||
|
from typing import (
|
||||||
|
IO,
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
AsyncGenerator,
|
||||||
|
Awaitable,
|
||||||
|
Callable,
|
||||||
|
Collection,
|
||||||
|
ContextManager,
|
||||||
|
Coroutine,
|
||||||
|
Deque,
|
||||||
|
Dict,
|
||||||
|
Generic,
|
||||||
|
Iterable,
|
||||||
|
List,
|
||||||
|
Mapping,
|
||||||
|
NoReturn,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
Set,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
cast,
|
||||||
|
)
|
||||||
|
|
||||||
|
import sniffio
|
||||||
|
import trio.from_thread
|
||||||
|
from outcome import Error, Outcome, Value
|
||||||
|
from trio.socket import SocketType as TrioSocketType
|
||||||
|
from trio.to_thread import run_sync
|
||||||
|
|
||||||
|
from .. import CapacityLimiterStatistics, EventStatistics, TaskInfo, abc
|
||||||
|
from .._core._compat import DeprecatedAsyncContextManager, DeprecatedAwaitable, T
|
||||||
|
from .._core._eventloop import claim_worker_thread
|
||||||
|
from .._core._exceptions import (
|
||||||
|
BrokenResourceError,
|
||||||
|
BusyResourceError,
|
||||||
|
ClosedResourceError,
|
||||||
|
EndOfStream,
|
||||||
|
)
|
||||||
|
from .._core._exceptions import ExceptionGroup as BaseExceptionGroup
|
||||||
|
from .._core._sockets import convert_ipv6_sockaddr
|
||||||
|
from .._core._synchronization import CapacityLimiter as BaseCapacityLimiter
|
||||||
|
from .._core._synchronization import Event as BaseEvent
|
||||||
|
from .._core._synchronization import ResourceGuard
|
||||||
|
from .._core._tasks import CancelScope as BaseCancelScope
|
||||||
|
from ..abc import IPSockAddrType, UDPPacketType
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from trio_typing import TaskStatus
|
||||||
|
|
||||||
|
try:
|
||||||
|
from trio import lowlevel as trio_lowlevel
|
||||||
|
except ImportError:
|
||||||
|
from trio import hazmat as trio_lowlevel # type: ignore[no-redef]
|
||||||
|
from trio.hazmat import wait_readable, wait_writable
|
||||||
|
else:
|
||||||
|
from trio.lowlevel import wait_readable, wait_writable
|
||||||
|
|
||||||
|
try:
|
||||||
|
trio_open_process = trio_lowlevel.open_process # type: ignore[attr-defined]
|
||||||
|
except AttributeError:
|
||||||
|
from trio import open_process as trio_open_process
|
||||||
|
|
||||||
|
T_Retval = TypeVar("T_Retval")
|
||||||
|
T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Event loop
|
||||||
|
#
|
||||||
|
|
||||||
|
run = trio.run
|
||||||
|
current_token = trio.lowlevel.current_trio_token
|
||||||
|
RunVar = trio.lowlevel.RunVar
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Miscellaneous
|
||||||
|
#
|
||||||
|
|
||||||
|
sleep = trio.sleep
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Timeouts and cancellation
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class CancelScope(BaseCancelScope):
|
||||||
|
def __new__(
|
||||||
|
cls, original: Optional[trio.CancelScope] = None, **kwargs: object
|
||||||
|
) -> "CancelScope":
|
||||||
|
return object.__new__(cls)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, original: Optional[trio.CancelScope] = None, **kwargs: Any
|
||||||
|
) -> None:
|
||||||
|
self.__original = original or trio.CancelScope(**kwargs)
|
||||||
|
|
||||||
|
def __enter__(self) -> "CancelScope":
|
||||||
|
self.__original.__enter__()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[Type[BaseException]],
|
||||||
|
exc_val: Optional[BaseException],
|
||||||
|
exc_tb: Optional[TracebackType],
|
||||||
|
) -> Optional[bool]:
|
||||||
|
return self.__original.__exit__(exc_type, exc_val, exc_tb)
|
||||||
|
|
||||||
|
def cancel(self) -> DeprecatedAwaitable:
|
||||||
|
self.__original.cancel()
|
||||||
|
return DeprecatedAwaitable(self.cancel)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def deadline(self) -> float:
|
||||||
|
return self.__original.deadline
|
||||||
|
|
||||||
|
@deadline.setter
|
||||||
|
def deadline(self, value: float) -> None:
|
||||||
|
self.__original.deadline = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cancel_called(self) -> bool:
|
||||||
|
return self.__original.cancel_called
|
||||||
|
|
||||||
|
@property
|
||||||
|
def shield(self) -> bool:
|
||||||
|
return self.__original.shield
|
||||||
|
|
||||||
|
@shield.setter
|
||||||
|
def shield(self, value: bool) -> None:
|
||||||
|
self.__original.shield = value
|
||||||
|
|
||||||
|
|
||||||
|
CancelledError = trio.Cancelled
|
||||||
|
checkpoint = trio.lowlevel.checkpoint
|
||||||
|
checkpoint_if_cancelled = trio.lowlevel.checkpoint_if_cancelled
|
||||||
|
cancel_shielded_checkpoint = trio.lowlevel.cancel_shielded_checkpoint
|
||||||
|
current_effective_deadline = trio.current_effective_deadline
|
||||||
|
current_time = trio.current_time
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Task groups
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionGroup(BaseExceptionGroup, trio.MultiError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TaskGroup(abc.TaskGroup):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._active = False
|
||||||
|
self._nursery_manager = trio.open_nursery()
|
||||||
|
self.cancel_scope = None # type: ignore[assignment]
|
||||||
|
|
||||||
|
async def __aenter__(self) -> "TaskGroup":
|
||||||
|
self._active = True
|
||||||
|
self._nursery = await self._nursery_manager.__aenter__()
|
||||||
|
self.cancel_scope = CancelScope(self._nursery.cancel_scope)
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[Type[BaseException]],
|
||||||
|
exc_val: Optional[BaseException],
|
||||||
|
exc_tb: Optional[TracebackType],
|
||||||
|
) -> Optional[bool]:
|
||||||
|
try:
|
||||||
|
return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb)
|
||||||
|
except trio.MultiError as exc:
|
||||||
|
raise ExceptionGroup(exc.exceptions) from None
|
||||||
|
finally:
|
||||||
|
self._active = False
|
||||||
|
|
||||||
|
def start_soon(self, func: Callable, *args: object, name: object = None) -> None:
|
||||||
|
if not self._active:
|
||||||
|
raise RuntimeError(
|
||||||
|
"This task group is not active; no new tasks can be started."
|
||||||
|
)
|
||||||
|
|
||||||
|
self._nursery.start_soon(func, *args, name=name)
|
||||||
|
|
||||||
|
async def start(
|
||||||
|
self, func: Callable[..., Coroutine], *args: object, name: object = None
|
||||||
|
) -> object:
|
||||||
|
if not self._active:
|
||||||
|
raise RuntimeError(
|
||||||
|
"This task group is not active; no new tasks can be started."
|
||||||
|
)
|
||||||
|
|
||||||
|
return await self._nursery.start(func, *args, name=name)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Threads
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
async def run_sync_in_worker_thread(
|
||||||
|
func: Callable[..., T_Retval],
|
||||||
|
*args: object,
|
||||||
|
cancellable: bool = False,
|
||||||
|
limiter: Optional[trio.CapacityLimiter] = None,
|
||||||
|
) -> T_Retval:
|
||||||
|
def wrapper() -> T_Retval:
|
||||||
|
with claim_worker_thread("trio"):
|
||||||
|
return func(*args)
|
||||||
|
|
||||||
|
# TODO: remove explicit context copying when trio 0.20 is the minimum requirement
|
||||||
|
context = copy_context()
|
||||||
|
context.run(sniffio.current_async_library_cvar.set, None)
|
||||||
|
return await run_sync(
|
||||||
|
context.run, wrapper, cancellable=cancellable, limiter=limiter
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: remove this workaround when trio 0.20 is the minimum requirement
|
||||||
|
def run_async_from_thread(
|
||||||
|
fn: Callable[..., Awaitable[T_Retval]], *args: Any
|
||||||
|
) -> T_Retval:
|
||||||
|
async def wrapper() -> T_Retval:
|
||||||
|
retval: T_Retval
|
||||||
|
|
||||||
|
async def inner() -> None:
|
||||||
|
nonlocal retval
|
||||||
|
__tracebackhide__ = True
|
||||||
|
retval = await fn(*args)
|
||||||
|
|
||||||
|
async with trio.open_nursery() as n:
|
||||||
|
context.run(n.start_soon, inner)
|
||||||
|
|
||||||
|
__tracebackhide__ = True
|
||||||
|
return retval
|
||||||
|
|
||||||
|
context = copy_context()
|
||||||
|
context.run(sniffio.current_async_library_cvar.set, "trio")
|
||||||
|
return trio.from_thread.run(wrapper)
|
||||||
|
|
||||||
|
|
||||||
|
def run_sync_from_thread(fn: Callable[..., T_Retval], *args: Any) -> T_Retval:
|
||||||
|
# TODO: remove explicit context copying when trio 0.20 is the minimum requirement
|
||||||
|
retval = trio.from_thread.run_sync(copy_context().run, fn, *args)
|
||||||
|
return cast(T_Retval, retval)
|
||||||
|
|
||||||
|
|
||||||
|
class BlockingPortal(abc.BlockingPortal):
|
||||||
|
def __new__(cls) -> "BlockingPortal":
|
||||||
|
return object.__new__(cls)
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._token = trio.lowlevel.current_trio_token()
|
||||||
|
|
||||||
|
def _spawn_task_from_thread(
|
||||||
|
self,
|
||||||
|
func: Callable,
|
||||||
|
args: tuple,
|
||||||
|
kwargs: Dict[str, Any],
|
||||||
|
name: object,
|
||||||
|
future: Future,
|
||||||
|
) -> None:
|
||||||
|
context = copy_context()
|
||||||
|
context.run(sniffio.current_async_library_cvar.set, "trio")
|
||||||
|
trio.from_thread.run_sync(
|
||||||
|
context.run,
|
||||||
|
partial(self._task_group.start_soon, name=name),
|
||||||
|
self._call_func,
|
||||||
|
func,
|
||||||
|
args,
|
||||||
|
kwargs,
|
||||||
|
future,
|
||||||
|
trio_token=self._token,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Subprocesses
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(eq=False)
|
||||||
|
class ReceiveStreamWrapper(abc.ByteReceiveStream):
|
||||||
|
_stream: trio.abc.ReceiveStream
|
||||||
|
|
||||||
|
async def receive(self, max_bytes: Optional[int] = None) -> bytes:
|
||||||
|
try:
|
||||||
|
data = await self._stream.receive_some(max_bytes)
|
||||||
|
except trio.ClosedResourceError as exc:
|
||||||
|
raise ClosedResourceError from exc.__cause__
|
||||||
|
except trio.BrokenResourceError as exc:
|
||||||
|
raise BrokenResourceError from exc.__cause__
|
||||||
|
|
||||||
|
if data:
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
raise EndOfStream
|
||||||
|
|
||||||
|
async def aclose(self) -> None:
|
||||||
|
await self._stream.aclose()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(eq=False)
|
||||||
|
class SendStreamWrapper(abc.ByteSendStream):
|
||||||
|
_stream: trio.abc.SendStream
|
||||||
|
|
||||||
|
async def send(self, item: bytes) -> None:
|
||||||
|
try:
|
||||||
|
await self._stream.send_all(item)
|
||||||
|
except trio.ClosedResourceError as exc:
|
||||||
|
raise ClosedResourceError from exc.__cause__
|
||||||
|
except trio.BrokenResourceError as exc:
|
||||||
|
raise BrokenResourceError from exc.__cause__
|
||||||
|
|
||||||
|
async def aclose(self) -> None:
|
||||||
|
await self._stream.aclose()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(eq=False)
|
||||||
|
class Process(abc.Process):
|
||||||
|
_process: trio.Process
|
||||||
|
_stdin: Optional[abc.ByteSendStream]
|
||||||
|
_stdout: Optional[abc.ByteReceiveStream]
|
||||||
|
_stderr: Optional[abc.ByteReceiveStream]
|
||||||
|
|
||||||
|
async def aclose(self) -> None:
|
||||||
|
if self._stdin:
|
||||||
|
await self._stdin.aclose()
|
||||||
|
if self._stdout:
|
||||||
|
await self._stdout.aclose()
|
||||||
|
if self._stderr:
|
||||||
|
await self._stderr.aclose()
|
||||||
|
|
||||||
|
await self.wait()
|
||||||
|
|
||||||
|
async def wait(self) -> int:
|
||||||
|
return await self._process.wait()
|
||||||
|
|
||||||
|
def terminate(self) -> None:
|
||||||
|
self._process.terminate()
|
||||||
|
|
||||||
|
def kill(self) -> None:
|
||||||
|
self._process.kill()
|
||||||
|
|
||||||
|
def send_signal(self, signal: Signals) -> None:
|
||||||
|
self._process.send_signal(signal)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pid(self) -> int:
|
||||||
|
return self._process.pid
|
||||||
|
|
||||||
|
@property
|
||||||
|
def returncode(self) -> Optional[int]:
|
||||||
|
return self._process.returncode
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stdin(self) -> Optional[abc.ByteSendStream]:
|
||||||
|
return self._stdin
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stdout(self) -> Optional[abc.ByteReceiveStream]:
|
||||||
|
return self._stdout
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stderr(self) -> Optional[abc.ByteReceiveStream]:
|
||||||
|
return self._stderr
|
||||||
|
|
||||||
|
|
||||||
|
async def open_process(
|
||||||
|
command: Union[str, bytes, Sequence[Union[str, bytes]]],
|
||||||
|
*,
|
||||||
|
shell: bool,
|
||||||
|
stdin: Union[int, IO[Any], None],
|
||||||
|
stdout: Union[int, IO[Any], None],
|
||||||
|
stderr: Union[int, IO[Any], None],
|
||||||
|
cwd: Union[str, bytes, PathLike, None] = None,
|
||||||
|
env: Optional[Mapping[str, str]] = None,
|
||||||
|
start_new_session: bool = False,
|
||||||
|
) -> Process:
|
||||||
|
process = await trio_open_process(
|
||||||
|
command,
|
||||||
|
stdin=stdin,
|
||||||
|
stdout=stdout,
|
||||||
|
stderr=stderr,
|
||||||
|
shell=shell,
|
||||||
|
cwd=cwd,
|
||||||
|
env=env,
|
||||||
|
start_new_session=start_new_session,
|
||||||
|
)
|
||||||
|
stdin_stream = SendStreamWrapper(process.stdin) if process.stdin else None
|
||||||
|
stdout_stream = ReceiveStreamWrapper(process.stdout) if process.stdout else None
|
||||||
|
stderr_stream = ReceiveStreamWrapper(process.stderr) if process.stderr else None
|
||||||
|
return Process(process, stdin_stream, stdout_stream, stderr_stream)
|
||||||
|
|
||||||
|
|
||||||
|
class _ProcessPoolShutdownInstrument(trio.abc.Instrument):
|
||||||
|
def after_run(self) -> None:
|
||||||
|
super().after_run()
|
||||||
|
|
||||||
|
|
||||||
|
current_default_worker_process_limiter: RunVar = RunVar(
|
||||||
|
"current_default_worker_process_limiter"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _shutdown_process_pool(workers: Set[Process]) -> None:
|
||||||
|
process: Process
|
||||||
|
try:
|
||||||
|
await sleep(math.inf)
|
||||||
|
except trio.Cancelled:
|
||||||
|
for process in workers:
|
||||||
|
if process.returncode is None:
|
||||||
|
process.kill()
|
||||||
|
|
||||||
|
with CancelScope(shield=True):
|
||||||
|
for process in workers:
|
||||||
|
await process.aclose()
|
||||||
|
|
||||||
|
|
||||||
|
def setup_process_pool_exit_at_shutdown(workers: Set[Process]) -> None:
|
||||||
|
trio.lowlevel.spawn_system_task(_shutdown_process_pool, workers)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Sockets and networking
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class _TrioSocketMixin(Generic[T_SockAddr]):
|
||||||
|
def __init__(self, trio_socket: TrioSocketType) -> None:
|
||||||
|
self._trio_socket = trio_socket
|
||||||
|
self._closed = False
|
||||||
|
|
||||||
|
def _check_closed(self) -> None:
|
||||||
|
if self._closed:
|
||||||
|
raise ClosedResourceError
|
||||||
|
if self._trio_socket.fileno() < 0:
|
||||||
|
raise BrokenResourceError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _raw_socket(self) -> socket.socket:
|
||||||
|
return self._trio_socket._sock # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
async def aclose(self) -> None:
|
||||||
|
if self._trio_socket.fileno() >= 0:
|
||||||
|
self._closed = True
|
||||||
|
self._trio_socket.close()
|
||||||
|
|
||||||
|
def _convert_socket_error(self, exc: BaseException) -> "NoReturn":
|
||||||
|
if isinstance(exc, trio.ClosedResourceError):
|
||||||
|
raise ClosedResourceError from exc
|
||||||
|
elif self._trio_socket.fileno() < 0 and self._closed:
|
||||||
|
raise ClosedResourceError from None
|
||||||
|
elif isinstance(exc, OSError):
|
||||||
|
raise BrokenResourceError from exc
|
||||||
|
else:
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
|
||||||
|
class SocketStream(_TrioSocketMixin, abc.SocketStream):
|
||||||
|
def __init__(self, trio_socket: TrioSocketType) -> None:
|
||||||
|
super().__init__(trio_socket)
|
||||||
|
self._receive_guard = ResourceGuard("reading from")
|
||||||
|
self._send_guard = ResourceGuard("writing to")
|
||||||
|
|
||||||
|
async def receive(self, max_bytes: int = 65536) -> bytes:
|
||||||
|
with self._receive_guard:
|
||||||
|
try:
|
||||||
|
data = await self._trio_socket.recv(max_bytes)
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
|
||||||
|
if data:
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
raise EndOfStream
|
||||||
|
|
||||||
|
async def send(self, item: bytes) -> None:
|
||||||
|
with self._send_guard:
|
||||||
|
view = memoryview(item)
|
||||||
|
while view:
|
||||||
|
try:
|
||||||
|
bytes_sent = await self._trio_socket.send(view)
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
|
||||||
|
view = view[bytes_sent:]
|
||||||
|
|
||||||
|
async def send_eof(self) -> None:
|
||||||
|
self._trio_socket.shutdown(socket.SHUT_WR)
|
||||||
|
|
||||||
|
|
||||||
|
class UNIXSocketStream(SocketStream, abc.UNIXSocketStream):
|
||||||
|
async def receive_fds(self, msglen: int, maxfds: int) -> Tuple[bytes, List[int]]:
|
||||||
|
if not isinstance(msglen, int) or msglen < 0:
|
||||||
|
raise ValueError("msglen must be a non-negative integer")
|
||||||
|
if not isinstance(maxfds, int) or maxfds < 1:
|
||||||
|
raise ValueError("maxfds must be a positive integer")
|
||||||
|
|
||||||
|
fds = array.array("i")
|
||||||
|
await checkpoint()
|
||||||
|
with self._receive_guard:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
message, ancdata, flags, addr = await self._trio_socket.recvmsg(
|
||||||
|
msglen, socket.CMSG_LEN(maxfds * fds.itemsize)
|
||||||
|
)
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
else:
|
||||||
|
if not message and not ancdata:
|
||||||
|
raise EndOfStream
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
for cmsg_level, cmsg_type, cmsg_data in ancdata:
|
||||||
|
if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Received unexpected ancillary data; message = {message!r}, "
|
||||||
|
f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}"
|
||||||
|
)
|
||||||
|
|
||||||
|
fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
|
||||||
|
|
||||||
|
return message, list(fds)
|
||||||
|
|
||||||
|
async def send_fds(
|
||||||
|
self, message: bytes, fds: Collection[Union[int, IOBase]]
|
||||||
|
) -> None:
|
||||||
|
if not message:
|
||||||
|
raise ValueError("message must not be empty")
|
||||||
|
if not fds:
|
||||||
|
raise ValueError("fds must not be empty")
|
||||||
|
|
||||||
|
filenos: List[int] = []
|
||||||
|
for fd in fds:
|
||||||
|
if isinstance(fd, int):
|
||||||
|
filenos.append(fd)
|
||||||
|
elif isinstance(fd, IOBase):
|
||||||
|
filenos.append(fd.fileno())
|
||||||
|
|
||||||
|
fdarray = array.array("i", filenos)
|
||||||
|
await checkpoint()
|
||||||
|
with self._send_guard:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
await self._trio_socket.sendmsg(
|
||||||
|
[message],
|
||||||
|
[
|
||||||
|
(
|
||||||
|
socket.SOL_SOCKET,
|
||||||
|
socket.SCM_RIGHTS, # type: ignore[list-item]
|
||||||
|
fdarray,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
break
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
|
||||||
|
|
||||||
|
class TCPSocketListener(_TrioSocketMixin, abc.SocketListener):
|
||||||
|
def __init__(self, raw_socket: socket.socket):
|
||||||
|
super().__init__(trio.socket.from_stdlib_socket(raw_socket))
|
||||||
|
self._accept_guard = ResourceGuard("accepting connections from")
|
||||||
|
|
||||||
|
async def accept(self) -> SocketStream:
|
||||||
|
with self._accept_guard:
|
||||||
|
try:
|
||||||
|
trio_socket, _addr = await self._trio_socket.accept()
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
|
||||||
|
trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
return SocketStream(trio_socket)
|
||||||
|
|
||||||
|
|
||||||
|
class UNIXSocketListener(_TrioSocketMixin, abc.SocketListener):
|
||||||
|
def __init__(self, raw_socket: socket.socket):
|
||||||
|
super().__init__(trio.socket.from_stdlib_socket(raw_socket))
|
||||||
|
self._accept_guard = ResourceGuard("accepting connections from")
|
||||||
|
|
||||||
|
async def accept(self) -> UNIXSocketStream:
|
||||||
|
with self._accept_guard:
|
||||||
|
try:
|
||||||
|
trio_socket, _addr = await self._trio_socket.accept()
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
|
||||||
|
return UNIXSocketStream(trio_socket)
|
||||||
|
|
||||||
|
|
||||||
|
class UDPSocket(_TrioSocketMixin[IPSockAddrType], abc.UDPSocket):
|
||||||
|
def __init__(self, trio_socket: TrioSocketType) -> None:
|
||||||
|
super().__init__(trio_socket)
|
||||||
|
self._receive_guard = ResourceGuard("reading from")
|
||||||
|
self._send_guard = ResourceGuard("writing to")
|
||||||
|
|
||||||
|
async def receive(self) -> Tuple[bytes, IPSockAddrType]:
|
||||||
|
with self._receive_guard:
|
||||||
|
try:
|
||||||
|
data, addr = await self._trio_socket.recvfrom(65536)
|
||||||
|
return data, convert_ipv6_sockaddr(addr)
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
|
||||||
|
async def send(self, item: UDPPacketType) -> None:
|
||||||
|
with self._send_guard:
|
||||||
|
try:
|
||||||
|
await self._trio_socket.sendto(*item)
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectedUDPSocket(_TrioSocketMixin[IPSockAddrType], abc.ConnectedUDPSocket):
|
||||||
|
def __init__(self, trio_socket: TrioSocketType) -> None:
|
||||||
|
super().__init__(trio_socket)
|
||||||
|
self._receive_guard = ResourceGuard("reading from")
|
||||||
|
self._send_guard = ResourceGuard("writing to")
|
||||||
|
|
||||||
|
async def receive(self) -> bytes:
|
||||||
|
with self._receive_guard:
|
||||||
|
try:
|
||||||
|
return await self._trio_socket.recv(65536)
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
|
||||||
|
async def send(self, item: bytes) -> None:
|
||||||
|
with self._send_guard:
|
||||||
|
try:
|
||||||
|
await self._trio_socket.send(item)
|
||||||
|
except BaseException as exc:
|
||||||
|
self._convert_socket_error(exc)
|
||||||
|
|
||||||
|
|
||||||
|
async def connect_tcp(
|
||||||
|
host: str, port: int, local_address: Optional[IPSockAddrType] = None
|
||||||
|
) -> SocketStream:
|
||||||
|
family = socket.AF_INET6 if ":" in host else socket.AF_INET
|
||||||
|
trio_socket = trio.socket.socket(family)
|
||||||
|
trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
if local_address:
|
||||||
|
await trio_socket.bind(local_address)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await trio_socket.connect((host, port))
|
||||||
|
except BaseException:
|
||||||
|
trio_socket.close()
|
||||||
|
raise
|
||||||
|
|
||||||
|
return SocketStream(trio_socket)
|
||||||
|
|
||||||
|
|
||||||
|
async def connect_unix(path: str) -> UNIXSocketStream:
|
||||||
|
trio_socket = trio.socket.socket(socket.AF_UNIX)
|
||||||
|
try:
|
||||||
|
await trio_socket.connect(path)
|
||||||
|
except BaseException:
|
||||||
|
trio_socket.close()
|
||||||
|
raise
|
||||||
|
|
||||||
|
return UNIXSocketStream(trio_socket)
|
||||||
|
|
||||||
|
|
||||||
|
async def create_udp_socket(
|
||||||
|
family: socket.AddressFamily,
|
||||||
|
local_address: Optional[IPSockAddrType],
|
||||||
|
remote_address: Optional[IPSockAddrType],
|
||||||
|
reuse_port: bool,
|
||||||
|
) -> Union[UDPSocket, ConnectedUDPSocket]:
|
||||||
|
trio_socket = trio.socket.socket(family=family, type=socket.SOCK_DGRAM)
|
||||||
|
|
||||||
|
if reuse_port:
|
||||||
|
trio_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||||
|
|
||||||
|
if local_address:
|
||||||
|
await trio_socket.bind(local_address)
|
||||||
|
|
||||||
|
if remote_address:
|
||||||
|
await trio_socket.connect(remote_address)
|
||||||
|
return ConnectedUDPSocket(trio_socket)
|
||||||
|
else:
|
||||||
|
return UDPSocket(trio_socket)
|
||||||
|
|
||||||
|
|
||||||
|
getaddrinfo = trio.socket.getaddrinfo
|
||||||
|
getnameinfo = trio.socket.getnameinfo
|
||||||
|
|
||||||
|
|
||||||
|
async def wait_socket_readable(sock: socket.socket) -> None:
|
||||||
|
try:
|
||||||
|
await wait_readable(sock)
|
||||||
|
except trio.ClosedResourceError as exc:
|
||||||
|
raise ClosedResourceError().with_traceback(exc.__traceback__) from None
|
||||||
|
except trio.BusyResourceError:
|
||||||
|
raise BusyResourceError("reading from") from None
|
||||||
|
|
||||||
|
|
||||||
|
async def wait_socket_writable(sock: socket.socket) -> None:
|
||||||
|
try:
|
||||||
|
await wait_writable(sock)
|
||||||
|
except trio.ClosedResourceError as exc:
|
||||||
|
raise ClosedResourceError().with_traceback(exc.__traceback__) from None
|
||||||
|
except trio.BusyResourceError:
|
||||||
|
raise BusyResourceError("writing to") from None
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Synchronization
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class Event(BaseEvent):
|
||||||
|
def __new__(cls) -> "Event":
|
||||||
|
return object.__new__(cls)
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.__original = trio.Event()
|
||||||
|
|
||||||
|
def is_set(self) -> bool:
|
||||||
|
return self.__original.is_set()
|
||||||
|
|
||||||
|
async def wait(self) -> None:
|
||||||
|
return await self.__original.wait()
|
||||||
|
|
||||||
|
def statistics(self) -> EventStatistics:
|
||||||
|
orig_statistics = self.__original.statistics()
|
||||||
|
return EventStatistics(tasks_waiting=orig_statistics.tasks_waiting)
|
||||||
|
|
||||||
|
def set(self) -> DeprecatedAwaitable:
|
||||||
|
self.__original.set()
|
||||||
|
return DeprecatedAwaitable(self.set)
|
||||||
|
|
||||||
|
|
||||||
|
class CapacityLimiter(BaseCapacityLimiter):
|
||||||
|
def __new__(cls, *args: object, **kwargs: object) -> "CapacityLimiter":
|
||||||
|
return object.__new__(cls)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, *args: Any, original: Optional[trio.CapacityLimiter] = None
|
||||||
|
) -> None:
|
||||||
|
self.__original = original or trio.CapacityLimiter(*args)
|
||||||
|
|
||||||
|
async def __aenter__(self) -> None:
|
||||||
|
return await self.__original.__aenter__()
|
||||||
|
|
||||||
|
async def __aexit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[Type[BaseException]],
|
||||||
|
exc_val: Optional[BaseException],
|
||||||
|
exc_tb: Optional[TracebackType],
|
||||||
|
) -> Optional[bool]:
|
||||||
|
return await self.__original.__aexit__(exc_type, exc_val, exc_tb)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total_tokens(self) -> float:
|
||||||
|
return self.__original.total_tokens
|
||||||
|
|
||||||
|
@total_tokens.setter
|
||||||
|
def total_tokens(self, value: float) -> None:
|
||||||
|
self.__original.total_tokens = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def borrowed_tokens(self) -> int:
|
||||||
|
return self.__original.borrowed_tokens
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available_tokens(self) -> float:
|
||||||
|
return self.__original.available_tokens
|
||||||
|
|
||||||
|
def acquire_nowait(self) -> DeprecatedAwaitable:
|
||||||
|
self.__original.acquire_nowait()
|
||||||
|
return DeprecatedAwaitable(self.acquire_nowait)
|
||||||
|
|
||||||
|
def acquire_on_behalf_of_nowait(self, borrower: object) -> DeprecatedAwaitable:
|
||||||
|
self.__original.acquire_on_behalf_of_nowait(borrower)
|
||||||
|
return DeprecatedAwaitable(self.acquire_on_behalf_of_nowait)
|
||||||
|
|
||||||
|
async def acquire(self) -> None:
|
||||||
|
await self.__original.acquire()
|
||||||
|
|
||||||
|
async def acquire_on_behalf_of(self, borrower: object) -> None:
|
||||||
|
await self.__original.acquire_on_behalf_of(borrower)
|
||||||
|
|
||||||
|
def release(self) -> None:
|
||||||
|
return self.__original.release()
|
||||||
|
|
||||||
|
def release_on_behalf_of(self, borrower: object) -> None:
|
||||||
|
return self.__original.release_on_behalf_of(borrower)
|
||||||
|
|
||||||
|
def statistics(self) -> CapacityLimiterStatistics:
|
||||||
|
orig = self.__original.statistics()
|
||||||
|
return CapacityLimiterStatistics(
|
||||||
|
borrowed_tokens=orig.borrowed_tokens,
|
||||||
|
total_tokens=orig.total_tokens,
|
||||||
|
borrowers=orig.borrowers,
|
||||||
|
tasks_waiting=orig.tasks_waiting,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
_capacity_limiter_wrapper: RunVar = RunVar("_capacity_limiter_wrapper")
|
||||||
|
|
||||||
|
|
||||||
|
def current_default_thread_limiter() -> CapacityLimiter:
|
||||||
|
try:
|
||||||
|
return _capacity_limiter_wrapper.get()
|
||||||
|
except LookupError:
|
||||||
|
limiter = CapacityLimiter(
|
||||||
|
original=trio.to_thread.current_default_thread_limiter()
|
||||||
|
)
|
||||||
|
_capacity_limiter_wrapper.set(limiter)
|
||||||
|
return limiter
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Signal handling
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class _SignalReceiver(DeprecatedAsyncContextManager[T]):
|
||||||
|
def __init__(self, cm: ContextManager[T]):
|
||||||
|
self._cm = cm
|
||||||
|
|
||||||
|
def __enter__(self) -> T:
|
||||||
|
return self._cm.__enter__()
|
||||||
|
|
||||||
|
def __exit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[Type[BaseException]],
|
||||||
|
exc_val: Optional[BaseException],
|
||||||
|
exc_tb: Optional[TracebackType],
|
||||||
|
) -> Optional[bool]:
|
||||||
|
return self._cm.__exit__(exc_type, exc_val, exc_tb)
|
||||||
|
|
||||||
|
|
||||||
|
def open_signal_receiver(*signals: Signals) -> _SignalReceiver:
|
||||||
|
cm = trio.open_signal_receiver(*signals)
|
||||||
|
return _SignalReceiver(cm)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Testing and debugging
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_task() -> TaskInfo:
|
||||||
|
task = trio_lowlevel.current_task()
|
||||||
|
|
||||||
|
parent_id = None
|
||||||
|
if task.parent_nursery and task.parent_nursery.parent_task:
|
||||||
|
parent_id = id(task.parent_nursery.parent_task)
|
||||||
|
|
||||||
|
return TaskInfo(id(task), parent_id, task.name, task.coro)
|
||||||
|
|
||||||
|
|
||||||
|
def get_running_tasks() -> List[TaskInfo]:
|
||||||
|
root_task = trio_lowlevel.current_root_task()
|
||||||
|
task_infos = [TaskInfo(id(root_task), None, root_task.name, root_task.coro)]
|
||||||
|
nurseries = root_task.child_nurseries
|
||||||
|
while nurseries:
|
||||||
|
new_nurseries: List[trio.Nursery] = []
|
||||||
|
for nursery in nurseries:
|
||||||
|
for task in nursery.child_tasks:
|
||||||
|
task_infos.append(
|
||||||
|
TaskInfo(id(task), id(nursery.parent_task), task.name, task.coro)
|
||||||
|
)
|
||||||
|
new_nurseries.extend(task.child_nurseries)
|
||||||
|
|
||||||
|
nurseries = new_nurseries
|
||||||
|
|
||||||
|
return task_infos
|
||||||
|
|
||||||
|
|
||||||
|
def wait_all_tasks_blocked() -> Awaitable[None]:
|
||||||
|
import trio.testing
|
||||||
|
|
||||||
|
return trio.testing.wait_all_tasks_blocked()
|
||||||
|
|
||||||
|
|
||||||
|
class TestRunner(abc.TestRunner):
|
||||||
|
def __init__(self, **options: Any) -> None:
|
||||||
|
from collections import deque
|
||||||
|
from queue import Queue
|
||||||
|
|
||||||
|
self._call_queue: "Queue[Callable[..., object]]" = Queue()
|
||||||
|
self._result_queue: Deque[Outcome] = deque()
|
||||||
|
self._stop_event: Optional[trio.Event] = None
|
||||||
|
self._nursery: Optional[trio.Nursery] = None
|
||||||
|
self._options = options
|
||||||
|
|
||||||
|
async def _trio_main(self) -> None:
|
||||||
|
self._stop_event = trio.Event()
|
||||||
|
async with trio.open_nursery() as self._nursery:
|
||||||
|
await self._stop_event.wait()
|
||||||
|
|
||||||
|
async def _call_func(
|
||||||
|
self, func: Callable[..., Awaitable[object]], args: tuple, kwargs: dict
|
||||||
|
) -> None:
|
||||||
|
try:
|
||||||
|
retval = await func(*args, **kwargs)
|
||||||
|
except BaseException as exc:
|
||||||
|
self._result_queue.append(Error(exc))
|
||||||
|
else:
|
||||||
|
self._result_queue.append(Value(retval))
|
||||||
|
|
||||||
|
def _main_task_finished(self, outcome: object) -> None:
|
||||||
|
self._nursery = None
|
||||||
|
|
||||||
|
def _get_nursery(self) -> trio.Nursery:
|
||||||
|
if self._nursery is None:
|
||||||
|
trio.lowlevel.start_guest_run(
|
||||||
|
self._trio_main,
|
||||||
|
run_sync_soon_threadsafe=self._call_queue.put,
|
||||||
|
done_callback=self._main_task_finished,
|
||||||
|
**self._options,
|
||||||
|
)
|
||||||
|
while self._nursery is None:
|
||||||
|
self._call_queue.get()()
|
||||||
|
|
||||||
|
return self._nursery
|
||||||
|
|
||||||
|
def _call(
|
||||||
|
self, func: Callable[..., Awaitable[T_Retval]], *args: object, **kwargs: object
|
||||||
|
) -> T_Retval:
|
||||||
|
self._get_nursery().start_soon(self._call_func, func, args, kwargs)
|
||||||
|
while not self._result_queue:
|
||||||
|
self._call_queue.get()()
|
||||||
|
|
||||||
|
outcome = self._result_queue.pop()
|
||||||
|
return outcome.unwrap()
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
if self._stop_event:
|
||||||
|
self._stop_event.set()
|
||||||
|
while self._nursery is not None:
|
||||||
|
self._call_queue.get()()
|
||||||
|
|
||||||
|
def run_asyncgen_fixture(
|
||||||
|
self,
|
||||||
|
fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]],
|
||||||
|
kwargs: Dict[str, Any],
|
||||||
|
) -> Iterable[T_Retval]:
|
||||||
|
async def fixture_runner(*, task_status: "TaskStatus") -> None:
|
||||||
|
agen = fixture_func(**kwargs)
|
||||||
|
retval = await agen.asend(None)
|
||||||
|
task_status.started(retval)
|
||||||
|
await teardown_event.wait()
|
||||||
|
try:
|
||||||
|
await agen.asend(None)
|
||||||
|
except StopAsyncIteration:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
await agen.aclose()
|
||||||
|
raise RuntimeError("Async generator fixture did not stop")
|
||||||
|
|
||||||
|
teardown_event = trio.Event()
|
||||||
|
fixture_value = self._call(lambda: self._get_nursery().start(fixture_runner))
|
||||||
|
yield fixture_value
|
||||||
|
teardown_event.set()
|
||||||
|
|
||||||
|
def run_fixture(
|
||||||
|
self,
|
||||||
|
fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]],
|
||||||
|
kwargs: Dict[str, Any],
|
||||||
|
) -> T_Retval:
|
||||||
|
return self._call(fixture_func, **kwargs)
|
||||||
|
|
||||||
|
def run_test(
|
||||||
|
self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: Dict[str, Any]
|
||||||
|
) -> None:
|
||||||
|
self._call(test_func, **kwargs)
|
@ -0,0 +1,218 @@
|
|||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
from contextlib import AbstractContextManager
|
||||||
|
from types import TracebackType
|
||||||
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
AsyncContextManager,
|
||||||
|
Callable,
|
||||||
|
ContextManager,
|
||||||
|
Generator,
|
||||||
|
Generic,
|
||||||
|
Iterable,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ._testing import TaskInfo
|
||||||
|
else:
|
||||||
|
TaskInfo = object
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
AnyDeprecatedAwaitable = Union[
|
||||||
|
"DeprecatedAwaitable",
|
||||||
|
"DeprecatedAwaitableFloat",
|
||||||
|
"DeprecatedAwaitableList[T]",
|
||||||
|
TaskInfo,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def maybe_async(__obj: TaskInfo) -> TaskInfo:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def maybe_async(__obj: "DeprecatedAwaitableFloat") -> float:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def maybe_async(__obj: "DeprecatedAwaitableList[T]") -> List[T]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def maybe_async(__obj: "DeprecatedAwaitable") -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def maybe_async(
|
||||||
|
__obj: "AnyDeprecatedAwaitable[T]",
|
||||||
|
) -> Union[TaskInfo, float, List[T], None]:
|
||||||
|
"""
|
||||||
|
Await on the given object if necessary.
|
||||||
|
|
||||||
|
This function is intended to bridge the gap between AnyIO 2.x and 3.x where some functions and
|
||||||
|
methods were converted from coroutine functions into regular functions.
|
||||||
|
|
||||||
|
Do **not** try to use this for any other purpose!
|
||||||
|
|
||||||
|
:return: the result of awaiting on the object if coroutine, or the object itself otherwise
|
||||||
|
|
||||||
|
.. versionadded:: 2.2
|
||||||
|
|
||||||
|
"""
|
||||||
|
return __obj._unwrap()
|
||||||
|
|
||||||
|
|
||||||
|
class _ContextManagerWrapper:
|
||||||
|
def __init__(self, cm: ContextManager[T]):
|
||||||
|
self._cm = cm
|
||||||
|
|
||||||
|
async def __aenter__(self) -> T:
|
||||||
|
return self._cm.__enter__()
|
||||||
|
|
||||||
|
async def __aexit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[Type[BaseException]],
|
||||||
|
exc_val: Optional[BaseException],
|
||||||
|
exc_tb: Optional[TracebackType],
|
||||||
|
) -> Optional[bool]:
|
||||||
|
return self._cm.__exit__(exc_type, exc_val, exc_tb)
|
||||||
|
|
||||||
|
|
||||||
|
def maybe_async_cm(
|
||||||
|
cm: Union[ContextManager[T], AsyncContextManager[T]]
|
||||||
|
) -> AsyncContextManager[T]:
|
||||||
|
"""
|
||||||
|
Wrap a regular context manager as an async one if necessary.
|
||||||
|
|
||||||
|
This function is intended to bridge the gap between AnyIO 2.x and 3.x where some functions and
|
||||||
|
methods were changed to return regular context managers instead of async ones.
|
||||||
|
|
||||||
|
:param cm: a regular or async context manager
|
||||||
|
:return: an async context manager
|
||||||
|
|
||||||
|
.. versionadded:: 2.2
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not isinstance(cm, AbstractContextManager):
|
||||||
|
raise TypeError("Given object is not an context manager")
|
||||||
|
|
||||||
|
return _ContextManagerWrapper(cm)
|
||||||
|
|
||||||
|
|
||||||
|
def _warn_deprecation(
|
||||||
|
awaitable: "AnyDeprecatedAwaitable[Any]", stacklevel: int = 1
|
||||||
|
) -> None:
|
||||||
|
warn(
|
||||||
|
f'Awaiting on {awaitable._name}() is deprecated. Use "await '
|
||||||
|
f"anyio.maybe_async({awaitable._name}(...)) if you have to support both AnyIO 2.x "
|
||||||
|
f'and 3.x, or just remove the "await" if you are completely migrating to AnyIO 3+.',
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=stacklevel + 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedAwaitable:
|
||||||
|
def __init__(self, func: Callable[..., "DeprecatedAwaitable"]):
|
||||||
|
self._name = f"{func.__module__}.{func.__qualname__}"
|
||||||
|
|
||||||
|
def __await__(self) -> Generator[None, None, None]:
|
||||||
|
_warn_deprecation(self)
|
||||||
|
if False:
|
||||||
|
yield
|
||||||
|
|
||||||
|
def __reduce__(self) -> Tuple[Type[None], Tuple[()]]:
|
||||||
|
return type(None), ()
|
||||||
|
|
||||||
|
def _unwrap(self) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedAwaitableFloat(float):
|
||||||
|
def __new__(
|
||||||
|
cls, x: float, func: Callable[..., "DeprecatedAwaitableFloat"]
|
||||||
|
) -> "DeprecatedAwaitableFloat":
|
||||||
|
return super().__new__(cls, x)
|
||||||
|
|
||||||
|
def __init__(self, x: float, func: Callable[..., "DeprecatedAwaitableFloat"]):
|
||||||
|
self._name = f"{func.__module__}.{func.__qualname__}"
|
||||||
|
|
||||||
|
def __await__(self) -> Generator[None, None, float]:
|
||||||
|
_warn_deprecation(self)
|
||||||
|
if False:
|
||||||
|
yield
|
||||||
|
|
||||||
|
return float(self)
|
||||||
|
|
||||||
|
def __reduce__(self) -> Tuple[Type[float], Tuple[float]]:
|
||||||
|
return float, (float(self),)
|
||||||
|
|
||||||
|
def _unwrap(self) -> float:
|
||||||
|
return float(self)
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedAwaitableList(List[T]):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
iterable: Iterable[T] = (),
|
||||||
|
*,
|
||||||
|
func: Callable[..., "DeprecatedAwaitableList[T]"],
|
||||||
|
):
|
||||||
|
super().__init__(iterable)
|
||||||
|
self._name = f"{func.__module__}.{func.__qualname__}"
|
||||||
|
|
||||||
|
def __await__(self) -> Generator[None, None, List[T]]:
|
||||||
|
_warn_deprecation(self)
|
||||||
|
if False:
|
||||||
|
yield
|
||||||
|
|
||||||
|
return list(self)
|
||||||
|
|
||||||
|
def __reduce__(self) -> Tuple[Type[List[T]], Tuple[List[T]]]:
|
||||||
|
return list, (list(self),)
|
||||||
|
|
||||||
|
def _unwrap(self) -> List[T]:
|
||||||
|
return list(self)
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedAsyncContextManager(Generic[T], metaclass=ABCMeta):
|
||||||
|
@abstractmethod
|
||||||
|
def __enter__(self) -> T:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def __exit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[Type[BaseException]],
|
||||||
|
exc_val: Optional[BaseException],
|
||||||
|
exc_tb: Optional[TracebackType],
|
||||||
|
) -> Optional[bool]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def __aenter__(self) -> T:
|
||||||
|
warn(
|
||||||
|
f"Using {self.__class__.__name__} as an async context manager has been deprecated. "
|
||||||
|
f'Use "async with anyio.maybe_async_cm(yourcontextmanager) as foo:" if you have to '
|
||||||
|
f'support both AnyIO 2.x and 3.x, or just remove the "async" from "async with" if '
|
||||||
|
f"you are completely migrating to AnyIO 3+.",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
|
return self.__enter__()
|
||||||
|
|
||||||
|
async def __aexit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[Type[BaseException]],
|
||||||
|
exc_val: Optional[BaseException],
|
||||||
|
exc_tb: Optional[TracebackType],
|
||||||
|
) -> Optional[bool]:
|
||||||
|
return self.__exit__(exc_type, exc_val, exc_tb)
|
@ -0,0 +1,93 @@
|
|||||||
|
from traceback import format_exception
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class BrokenResourceError(Exception):
|
||||||
|
"""
|
||||||
|
Raised when trying to use a resource that has been rendered unusable due to external causes
|
||||||
|
(e.g. a send stream whose peer has disconnected).
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class BrokenWorkerProcess(Exception):
|
||||||
|
"""
|
||||||
|
Raised by :func:`run_sync_in_process` if the worker process terminates abruptly or otherwise
|
||||||
|
misbehaves.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class BusyResourceError(Exception):
|
||||||
|
"""Raised when two tasks are trying to read from or write to the same resource concurrently."""
|
||||||
|
|
||||||
|
def __init__(self, action: str):
|
||||||
|
super().__init__(f"Another task is already {action} this resource")
|
||||||
|
|
||||||
|
|
||||||
|
class ClosedResourceError(Exception):
|
||||||
|
"""Raised when trying to use a resource that has been closed."""
|
||||||
|
|
||||||
|
|
||||||
|
class DelimiterNotFound(Exception):
|
||||||
|
"""
|
||||||
|
Raised during :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the
|
||||||
|
maximum number of bytes has been read without the delimiter being found.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, max_bytes: int) -> None:
|
||||||
|
super().__init__(
|
||||||
|
f"The delimiter was not found among the first {max_bytes} bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EndOfStream(Exception):
|
||||||
|
"""Raised when trying to read from a stream that has been closed from the other end."""
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionGroup(BaseException):
|
||||||
|
"""
|
||||||
|
Raised when multiple exceptions have been raised in a task group.
|
||||||
|
|
||||||
|
:var ~typing.Sequence[BaseException] exceptions: the sequence of exceptions raised together
|
||||||
|
"""
|
||||||
|
|
||||||
|
SEPARATOR = "----------------------------\n"
|
||||||
|
|
||||||
|
exceptions: List[BaseException]
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
tracebacks = [
|
||||||
|
"".join(format_exception(type(exc), exc, exc.__traceback__))
|
||||||
|
for exc in self.exceptions
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
f"{len(self.exceptions)} exceptions were raised in the task group:\n"
|
||||||
|
f"{self.SEPARATOR}{self.SEPARATOR.join(tracebacks)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
exception_reprs = ", ".join(repr(exc) for exc in self.exceptions)
|
||||||
|
return f"<{self.__class__.__name__}: {exception_reprs}>"
|
||||||
|
|
||||||
|
|
||||||
|
class IncompleteRead(Exception):
|
||||||
|
"""
|
||||||
|
Raised during :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or
|
||||||
|
:meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the
|
||||||
|
connection is closed before the requested amount of bytes has been read.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__(
|
||||||
|
"The stream was closed before the read operation could be completed"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TypedAttributeLookupError(LookupError):
|
||||||
|
"""
|
||||||
|
Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute is not
|
||||||
|
found and no default value has been given.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class WouldBlock(Exception):
|
||||||
|
"""Raised by ``X_nowait`` functions if ``X()`` would block."""
|
@ -0,0 +1,607 @@
|
|||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from functools import partial
|
||||||
|
from os import PathLike
|
||||||
|
from typing import (
|
||||||
|
IO,
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
AnyStr,
|
||||||
|
AsyncIterator,
|
||||||
|
Callable,
|
||||||
|
Generic,
|
||||||
|
Iterable,
|
||||||
|
Iterator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
Tuple,
|
||||||
|
Union,
|
||||||
|
cast,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .. import to_thread
|
||||||
|
from ..abc import AsyncResource
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 8):
|
||||||
|
from typing import Final
|
||||||
|
else:
|
||||||
|
from typing_extensions import Final
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer
|
||||||
|
else:
|
||||||
|
ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncFile(AsyncResource, Generic[AnyStr]):
|
||||||
|
"""
|
||||||
|
An asynchronous file object.
|
||||||
|
|
||||||
|
This class wraps a standard file object and provides async friendly versions of the following
|
||||||
|
blocking methods (where available on the original file object):
|
||||||
|
|
||||||
|
* read
|
||||||
|
* read1
|
||||||
|
* readline
|
||||||
|
* readlines
|
||||||
|
* readinto
|
||||||
|
* readinto1
|
||||||
|
* write
|
||||||
|
* writelines
|
||||||
|
* truncate
|
||||||
|
* seek
|
||||||
|
* tell
|
||||||
|
* flush
|
||||||
|
|
||||||
|
All other methods are directly passed through.
|
||||||
|
|
||||||
|
This class supports the asynchronous context manager protocol which closes the underlying file
|
||||||
|
at the end of the context block.
|
||||||
|
|
||||||
|
This class also supports asynchronous iteration::
|
||||||
|
|
||||||
|
async with await open_file(...) as f:
|
||||||
|
async for line in f:
|
||||||
|
print(line)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, fp: IO[AnyStr]) -> None:
|
||||||
|
self._fp: Any = fp
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> object:
|
||||||
|
return getattr(self._fp, name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wrapped(self) -> IO[AnyStr]:
|
||||||
|
"""The wrapped file object."""
|
||||||
|
return self._fp
|
||||||
|
|
||||||
|
async def __aiter__(self) -> AsyncIterator[AnyStr]:
|
||||||
|
while True:
|
||||||
|
line = await self.readline()
|
||||||
|
if line:
|
||||||
|
yield line
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
async def aclose(self) -> None:
|
||||||
|
return await to_thread.run_sync(self._fp.close)
|
||||||
|
|
||||||
|
async def read(self, size: int = -1) -> AnyStr:
|
||||||
|
return await to_thread.run_sync(self._fp.read, size)
|
||||||
|
|
||||||
|
async def read1(self: "AsyncFile[bytes]", size: int = -1) -> bytes:
|
||||||
|
return await to_thread.run_sync(self._fp.read1, size)
|
||||||
|
|
||||||
|
async def readline(self) -> AnyStr:
|
||||||
|
return await to_thread.run_sync(self._fp.readline)
|
||||||
|
|
||||||
|
async def readlines(self) -> List[AnyStr]:
|
||||||
|
return await to_thread.run_sync(self._fp.readlines)
|
||||||
|
|
||||||
|
async def readinto(self: "AsyncFile[bytes]", b: WriteableBuffer) -> bytes:
|
||||||
|
return await to_thread.run_sync(self._fp.readinto, b)
|
||||||
|
|
||||||
|
async def readinto1(self: "AsyncFile[bytes]", b: WriteableBuffer) -> bytes:
|
||||||
|
return await to_thread.run_sync(self._fp.readinto1, b)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def write(self: "AsyncFile[bytes]", b: ReadableBuffer) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def write(self: "AsyncFile[str]", b: str) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def write(self, b: Union[ReadableBuffer, str]) -> int:
|
||||||
|
return await to_thread.run_sync(self._fp.write, b)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def writelines(
|
||||||
|
self: "AsyncFile[bytes]", lines: Iterable[ReadableBuffer]
|
||||||
|
) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def writelines(self: "AsyncFile[str]", lines: Iterable[str]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def writelines(
|
||||||
|
self, lines: Union[Iterable[ReadableBuffer], Iterable[str]]
|
||||||
|
) -> None:
|
||||||
|
return await to_thread.run_sync(self._fp.writelines, lines)
|
||||||
|
|
||||||
|
async def truncate(self, size: Optional[int] = None) -> int:
|
||||||
|
return await to_thread.run_sync(self._fp.truncate, size)
|
||||||
|
|
||||||
|
async def seek(self, offset: int, whence: Optional[int] = os.SEEK_SET) -> int:
|
||||||
|
return await to_thread.run_sync(self._fp.seek, offset, whence)
|
||||||
|
|
||||||
|
async def tell(self) -> int:
|
||||||
|
return await to_thread.run_sync(self._fp.tell)
|
||||||
|
|
||||||
|
async def flush(self) -> None:
|
||||||
|
return await to_thread.run_sync(self._fp.flush)
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def open_file(
|
||||||
|
file: Union[str, "PathLike[str]", int],
|
||||||
|
mode: OpenBinaryMode,
|
||||||
|
buffering: int = ...,
|
||||||
|
encoding: Optional[str] = ...,
|
||||||
|
errors: Optional[str] = ...,
|
||||||
|
newline: Optional[str] = ...,
|
||||||
|
closefd: bool = ...,
|
||||||
|
opener: Optional[Callable[[str, int], int]] = ...,
|
||||||
|
) -> AsyncFile[bytes]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def open_file(
|
||||||
|
file: Union[str, "PathLike[str]", int],
|
||||||
|
mode: OpenTextMode = ...,
|
||||||
|
buffering: int = ...,
|
||||||
|
encoding: Optional[str] = ...,
|
||||||
|
errors: Optional[str] = ...,
|
||||||
|
newline: Optional[str] = ...,
|
||||||
|
closefd: bool = ...,
|
||||||
|
opener: Optional[Callable[[str, int], int]] = ...,
|
||||||
|
) -> AsyncFile[str]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def open_file(
|
||||||
|
file: Union[str, "PathLike[str]", int],
|
||||||
|
mode: str = "r",
|
||||||
|
buffering: int = -1,
|
||||||
|
encoding: Optional[str] = None,
|
||||||
|
errors: Optional[str] = None,
|
||||||
|
newline: Optional[str] = None,
|
||||||
|
closefd: bool = True,
|
||||||
|
opener: Optional[Callable[[str, int], int]] = None,
|
||||||
|
) -> AsyncFile[Any]:
|
||||||
|
"""
|
||||||
|
Open a file asynchronously.
|
||||||
|
|
||||||
|
The arguments are exactly the same as for the builtin :func:`open`.
|
||||||
|
|
||||||
|
:return: an asynchronous file object
|
||||||
|
|
||||||
|
"""
|
||||||
|
fp = await to_thread.run_sync(
|
||||||
|
open, file, mode, buffering, encoding, errors, newline, closefd, opener
|
||||||
|
)
|
||||||
|
return AsyncFile(fp)
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_file(file: IO[AnyStr]) -> AsyncFile[AnyStr]:
|
||||||
|
"""
|
||||||
|
Wrap an existing file as an asynchronous file.
|
||||||
|
|
||||||
|
:param file: an existing file-like object
|
||||||
|
:return: an asynchronous file object
|
||||||
|
|
||||||
|
"""
|
||||||
|
return AsyncFile(file)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(eq=False)
|
||||||
|
class _PathIterator(AsyncIterator["Path"]):
|
||||||
|
iterator: Iterator["PathLike[str]"]
|
||||||
|
|
||||||
|
async def __anext__(self) -> "Path":
|
||||||
|
nextval = await to_thread.run_sync(next, self.iterator, None, cancellable=True)
|
||||||
|
if nextval is None:
|
||||||
|
raise StopAsyncIteration from None
|
||||||
|
|
||||||
|
return Path(cast("PathLike[str]", nextval))
|
||||||
|
|
||||||
|
|
||||||
|
class Path:
|
||||||
|
"""
|
||||||
|
An asynchronous version of :class:`pathlib.Path`.
|
||||||
|
|
||||||
|
This class cannot be substituted for :class:`pathlib.Path` or :class:`pathlib.PurePath`, but
|
||||||
|
it is compatible with the :class:`os.PathLike` interface.
|
||||||
|
|
||||||
|
It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for the
|
||||||
|
deprecated :meth:`~pathlib.Path.link_to` method.
|
||||||
|
|
||||||
|
Any methods that do disk I/O need to be awaited on. These methods are:
|
||||||
|
|
||||||
|
* :meth:`~pathlib.Path.absolute`
|
||||||
|
* :meth:`~pathlib.Path.chmod`
|
||||||
|
* :meth:`~pathlib.Path.cwd`
|
||||||
|
* :meth:`~pathlib.Path.exists`
|
||||||
|
* :meth:`~pathlib.Path.expanduser`
|
||||||
|
* :meth:`~pathlib.Path.group`
|
||||||
|
* :meth:`~pathlib.Path.hardlink_to`
|
||||||
|
* :meth:`~pathlib.Path.home`
|
||||||
|
* :meth:`~pathlib.Path.is_block_device`
|
||||||
|
* :meth:`~pathlib.Path.is_char_device`
|
||||||
|
* :meth:`~pathlib.Path.is_dir`
|
||||||
|
* :meth:`~pathlib.Path.is_fifo`
|
||||||
|
* :meth:`~pathlib.Path.is_file`
|
||||||
|
* :meth:`~pathlib.Path.is_mount`
|
||||||
|
* :meth:`~pathlib.Path.lchmod`
|
||||||
|
* :meth:`~pathlib.Path.lstat`
|
||||||
|
* :meth:`~pathlib.Path.mkdir`
|
||||||
|
* :meth:`~pathlib.Path.open`
|
||||||
|
* :meth:`~pathlib.Path.owner`
|
||||||
|
* :meth:`~pathlib.Path.read_bytes`
|
||||||
|
* :meth:`~pathlib.Path.read_text`
|
||||||
|
* :meth:`~pathlib.Path.readlink`
|
||||||
|
* :meth:`~pathlib.Path.rename`
|
||||||
|
* :meth:`~pathlib.Path.replace`
|
||||||
|
* :meth:`~pathlib.Path.rmdir`
|
||||||
|
* :meth:`~pathlib.Path.samefile`
|
||||||
|
* :meth:`~pathlib.Path.stat`
|
||||||
|
* :meth:`~pathlib.Path.touch`
|
||||||
|
* :meth:`~pathlib.Path.unlink`
|
||||||
|
* :meth:`~pathlib.Path.write_bytes`
|
||||||
|
* :meth:`~pathlib.Path.write_text`
|
||||||
|
|
||||||
|
Additionally, the following methods return an async iterator yielding :class:`~.Path` objects:
|
||||||
|
|
||||||
|
* :meth:`~pathlib.Path.glob`
|
||||||
|
* :meth:`~pathlib.Path.iterdir`
|
||||||
|
* :meth:`~pathlib.Path.rglob`
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = "_path", "__weakref__"
|
||||||
|
|
||||||
|
__weakref__: Any
|
||||||
|
|
||||||
|
def __init__(self, *args: Union[str, "PathLike[str]"]) -> None:
|
||||||
|
self._path: Final[pathlib.Path] = pathlib.Path(*args)
|
||||||
|
|
||||||
|
def __fspath__(self) -> str:
|
||||||
|
return self._path.__fspath__()
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self._path.__str__()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.__class__.__name__}({self.as_posix()!r})"
|
||||||
|
|
||||||
|
def __bytes__(self) -> bytes:
|
||||||
|
return self._path.__bytes__()
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return self._path.__hash__()
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
target = other._path if isinstance(other, Path) else other
|
||||||
|
return self._path.__eq__(target)
|
||||||
|
|
||||||
|
def __lt__(self, other: "Path") -> bool:
|
||||||
|
target = other._path if isinstance(other, Path) else other
|
||||||
|
return self._path.__lt__(target)
|
||||||
|
|
||||||
|
def __le__(self, other: "Path") -> bool:
|
||||||
|
target = other._path if isinstance(other, Path) else other
|
||||||
|
return self._path.__le__(target)
|
||||||
|
|
||||||
|
def __gt__(self, other: "Path") -> bool:
|
||||||
|
target = other._path if isinstance(other, Path) else other
|
||||||
|
return self._path.__gt__(target)
|
||||||
|
|
||||||
|
def __ge__(self, other: "Path") -> bool:
|
||||||
|
target = other._path if isinstance(other, Path) else other
|
||||||
|
return self._path.__ge__(target)
|
||||||
|
|
||||||
|
def __truediv__(self, other: Any) -> "Path":
|
||||||
|
return Path(self._path / other)
|
||||||
|
|
||||||
|
def __rtruediv__(self, other: Any) -> "Path":
|
||||||
|
return Path(other) / self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parts(self) -> Tuple[str, ...]:
|
||||||
|
return self._path.parts
|
||||||
|
|
||||||
|
@property
|
||||||
|
def drive(self) -> str:
|
||||||
|
return self._path.drive
|
||||||
|
|
||||||
|
@property
|
||||||
|
def root(self) -> str:
|
||||||
|
return self._path.root
|
||||||
|
|
||||||
|
@property
|
||||||
|
def anchor(self) -> str:
|
||||||
|
return self._path.anchor
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parents(self) -> Sequence["Path"]:
|
||||||
|
return tuple(Path(p) for p in self._path.parents)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parent(self) -> "Path":
|
||||||
|
return Path(self._path.parent)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._path.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def suffix(self) -> str:
|
||||||
|
return self._path.suffix
|
||||||
|
|
||||||
|
@property
|
||||||
|
def suffixes(self) -> List[str]:
|
||||||
|
return self._path.suffixes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stem(self) -> str:
|
||||||
|
return self._path.stem
|
||||||
|
|
||||||
|
async def absolute(self) -> "Path":
|
||||||
|
path = await to_thread.run_sync(self._path.absolute)
|
||||||
|
return Path(path)
|
||||||
|
|
||||||
|
def as_posix(self) -> str:
|
||||||
|
return self._path.as_posix()
|
||||||
|
|
||||||
|
def as_uri(self) -> str:
|
||||||
|
return self._path.as_uri()
|
||||||
|
|
||||||
|
def match(self, path_pattern: str) -> bool:
|
||||||
|
return self._path.match(path_pattern)
|
||||||
|
|
||||||
|
def is_relative_to(self, *other: Union[str, "PathLike[str]"]) -> bool:
|
||||||
|
try:
|
||||||
|
self.relative_to(*other)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None:
|
||||||
|
func = partial(os.chmod, follow_symlinks=follow_symlinks)
|
||||||
|
return await to_thread.run_sync(func, self._path, mode)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def cwd(cls) -> "Path":
|
||||||
|
path = await to_thread.run_sync(pathlib.Path.cwd)
|
||||||
|
return cls(path)
|
||||||
|
|
||||||
|
async def exists(self) -> bool:
|
||||||
|
return await to_thread.run_sync(self._path.exists, cancellable=True)
|
||||||
|
|
||||||
|
async def expanduser(self) -> "Path":
|
||||||
|
return Path(await to_thread.run_sync(self._path.expanduser, cancellable=True))
|
||||||
|
|
||||||
|
def glob(self, pattern: str) -> AsyncIterator["Path"]:
|
||||||
|
gen = self._path.glob(pattern)
|
||||||
|
return _PathIterator(gen)
|
||||||
|
|
||||||
|
async def group(self) -> str:
|
||||||
|
return await to_thread.run_sync(self._path.group, cancellable=True)
|
||||||
|
|
||||||
|
async def hardlink_to(self, target: Union[str, pathlib.Path, "Path"]) -> None:
|
||||||
|
if isinstance(target, Path):
|
||||||
|
target = target._path
|
||||||
|
|
||||||
|
await to_thread.run_sync(os.link, target, self)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def home(cls) -> "Path":
|
||||||
|
home_path = await to_thread.run_sync(pathlib.Path.home)
|
||||||
|
return cls(home_path)
|
||||||
|
|
||||||
|
def is_absolute(self) -> bool:
|
||||||
|
return self._path.is_absolute()
|
||||||
|
|
||||||
|
async def is_block_device(self) -> bool:
|
||||||
|
return await to_thread.run_sync(self._path.is_block_device, cancellable=True)
|
||||||
|
|
||||||
|
async def is_char_device(self) -> bool:
|
||||||
|
return await to_thread.run_sync(self._path.is_char_device, cancellable=True)
|
||||||
|
|
||||||
|
async def is_dir(self) -> bool:
|
||||||
|
return await to_thread.run_sync(self._path.is_dir, cancellable=True)
|
||||||
|
|
||||||
|
async def is_fifo(self) -> bool:
|
||||||
|
return await to_thread.run_sync(self._path.is_fifo, cancellable=True)
|
||||||
|
|
||||||
|
async def is_file(self) -> bool:
|
||||||
|
return await to_thread.run_sync(self._path.is_file, cancellable=True)
|
||||||
|
|
||||||
|
async def is_mount(self) -> bool:
|
||||||
|
return await to_thread.run_sync(os.path.ismount, self._path, cancellable=True)
|
||||||
|
|
||||||
|
def is_reserved(self) -> bool:
|
||||||
|
return self._path.is_reserved()
|
||||||
|
|
||||||
|
async def is_socket(self) -> bool:
|
||||||
|
return await to_thread.run_sync(self._path.is_socket, cancellable=True)
|
||||||
|
|
||||||
|
async def is_symlink(self) -> bool:
|
||||||
|
return await to_thread.run_sync(self._path.is_symlink, cancellable=True)
|
||||||
|
|
||||||
|
def iterdir(self) -> AsyncIterator["Path"]:
|
||||||
|
gen = self._path.iterdir()
|
||||||
|
return _PathIterator(gen)
|
||||||
|
|
||||||
|
def joinpath(self, *args: Union[str, "PathLike[str]"]) -> "Path":
|
||||||
|
return Path(self._path.joinpath(*args))
|
||||||
|
|
||||||
|
async def lchmod(self, mode: int) -> None:
|
||||||
|
await to_thread.run_sync(self._path.lchmod, mode)
|
||||||
|
|
||||||
|
async def lstat(self) -> os.stat_result:
|
||||||
|
return await to_thread.run_sync(self._path.lstat, cancellable=True)
|
||||||
|
|
||||||
|
async def mkdir(
|
||||||
|
self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False
|
||||||
|
) -> None:
|
||||||
|
await to_thread.run_sync(self._path.mkdir, mode, parents, exist_ok)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def open(
|
||||||
|
self,
|
||||||
|
mode: OpenBinaryMode,
|
||||||
|
buffering: int = ...,
|
||||||
|
encoding: Optional[str] = ...,
|
||||||
|
errors: Optional[str] = ...,
|
||||||
|
newline: Optional[str] = ...,
|
||||||
|
) -> AsyncFile[bytes]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def open(
|
||||||
|
self,
|
||||||
|
mode: OpenTextMode = ...,
|
||||||
|
buffering: int = ...,
|
||||||
|
encoding: Optional[str] = ...,
|
||||||
|
errors: Optional[str] = ...,
|
||||||
|
newline: Optional[str] = ...,
|
||||||
|
) -> AsyncFile[str]:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def open(
|
||||||
|
self,
|
||||||
|
mode: str = "r",
|
||||||
|
buffering: int = -1,
|
||||||
|
encoding: Optional[str] = None,
|
||||||
|
errors: Optional[str] = None,
|
||||||
|
newline: Optional[str] = None,
|
||||||
|
) -> AsyncFile[Any]:
|
||||||
|
fp = await to_thread.run_sync(
|
||||||
|
self._path.open, mode, buffering, encoding, errors, newline
|
||||||
|
)
|
||||||
|
return AsyncFile(fp)
|
||||||
|
|
||||||
|
async def owner(self) -> str:
|
||||||
|
return await to_thread.run_sync(self._path.owner, cancellable=True)
|
||||||
|
|
||||||
|
async def read_bytes(self) -> bytes:
|
||||||
|
return await to_thread.run_sync(self._path.read_bytes)
|
||||||
|
|
||||||
|
async def read_text(
|
||||||
|
self, encoding: Optional[str] = None, errors: Optional[str] = None
|
||||||
|
) -> str:
|
||||||
|
return await to_thread.run_sync(self._path.read_text, encoding, errors)
|
||||||
|
|
||||||
|
def relative_to(self, *other: Union[str, "PathLike[str]"]) -> "Path":
|
||||||
|
return Path(self._path.relative_to(*other))
|
||||||
|
|
||||||
|
async def readlink(self) -> "Path":
|
||||||
|
target = await to_thread.run_sync(os.readlink, self._path)
|
||||||
|
return Path(cast(str, target))
|
||||||
|
|
||||||
|
async def rename(self, target: Union[str, pathlib.PurePath, "Path"]) -> "Path":
|
||||||
|
if isinstance(target, Path):
|
||||||
|
target = target._path
|
||||||
|
|
||||||
|
await to_thread.run_sync(self._path.rename, target)
|
||||||
|
return Path(target)
|
||||||
|
|
||||||
|
async def replace(self, target: Union[str, pathlib.PurePath, "Path"]) -> "Path":
|
||||||
|
if isinstance(target, Path):
|
||||||
|
target = target._path
|
||||||
|
|
||||||
|
await to_thread.run_sync(self._path.replace, target)
|
||||||
|
return Path(target)
|
||||||
|
|
||||||
|
async def resolve(self, strict: bool = False) -> "Path":
|
||||||
|
func = partial(self._path.resolve, strict=strict)
|
||||||
|
return Path(await to_thread.run_sync(func, cancellable=True))
|
||||||
|
|
||||||
|
def rglob(self, pattern: str) -> AsyncIterator["Path"]:
|
||||||
|
gen = self._path.rglob(pattern)
|
||||||
|
return _PathIterator(gen)
|
||||||
|
|
||||||
|
async def rmdir(self) -> None:
|
||||||
|
await to_thread.run_sync(self._path.rmdir)
|
||||||
|
|
||||||
|
async def samefile(
|
||||||
|
self, other_path: Union[str, bytes, int, pathlib.Path, "Path"]
|
||||||
|
) -> bool:
|
||||||
|
if isinstance(other_path, Path):
|
||||||
|
other_path = other_path._path
|
||||||
|
|
||||||
|
return await to_thread.run_sync(
|
||||||
|
self._path.samefile, other_path, cancellable=True
|
||||||
|
)
|
||||||
|
|
||||||
|
async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result:
|
||||||
|
func = partial(os.stat, follow_symlinks=follow_symlinks)
|
||||||
|
return await to_thread.run_sync(func, self._path, cancellable=True)
|
||||||
|
|
||||||
|
async def symlink_to(
|
||||||
|
self,
|
||||||
|
target: Union[str, pathlib.Path, "Path"],
|
||||||
|
target_is_directory: bool = False,
|
||||||
|
) -> None:
|
||||||
|
if isinstance(target, Path):
|
||||||
|
target = target._path
|
||||||
|
|
||||||
|
await to_thread.run_sync(self._path.symlink_to, target, target_is_directory)
|
||||||
|
|
||||||
|
async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None:
|
||||||
|
await to_thread.run_sync(self._path.touch, mode, exist_ok)
|
||||||
|
|
||||||
|
async def unlink(self, missing_ok: bool = False) -> None:
|
||||||
|
try:
|
||||||
|
await to_thread.run_sync(self._path.unlink)
|
||||||
|
except FileNotFoundError:
|
||||||
|
if not missing_ok:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def with_name(self, name: str) -> "Path":
|
||||||
|
return Path(self._path.with_name(name))
|
||||||
|
|
||||||
|
def with_stem(self, stem: str) -> "Path":
|
||||||
|
return Path(self._path.with_name(stem + self._path.suffix))
|
||||||
|
|
||||||
|
def with_suffix(self, suffix: str) -> "Path":
|
||||||
|
return Path(self._path.with_suffix(suffix))
|
||||||
|
|
||||||
|
async def write_bytes(self, data: bytes) -> int:
|
||||||
|
return await to_thread.run_sync(self._path.write_bytes, data)
|
||||||
|
|
||||||
|
async def write_text(
|
||||||
|
self,
|
||||||
|
data: str,
|
||||||
|
encoding: Optional[str] = None,
|
||||||
|
errors: Optional[str] = None,
|
||||||
|
newline: Optional[str] = None,
|
||||||
|
) -> int:
|
||||||
|
# Path.write_text() does not support the "newline" parameter before Python 3.10
|
||||||
|
def sync_write_text() -> int:
|
||||||
|
with self._path.open(
|
||||||
|
"w", encoding=encoding, errors=errors, newline=newline
|
||||||
|
) as fp:
|
||||||
|
return fp.write(data)
|
||||||
|
|
||||||
|
return await to_thread.run_sync(sync_write_text)
|
||||||
|
|
||||||
|
|
||||||
|
PathLike.register(Path)
|
@ -0,0 +1,16 @@
|
|||||||
|
from ..abc import AsyncResource
|
||||||
|
from ._tasks import CancelScope
|
||||||
|
|
||||||
|
|
||||||
|
async def aclose_forcefully(resource: AsyncResource) -> None:
|
||||||
|
"""
|
||||||
|
Close an asynchronous resource in a cancelled scope.
|
||||||
|
|
||||||
|
Doing this closes the resource without waiting on anything.
|
||||||
|
|
||||||
|
:param resource: the resource to close
|
||||||
|
|
||||||
|
"""
|
||||||
|
with CancelScope() as scope:
|
||||||
|
scope.cancel()
|
||||||
|
await resource.aclose()
|
@ -0,0 +1,24 @@
|
|||||||
|
from typing import AsyncIterator
|
||||||
|
|
||||||
|
from ._compat import DeprecatedAsyncContextManager
|
||||||
|
from ._eventloop import get_asynclib
|
||||||
|
|
||||||
|
|
||||||
|
def open_signal_receiver(
|
||||||
|
*signals: int,
|
||||||
|
) -> DeprecatedAsyncContextManager[AsyncIterator[int]]:
|
||||||
|
"""
|
||||||
|
Start receiving operating system signals.
|
||||||
|
|
||||||
|
:param signals: signals to receive (e.g. ``signal.SIGINT``)
|
||||||
|
:return: an asynchronous context manager for an asynchronous iterator which yields signal
|
||||||
|
numbers
|
||||||
|
|
||||||
|
.. warning:: Windows does not support signals natively so it is best to avoid relying on this
|
||||||
|
in cross-platform applications.
|
||||||
|
|
||||||
|
.. warning:: On asyncio, this permanently replaces any previous signal handler for the given
|
||||||
|
signals, as set via :meth:`~asyncio.loop.add_signal_handler`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return get_asynclib().open_signal_receiver(*signals)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue