Compare commits
No commits in common. 'prd' and 'dev' have entirely different histories.
@ -1,5 +1,4 @@
|
||||
chcp 65001
|
||||
cd /d "%~dp0"
|
||||
cd %~dp0
|
||||
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
|
||||
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
|
||||
|
@ -1,5 +1,4 @@
|
||||
chcp 65001
|
||||
cd /d "%~dp0"
|
||||
cd %~dp0
|
||||
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
|
||||
.\..\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 /d "%~dp0"
|
||||
call init-python-env.cmd orpa-orc.exe orpa-orc.exe
|
||||
orpa-orc.exe "config.py"
|
||||
cd %~dp0
|
||||
taskkill /im "orpa-orc.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-orc.exe
|
||||
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-orc.exe "config.py"
|
||||
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()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue