How to handle "flood wait" errors when using telethon.sync? - telethon

Looks like sync version of the client doesn't throw any errors?
What is the right way to handle errors when working with telethon.sync?
the code below results in client going to "sleep", but no errors are cought.
I tried to do the same with explicit exception for FloodWaitError, it doesn't solve the issue.
from telethon.sync import TelegramClient
from telethon.tl.functions.channels import GetFullChannelRequest
if __name__ == '__main__':
setup_logging(level=logging.INFO)
tg = TelegramClient(
'anon',
api_id=config.API_ID,
api_hash=config.API_HASH,
)
with tg as client:
try:
result = client(GetFullChannelRequest(-1001100118939))
except ValueError as e:
print(e)
break;
# print('Flood wait for ', e.seconds)
# time.sleep(e.seconds)
print(result)

telethon.sync doesn't change the behavior of exceptions. However, FloodWaitError is not a ValueError, so your except won't catch it. The following will work:
from telethon import errors
try:
...
except errors.FloodWaitError as e:
print('Flood wait for ', e.seconds)
Note that the library automatically sleeps if the flood error is less than a minute by default, in which case it will wait and not raise for convenience.

Related

how to read the console output in python without executing any command

I have an API which gets the success or error message on console.I am new to python and trying to read the response. Google throws so many examples to use subprocess but I dont want to run,call any command or sub process. I just want to read the output after below API call.
This is the response in console when success
17:50:52 | Logged in!!
This is the github link for the sdk and documentation
https://github.com/5paisa/py5paisa
This is the code
from py5paisa import FivePaisaClient
email = "myemailid#gmail.com"
pw = "mypassword"
dob = "mydateofbirth"
cred={
"APP_NAME":"app-name",
"APP_SOURCE":"app-src",
"USER_ID":"user-id",
"PASSWORD":"pw",
"USER_KEY":"user-key",
"ENCRYPTION_KEY":"enc-key"
}
client = FivePaisaClient(email=email, passwd=pw, dob=dob,cred=cred)
client.login()
In general it is bad practice to get a value from STDOUT. There are some ways but it's pretty tricky (it's not made for it). And the problem doesn't come from you but from the API which is wrongly designed, it should return a value e.g. True or False (at least) to tell you if you logged in, and they don't do it.
So, according to their documentation it is not possible to know if you're logged in, but you may be able to see if you're logged in by checking the attribute client_code in the client object.
If client.client_code is equal to something then it should be logged in and if it is equal to something else then not. You can try comparing it's value when you successfully login or when it fails (wrong credential for instance). Then you can put a condition : if it is None or False or 0 (you will have to see this by yourself) then it is failed.
Can you try doing the following with a successful and failed login:
client.login()
print(client.client_code)
Source of the API:
# Login function :
# (...)
message = res["body"]["Message"]
if message == "":
log_response("Logged in!!")
else:
log_response(message)
self._set_client_code(res["body"]["ClientCode"])
# (...)
# _set_client_code function :
def _set_client_code(self, client_code):
try:
self.client_code = client_code # <<<< That's what we want
except Exception as e:
log_response(e)
Since this questions asks how to capture "stdout" one way you can accomplish this is to intercept the log message before it hits stdout.
The minimum code to capture a log message within a Python script looks this:
#!/usr/bin/env python3
import logging
logger = logging.getLogger(__name__)
class RequestHandler(logging.Handler):
def emit(self, record):
if record.getMessage().startswith("Hello"):
print("hello detected")
handler = RequestHandler()
logger.addHandler(handler)
logger.warning("Hello world")
Putting it all together you may be able to do something like this:
import logging
from py5paisa import FivePaisaClient
email = "myemailid#gmail.com"
pw = "mypassword"
dob = "mydateofbirth"
cred={
"APP_NAME":"app-name",
"APP_SOURCE":"app-src",
"USER_ID":"user-id",
"PASSWORD":"pw",
"USER_KEY":"user-key",
"ENCRYPTION_KEY":"enc-key"
}
client = FivePaisaClient(email=email, passwd=pw, dob=dob,cred=cred)
class PaisaClient(logging.Handler):
def __init__():
self.loggedin = False # this is the variable we can use to see if we are "logged in"
def emit(self, record):
if record.getMessage().startswith("Logged in!!")
self.loggedin = True
def login():
client.login()
logging.getLogger(py5paisa) # get the logger for the py5paisa library
# tutorial here: https://betterstack.com/community/questions/how-to-disable-logging-from-python-request-library/
logging.basicConfig(handlers=[PaisaClient()], level=0, force=True)
c = PaisaClient()
c.login()

How can I know the command used? - Discord.py

I am trying to make a errorhandling for my Discord.py, how do I know what command was used for the error to pop up?
#bot.event
async def on_command_error(ctx, error):
print("error: ",error)
if search("not found", str(error)):
c_f = random.choice([f"`{command used}` was not found, silly.", "Ehm.. Since when do we have `{command used}`?", "I don't know what `{command used}` is?"])
embed=discord.Embed(title=c_f, description=f"Please use existing commands. {ctx.author.mention}", color=error_color)
embed.timestamp = datetime.utcnow()
embed.set_footer(text=bot_name, icon_url=icon_uri)
await ctx.send(embed=embed)
elif search("cooldown", str(error)):
c_d = random.choice(["Did you drink energy drinks!?", "Why are you stressing, buddy.", "Duhh, wait, you're on cooldown!"])
second_remain = round(error.retry_after, 1)
embed=discord.Embed(title=c_d, description=f"Try again after {second_remain}s. {ctx.author.mention}", color=error_color)
embed.timestamp = datetime.utcnow()
embed.set_footer(text=bot_name, icon_url=icon_uri)
await ctx.send(embed=embed)
else:
raise error
Any attribute I can use?
You can use ctx.command
#bot.event
async def on_command_error(ctx, exception):
error = getattr(exception, "original", exception)
if hasattr(ctx.command, "on_error"): # If a command has it's own handler
return
elif isinstance(error, CommandNotFound):
return
if isinstance(error, discord.CommandInvokeError):
print(ctx.command)
Your solution is to add them to the command specifically, this also means it can help diagnose an issue with a command more exact.
You can also add any error events to the specific listener, just like how you done it for all commands, instead add them individually.
#bot.command()
async def command_name(ctx):
# ...
#command_name.error
async def command_name_error(ctx, error):
if isinstance(error, commands.CommandInvokeError):
await ctx.send("An error from this command" + error)
With #command_name.error put your command name before the .error, then this makes an error listener for that command, if it produces an error.

Detect whether test has failed within fixture

I am debugging an intermittent test failure. For this purposes I want to dump a lot of debug information if a test failed. Dumping debug stuff is quite slow process which produces a lot of data, so I do not want to do this for every test.
I am using pytest and yield autouse fixture should work great
#pytest.yield_fixture(scope="function", autouse=True)
def dump_on_failue(request):
prepare_debug_dump()
yield
if test_failed(request):
debug_dump()
The problem is that I can't figure out how do I detect whether test has failed or not. There was a questions already and even note on pytest website:
if request.node.rep_setup.failed:
print ("setting up a test failed!", request.node.nodeid)
elif request.node.rep_setup.passed:
if request.node.rep_call.failed:
print ("executing test failed", request.node.nodeid)
Unfortunately this code does not work anymore. There are no rep_setup and rep_calls symbols in node object. I tried to dig request and node object, but no luck.
Anybody knows how to detect whether test failed?
There are no rep_setup and rep_calls symbols in node object.
No.rep_setup and rep_calls symbols are still there.
Add this code into your root conftest.py. It will check pass/fail for every test function.
import pytest
#pytest.mark.tryfirst
def pytest_runtest_makereport(item, call, __multicall__):
rep = __multicall__.execute()
setattr(item, "rep_" + rep.when, rep)
return rep
#pytest.fixture(scope='function', autouse=True)
def test_debug_log(request):
def test_result():
if request.node.rep_setup.failed:
print ("setting up a test failed!", request.node.nodeid)
elif request.node.rep_setup.passed:
if request.node.rep_call.failed:
print ("executing test failed", request.node.nodeid)
request.addfinalizer(test_result)
I use something like this, maybe it can be useful in your case:
#pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item):
outcome = yield
rep = outcome.get_result()
if rep.failed:
if rep.when == "setup":
print("Setup failed!")
elif rep.when == "call":
print("Test method failed")
elif rep.passed and rep.when == "call":
print("Test {} passed.".format(item.name))

Problems with selenium 2 and python 3.4.1

