Compare commits
No commits in common. 'prd' and 'dev' have entirely different histories.
@ -1,5 +1,4 @@
|
|||||||
chcp 65001
|
cd %~dp0
|
||||||
cd /d "%~dp0"
|
|
||||||
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
|
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
|
||||||
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\pythonw.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
|
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"
|
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
chcp 65001
|
cd %~dp0
|
||||||
cd /d "%~dp0"
|
|
||||||
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
|
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
|
||||||
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
|
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"
|
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
git pull
|
||||||
|
pause>nul
|
@ -1,2 +0,0 @@
|
|||||||
git pull
|
|
||||||
pause>nul
|
|
@ -0,0 +1,57 @@
|
|||||||
|
# !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
|
@ -0,0 +1,54 @@
|
|||||||
|
# !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
|
@ -1,28 +0,0 @@
|
|||||||
|
|
||||||
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"))
|
|
@ -1,4 +0,0 @@
|
|||||||
$(document).ready(function() {
|
|
||||||
$('#test8-progress').progress();
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,39 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
|||||||
$('.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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
|||||||
Это лог
|
|
@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
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"))
|
|
@ -1,33 +0,0 @@
|
|||||||
<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>
|
|
@ -1,4 +0,0 @@
|
|||||||
$(document).ready(function() {
|
|
||||||
$('#test8-progress').progress();
|
|
||||||
|
|
||||||
});
|
|
@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
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)
|
|
@ -1,7 +0,0 @@
|
|||||||
$('.ui.dropdown')
|
|
||||||
.dropdown('clear')
|
|
||||||
;
|
|
||||||
|
|
||||||
$('.ui.dropdown')
|
|
||||||
.dropdown('clear')
|
|
||||||
;
|
|
@ -1,24 +0,0 @@
|
|||||||
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
|
|
@ -1,4 +0,0 @@
|
|||||||
chcp 65001
|
|
||||||
cd /d "%~dp0"
|
|
||||||
call init-python-env.cmd orpa-rbt.exe orpa-rbt.exe
|
|
||||||
cmd
|
|
@ -1,5 +1,5 @@
|
|||||||
chcp 65001
|
cd %~dp0
|
||||||
cd /d "%~dp0"
|
taskkill /im "orpa-orc.exe" /F /fi "username eq %username%"
|
||||||
call init-python-env.cmd orpa-orc.exe orpa-orc.exe
|
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-orc.exe
|
||||||
orpa-orc.exe "config.py"
|
.\..\Resources\WPy64-3720\python-3.7.2.amd64\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.
@ -1 +0,0 @@
|
|||||||
https://github.com/intxcc/pyaudio_portaudio
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -1,172 +0,0 @@
|
|||||||
#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
|
|
@ -1,424 +0,0 @@
|
|||||||
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): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
|
|
||||||
|
|
@ -0,0 +1,992 @@
|
|||||||
|
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
|
||||||
|
})
|
||||||
|
;
|
||||||
|
});
|
@ -0,0 +1,499 @@
|
|||||||
|
<!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>
|
||||||
|
|
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 110 KiB |
@ -1,11 +0,0 @@
|
|||||||
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)
|
|
@ -1,17 +0,0 @@
|
|||||||
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,
|
|
||||||
))
|
|
@ -1,7 +0,0 @@
|
|||||||
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.
@ -1,20 +0,0 @@
|
|||||||
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.
|
|
@ -1,82 +0,0 @@
|
|||||||
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
|
|
@ -1,5 +0,0 @@
|
|||||||
Wheel-Version: 1.0
|
|
||||||
Generator: bdist_wheel (0.37.1)
|
|
||||||
Root-Is-Purelib: true
|
|
||||||
Tag: py3-none-any
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
[pytest11]
|
|
||||||
anyio = anyio.pytest_plugin
|
|
@ -1 +0,0 @@
|
|||||||
anyio
|
|
@ -1,167 +0,0 @@
|
|||||||
__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
@ -1,988 +0,0 @@
|
|||||||
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)
|
|
@ -1,218 +0,0 @@
|
|||||||
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)
|
|
@ -1,93 +0,0 @@
|
|||||||
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."""
|
|
@ -1,607 +0,0 @@
|
|||||||
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)
|
|
@ -1,16 +0,0 @@
|
|||||||
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()
|
|
@ -1,24 +0,0 @@
|
|||||||
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)
|
|
@ -1,45 +0,0 @@
|
|||||||
import math
|
|
||||||
from typing import Any, Optional, Tuple, Type, TypeVar, overload
|
|
||||||
|
|
||||||
from ..streams.memory import (
|
|
||||||
MemoryObjectReceiveStream,
|
|
||||||
MemoryObjectSendStream,
|
|
||||||
MemoryObjectStreamState,
|
|
||||||
)
|
|
||||||
|
|
||||||
T_Item = TypeVar("T_Item")
|
|
||||||
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def create_memory_object_stream(
|
|
||||||
max_buffer_size: float, item_type: Type[T_Item]
|
|
||||||
) -> Tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def create_memory_object_stream(
|
|
||||||
max_buffer_size: float = 0,
|
|
||||||
) -> Tuple[MemoryObjectSendStream[Any], MemoryObjectReceiveStream[Any]]:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def create_memory_object_stream(
|
|
||||||
max_buffer_size: float = 0, item_type: Optional[Type[T_Item]] = None
|
|
||||||
) -> Tuple[MemoryObjectSendStream[Any], MemoryObjectReceiveStream[Any]]:
|
|
||||||
"""
|
|
||||||
Create a memory object stream.
|
|
||||||
|
|
||||||
:param max_buffer_size: number of items held in the buffer until ``send()`` starts blocking
|
|
||||||
:param item_type: type of item, for marking the streams with the right generic type for
|
|
||||||
static typing (not used at run time)
|
|
||||||
:return: a tuple of (send stream, receive stream)
|
|
||||||
|
|
||||||
"""
|
|
||||||
if max_buffer_size != math.inf and not isinstance(max_buffer_size, int):
|
|
||||||
raise ValueError("max_buffer_size must be either an integer or math.inf")
|
|
||||||
if max_buffer_size < 0:
|
|
||||||
raise ValueError("max_buffer_size cannot be negative")
|
|
||||||
|
|
||||||
state: MemoryObjectStreamState = MemoryObjectStreamState(max_buffer_size)
|
|
||||||
return MemoryObjectSendStream(state), MemoryObjectReceiveStream(state)
|
|
@ -1,136 +0,0 @@
|
|||||||
from io import BytesIO
|
|
||||||
from os import PathLike
|
|
||||||
from subprocess import DEVNULL, PIPE, CalledProcessError, CompletedProcess
|
|
||||||
from typing import (
|
|
||||||
IO,
|
|
||||||
Any,
|
|
||||||
AsyncIterable,
|
|
||||||
List,
|
|
||||||
Mapping,
|
|
||||||
Optional,
|
|
||||||
Sequence,
|
|
||||||
Union,
|
|
||||||
cast,
|
|
||||||
)
|
|
||||||
|
|
||||||
from ..abc import Process
|
|
||||||
from ._eventloop import get_asynclib
|
|
||||||
from ._tasks import create_task_group
|
|
||||||
|
|
||||||
|
|
||||||
async def run_process(
|
|
||||||
command: Union[str, bytes, Sequence[Union[str, bytes]]],
|
|
||||||
*,
|
|
||||||
input: Optional[bytes] = None,
|
|
||||||
stdout: Union[int, IO[Any], None] = PIPE,
|
|
||||||
stderr: Union[int, IO[Any], None] = PIPE,
|
|
||||||
check: bool = True,
|
|
||||||
cwd: Union[str, bytes, "PathLike[str]", None] = None,
|
|
||||||
env: Optional[Mapping[str, str]] = None,
|
|
||||||
start_new_session: bool = False,
|
|
||||||
) -> "CompletedProcess[bytes]":
|
|
||||||
"""
|
|
||||||
Run an external command in a subprocess and wait until it completes.
|
|
||||||
|
|
||||||
.. seealso:: :func:`subprocess.run`
|
|
||||||
|
|
||||||
:param command: either a string to pass to the shell, or an iterable of strings containing the
|
|
||||||
executable name or path and its arguments
|
|
||||||
:param input: bytes passed to the standard input of the subprocess
|
|
||||||
:param stdout: either :data:`subprocess.PIPE` or :data:`subprocess.DEVNULL`
|
|
||||||
:param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL` or
|
|
||||||
:data:`subprocess.STDOUT`
|
|
||||||
:param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the process
|
|
||||||
terminates with a return code other than 0
|
|
||||||
:param cwd: If not ``None``, change the working directory to this before running the command
|
|
||||||
:param env: if not ``None``, this mapping replaces the inherited environment variables from the
|
|
||||||
parent process
|
|
||||||
:param start_new_session: if ``true`` the setsid() system call will be made in the child
|
|
||||||
process prior to the execution of the subprocess. (POSIX only)
|
|
||||||
:return: an object representing the completed process
|
|
||||||
:raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process exits with a
|
|
||||||
nonzero return code
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None:
|
|
||||||
buffer = BytesIO()
|
|
||||||
async for chunk in stream:
|
|
||||||
buffer.write(chunk)
|
|
||||||
|
|
||||||
stream_contents[index] = buffer.getvalue()
|
|
||||||
|
|
||||||
async with await open_process(
|
|
||||||
command,
|
|
||||||
stdin=PIPE if input else DEVNULL,
|
|
||||||
stdout=stdout,
|
|
||||||
stderr=stderr,
|
|
||||||
cwd=cwd,
|
|
||||||
env=env,
|
|
||||||
start_new_session=start_new_session,
|
|
||||||
) as process:
|
|
||||||
stream_contents: List[Optional[bytes]] = [None, None]
|
|
||||||
try:
|
|
||||||
async with create_task_group() as tg:
|
|
||||||
if process.stdout:
|
|
||||||
tg.start_soon(drain_stream, process.stdout, 0)
|
|
||||||
if process.stderr:
|
|
||||||
tg.start_soon(drain_stream, process.stderr, 1)
|
|
||||||
if process.stdin and input:
|
|
||||||
await process.stdin.send(input)
|
|
||||||
await process.stdin.aclose()
|
|
||||||
|
|
||||||
await process.wait()
|
|
||||||
except BaseException:
|
|
||||||
process.kill()
|
|
||||||
raise
|
|
||||||
|
|
||||||
output, errors = stream_contents
|
|
||||||
if check and process.returncode != 0:
|
|
||||||
raise CalledProcessError(cast(int, process.returncode), command, output, errors)
|
|
||||||
|
|
||||||
return CompletedProcess(command, cast(int, process.returncode), output, errors)
|
|
||||||
|
|
||||||
|
|
||||||
async def open_process(
|
|
||||||
command: Union[str, bytes, Sequence[Union[str, bytes]]],
|
|
||||||
*,
|
|
||||||
stdin: Union[int, IO[Any], None] = PIPE,
|
|
||||||
stdout: Union[int, IO[Any], None] = PIPE,
|
|
||||||
stderr: Union[int, IO[Any], None] = PIPE,
|
|
||||||
cwd: Union[str, bytes, "PathLike[str]", None] = None,
|
|
||||||
env: Optional[Mapping[str, str]] = None,
|
|
||||||
start_new_session: bool = False,
|
|
||||||
) -> Process:
|
|
||||||
"""
|
|
||||||
Start an external command in a subprocess.
|
|
||||||
|
|
||||||
.. seealso:: :class:`subprocess.Popen`
|
|
||||||
|
|
||||||
:param command: either a string to pass to the shell, or an iterable of strings containing the
|
|
||||||
executable name or path and its arguments
|
|
||||||
:param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a
|
|
||||||
file-like object, or ``None``
|
|
||||||
:param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
|
|
||||||
a file-like object, or ``None``
|
|
||||||
:param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
|
|
||||||
:data:`subprocess.STDOUT`, a file-like object, or ``None``
|
|
||||||
:param cwd: If not ``None``, the working directory is changed before executing
|
|
||||||
:param env: If env is not ``None``, it must be a mapping that defines the environment
|
|
||||||
variables for the new process
|
|
||||||
:param start_new_session: if ``true`` the setsid() system call will be made in the child
|
|
||||||
process prior to the execution of the subprocess. (POSIX only)
|
|
||||||
:return: an asynchronous process object
|
|
||||||
|
|
||||||
"""
|
|
||||||
shell = isinstance(command, str)
|
|
||||||
return await get_asynclib().open_process(
|
|
||||||
command,
|
|
||||||
shell=shell,
|
|
||||||
stdin=stdin,
|
|
||||||
stdout=stdout,
|
|
||||||
stderr=stderr,
|
|
||||||
cwd=cwd,
|
|
||||||
env=env,
|
|
||||||
start_new_session=start_new_session,
|
|
||||||
)
|
|
@ -1,595 +0,0 @@
|
|||||||
from collections import deque
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from types import TracebackType
|
|
||||||
from typing import Deque, Optional, Tuple, Type
|
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from ..lowlevel import cancel_shielded_checkpoint, checkpoint, checkpoint_if_cancelled
|
|
||||||
from ._compat import DeprecatedAwaitable
|
|
||||||
from ._eventloop import get_asynclib
|
|
||||||
from ._exceptions import BusyResourceError, WouldBlock
|
|
||||||
from ._tasks import CancelScope
|
|
||||||
from ._testing import TaskInfo, get_current_task
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class EventStatistics:
|
|
||||||
"""
|
|
||||||
:ivar int tasks_waiting: number of tasks waiting on :meth:`~.Event.wait`
|
|
||||||
"""
|
|
||||||
|
|
||||||
tasks_waiting: int
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class CapacityLimiterStatistics:
|
|
||||||
"""
|
|
||||||
:ivar int borrowed_tokens: number of tokens currently borrowed by tasks
|
|
||||||
:ivar float total_tokens: total number of available tokens
|
|
||||||
:ivar tuple borrowers: tasks or other objects currently holding tokens borrowed from this
|
|
||||||
limiter
|
|
||||||
:ivar int tasks_waiting: number of tasks waiting on :meth:`~.CapacityLimiter.acquire` or
|
|
||||||
:meth:`~.CapacityLimiter.acquire_on_behalf_of`
|
|
||||||
"""
|
|
||||||
|
|
||||||
borrowed_tokens: int
|
|
||||||
total_tokens: float
|
|
||||||
borrowers: Tuple[object, ...]
|
|
||||||
tasks_waiting: int
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class LockStatistics:
|
|
||||||
"""
|
|
||||||
:ivar bool locked: flag indicating if this lock is locked or not
|
|
||||||
:ivar ~anyio.TaskInfo owner: task currently holding the lock (or ``None`` if the lock is not
|
|
||||||
held by any task)
|
|
||||||
:ivar int tasks_waiting: number of tasks waiting on :meth:`~.Lock.acquire`
|
|
||||||
"""
|
|
||||||
|
|
||||||
locked: bool
|
|
||||||
owner: Optional[TaskInfo]
|
|
||||||
tasks_waiting: int
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class ConditionStatistics:
|
|
||||||
"""
|
|
||||||
:ivar int tasks_waiting: number of tasks blocked on :meth:`~.Condition.wait`
|
|
||||||
:ivar ~anyio.LockStatistics lock_statistics: statistics of the underlying :class:`~.Lock`
|
|
||||||
"""
|
|
||||||
|
|
||||||
tasks_waiting: int
|
|
||||||
lock_statistics: LockStatistics
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class SemaphoreStatistics:
|
|
||||||
"""
|
|
||||||
:ivar int tasks_waiting: number of tasks waiting on :meth:`~.Semaphore.acquire`
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
tasks_waiting: int
|
|
||||||
|
|
||||||
|
|
||||||
class Event:
|
|
||||||
def __new__(cls) -> "Event":
|
|
||||||
return get_asynclib().Event()
|
|
||||||
|
|
||||||
def set(self) -> DeprecatedAwaitable:
|
|
||||||
"""Set the flag, notifying all listeners."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def is_set(self) -> bool:
|
|
||||||
"""Return ``True`` if the flag is set, ``False`` if not."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
async def wait(self) -> None:
|
|
||||||
"""
|
|
||||||
Wait until the flag has been set.
|
|
||||||
|
|
||||||
If the flag has already been set when this method is called, it returns immediately.
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def statistics(self) -> EventStatistics:
|
|
||||||
"""Return statistics about the current state of this event."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
class Lock:
|
|
||||||
_owner_task: Optional[TaskInfo] = None
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self._waiters: Deque[Tuple[TaskInfo, Event]] = deque()
|
|
||||||
|
|
||||||
async def __aenter__(self) -> None:
|
|
||||||
await self.acquire()
|
|
||||||
|
|
||||||
async def __aexit__(
|
|
||||||
self,
|
|
||||||
exc_type: Optional[Type[BaseException]],
|
|
||||||
exc_val: Optional[BaseException],
|
|
||||||
exc_tb: Optional[TracebackType],
|
|
||||||
) -> None:
|
|
||||||
self.release()
|
|
||||||
|
|
||||||
async def acquire(self) -> None:
|
|
||||||
"""Acquire the lock."""
|
|
||||||
await checkpoint_if_cancelled()
|
|
||||||
try:
|
|
||||||
self.acquire_nowait()
|
|
||||||
except WouldBlock:
|
|
||||||
task = get_current_task()
|
|
||||||
event = Event()
|
|
||||||
token = task, event
|
|
||||||
self._waiters.append(token)
|
|
||||||
try:
|
|
||||||
await event.wait()
|
|
||||||
except BaseException:
|
|
||||||
if not event.is_set():
|
|
||||||
self._waiters.remove(token)
|
|
||||||
elif self._owner_task == task:
|
|
||||||
self.release()
|
|
||||||
|
|
||||||
raise
|
|
||||||
|
|
||||||
assert self._owner_task == task
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
await cancel_shielded_checkpoint()
|
|
||||||
except BaseException:
|
|
||||||
self.release()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def acquire_nowait(self) -> None:
|
|
||||||
"""
|
|
||||||
Acquire the lock, without blocking.
|
|
||||||
|
|
||||||
:raises ~WouldBlock: if the operation would block
|
|
||||||
|
|
||||||
"""
|
|
||||||
task = get_current_task()
|
|
||||||
if self._owner_task == task:
|
|
||||||
raise RuntimeError("Attempted to acquire an already held Lock")
|
|
||||||
|
|
||||||
if self._owner_task is not None:
|
|
||||||
raise WouldBlock
|
|
||||||
|
|
||||||
self._owner_task = task
|
|
||||||
|
|
||||||
def release(self) -> DeprecatedAwaitable:
|
|
||||||
"""Release the lock."""
|
|
||||||
if self._owner_task != get_current_task():
|
|
||||||
raise RuntimeError("The current task is not holding this lock")
|
|
||||||
|
|
||||||
if self._waiters:
|
|
||||||
self._owner_task, event = self._waiters.popleft()
|
|
||||||
event.set()
|
|
||||||
else:
|
|
||||||
del self._owner_task
|
|
||||||
|
|
||||||
return DeprecatedAwaitable(self.release)
|
|
||||||
|
|
||||||
def locked(self) -> bool:
|
|
||||||
"""Return True if the lock is currently held."""
|
|
||||||
return self._owner_task is not None
|
|
||||||
|
|
||||||
def statistics(self) -> LockStatistics:
|
|
||||||
"""
|
|
||||||
Return statistics about the current state of this lock.
|
|
||||||
|
|
||||||
.. versionadded:: 3.0
|
|
||||||
"""
|
|
||||||
return LockStatistics(self.locked(), self._owner_task, len(self._waiters))
|
|
||||||
|
|
||||||
|
|
||||||
class Condition:
|
|
||||||
_owner_task: Optional[TaskInfo] = None
|
|
||||||
|
|
||||||
def __init__(self, lock: Optional[Lock] = None):
|
|
||||||
self._lock = lock or Lock()
|
|
||||||
self._waiters: Deque[Event] = deque()
|
|
||||||
|
|
||||||
async def __aenter__(self) -> None:
|
|
||||||
await self.acquire()
|
|
||||||
|
|
||||||
async def __aexit__(
|
|
||||||
self,
|
|
||||||
exc_type: Optional[Type[BaseException]],
|
|
||||||
exc_val: Optional[BaseException],
|
|
||||||
exc_tb: Optional[TracebackType],
|
|
||||||
) -> None:
|
|
||||||
self.release()
|
|
||||||
|
|
||||||
def _check_acquired(self) -> None:
|
|
||||||
if self._owner_task != get_current_task():
|
|
||||||
raise RuntimeError("The current task is not holding the underlying lock")
|
|
||||||
|
|
||||||
async def acquire(self) -> None:
|
|
||||||
"""Acquire the underlying lock."""
|
|
||||||
await self._lock.acquire()
|
|
||||||
self._owner_task = get_current_task()
|
|
||||||
|
|
||||||
def acquire_nowait(self) -> None:
|
|
||||||
"""
|
|
||||||
Acquire the underlying lock, without blocking.
|
|
||||||
|
|
||||||
:raises ~WouldBlock: if the operation would block
|
|
||||||
|
|
||||||
"""
|
|
||||||
self._lock.acquire_nowait()
|
|
||||||
self._owner_task = get_current_task()
|
|
||||||
|
|
||||||
def release(self) -> DeprecatedAwaitable:
|
|
||||||
"""Release the underlying lock."""
|
|
||||||
self._lock.release()
|
|
||||||
return DeprecatedAwaitable(self.release)
|
|
||||||
|
|
||||||
def locked(self) -> bool:
|
|
||||||
"""Return True if the lock is set."""
|
|
||||||
return self._lock.locked()
|
|
||||||
|
|
||||||
def notify(self, n: int = 1) -> None:
|
|
||||||
"""Notify exactly n listeners."""
|
|
||||||
self._check_acquired()
|
|
||||||
for _ in range(n):
|
|
||||||
try:
|
|
||||||
event = self._waiters.popleft()
|
|
||||||
except IndexError:
|
|
||||||
break
|
|
||||||
|
|
||||||
event.set()
|
|
||||||
|
|
||||||
def notify_all(self) -> None:
|
|
||||||
"""Notify all the listeners."""
|
|
||||||
self._check_acquired()
|
|
||||||
for event in self._waiters:
|
|
||||||
event.set()
|
|
||||||
|
|
||||||
self._waiters.clear()
|
|
||||||
|
|
||||||
async def wait(self) -> None:
|
|
||||||
"""Wait for a notification."""
|
|
||||||
await checkpoint()
|
|
||||||
event = Event()
|
|
||||||
self._waiters.append(event)
|
|
||||||
self.release()
|
|
||||||
try:
|
|
||||||
await event.wait()
|
|
||||||
except BaseException:
|
|
||||||
if not event.is_set():
|
|
||||||
self._waiters.remove(event)
|
|
||||||
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
with CancelScope(shield=True):
|
|
||||||
await self.acquire()
|
|
||||||
|
|
||||||
def statistics(self) -> ConditionStatistics:
|
|
||||||
"""
|
|
||||||
Return statistics about the current state of this condition.
|
|
||||||
|
|
||||||
.. versionadded:: 3.0
|
|
||||||
"""
|
|
||||||
return ConditionStatistics(len(self._waiters), self._lock.statistics())
|
|
||||||
|
|
||||||
|
|
||||||
class Semaphore:
|
|
||||||
def __init__(self, initial_value: int, *, max_value: Optional[int] = None):
|
|
||||||
if not isinstance(initial_value, int):
|
|
||||||
raise TypeError("initial_value must be an integer")
|
|
||||||
if initial_value < 0:
|
|
||||||
raise ValueError("initial_value must be >= 0")
|
|
||||||
if max_value is not None:
|
|
||||||
if not isinstance(max_value, int):
|
|
||||||
raise TypeError("max_value must be an integer or None")
|
|
||||||
if max_value < initial_value:
|
|
||||||
raise ValueError(
|
|
||||||
"max_value must be equal to or higher than initial_value"
|
|
||||||
)
|
|
||||||
|
|
||||||
self._value = initial_value
|
|
||||||
self._max_value = max_value
|
|
||||||
self._waiters: Deque[Event] = deque()
|
|
||||||
|
|
||||||
async def __aenter__(self) -> "Semaphore":
|
|
||||||
await self.acquire()
|
|
||||||
return self
|
|
||||||
|
|
||||||
async def __aexit__(
|
|
||||||
self,
|
|
||||||
exc_type: Optional[Type[BaseException]],
|
|
||||||
exc_val: Optional[BaseException],
|
|
||||||
exc_tb: Optional[TracebackType],
|
|
||||||
) -> None:
|
|
||||||
self.release()
|
|
||||||
|
|
||||||
async def acquire(self) -> None:
|
|
||||||
"""Decrement the semaphore value, blocking if necessary."""
|
|
||||||
await checkpoint_if_cancelled()
|
|
||||||
try:
|
|
||||||
self.acquire_nowait()
|
|
||||||
except WouldBlock:
|
|
||||||
event = Event()
|
|
||||||
self._waiters.append(event)
|
|
||||||
try:
|
|
||||||
await event.wait()
|
|
||||||
except BaseException:
|
|
||||||
if not event.is_set():
|
|
||||||
self._waiters.remove(event)
|
|
||||||
else:
|
|
||||||
self.release()
|
|
||||||
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
await cancel_shielded_checkpoint()
|
|
||||||
except BaseException:
|
|
||||||
self.release()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def acquire_nowait(self) -> None:
|
|
||||||
"""
|
|
||||||
Acquire the underlying lock, without blocking.
|
|
||||||
|
|
||||||
:raises ~WouldBlock: if the operation would block
|
|
||||||
|
|
||||||
"""
|
|
||||||
if self._value == 0:
|
|
||||||
raise WouldBlock
|
|
||||||
|
|
||||||
self._value -= 1
|
|
||||||
|
|
||||||
def release(self) -> DeprecatedAwaitable:
|
|
||||||
"""Increment the semaphore value."""
|
|
||||||
if self._max_value is not None and self._value == self._max_value:
|
|
||||||
raise ValueError("semaphore released too many times")
|
|
||||||
|
|
||||||
if self._waiters:
|
|
||||||
self._waiters.popleft().set()
|
|
||||||
else:
|
|
||||||
self._value += 1
|
|
||||||
|
|
||||||
return DeprecatedAwaitable(self.release)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def value(self) -> int:
|
|
||||||
"""The current value of the semaphore."""
|
|
||||||
return self._value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_value(self) -> Optional[int]:
|
|
||||||
"""The maximum value of the semaphore."""
|
|
||||||
return self._max_value
|
|
||||||
|
|
||||||
def statistics(self) -> SemaphoreStatistics:
|
|
||||||
"""
|
|
||||||
Return statistics about the current state of this semaphore.
|
|
||||||
|
|
||||||
.. versionadded:: 3.0
|
|
||||||
"""
|
|
||||||
return SemaphoreStatistics(len(self._waiters))
|
|
||||||
|
|
||||||
|
|
||||||
class CapacityLimiter:
|
|
||||||
def __new__(cls, total_tokens: float) -> "CapacityLimiter":
|
|
||||||
return get_asynclib().CapacityLimiter(total_tokens)
|
|
||||||
|
|
||||||
async def __aenter__(self) -> None:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
async def __aexit__(
|
|
||||||
self,
|
|
||||||
exc_type: Optional[Type[BaseException]],
|
|
||||||
exc_val: Optional[BaseException],
|
|
||||||
exc_tb: Optional[TracebackType],
|
|
||||||
) -> Optional[bool]:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
|
||||||
def total_tokens(self) -> float:
|
|
||||||
"""
|
|
||||||
The total number of tokens available for borrowing.
|
|
||||||
|
|
||||||
This is a read-write property. If the total number of tokens is increased, the
|
|
||||||
proportionate number of tasks waiting on this limiter will be granted their tokens.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.0
|
|
||||||
The property is now writable.
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@total_tokens.setter
|
|
||||||
def total_tokens(self, value: float) -> None:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
async def set_total_tokens(self, value: float) -> None:
|
|
||||||
warn(
|
|
||||||
"CapacityLimiter.set_total_tokens has been deprecated. Set the value of the"
|
|
||||||
'"total_tokens" attribute directly.',
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
self.total_tokens = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def borrowed_tokens(self) -> int:
|
|
||||||
"""The number of tokens that have currently been borrowed."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
|
||||||
def available_tokens(self) -> float:
|
|
||||||
"""The number of tokens currently available to be borrowed"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def acquire_nowait(self) -> DeprecatedAwaitable:
|
|
||||||
"""
|
|
||||||
Acquire a token for the current task without waiting for one to become available.
|
|
||||||
|
|
||||||
:raises ~anyio.WouldBlock: if there are no tokens available for borrowing
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def acquire_on_behalf_of_nowait(self, borrower: object) -> DeprecatedAwaitable:
|
|
||||||
"""
|
|
||||||
Acquire a token without waiting for one to become available.
|
|
||||||
|
|
||||||
:param borrower: the entity borrowing a token
|
|
||||||
:raises ~anyio.WouldBlock: if there are no tokens available for borrowing
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
async def acquire(self) -> None:
|
|
||||||
"""
|
|
||||||
Acquire a token for the current task, waiting if necessary for one to become available.
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
async def acquire_on_behalf_of(self, borrower: object) -> None:
|
|
||||||
"""
|
|
||||||
Acquire a token, waiting if necessary for one to become available.
|
|
||||||
|
|
||||||
:param borrower: the entity borrowing a token
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def release(self) -> None:
|
|
||||||
"""
|
|
||||||
Release the token held by the current task.
|
|
||||||
:raises RuntimeError: if the current task has not borrowed a token from this limiter.
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def release_on_behalf_of(self, borrower: object) -> None:
|
|
||||||
"""
|
|
||||||
Release the token held by the given borrower.
|
|
||||||
|
|
||||||
:raises RuntimeError: if the borrower has not borrowed a token from this limiter.
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def statistics(self) -> CapacityLimiterStatistics:
|
|
||||||
"""
|
|
||||||
Return statistics about the current state of this limiter.
|
|
||||||
|
|
||||||
.. versionadded:: 3.0
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
def create_lock() -> Lock:
|
|
||||||
"""
|
|
||||||
Create an asynchronous lock.
|
|
||||||
|
|
||||||
:return: a lock object
|
|
||||||
|
|
||||||
.. deprecated:: 3.0
|
|
||||||
Use :class:`~Lock` directly.
|
|
||||||
|
|
||||||
"""
|
|
||||||
warn("create_lock() is deprecated -- use Lock() directly", DeprecationWarning)
|
|
||||||
return Lock()
|
|
||||||
|
|
||||||
|
|
||||||
def create_condition(lock: Optional[Lock] = None) -> Condition:
|
|
||||||
"""
|
|
||||||
Create an asynchronous condition.
|
|
||||||
|
|
||||||
:param lock: the lock to base the condition object on
|
|
||||||
:return: a condition object
|
|
||||||
|
|
||||||
.. deprecated:: 3.0
|
|
||||||
Use :class:`~Condition` directly.
|
|
||||||
|
|
||||||
"""
|
|
||||||
warn(
|
|
||||||
"create_condition() is deprecated -- use Condition() directly",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
return Condition(lock=lock)
|
|
||||||
|
|
||||||
|
|
||||||
def create_event() -> Event:
|
|
||||||
"""
|
|
||||||
Create an asynchronous event object.
|
|
||||||
|
|
||||||
:return: an event object
|
|
||||||
|
|
||||||
.. deprecated:: 3.0
|
|
||||||
Use :class:`~Event` directly.
|
|
||||||
|
|
||||||
"""
|
|
||||||
warn("create_event() is deprecated -- use Event() directly", DeprecationWarning)
|
|
||||||
return get_asynclib().Event()
|
|
||||||
|
|
||||||
|
|
||||||
def create_semaphore(value: int, *, max_value: Optional[int] = None) -> Semaphore:
|
|
||||||
"""
|
|
||||||
Create an asynchronous semaphore.
|
|
||||||
|
|
||||||
:param value: the semaphore's initial value
|
|
||||||
:param max_value: if set, makes this a "bounded" semaphore that raises :exc:`ValueError` if the
|
|
||||||
semaphore's value would exceed this number
|
|
||||||
:return: a semaphore object
|
|
||||||
|
|
||||||
.. deprecated:: 3.0
|
|
||||||
Use :class:`~Semaphore` directly.
|
|
||||||
|
|
||||||
"""
|
|
||||||
warn(
|
|
||||||
"create_semaphore() is deprecated -- use Semaphore() directly",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
return Semaphore(value, max_value=max_value)
|
|
||||||
|
|
||||||
|
|
||||||
def create_capacity_limiter(total_tokens: float) -> CapacityLimiter:
|
|
||||||
"""
|
|
||||||
Create a capacity limiter.
|
|
||||||
|
|
||||||
:param total_tokens: the total number of tokens available for borrowing (can be an integer or
|
|
||||||
:data:`math.inf`)
|
|
||||||
:return: a capacity limiter object
|
|
||||||
|
|
||||||
.. deprecated:: 3.0
|
|
||||||
Use :class:`~CapacityLimiter` directly.
|
|
||||||
|
|
||||||
"""
|
|
||||||
warn(
|
|
||||||
"create_capacity_limiter() is deprecated -- use CapacityLimiter() directly",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
return get_asynclib().CapacityLimiter(total_tokens)
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceGuard:
|
|
||||||
__slots__ = "action", "_guarded"
|
|
||||||
|
|
||||||
def __init__(self, action: str):
|
|
||||||
self.action = action
|
|
||||||
self._guarded = False
|
|
||||||
|
|
||||||
def __enter__(self) -> None:
|
|
||||||
if self._guarded:
|
|
||||||
raise BusyResourceError(self.action)
|
|
||||||
|
|
||||||
self._guarded = True
|
|
||||||
|
|
||||||
def __exit__(
|
|
||||||
self,
|
|
||||||
exc_type: Optional[Type[BaseException]],
|
|
||||||
exc_val: Optional[BaseException],
|
|
||||||
exc_tb: Optional[TracebackType],
|
|
||||||
) -> Optional[bool]:
|
|
||||||
self._guarded = False
|
|
||||||
return None
|
|
@ -1,178 +0,0 @@
|
|||||||
import math
|
|
||||||
from types import TracebackType
|
|
||||||
from typing import Optional, Type
|
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from ..abc._tasks import TaskGroup, TaskStatus
|
|
||||||
from ._compat import (
|
|
||||||
DeprecatedAsyncContextManager,
|
|
||||||
DeprecatedAwaitable,
|
|
||||||
DeprecatedAwaitableFloat,
|
|
||||||
)
|
|
||||||
from ._eventloop import get_asynclib
|
|
||||||
|
|
||||||
|
|
||||||
class _IgnoredTaskStatus(TaskStatus):
|
|
||||||
def started(self, value: object = None) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
TASK_STATUS_IGNORED = _IgnoredTaskStatus()
|
|
||||||
|
|
||||||
|
|
||||||
class CancelScope(DeprecatedAsyncContextManager["CancelScope"]):
|
|
||||||
"""
|
|
||||||
Wraps a unit of work that can be made separately cancellable.
|
|
||||||
|
|
||||||
:param deadline: The time (clock value) when this scope is cancelled automatically
|
|
||||||
:param shield: ``True`` to shield the cancel scope from external cancellation
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __new__(
|
|
||||||
cls, *, deadline: float = math.inf, shield: bool = False
|
|
||||||
) -> "CancelScope":
|
|
||||||
return get_asynclib().CancelScope(shield=shield, deadline=deadline)
|
|
||||||
|
|
||||||
def cancel(self) -> DeprecatedAwaitable:
|
|
||||||
"""Cancel this scope immediately."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
|
||||||
def deadline(self) -> float:
|
|
||||||
"""
|
|
||||||
The time (clock value) when this scope is cancelled automatically.
|
|
||||||
|
|
||||||
Will be ``float('inf')`` if no timeout has been set.
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@deadline.setter
|
|
||||||
def deadline(self, value: float) -> None:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cancel_called(self) -> bool:
|
|
||||||
"""``True`` if :meth:`cancel` has been called."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
|
||||||
def shield(self) -> bool:
|
|
||||||
"""
|
|
||||||
``True`` if this scope is shielded from external cancellation.
|
|
||||||
|
|
||||||
While a scope is shielded, it will not receive cancellations from outside.
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@shield.setter
|
|
||||||
def shield(self, value: bool) -> None:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def __enter__(self) -> "CancelScope":
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def __exit__(
|
|
||||||
self,
|
|
||||||
exc_type: Optional[Type[BaseException]],
|
|
||||||
exc_val: Optional[BaseException],
|
|
||||||
exc_tb: Optional[TracebackType],
|
|
||||||
) -> Optional[bool]:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
def open_cancel_scope(*, shield: bool = False) -> CancelScope:
|
|
||||||
"""
|
|
||||||
Open a cancel scope.
|
|
||||||
|
|
||||||
:param shield: ``True`` to shield the cancel scope from external cancellation
|
|
||||||
:return: a cancel scope
|
|
||||||
|
|
||||||
.. deprecated:: 3.0
|
|
||||||
Use :class:`~CancelScope` directly.
|
|
||||||
|
|
||||||
"""
|
|
||||||
warn(
|
|
||||||
"open_cancel_scope() is deprecated -- use CancelScope() directly",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
return get_asynclib().CancelScope(shield=shield)
|
|
||||||
|
|
||||||
|
|
||||||
class FailAfterContextManager(DeprecatedAsyncContextManager[CancelScope]):
|
|
||||||
def __init__(self, cancel_scope: CancelScope):
|
|
||||||
self._cancel_scope = cancel_scope
|
|
||||||
|
|
||||||
def __enter__(self) -> CancelScope:
|
|
||||||
return self._cancel_scope.__enter__()
|
|
||||||
|
|
||||||
def __exit__(
|
|
||||||
self,
|
|
||||||
exc_type: Optional[Type[BaseException]],
|
|
||||||
exc_val: Optional[BaseException],
|
|
||||||
exc_tb: Optional[TracebackType],
|
|
||||||
) -> Optional[bool]:
|
|
||||||
retval = self._cancel_scope.__exit__(exc_type, exc_val, exc_tb)
|
|
||||||
if self._cancel_scope.cancel_called:
|
|
||||||
raise TimeoutError
|
|
||||||
|
|
||||||
return retval
|
|
||||||
|
|
||||||
|
|
||||||
def fail_after(delay: Optional[float], shield: bool = False) -> FailAfterContextManager:
|
|
||||||
"""
|
|
||||||
Create a context manager which raises a :class:`TimeoutError` if does not finish in time.
|
|
||||||
|
|
||||||
:param delay: maximum allowed time (in seconds) before raising the exception, or ``None`` to
|
|
||||||
disable the timeout
|
|
||||||
:param shield: ``True`` to shield the cancel scope from external cancellation
|
|
||||||
:return: a context manager that yields a cancel scope
|
|
||||||
:rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.abc.CancelScope`\\]
|
|
||||||
|
|
||||||
"""
|
|
||||||
deadline = (
|
|
||||||
(get_asynclib().current_time() + delay) if delay is not None else math.inf
|
|
||||||
)
|
|
||||||
cancel_scope = get_asynclib().CancelScope(deadline=deadline, shield=shield)
|
|
||||||
return FailAfterContextManager(cancel_scope)
|
|
||||||
|
|
||||||
|
|
||||||
def move_on_after(delay: Optional[float], shield: bool = False) -> CancelScope:
|
|
||||||
"""
|
|
||||||
Create a cancel scope with a deadline that expires after the given delay.
|
|
||||||
|
|
||||||
:param delay: maximum allowed time (in seconds) before exiting the context block, or ``None``
|
|
||||||
to disable the timeout
|
|
||||||
:param shield: ``True`` to shield the cancel scope from external cancellation
|
|
||||||
:return: a cancel scope
|
|
||||||
|
|
||||||
"""
|
|
||||||
deadline = (
|
|
||||||
(get_asynclib().current_time() + delay) if delay is not None else math.inf
|
|
||||||
)
|
|
||||||
return get_asynclib().CancelScope(deadline=deadline, shield=shield)
|
|
||||||
|
|
||||||
|
|
||||||
def current_effective_deadline() -> DeprecatedAwaitableFloat:
|
|
||||||
"""
|
|
||||||
Return the nearest deadline among all the cancel scopes effective for the current task.
|
|
||||||
|
|
||||||
:return: a clock value from the event loop's internal clock (``float('inf')`` if there is no
|
|
||||||
deadline in effect)
|
|
||||||
:rtype: float
|
|
||||||
|
|
||||||
"""
|
|
||||||
return DeprecatedAwaitableFloat(
|
|
||||||
get_asynclib().current_effective_deadline(), current_effective_deadline
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def create_task_group() -> "TaskGroup":
|
|
||||||
"""
|
|
||||||
Create a task group.
|
|
||||||
|
|
||||||
:return: a task group
|
|
||||||
|
|
||||||
"""
|
|
||||||
return get_asynclib().TaskGroup()
|
|
@ -1,80 +0,0 @@
|
|||||||
from typing import Any, Awaitable, Generator, Optional, Union
|
|
||||||
|
|
||||||
from ._compat import DeprecatedAwaitableList, _warn_deprecation
|
|
||||||
from ._eventloop import get_asynclib
|
|
||||||
|
|
||||||
|
|
||||||
class TaskInfo:
|
|
||||||
"""
|
|
||||||
Represents an asynchronous task.
|
|
||||||
|
|
||||||
:ivar int id: the unique identifier of the task
|
|
||||||
:ivar parent_id: the identifier of the parent task, if any
|
|
||||||
:vartype parent_id: Optional[int]
|
|
||||||
:ivar str name: the description of the task (if any)
|
|
||||||
:ivar ~collections.abc.Coroutine coro: the coroutine object of the task
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = "_name", "id", "parent_id", "name", "coro"
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
id: int,
|
|
||||||
parent_id: Optional[int],
|
|
||||||
name: Optional[str],
|
|
||||||
coro: Union[Generator, Awaitable[Any]],
|
|
||||||
):
|
|
||||||
func = get_current_task
|
|
||||||
self._name = f"{func.__module__}.{func.__qualname__}"
|
|
||||||
self.id: int = id
|
|
||||||
self.parent_id: Optional[int] = parent_id
|
|
||||||
self.name: Optional[str] = name
|
|
||||||
self.coro: Union[Generator, Awaitable[Any]] = coro
|
|
||||||
|
|
||||||
def __eq__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, TaskInfo):
|
|
||||||
return self.id == other.id
|
|
||||||
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
|
||||||
return hash(self.id)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})"
|
|
||||||
|
|
||||||
def __await__(self) -> Generator[None, None, "TaskInfo"]:
|
|
||||||
_warn_deprecation(self)
|
|
||||||
if False:
|
|
||||||
yield
|
|
||||||
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _unwrap(self) -> "TaskInfo":
|
|
||||||
return self
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_task() -> TaskInfo:
|
|
||||||
"""
|
|
||||||
Return the current task.
|
|
||||||
|
|
||||||
:return: a representation of the current task
|
|
||||||
|
|
||||||
"""
|
|
||||||
return get_asynclib().get_current_task()
|
|
||||||
|
|
||||||
|
|
||||||
def get_running_tasks() -> DeprecatedAwaitableList[TaskInfo]:
|
|
||||||
"""
|
|
||||||
Return a list of running tasks in the current event loop.
|
|
||||||
|
|
||||||
:return: a list of task info objects
|
|
||||||
|
|
||||||
"""
|
|
||||||
tasks = get_asynclib().get_running_tasks()
|
|
||||||
return DeprecatedAwaitableList(tasks, func=get_running_tasks)
|
|
||||||
|
|
||||||
|
|
||||||
async def wait_all_tasks_blocked() -> None:
|
|
||||||
"""Wait until all other tasks are waiting for something."""
|
|
||||||
await get_asynclib().wait_all_tasks_blocked()
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue