"AssertionError: write() argument must be a bytes instance" when running WSGISOAPHandler - python-3.x

I have a SOAP server with pysimplesoap in Python 3.
Code
from wsgiref.simple_server import make_server
application = WSGISOAPHandler(dispatcher)
wsgid = make_server('', 8008, application)
wsgid.serve_forever()
I don't know why am I get the following error.
Error
Traceback (most recent call last):
File "/usr/lib/python3.4/wsgiref/handlers.py", line 138, in run
self.finish_response()
File "/usr/lib/python3.4/wsgiref/handlers.py", line 180, in finish_response
self.write(data)
File "/usr/lib/python3.4/wsgiref/handlers.py", line 266, in write
"write() argument must be a bytes instance"
AssertionError: write() argument must be a bytes instance

It's all because of WSGI is made for Python 2, so you can face some troubles using it in Python 3. If you dont want to change library's behavior like in first answer, workaround is to encode() all text data like:
def application(environ,start_response):
response_body = 'Hello World'
return [response_body.encode()]

Wsgi framework is built around Python 2. Therefore, if there is stuff in your program that does not include Python 3 dependencies, run the app with Python 2.

in "handlers.py" line 180
self.write(data.encode()) instead of self.write(data)

In my case the problem turned out to be that I was unintentionally outputting an object instead of a string. Fixed by encoding my result as a string via json.dumps(obj).

+++ pysimplesoap/server.py
e['name'] = k
if array:
e[:] = {'minOccurs': "0", 'maxOccurs': "unbounded"}
- if v in TYPE_MAP.keys():
- t = 'xsd:%s' % TYPE_MAP[v]
- elif v is None:
+
+ # check list and dict first to avoid
+ # TypeError: unhashable type: 'list' or
+ # TypeError: unhashable type: 'dict'
+ if v is None:
t = 'xsd:anyType'
elif isinstance(v, list):
n = "ArrayOf%s%s" % (name, k)
n = "%s%s" % (name, k)
parse_element(n, v.items(), complex=True)
t = "tns:%s" % n
+ elif v in TYPE_MAP.keys():
+ t = 'xsd:%s' % TYPE_MAP[v]
else:
raise TypeError("unknonw type v for marshalling" % str(v))
e.add_attribute('type', t)

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!

Python: read from STDIN unless a file is specified, how is it done?

I'm writing a Python script which expects a regex pattern and a file name and looks for that regex pattern within the file.
By default, the script requires a file to work on.
I want to change the script so by default it would take it's input from STDIN unless a file is specified (-f filename).
My code looks like so:
#!/usr/bin/env python3
# This Python script searches for lines matching regular expression -r (--regex) in file/s -f (--files).
import re
import argparse
#import sys
class colored:
CYAN = '\033[96m'
UNDERLINE = '\033[4m'
END = '\033[0m'
def main(regex, file, underline, color):
pattern = re.compile(regex)
try:
for i, line in enumerate(open(file, encoding="ascii")):
for match in re.finditer(pattern, line):
message = "Pattern {} was found on file: {} in line {}. The line is: ".format(regex, file, i+1)
if args.color and args.underline:
#message = "Pattern {} was found on file: {} in line {}. The line is: ".format(regex, file, i+1)
l = len(line)
print(message + colored.CYAN + line + colored.END, end="")
print(" " ,"^" * l)
break
if args.underline:
l = len(line)
print(message + line, end="")
print(" " ,"^" * l)
break
if args.color:
print(message + colored.CYAN + line + colored.END, end="")
break
if args.machine:
print("{}:{}:{}".format(file, i+1, line), end="")
break
else:
print(message + line, end="")
break
except FileNotFoundError:
print("File not found, please supply")
pass
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Python regex finder', epilog = './python_parser.py --regex [pattern] --files [file]')
requiredNamed = parser.add_argument_group('required named arguments')
requiredNamed.add_argument('-r', '--regex',
help='regex pattern', required=True)
parser.add_argument('-f', '--file',
help='file to search pattern inside')
parser.add_argument('-u', '--underline', action='store_true',
help='underline')
parser.add_argument('-c', '--color', action='store_true',
help='color')
parser.add_argument('-m', '--machine', action='store_true',
help='machine')
args = parser.parse_args()
main(args.regex, args.file, args.underline, args.color)
You can see how a run looks here.
I tried using the answer from this SO question, but getting the following error:
for i, line in enumerate(open(file, encoding="ascii")):
TypeError: expected str, bytes or os.PathLike object, not _io.TextIOWrapper
Edit #1:
This is the file:
Itai
# something
uuu
UuU
# Itai
# this is a test
this is a test without comment
sjhsg763
3989746
# ddd ksjdj #kkl
I get the above error when I supply no file.
Edit#2:
When I change the file argument to that:
parser.add_argument('-f', '--file',
help='file to search pattern inside',
default=sys.stdin,
type=argparse.FileType('r'),
nargs='?'
)
And then run the script like so:
~ echo Itai | ./python_parser.py -r "[a-z]" -m
Traceback (most recent call last):
File "./python_parser.py", line 59, in <module>
main(args.regex, args.file, args.underline, args.color)
File "./python_parser.py", line 16, in main
for i, line in enumerate(open(file, encoding="ascii")):
TypeError: expected str, bytes or os.PathLike object, not NoneType
➜ ~
args.file = tmpfile
which is a file in the same directory where the script runs.
What am I doing wrong?
You wrote this:
def main(regex, file, underline, color):
...
for i, line in enumerate(open(file, encoding="ascii")):
You have some confusion about whether file denotes a filename or an open file descriptor. You want it to be an open file descriptor, so you may pass in sys.stdin. That means main() should not attempt to open(), rather it should rely on the caller to pass in an already open file descriptor.
Pushing the responsibility for calling open() up into main() will let you assign file = sys.stdin by default, and then re-assign the result of open() if it turns out that a filename was specified.

