python3 pexpect strange behaviour - python-3.x

I have a single threaded program that simply executes commands over ssh and simply looks for output over ssh. But after a while I start getting extrodinarily strange behaviour:
ssh_cmd = 'ssh %s#%s %s' % (user, addr, options)
ssh = pexpect.spawn(ssh_cmd, timeout=60)
lgsuc = ['(?i)(password)')]
for item in loginsuccess:
lgsuc.append(item)
retval = ssh.expect(lgsuc)
for cmd in cmdlist:
time.sleep(0.1)
#this is regex to match anything. Essentially clears the buffer so you don't get an invalid match from before
ssh.expect(['(?s).*'])
ssh.sendline(cmd)
foundind = ssh.expect([re.escape("root#")], 30) #very slow
#repr escape all the wierd stuff so madness doesn't happen with ctr chars
rettxt = repr(ssh.before.decode("us-ascii") + "root:#")
print("We Found:" + rettxt
And it will be fine for about 20 commands or so then madness occurs Assume the right echo is blablabla each time:
We found 'blablabla \r\n\r\n[edit]\r\nroot#'
We found 'blablabla \r\n\r\n[edit]\r\nroot#'
We found 'blablabla \r\n\r\n[edit]\r\nroot#'
... about 20 commands...
We found 'bl\r\nroot#' # here it just missed part of the string in the middle
We found 'alala\r\nroot#'
here is the remainder of the echo from the previous command!!! and the echo of the current command will show up some time later!! and it gets worse and worse. The thing that is strange is it is in the middle of the return byte array.
Now there are some wierd control codes coming back from this device so if I replace:
rettxt = repr(ssh.before.decode("us-ascii") + "root:#")
with
rettxt = repr(ssh.before.decode("us-ascii") + "root:#")
then
print("We Found:" + rettxt)
returns:
root#e Found lala
Anyway there is really strange stuff going on with pexpect and the buffers, and I can't figure out what it is so any help would be appreciated. I should mention I never get teh timeout, the dive always responds. Also the total number of "root:#"in the log file exceedes the total number of lines sent.
If I go through and remove all ctl codes, the output looks cleaner but the problem still persists, its as if pextect cant handle ctl coodes in its buffer correctly. Any help is appreciated
UPDATE Minimum verifiable example
Ok I have been able to recreate PART of the problem on an isolated ubuntu environment sshing into itself.
first I need to create 4 commands that can be run on a host target, so put the follwing for files in ~/ I did this in ubuntu
~/check.py
#!/usr/bin/python3
import time
import io
#abcd^H^H^H^H^MABC
#mybytes = b'\x61\x62\x63\x64\x08\x08\x08\x0D\x41\x42\x43'
#abcdACB
mybytes = b'\x61\x62\x63\x64\x41\x42\x43'
f = open('test.txt', 'wb')
#time.sleep(1)
f.write(mybytes)
print(mybytes.decode('ascii'))
f.close()
~/check2.py
#!/usr/bin/python3
import time
import io
#0123^H^H^H^H^MABC
mybytes = b'\x30\x31\x32\x33\x08\x0D\x0D\x08\x08\x08\x08\x0D\x41\x42\x43'
f = open('test2.txt', 'wb')
#time.sleep(0.1)
f.write(mybytes)
print(mybytes.decode('ascii'))
f.close()
~/check3.py
#!/usr/bin/python3
import time
import io
#789:^H^H^H^H^DABC
mybytes = b'\x37\x38\x39\x3A\x08\x08\x08\x08\x08\x08\x08\x0D\x0D\x41\x42\x43'
f = open('test3.txt', 'wb')
#time.sleep(0.1)
f.write(mybytes)
print(mybytes.decode('ascii'))
f.close()
And lastly check4.py Sorry it took a wierd combination for the problem to show back up
#!/usr/bin/python3
import time
import io
#abcd^H^H^H^HABC
mybytes = b'\x61\x62\x63\x64\x08\x08\x08\x0D\x41\x42\x43'
f = open('test.txt', 'wb')
time.sleep(4)
f.write(mybytes)
print(mybytes.decode('ascii'))
f.close()
Noticing that the last one has a bigger sleep, this is to encounter texpect timeout. Though on my actual testing this doesn't occue, I have commands that take over 6 minutes to return any text so this might be part of it. Ok and the final file to run everything. It might look ugly but I did a massive trim so I could post it here:
#! /usr/bin/python3
#import paramiko
import time
import sys
import xml.etree.ElementTree as ET
import xml
import os.path
import traceback
import re
import datetime
import pexpect
import os
import os.path
ssh = None
respFile = None
#Error Codes:
DEBUG = True
NO_ERROR=0
EXCEPTION_THROWS=1
RETURN_TEXT_NEVER_FOUND = 2
LOG_CONSOLE = 1
LOG_FILE = 2
LOG_BOTH = 3
def log(out, dummy=None):
print(str(log))
def connect(user, addr, passwd):
global ssh
fout = open('session.log', 'wb')
#options = '-q -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oPubkeyAuthentication=no'
options = ' -oUserKnownHostsFile=/dev/null '
#options = ''
#REPLACE WITH YOU LOCAL USER NAME
#user = 'user'
#REPLACE WITH YOUR LOCAL PASSWORD
#passwd = '123TesT321'
#addr = '127.0.0.1'
ssh_cmd = 'ssh %s#%s %s' % (user, addr, options)
public = None
private = None
retval = 0
try:
ssh = pexpect.spawn(ssh_cmd, timeout=60)
ssh.logfile = fout
#the most common prompts
loginsuccess = [re.escape("#"), re.escape("$")]
lgsuc = ['(?i)(password)', re.escape("connecting (yes/no)? ")]
for item in loginsuccess:
lgsuc.append(item)
retval = ssh.expect(lgsuc)
except pexpect.TIMEOUT as exc:
log("Server never connected to SSH tunnel")
return 0
print('where here ret val = ' + str(retval))
try:
if(retval > 1):
return 1
elif(retval == 1):
hostkey = ssh.before.decode("utf-8")
ssh.sendline("yes")
log("Warning! new host key was added to the database: " + hostkey.split("\n")[1])
lgsuc = ['password: ']
for item in loginsuccess:
lgsuc.append(item)
retval = ssh.expect(lgsuc)
if(retval > 0):
return 1
else:
if(public is not None):
log("Warning public key authentication failed trying password if available...")
else:
if public is not None:
log("Warning public key authentication failed trying password if available...")
if(passwd is None):
log("No password and certificate authentication failed...")
return 0
ssh.sendline(passwd)
login = ['password: ' ]
for item in loginsuccess:
login.append(item)
retval = ssh.expect(login)
except pexpect.TIMEOUT as exc:
log("Server Never responded with expected login prompt: "+lgstr)
return 0
#return 0
if retval > 0:
retval = 1
if retval == 0:
log("Failed to connect to IP:"+addr +" User:"+user+" Password:"+passwd)
return retval
def disconnect():
log("Disconnecting...")
global ssh
if ssh is not None:
ssh.close()
else:
log("Something wierd happened with the SSH client while closing the session. Shouldn't really matter", False)
def RunCommand(cmd, waitTXT, timeout = 5):
global ssh
Error = 0
if DEBUG:
print('Debugging: cmd: '+ cmd+'. timeout: '+str(timeout) +'. len of txt tags: '+ str(len(waitTXT)))
if(type(waitTXT) is str):
waitTXT = [re.excape(waitTXT)]
elif(not hasattr(waitTXT ,'__iter__')):
waitTXT = [re.escape(str(waitTXT))]
else:
cnter = 0
for TXT in waitTXT:
waitTXT[cnter] = re.escape(str(TXT))
cnter +=1
#start = time.time()
#print("type3: "+str(type(ssh)))
#time.sleep(1)
#this is regex to match anything. Essentially clears the buffer so you don't get an invalid match from before
ssh.expect(['(?s).*'])
ssh.sendline(cmd)
print("Debugging: sent: "+cmd)
#GoOn = True
rettxt = ""
try:
foundind = ssh.expect(waitTXT, timeout)
allbytes = ssh.before
newbytes = bytearray()
for onebyte in allbytes:
if onebyte > 31:
newbytes.append(onebyte)
allbytes = bytes(newbytes)
rettxt = repr(allbytes.decode("us-ascii") + waitTXT[foundind])
#rettxt = ssh.before + waitTXT[foundind]
if DEBUG:
print("Debugging: We found "+rettxt)
except pexpect.TIMEOUT as exc:
if DEBUG:
txtret = ""
for strtxt in waitTXT:
txtret += strtxt +", "
print("ERROR Debugging: we timed out waiting for text:"+txtret)
pass
return (rettxt, Error)
def CloseAndExit():
disconnect()
global respFile
if respFile is not None and '_io.TextIOWrapper' in str(type(respFile)):
if not respFile.closed:
respFile.close()
def main(argv):
try:
cmds = ['~/check.py', '~/check2.py', '~/check3.py', '~/check2.py', '~/check3.py','~/check.py', '~/check2.py', '~/check3.py', '~/check2.py', '~/check3.py', '~/check4.py', '~/check3.py','~/check.py', '~/check2.py',]
##CHANGE THESE TO MTACH YOUR SSH HOST
ret = connect('user', '127.0.0.1', 'abcd1234')
for cmd in cmds:
cmdtxt = str(cmd)
#rett = RunCommand(ssh, "ls", "root", 0, 5)
strlen = (170 - (len(cmdtxt)))/2
dashval = ''
starval = ''
tcnt = 0
while(tcnt < strlen):
dashval +='-'
starval +='*'
tcnt +=1
if DEBUG:
print(dashval+cmdtxt+dashval)
#checkval = ['ABC']
#REPLACE THE FOLLOWING LINE WITH YOUR TARGET PROMPT
checkval = ['user-virtual-machine:~$']
rett = RunCommand(cmdtxt, checkval, 2)
if DEBUG:
print(starval+cmdtxt+starval)
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
print(exc_type, fname, exc_tb.tb_lineno)
print(traceback.format_exc())
CloseAndExit()
#disconnect()
#respFile.close()
main(sys.argv)
Make sure that all for checks and the main python script are executble in permission via sudo chmod 774 or similar. In the main function call set your username ipaddress and password to where your target is that has the check.py and make sure they are in your ~/ directory.
Once you run this you can look at the session.log and at least on mind there is some wierd stuff going on with the buffer:
~/check4.py^M
~/check3.py
~/check3.py^M
abcd^H^H^H^MABC^M
^[]0;user#user-virtual-machine: ~^Guser#user-virtual-machine:~$ ~/check.py
~/check3.py^M
789:^H^H^H^H^H^H^H^M^MABC^M
^[]0;user#user-virtual-machine: ~^Guser#user-virtual-machine:~$ ~/check.py~/check2.py
And unfortunately its not as corrupt as my actual prbolem but I have several hundred commands I an embedded custom linux kernal that I obviously can't recreate for everyone. But anyway any help is greatly appreciated. Hopefully these examples work you you, I am just on ubuntu 16.04 lts. Also make sure to replace 'user-virtual-machine:~$' with whatever your target login prompt looks like

Related

I have a python script that works perfectly in the Thonny IDE, but fails in terminal

Firstly, here's the code:
#!/usr/bin/python3
import re, pexpect, os
file = '/home/homebridge/flags/Restart.flag'
f = open(file, 'w')
f.close()
os.system("sudo systemctl stop homebridge")
os.system("sudo chmod -R a+rwx /var/lib/homebridge")
child = pexpect.spawn('tuya-cli wizard')
child.expect('\r\n')
child.sendline('y')
child.expect('\r\n')
child.sendline('XXXXXXXXXXXXXXXX')
data = child.read()
data = data.decode("utf-8")
devices = data.split('},')
devicesO = []
class device:
name = ""
ID = ""
key = ""
def __init__(self, name, ID, key):
self.name = name
self.ID = ID
self.key = key
def __lt__(self, other):
return self.name < other.name
for i in devices:
n = re.search("name: \'(.*)\'", str(i)).group(1)
I = re.search("id: \'(.*)\'", str(i)).group(1)
k = re.search("key: \'(.*)\'", str(i)).group(1)
if n != ("Clock"):
devicesO.append(device(n, I, k))
entries = []
devicesO.sort()
for device in devicesO:
if "phone charger" not in device.name:
s1 = "{\n\"name\": \"" + device.name + "\",\n\"id\": \"" + device.ID + "\",\n\"key\": \"" + device.key + "\","
s2 = """
"type": "RGBTWLight",
"manufacturer": "SmartLife",
"model": "Light",
"dpPower": "20",
"dpMode": "21",
"dpBrightness": "22",
"dpColorTemperature": "23",
"dpColor": "24",
"colorFunction": "HSB",
"scaleBrightness": 1000
}"""
else:
s1 = "{\n\"name\": \"" + device.name + "\",\n\"id\": \"" + device.ID + "\",\n\"key\": \"" + device.key + "\","
s2 = """
"type": "Outlet",
"manufacturer": "SmartLife",
"model": "Outlet",
"dpPower": "1"
}"""
entries.append(s1 + s2)
string = ",\n".join([str(entry) for entry in entries])
config = open('/var/lib/homebridge/config.json', 'r+')
x = config.read()
config.close()
#print(x)
x = re.sub("\"TuyaLan\",\n.*\"devices\": \[((.|\n)*?)\]", "\"TuyaLan\",\n\"devices\": [\n" + string + "\n]", x)
#print(x)
#x = re.sub("\"TuyaLan\",\n.*\"devices\": \[((.|\n)*?)\]", "\"TuyaLan\",\n.*\"devices\": [\nTEST\n]", x)
config = open('/var/lib/homebridge/config.json', 'w+')
config.write(x)
config.close()
config = open('/var/lib/homebridge/config.json', 'r+')
print (config.read())
config.close()
os.remove(file)
os.system("sudo systemctl restart homebridge")
This executes as expected in the IDE, stopping the homebridge service, pulling relevant data from the tuya-cli utility, regex and text replacement, all of it. However, when I try and run it in the terminal without sudo, the first regex search returns an empty object and the script fails. When I run it with sudo, it stalls for a while then times out on the pexpect step at the beginning. I've researched before posting, but I have no clue how to solve this one. It doesn't appear to be a path issue, I used pip3 to install both re and pexpect, and os is obviously packaged with the raspbian install. Any clues would be great.
Error without sudo:
pi#raspberrypi:~ $ /home/homebridge/scripts/updateConfig.py
Traceback (most recent call last):
File "/home/homebridge/scripts/updateConfig.py", line 34, in <module>
n = re.search("name: \'(.*)\'", str(i)).group(1)
AttributeError: 'NoneType' object has no attribute 'group'
With sudo:
pi#raspberrypi:~ $ sudo /home/homebridge/scripts/updateConfig.py
Traceback (most recent call last):
File "/home/homebridge/scripts/updateConfig.py", line 10, in <module>
child.expect('\r\n')
File "/usr/local/lib/python3.7/dist-packages/pexpect/spawnbase.py", line 344, in expect
timeout, searchwindowsize, async_)
File "/usr/local/lib/python3.7/dist-packages/pexpect/spawnbase.py", line 372, in expect_list
return exp.expect_loop(timeout)
File "/usr/local/lib/python3.7/dist-packages/pexpect/expect.py", line 181, in expect_loop
return self.timeout(e)
File "/usr/local/lib/python3.7/dist-packages/pexpect/expect.py", line 144, in timeout
raise exc
pexpect.exceptions.TIMEOUT: Timeout exceeded.
<pexpect.pty_spawn.spawn object at 0x766c4510>
command: /usr/bin/tuya-cli
args: ['/usr/bin/tuya-cli', 'wizard']
buffer (last 100 chars): b'\x1b[32m?\x1b[39m \x1b[1mThe API key from tuya.com:\x1b[22m\x1b[0m \x1b[0m\x1b[29D\x1b[29C'
before (last 100 chars): b'\x1b[32m?\x1b[39m \x1b[1mThe API key from tuya.com:\x1b[22m\x1b[0m \x1b[0m\x1b[29D\x1b[29C'
after: <class 'pexpect.exceptions.TIMEOUT'>
match: None
match_index: None
exitstatus: None
flag_eof: False
pid: 1470
child_fd: 5
closed: False
timeout: 30
delimiter: <class 'pexpect.exceptions.EOF'>
logfile: None
logfile_read: None
logfile_send: None
maxread: 2000
ignorecase: False
searchwindowsize: None
delaybeforesend: 0.05
delayafterclose: 0.1
delayafterterminate: 0.1
searcher: searcher_re:
0: re.compile(b'\r\n')
Possible short answer: Your IDE is probably automatically adding carriage returns with your sendlines, which is why your code runs in the IDE, but not at the terminal. Sendline adds a line feed (\n), but not a carriage return (\r). You should add a \r after each sendline (e.g., child.sendline('XXXXXXXXXXXXXXXX\r')) to complete the CRLF (\r\n).
Long explanation:
Based on your code, when you spawned the child, you expected a CRLF. However, pexpect searches are not greedy and will stop at the first CRLF they encounter. Unfortunately, when I tested your code, pexpect stopped at the CRLF after the command you entered, not the prompt afterwards:
child = pexpect.spawn('tuya-cli wizard')
child.expect('\r\n')
print(child.before)
Output
b" tuya-cli wizard"
You should be looking for a prompt or a message instead, such as The API key from tuya.com: or Password::
# tuya-cli wizard
The API key from tuya.com:
The API secret from tuya.com:
Provide a 'virtual ID' of a device currently registered in the app:
or
# sudo tuya-cli wizard
Password:
However, I think both of your errors occurred because you did not include a carriage return (\r) with your sendlines. The first error occurred because, at the prompt, The API key from tuya.com:, you sent 'y', not 'y\r', so nothing was entered at the prompt. You then searched for CRLF, but since you had not included a \r, pexpect found the original CRLF after b" tuya-cli wizard".
The expect call actually caused a carriage return, but, unfortunately, your code was now a step behind, and was interacting with the previous prompt, not the current prompt. That is why data = child.read() ended up reading the wrong output, resulting in a NoneType object.
The second error occurred because the pexpect cursor moved up to the The API key from tuya.com: prompt, looking for a CRLF. Since it is a prompt, it does not end with a CRLF, so the pexpect search timed out (those \x1b escape sequences are just for formatting and color):
pexpect.exceptions.TIMEOUT: Timeout exceeded.
args: ['/usr/bin/tuya-cli', 'wizard']
before (last 100 chars): b'\x1b[32m?\x1b[39m \x1b[1mThe API key from tuya.com:\x1b[22m\x1b[0m \x1b[0m\x1b[29D\x1b[29C'
searcher: searcher_re:
0: re.compile(b'\r\n')
Note that there is no \r\n in the buffer or before byte strings.
I would do something like:
...
while True:
index = child.expect(
["Password:", "The API key from tuya.com:", pexpect.TIMEOUT, pexpect.EOF, ])
if index == 0:
password = getpass() # You will need 'from getpass import getpass'
child.sendline(password) # CR's are usually not needed with variables
elif index == 1:
# This is what you want
child.sendline("XXXXXXXXXXXXXXXX\r")
break
elif index == 2:
raise RuntimeError("Search string not found.")
elif index ==3:
raise RuntimeError("Child closed.")
child.expect("The API secret from tuya.com:")
child.sendline("XXXXXXXXXXXXXXXX\r")
...
Good luck with your code!

Piping bash commands using Python subprocess.call - Syntax error: "|" unexpected

I was testing pipe using subprocess.call and I came to the following problem.
For example an automated ssh using users as strings and as a text file.
The pipe works perfectly using strings (Example 1) but it fails when opening a text file to check the users (Example 2).
Is there a limitation when piping values from a file?
Example 1:
$ python3 script.py myhost
import subprocess
import sys
host = sys.argv[1]
for j in range(0, 2):
words = ['test1', 'test2']
user = words[j]
print(user)
print('Loggin in %s...' % repr(user))
subp = subprocess.call(['echo %s | ssh %s' % (user, host)],
shell=True, stdout=None, stderr=None)
Output:
test1
Trying 'test1'...
Logged in
test2
Trying 'test2'...
Logged in
Example 2:
$ python3 script.py myhost users.txt
import subprocess
import sys
host = sys.argv[1]
user_list = sys.argv[2]
with open(user_list) as usr:
user = usr.readline()
cnt = 0
while user:
print(user.strip())
user = usr.readline()
subp = subprocess.call(['echo %s | ssh %s' % (user, host)],
shell=True, stdout=None, stderr=None)
cnt += 1
Output:
test1
/bin/sh: 2: Syntax error: "|" unexpected
test2
/bin/sh: 2: Syntax error: "|" unexpected
In case someone stumbles into this, I ended up fixing by getting rid of the first readline, adding a strip and modifying the while condition:
with open(user_list) as usr:
cnt = 0
while usr:
line = usr.readline()
user = line.strip()
subp = subprocess.call(['echo %s | ssh %s' % (user, host)],
shell=True, stdout=None, stderr=None)
cnt += 1

