Function not allowing 2nd argument - python-3.x

I made a logging function , which takes 2 arguments: log_message and mode. For some reason, when I use the function and pass arguments, I get the following error:
Traceback (most recent call last):
File "/Users/user/git/rip/rip.py", line 248, in <module>
main()
File "/Users/user/git/rip/rip.py", line 195, in main
log('STARTING RIPPER', 'i')
TypeError: log() takes 1 positional argument but 2 were given
Which is strange, since log() definitely takes 2 arguments.
Here is my code:
import os
import sys
import time
import mmap
import json
import requests
from bs4 import BeautifulSoup
from clint.textui import puts, colored
def log(log_message, mode='s'):
log_date = '[' + time.strftime("%d.%m_%H:%M:%S") + ']'
if mode == 'e':
log_file = 'test_error.log'
log_ouput = colored.white(log_date) + colored.red('[ERROR]' + log_message)
elif mode == 'i':
log_file = 'test_info.log'
log_ouput = colored.white(log_date) + colored.yellow('[INFO]' + log_message)
elif mode == 'c':
log_file = 'test_info.log'
log_ouput = colored.white(log_date) + colored.white('[COMMENT]' + log_message)
else:
log_file = 'test_download.log'
log_ouput = colored.white(log_date) + colored.green(log_message)
with open(log_file, 'a') as file_writer:
file_writer.write(log_message + '\n')
file_writer.close()
puts(log_ouput)
def main():
log('STARTING RIPPER', 'i')

Maybe your interpreter (and don't know why) thinks that 'i' is also a positonal argument (argument function with no name).
Try writing instead
log('STARTING RIPPER', mode='i')
And given that, btw, explicit is better than implicit, you should even write
log(log_message='STARTING RIPPER', mode='i')

This minimal example works for me:
def log(log_message, mode='s'):
print(log_message, mode)
log('STARTING RIPPER', 'i')
I think the log you are calling is a different log. Try
print(log.__module__)
to find out where the function comes from.
Edit:
To make sure that the signature of your function is what you expect, you can use
import inspect
print(inspect.signature(log))
which should return
(log_message, mode='s')

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 - 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

Pass an array as command line argument to the script

I'd like to experiment codes from command line, so import argv form sys.
from sys import argv
def binary_search(array, item):
low = 0
high = len(array) - 1
while low <= high:
mid = (low + high) // 2 # round down if not an even
guess = array[mid]
if guess == item:
return mid
if guess > item:
high = mid - 1
else:
low = mid + 1
return None
def main():
script, array, item = argv
binary_search(array, item)
When run it on the command line:
$ python3 binary_search.py [1, 2, 3] 8
Traceback (most recent call last): File "binary_search.py", line 94, in <module>
main() File "binary_search.py", line 89, in main
script, array, item = argvValueError: too many values to unpack (expected 3)
I tested and found that arguments passed from command line are treated as str by argv.
How can pass an array as argument?
There are a couple different ways you can do this...
using re
Using regular expressions may be one of the easiest ways of handling this.
from sys import argv
import re
def binary_search(array, item):
low = 0
high = len(array) - 1
while low <= high:
mid = (low + high) // 2 # round down if not an even
guess = array[mid]
if guess == item:
return mid
if guess > item:
high = mid - 1
else:
low = mid + 1
return None
def main():
array = re.findall(r"[\w]+", argv[1])
array = [int(i) for i in array]
item = int(argv[2])
binary_search(array,item)
if __name__== "__main__":
main()
using exec()
You can also use exec() which may be more risky and complicated. Here's a simple example:
from sys import argv
command = 'mylist = {0}'.format(argv[1])
exec(command)
for item in mylist:
print(item)
example output:
C:\path>py foo.py [1,2,3]
1
2
3
The arguments on the command line are strings, they're not parsed like literals in the program.
argv construct the strings automatically to a list from command line arguments (as separated by spaces), in short,
sys.argv is a list.
Additionally, module argparse helps
The argparse module makes it easy to write user-friendly command-line interfaces. The program defines what arguments it requires, and argparse will figure out how to parse those out of sys.argv. The argparse module also automatically generates help and usage messages and issues errors when users give the program invalid arguments.

"AssertionError: write() argument must be a bytes instance" when running WSGISOAPHandler

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)

Executing mathematical user inputed statements as code in Python 3

How would one allow the user to input a statement such as "math.sin(x)**2" and calculate the answer within python code.
In telling me the answer please explain why both exec() compile() are not producing the desired result.
import math
def getValue(function, x):
function = "val = " + function
#compile(function, '', 'exec')
exec(function)
print(val)
function = input("Enter a function f(x):\n")
getValue(function, 10)
Much Appreciated!
To answer your question, use eval:
>>> eval('math.sin(1)**2')
0.7080734182735712
exec is working, but you are not retrieving the result. Notice:
>>> val
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'val' is not defined
>>> exec('val=math.sin(1)**2')
>>> val
0.7080734182735712
So, using eval instead of exec works like so:
def getValue(function, x):
function = "{}({})".format(function, x)
print(function)
val=eval(function)
print(val)
That said -- it is considered an extreme security risk to execute arbitrary user code.
If you are building a calculator, a safer approach you might consider using SymPy or building your own parser using something like PyParsing (which is used in SymPy)
An example PyParsing calculator:
import sys
import operator
from pyparsing import nums, oneOf, Word, Literal, Suppress
from pyparsing import ParseException, Forward, Group
op_map = { '*' : operator.mul,\
'+' : operator.add,\
'/' : operator.div,\
'-' : operator.sub}
exp = Forward()
number = Word(nums).setParseAction(lambda s, l, t: int(t[0]))
lparen = Literal('(').suppress()
rparen = Literal(')').suppress()
op = oneOf('+ - * /').setResultsName('op').setParseAction(lambda s, l, t: op_map[t[0]])
exp << Group(lparen + op + (number | exp) + (number | exp) + rparen)
def processArg(arg):
if isinstance(arg, int):
return arg
else:
return processList(arg)
def processList(lst):
args = [processArg(x) for x in lst[1:]]
return lst.op(args[0], args[1])
def handleLine(line):
result = exp.parseString(line)
return processList(result[0])
while True:
try:
print handleLine(raw_input('> '))
except ParseException, e:
print >>sys.stderr,\
"Syntax error at position %d: %s" % (e.col, e.line)
except ZeroDivisionError:
print >>sys.stderr,\
"Division by zero error"
Which can easily be extended to include other functions.

Resources