I have a simple automation to fill login form fields. Actually, it passes good, but there's the problem. I need to see actual output in my console after the script filled fields, like "Logged in successfully" or "Username not found". I tried many stuff, but nothing worked this way, my last try was while loop and it works great, but only when I have positive result. I wrote a second condition, but when I type incorrect data, it drives me crazy to see all these errors in my console. So here's the code and part of output.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
baseurl = "http://www.somesite/login"
email = input("Type an email: ")
password = input("Type a password: ")
xpaths = { 'loginBox' : "//input[#id='session_email']",
'passwordBox' : "//input[#id='session_password']",
'submitButton' : "//input[#class='ufs-but']",
'success' : "//div[#class='flash-message success']",
'error' : "//span[#class='form_error']"
}
mydriver = webdriver.Firefox()
mydriver.get(baseurl)
mydriver.find_element_by_xpath(xpaths['loginBox']).send_keys(email)
mydriver.find_element_by_xpath(xpaths['passwordBox']).send_keys(password)
mydriver.find_element_by_xpath(xpaths['submitButton']).click()
while mydriver.find_element_by_xpath(xpaths['success']):
print("Success")
if mydriver.find_element_by_xpath(xpaths['error']):
print("No")
And there's what I got when I try to interrupt an error:
File "ab.py", line 32, in <module>
while mydriver.find_element_by_xpath(xpaths['success']):
File "/usr/local/lib/python3.4/site-packages/selenium-2.43.0-py3.4.egg/selenium/webdriver/remote/webdriver.py", line 230, in find_element_by_xpath
return self.find_element(by=By.XPATH, value=xpath)
File "/usr/local/lib/python3.4/site-packages/selenium-2.43.0-py3.4.egg/selenium/webdriver/remote/webdriver.py", line 662, in find_element
{'using': by, 'value': value})['value']
File "/usr/local/lib/python3.4/site-packages/selenium-2.43.0-py3.4.egg/selenium/webdriver/remote/webdriver.py", line 173, in execute
self.error_handler.check_response(response)
File "/usr/local/lib/python3.4/site-packages/selenium-2.43.0-py3.4.egg/selenium/webdriver/remote/errorhandler.py", line 166, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: 'Unable to locate element: {"method":"xpath","selector":"//div[#class=\'flash-message success\']"}' ; Stacktrace:
at FirefoxDriver.prototype.findElementInternal_ (file:///tmp/tmpjax8kj1u/extensions/fxdriver#googlecode.com/components/driver-component.js:9618:26)
at FirefoxDriver.prototype.findElement (file:///tmp/tmpjax8kj1u/extensions/fxdriver#googlecode.com/components/driver-component.js:9627:3)
at DelayedCommand.prototype.executeInternal_/h (file:///tmp/tmpjax8kj1u/extensions/fxdriver#googlecode.com/components/command-processor.js:11612:16)
at DelayedCommand.prototype.executeInternal_ (file:///tmp/tmpjax8kj1u/extensions/fxdriver#googlecode.com/components/command-processor.js:11617:7)
at DelayedCommand.prototype.execute/< (file:///tmp/tmpjax8kj1u/extensions/fxdriver#googlecode.com/components/command-processor.js:11559:5)
As I said, successfull result ain't a problem.
UPD. I corrected the last part of my code a little bit and now I have this:
while mydriver.find_element_by_xpath(xpaths['success']):
print("Success")
break
while mydriver.find_element_by_xpath(xpaths['error']):
print("No")
break
And it works, but not like I want, the output when I want a negative result:
Type an email: w
Type a password: wer
Success
No
As you see, I wanna see 'success' when result is positive and 'no' when it's negative, but I don't want to see them at the same time.
UPD. Props to Macro Giancarli for huge help, so that's how I got what I exactly want:
try:
success = True
success_element = mydriver.find_element_by_xpath(xpaths['success'])
except NoSuchElementException:
success = False
print("Can't log in. Check email and/or password")
try:
failure = True
failure_element = mydriver.find_element_by_xpath(xpaths['error'])
except NoSuchElementException :
failure = False
print("Logged in successfully")
The problem looks like it's in the way you structure your while loop at the end. You shouldn't need to loop in order to check for success or failure.
Consider that there are four outcomes, assuming that you input the login data. You could either find the element that determines success, find the element that determines failure, find both (should be impossible), or find neither (probably in the case of an unexpected screen, or a failure to load the page).
Instead of expecting some values to be returned from the webdriver queries, try putting them in a try block to catch a NoSuchElementException and checking for non-None contents. Also, try handling each of the four cases so that your program will crash less often.
Edit:
Try this.
try:
success = True
success_element = mydriver.find_element_by_xpath(xpaths['success'])
except NoSuchElementException:
success = False
try:
failure = True
failure_element = mydriver.find_element_by_xpath(xpaths['error'])
except NoSuchElementException :
failure = False
# now handle the four possibilities

How do I catch SocketExceptions in MonkeyRunner?

When using MonkeyRunner, every so often I get an error like:
120830 18:39:32.755:S [MainThread] [com.android.chimpchat.adb.AdbChimpDevice] Unable to get variable: display.density
120830 18:39:32.755:S [MainThread] [com.android.chimpchat.adb.AdbChimpDevice]java.net.SocketException: Connection reset
From what I've read, sometimes the adb connection goes bad, and you need to reconnect. The only problem is, I'm not able to catch the SocketException. I'll wrap my code like so:
try:
density = self.device.getProperty('display.density')
except:
print 'This will never print.'
But the exception is apparently not raised all the way to the caller. I've verified that MonkeyRunner/jython can catch Java exceptions the way I'd expect:
>>> from java.io import FileInputStream
>>> def test_java_exceptions():
... try:
... FileInputStream('bad mojo')
... except:
... print 'Caught it!'
...
>>> test_java_exceptions()
Caught it!
How can I deal with these socket exceptions?
You will get that error every odd time you start MonkeyRunner because the monkey --port 12345 command on the device isn't stopped when your script stops. It is a bug in monkey.
A nicer way to solve this issue is killing monkey when SIGINT is sent to your script (when you ctrl+c). In other words: $ killall com.android.commands.monkey.
Quick way to do it:
from sys, signal
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
device = None
def execute():
device = MonkeyRunner.waitForConnection()
# your code
def exitGracefully(self, signum, frame=None):
signal.signal(signal.SIGINT, signal.getsignal(signal.SIGINT))
device.shell('killall com.android.commands.monkey')
sys.exit(1)
if __name__ == '__main__':
signal.signal(signal.SIGINT, exitGracefully)
execute()
Edit:
as an addendum, I also found a way to notice the Java errors: Monkey Runner throwing socket exception broken pipe on touuch
Edit:
The signal seems to require 2 parameters, not sure it's always the case, made the third optional.
Below is the workaround I ended up using. Any function that can suffer from adb failures just needs to use the following decorator:
from subprocess import call, PIPE, Popen
from time import sleep
def check_connection(f):
"""
adb is unstable and cannot be trusted. When there's a problem, a
SocketException will be thrown, but caught internally by MonkeyRunner
and simply logged. As a hacky solution, this checks if the stderr log
grows after f is called (a false positive isn't going to cause any harm).
If so, the connection will be repaired and the decorated function/method
will be called again.
Make sure that stderr is redirected at the command line to the file
specified by config.STDERR. Also, this decorator will only work for
functions/methods that take a Device object as the first argument.
"""
def wrapper(*args, **kwargs):
while True:
cmd = "wc -l %s | awk '{print $1}'" % config.STDERR
p = Popen(cmd, shell=True, stdout=PIPE)
(line_count_pre, stderr) = p.communicate()
line_count_pre = line_count_pre.strip()
f(*args, **kwargs)
p = Popen(cmd, shell=True, stdout=PIPE)
(line_count_post, stderr) = p.communicate()
line_count_post = line_count_post.strip()
if line_count_pre == line_count_post:
# the connection was fine
break
print 'Connection error. Restarting adb...'
sleep(1)
call('adb kill-server', shell=True)
call('adb start-server', shell=True)
args[0].connection = MonkeyRunner.waitForConnection()
return wrapper
Because this may create a new connection, you need to wrap your current connection in a Device object so that it can be changed. Here's my Device class (most of the class is for convenience, the only thing that's necessary is the connection member:
class Device:
def __init__(self):
self.connection = MonkeyRunner.waitForConnection()
self.width = int(self.connection.getProperty('display.width'))
self.height = int(self.connection.getProperty('display.height'))
self.model = self.connection.getProperty('build.model')
def touch(self, x, y, press=MonkeyDevice.DOWN_AND_UP):
self.connection.touch(x, y, press)
An example on how to use the decorator:
#check_connection
def screenshot(device, filename):
screen = device.connection.takeSnapshot()
screen.writeToFile(filename + '.png', 'png')