Python Script dont wake Up

I have a script to take screenshots of our garden and after sunset he makes a time lapse from the pictures. I send the script to sleep until the next day, but he hasn't woken up. I can't find the problem. maybe you can help me this Script is running in a Docker on Fedora and Fedora can't run python3 -u /script >& output & ... attached my script (sry i am still a beginner)
import datetime
from astral import LocationInfo
from astral.sun import sun
from time import sleep
import os
import requests
import pika
import sys
channel_id = '#####'
class pikacord:
def push_msg(self, msg):
url = ('amqp://#####')
params = pika.URLParameters(url)
connection = pika.BlockingConnection(params)
channel = connection.channel()
channel.basic_publish(exchange='amq.topic', routing_key='discord.msg',
properties=pika.BasicProperties(
headers={'channelId': channel_id}),
body=(msg))
connection.close()
return connection.close
class Sunclock:
def suntime(self):
#Locationdata
l = LocationInfo('City', 'Deutschland', 'Europe/Berlin', ######cords, ######cords)
#Suncal
#sunrise
s = sun(l.observer, date=datetime.date.today())
sunrise = s["sunrise"]
self.sunrisetime = sunrise.strftime('%H:%M')
#sunset
sunset = s["sunset"]
self.sunsettime = sunset.strftime('%H:%M')
#current
current = datetime.datetime.utcnow()
self.currenttime = current.strftime('%H:%M')
self.currenttimelong = current.strftime('%Y-%m-%d %H:%M')
#date
self.date = datetime.date.today()
#init def suntime
sunfloat = Sunclock()
sunfloat.suntime()
try_count = 0
loopi = True
while loopi == True:
sunfloat = Sunclock()
sunfloat.suntime()
get_sunset = sunfloat.sunsettime
get_sunrise = sunfloat.sunrisetime
get_current = sunfloat.currenttime
print (get_sunrise)
print (get_sunset)
print (get_current)
if try_count <=3:
try:
if get_current >= get_sunrise and get_current <= get_sunset:
#take Pictures
foldercheck = os.path.isdir('/apps/media')
if foldercheck == False:
os.mkdir('/apps/media')
os.mkdir('/apps/motioneye')
else:
print ('Folder exist')
os.chdir('/apps/media')
take_pic = 'curl ' "'http://######/picture/3/current/?_username=######&_signature=########'" ' --output `date +%Y-%m-%d_%H-%M-%S`snapshot.jpg'
print (take_pic)
os.system(take_pic)
print ('please smile')
sleep(180)
else:
#make Timelapse
os.chdir('/apps/media/')
delete = 'sudo rm /apps/media/*snapshot.jpg'
move = 'mv *.mp4 /apps/motioneye/Camera3'
timelapse = 'ffmpeg -r 30 -pattern_type glob -i "*snapshot.jpg" -c:v libx264 -pix_fmt yuv420p -movflags +faststart `date +%Y-%m-%d_%H-%M-%S`timelapse.mp4'
os.system(timelapse)
os.system(move)
os.system(delete)
#time to sleep
Problem---> while get_sunrise > get_current or get_sunset < get_current:
get_sunset = sunfloat.sunsettime
get_sunrise = sunfloat.sunrisetime
get_current = sunfloat.currenttime
sleep(300)
print ('good morning sun is rising at :' + get_sunrise)
except Exception as e:
print ('somthing was wrong:' +str(e))
try_count += +1
if try_count ==3:
broker = pikacord()
text = ('Greencam abort, to many errors please help:' +str(e))
print ('abort mission!!!')
#send abort Massage to Discord
discord = broker.push_msg(text)
else:
sleep(300)
else:
loopi = False
Explanation
The reason as to why your script hasn't "woken up" yet is because you're either misunderstanding how classes work or you've just overlooked a part of your code (I'll assume it's the former as the answer is more informative that way).
A class is made of methods (functions) and attributes (variables). They can be used either directly (statically) or through instances. In your case you're trying to use the class Sunclock as an instance named sunfloat: sunfloat = Sunclock().
In the next line you run the suntime method using sunfloat and that sets the attributes: sunrisetime, sunsettime, curenttime, curenttimelong and date. Afterwards you just assign variables to some of those attributes.
Now, let's go to the root of the problem:
while get_sunrise > get_current or get_sunset < get_current:
get_sunset = sunfloat.sunsettime
get_sunrise = sunfloat.sunrisetime
get_current = sunfloat.currenttime
sleep(300)
What you're doing here is assigning the variables to the same attributes all the time. sunfloat.sunsettime, sunfloat.sunrisetime and sunfloat.currenttime will never change while you're in the loop, they won't get new values out of thin air - their values need to be changed.
Solution
In order to change the values of the attributes, you'll want to call the suntime method again. The method will set all the attributes to the most up-to-date values:
while get_sunrise > get_current or get_sunset < get_current:
# All the attributes get re-assigned to the most up-to-date values.
sunfloat.suntime()
get_current = sunfloat.currenttime
get_sunset = sunfloat.sunsettime
get_sunrise = sunfloat.sunrisetime
sleep(300)

Save CSV for every functioncall with another name

at the moment I am able to create one CSV file with all the content I get at once.
Now I would like to create a list where I have different names in it.
How can I produce for every functioncall a different CSV file name? I thought about looping a list but I just want a +1 iteration at each call. I thought about saving my state somehow and use it in next functioncall. Everytime I initialize my variable with 0 and so I don't get 1. I think I could do it with Python Function Parameter calls but I have no idea how to use it. Can someone give me a little tip or example? If there are better ideas (maybe my idea is totally bullshit), how to solve this, just help please.
The comments in the code shall represent my imagination.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tenable.sc import SecurityCenter as SC
import os.path
import sys
import getpass
import csv
SC_HOST = '...'
def parse_entry(entry):
split_after_path = ''
ip = entry.get('ip', None)
pluginText = entry.get('pluginText', None)
if 'Path : ' in pluginText:
for line in pluginText.splitlines(0):
if 'Path : ' in line:
split_after_path_in_plugintext = line.split("Path : ",1)[1]
# place = ['place1', 'place2', 'place3', 'place4', 'place5']
# i = 0
# i = i+1
file_exists = os.path.isfile('testfile_path.csv')
# file_exists = os.path.isfile('testfile_path_'+place[i]+'.csv')
data = open('testfile_path.csv', 'a')
# data = open('testfile_path_'+place[i]+'.csv', 'a')
with data as csvfile:
header = ['IP Address', 'Path']
writer = csv.DictWriter(csvfile, lineterminator='\n', quoting=csv.QUOTE_NONNUMERIC, fieldnames=header)
if not file_exists:
writer.writeheader()
writer.writerow({'IP Address': ip, 'Path': split_after_path})
data.close()
def main():
sc_user = input('[<<] username: ')
sc_pass = getpass.getpass('[<<] password: ')
sc = SC(SC_HOST)
sc.login(sc_user, sc_pass)
# Query API for data
# asset = [12,13,14,25,29]
# i = 0
# assetid = asset[i]
# vuln = sc.analysis.vulns(('pluginID', '=', '25072')('asset','=','assetid'))
# i = i+1
vuln = sc.analysis.vulns(('pluginID', '=', '25072'),('asset','=','11'))
for entry in vuln:
parse_entry(entry)
sc.logout()
return 0
if __name__ == '__main__':
sys.exit(main())
The simplest and most obvious solution is to pass the full file path to your parse_entry function, ie:
def parse_entry(entry, filepath):
# ...
if 'Path : ' in pluginText:
for line in pluginText.splitlines(0):
if 'Path : ' in line:
# ...
file_exists = os.path.isfile(filepath)
with open(filepath, 'a') as csvfile:
# ...
Then in main() use enumerate() to build sequential filenames:
def main():
# ...
for i, entry in enumerate(vuln):
path = "'testfile_path{}.csv".format(i)
parse_entry(entry, path)
You can use a function attribute to keep track of the number of times the function has been called.
def parse_entry(entry):
parse_entry.i += 1
# outside the function you have to initialize the attribute
parse_entry.i = 0
Or you can look at other ways to initialize the function attribute in this post.
Alternatively, you can use glob to get the current number of files.
from glob import glob
i = len(glob('testfile_path_*.csv'))

Getting error "pywintypes.error: (5, 'OpenClipboard', 'Access is denied.')"

I am trying to use selenium with python to get latitude and longitude from this site. I am also using win32lipboard. But whenever I run my code, randomly it throws me this error pywintypes.error: (5, 'OpenClipboard', 'Access is denied.').
This is my code:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import csv
import win32clipboard
csvreader = csv.reader(open("master_data.csv", 'r'))
csvwriter = csv.writer(open('final_master_data.csv', 'w', newline=''))
headers = next(csvreader)
headers.append("latitude")
headers.append("longitude")
csvwriter.writerow(headers)
locations = list(csvreader)
chromedriver = 'C:\\Users\\UserName\\Downloads\\chromedriver.exe'
driver = webdriver.Chrome(chromedriver)
driver.get('http://www.whatsmygps.com')
for places in locations:
place = places[6] + ", " + places[4] + ", " + places[2]
location = driver.find_element_by_id("address")
location.send_keys(Keys.CONTROL, 'a')
location.send_keys(place)
location.submit()
time.sleep(3)
lat_input = driver.find_element_by_id("latitude")
lat_input.send_keys(Keys.CONTROL, 'a')
lat_input.send_keys(Keys.CONTROL, 'c')
win32clipboard.OpenClipboard()
lat = win32clipboard.GetClipboardData()
places.append(lat)
win32clipboard.CloseClipboard()
lon_input = driver.find_element_by_id("longitude")
lon_input.send_keys(Keys.CONTROL, 'a')
lon_input.send_keys(Keys.CONTROL, 'c')
win32clipboard.OpenClipboard()
lon = win32clipboard.GetClipboardData()
win32clipboard.CloseClipboard()
places.append(lon)
print(places)
csvwriter.writerow(places)
driver.close()
so, whenever I run this code, it starts with no issues, it reads csv file and enter location names into this sites and starts to copy latitude and longitude from the site and insert those into another csv file. But after some time, randomly, it throws error pywintypes.error: (5, 'OpenClipboard', 'Access is denied.'). I am unable to find the solution about this since yesterday.
UPDATE: I am using Anaconda and I am running anaconda shell as an administrator, so there is no issue with the access permission.
Access denied error may occur if clipboard is locked by another process. To avoid python messages, you can use WinAPI version of clipboard as described in this SO link: https://stackoverflow.com/a/23285159/4603670
As an alternative, use BingMap which requires API key. As of this writing, you can register a developer account at https://www.bingmapsportal.com for the free API key (I am not sure about the quota).
import pythoncom
import win32com.client
import json
pythoncom.CoInitialize()
winhttp = win32com.client.Dispatch('WinHTTP.WinHTTPRequest.5.1')
def bing_find_gps(addressLine, postalCode, country):
q = 'http://dev.virtualearth.net/REST/v1/Locations?key='
q = q + 'my_api_key'
if country: q = q + '&countryRegion=' + country
if postalCode: q = q + '&postalCode=' + postalCode
if addressLine: q = q + '&addressLine=' + addressLine
try:
winhttp.Open('GET', q, False)
winhttp.Send()
if not winhttp.responseText:
return 0
list = json.loads(winhttp.responseText)
if list['statusCode'] != 200:
return 0
gps = list['resourceSets'][0]['resources'][0]['point']['coordinates']
if gps:
return (1, gps[0], gps[1])
except:
return 0
res = bing_find_gps('One Microsoft Way, Redmond, WA, 98052-6399', '0', 'United States')
if res:
print("lat/long %s, %s" % (res[1], res[2]))
res = bing_find_gps(0, '98052-6399', 'United States')
if res:
print("lat/long %s, %s" % (res[1], res[2]))
Or use openstreetmap.org:
address = "98052-6399" #Testing with Microsoft zip code
url = "https://nominatim.openstreetmap.org/search?format=json&q=" + address
winhttp.Open('GET', url, False)
winhttp.Send()
list = json.loads(winhttp.responseText)
print(list[0].get('lat'))
print(list[0].get('lon'))
Expected output:
Latitude: 47.670119
Longitude: -122.118237
Or you may also wish to avoid copying the element altogether, use get_attribute('value') to read the value in latitude and longitude. Example:
chromedriver = 'C:\\Users\\UserName\\Downloads\\chromedriver.exe'
driver = webdriver.Chrome(chromedriver)
driver.get('http://www.whatsmygps.com')
element = driver.find_element_by_id("address")
element.send_keys(Keys.CONTROL, 'a')
#enter Microsoft's zip code
element.send_keys('98052-6399')
element.submit()
time.sleep(3)
lat_input = driver.find_element_by_id("latitude")
print('latitude: ')
print(lat_input.get_attribute('value'))
lon_input = driver.find_element_by_id("longitude")
print('longitude: ')
print(lon_input.get_attribute('value'))
driver.close()

Resources