Python - comparing imported values

I would like to import the list from the file, read it line by line (already works). Each line containing a string representing a list.
I have to execute couple of tasks on it, however I got twisted and I dont know why below doesn't work. It gives me the following error :
ErrorCode:
Traceback (most recent call last):
File "main.py", line 8, in <module>
if len(n) != len(str(n + 1)):
TypeError: must be str, not int
f = open('listy.txt', 'r')
content = f.read().split('\n')
for n in content:
n.split(',')
## checking lengh
if len(n) != len(str(n + 1)):
print('Different lengh')
else:
print('All fine.')
Change
n.split(',')
if len(n) != len(str(n + 1)):
to:
n = n.split(',')
len(n[0]) != len(n[1]):
and don't forget to close your file with a f.close()
Better even, use with, example:
with open('listy.txt', 'r') as f:
content = f.read().split('\n')
you do not need a close() method when using with

struct.pack in Python3 got struct.error: required argument is not an integer

I'm doing a zipping func of this:
open_file = open(file_path,'rb')
open_save = open(save_path,'wb+')
try:
open_read = open_file.read()
data = zip_data(open_read)
head_xis = b'XIS'
head_version = bytes(0)
head_type1 = bytes(1)
head_type2 = bytes(1)
head_en_type = bytes(0)
head_com_type = bytes(1)
head_sour_len = len(open_read)
# try:
md5 = hashlib.md5()
md5.update(open_read)
head_md5 = md5.digest()
print("byteeeeee")
# except:
# head_md5 = None
randoms = str(uuid.uuid1()).split('-')[0]
head_random = bytes(randoms,encoding = "utf-8")
print(head_md5)
print(head_random)
head_resour_len= len(data)
print(type(head_xis))
print(type(head_version))
print(type(head_type1))
print(type(head_type2))
print(type(head_en_type))
print(type(head_com_type))
print(type(head_sour_len))
print(type(head_md5))
print(type(head_random))
print(type(head_resour_len))
head = struct.pack('3sBBBBBI16s8sI',
head_xis,
head_version,
head_type1,
head_type2,
head_en_type,
head_com_type,
head_sour_len,
head_md5,
head_random,
head_resour_len
)
open_save.write(head)
# except Exception as e:
# print("eeeee" + str(e))
# return False,str(e)
# else:
# open_save.write(data)
# return True,''
finally:
open_file.close()
open_save.close()
and it shows exception and print like below:
byteeeeee
b'\xf9\xf4\xf2\xcb\xbfM\x11\xb5\xeeNP/\x02H\xebK'
b'f9f33502'
class 'bytes'
class 'bytes'
class 'bytes'
class 'bytes'
class 'bytes'
class 'bytes'
class 'int'
class 'bytes'
class 'bytes'
class 'int'
Traceback (most recent call last):
File "entrance.py", line 52, in startProcess
Helper.processCombine(self.selectedVer,False)
File "/Users/mapzchen/myShell/qtPro/fastColuaco/Helper.py", line 86, in processCombine
itemConf.get(version,suffix),True,True)
File "/Users/mapzchen/myShell/qtPro/fastColuaco/coluaco.py", line 514, in process
compress(build_dir,zip_dir,filter_file)
File "/Users/mapzchen/myShell/qtPro/fastColuaco/coluaco.py", line 400, in compress
zfile = zip_file(src_file_path,save_path)
File "/Users/mapzchen/myShell/qtPro/fastColuaco/coluaco.py", line 131, in zip_file
head_resour_len
struct.error: required argument is not an integer
I have tried to print types of arguments,
and it seems to fit 3sBBBBBI16s8sI correctly
I'm confused by what arg that performs this exception
In structure formating string "B" for bytes need variable type 'int', that is main problem. So instead of head_type1 = bytes(1) use head_type1 = 1
Here given below code gives same error.
Problem:
n = bytes(2)
x = struct.pack("B", n)
Solution:
n = 2
x = struct.pack("B", n)
Suggestion:
Use Byte Orders like '=', while dealing with integers like 'I'.

I can't convert my variable from integer to string

This is the code:
import time
import os
os.system("cls")
a=1
while True:
if a>512:
a=1
print (a + " kb")
if a<1024:
print (a + " bytes")
a *= 2
time.sleep(.5)
But it gives me this error:
> Traceback (most recent call last):
> File "Sequence1.py", line 10, in <module>
> print (a + " bytes")
> TypeError: unsupported operand type(s) for +: 'int' and 'str'
If I changed it to a string then my if statements wouldn't work. Sorry if this question has been asked before. Thanks.
While you need a to be integer for you logic you need it as string just for presentational purposes. So convert it only when printing it. You have several ways to do that, as you certainly know. Some of them:
print('{0} bytes'.format(a))
print('%s bytes' % a)
print(str(a) + ' bytes')

Resources