Child thread can't update parent thread variable? - python

I found a very perplexing issue in aiosmtpd/244, sharing my puzzlement here to help me find inspiration on how to troubleshoot.
Situation
Two servers running Red Hat 7.9
Works on one, doesn't work on the other
Problematic code, simplified:
>>> from aiosmtpd.controller import Controller
>>> from aiosmtpd.handlers import Sink
>>> cont = Controller(Sink())
>>> cont.start()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.6/site-packages/aiosmtpd/controller.py", line 180, in start
raise RuntimeError("Unknown Error, failed to init SMTP server")
RuntimeError: Unknown Error, failed to init SMTP server
The above RuntimeError can only be generated if, at the end of the start() method, self.smtpd is still None.
Expected flow
start() -> _run() -> loop.create_server()
Then upon first connection:
loop.create_server() -> _factory_invoker() -> factory()
The attribute smtpd is set within _factory_invoker in these lines:
try:
self.smtpd = self.factory()
if self.smtpd is None:
raise RuntimeError("factory() returned None")
return self.smtpd
except Exception as err:
self._thread_exception = err
return _FakeServer(self.loop)
self._thread_exception is interpreted in these lines:
if self._thread_exception is not None:
raise self._thread_exception
# Defensive
if self.smtpd is None:
raise RuntimeError("Unknown Error, failed to init SMTP server")
As you can see, if self.smtpd is None, then it would only happen if there's an error in _factory_invoker(). If so, the error should've been caught and recorded in self._thread_exception. If self._thread_exception is None, then _factory_invoker() had succeeded, and thus self.smtpd couldn't be None.
My main problem in troubleshooting this is that on all my test systems (Windows, Ubuntu, OpenSUSE, MacOS, and FreeBSD), I never encountered a similar error.
So I'm stumped. I'd appreciate any ideas on solving this problem.

Okay, so apparently I was sent on a wild goose chase.
The compounding factor is because I have suppressed ALL exceptions in these lines:
try:
self._testconn()
except Exception:
# We totally don't care of exceptions experienced by _testconn,
# which _will_ happen if factory() experienced problems.
pass
As a result, a very helpful exception raised within self._testconn() was swallowed.
Apparently self._testconn() failed in its attempt to connect to the host:port, and thus _factory_invoker() never got called.
Once the try..except block is modified to only swallow socket.socket_timeout, the actual problem reared its head, and we can quickly fix it.
It's all documented in aiosmtpd/244 and thus I won't repeat them here :-)
Thank you all who spent the time & effort to try solving this puzzle!

Related

What does a "with" statement do without the "as"?

with dpg.window(label="Window"):
dpg.add_text("Hello, world")
dpg.add_button(label="Save")
dpg.add_input_text(label="string", default_value="Quick brown fox")
dpg.add_slider_float(label="float", default_value=0.273, max_value=1)
dpg.add_color_picker(label="Pick", default_value=(0, 0, 0))
This code runs without error (given the correct imports and setup)
dpg.window(label="Window")
dpg.add_text("Hello, world")
dpg.add_button(label="Save")
dpg.add_input_text(label="string", default_value="Quick brown fox")
dpg.add_slider_float(label="float", default_value=0.273, max_value=1)
dpg.add_color_picker(label="Pick", default_value=(0, 0, 0))
This code does not. A runtime error occurs on line 2. I do not understand how a with statement with no as affects the contents of the with. I've seen another similar post, but I can't understand how the explanation answers my question.
Here is the stacktrace for the error:
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Users\18328\PycharmProjects\sandbox\main.py", line 10, in <module>
dpg.add_text("Hello, world")
File "C:\Users\18328\AppData\Local\Programs\Python\Python39\lib\site-packages\dearpygui\dearpygui.py", line 7019, in add_text
return internal_dpg.add_text(default_value, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, indent=indent, parent=parent, before=before, source=source, payload_type=payload_type, drag_callback=drag_callback, drop_callback=drop_callback, show=show, pos=pos, filter_key=filter_key, tracked=tracked, track_offset=track_offset, wrap=wrap, bullet=bullet, color=color, show_label=show_label, **kwargs)
SystemError: <built-in function add_text> returned a result with an error set
On the Python side: Context managers run completely arbitrary code on enter and exit. Python doesn't force that code to do things "behind your back", but neither does it present them from doing so; so the implementation of the specific context manager at hand needs to be inspected to know why it's modifying behavior.
From a DearPyGui perspective: The C++ library that dpg backends into maintains a "container stack". Entering a container as a context manager ensures that it's on the top of your stack. When you create a new DearPyGui object that needs to be attached to a container, it refers to that stack, and attaches itself to the thing that is currently on top.
Using the translation provided by PEP-343, you can see that
with dpg.window(label="Window"):
...
is equivalent to
mgr = dpg.window(label="Window")
exit = type(mgr.__exit__)
value = type(mgr).__enter__(mgr)
exc = True
try:
try:
...
except:
exc = False
if not exit(mgr, *sys.exc_info()):
raise
finally:
if exc:
exit(mgr, None, None, None)
You can compare the above to the fuller translation in PEP-343 to see the single line I omitted that results from the use of as.
In your examples, dpg.window(label="Window").__enter__ is called in the first example, but not in the second.

Identify a specific exception in python

I have a problem with identifying an exception.
Im writing a scraper that scrapes a lot of different websites, and some errors I want to handle and some I only want to ignore.
I except my exceptions like this:
except Exception as e:
most of the exceptions I can identify like this:
type(e).__name__ == "IOError"
But I have one exception "[Errno 10054] An existing connection was forcibly closed by the remote host"
that has the name "error" which is too vague and Im guessing other errors also have that name. Im guessing I can somehow get the errno number from my exception and thus identify it. But I don't know how.
First, you should not rely on the exception's class name, but on the class itself - two classes from two different modules can have the same value for the __name__ attribute while being different exceptions. So what you want is:
try:
something_that_may_raise()
except IOError as e:
handle_io_error(e)
except SomeOtherError as e:
handle_some_other_error(e)
etc...
Then you have two kind of exceptions: the one that you can actually handle one way or another, and the other ones. If the program is only for your personal use, the best way to handle "the other ones" is usually to not handle them at all - the Python runtime will catch them, display a nice traceback with all relevant informations (so you know what happened and where and can eventually add some handling for this case).
If it's a "public" program and/or if you do have some things to clean up before the program crash, you can add a last "catch all" except clause at the program's top level that will log the error and traceback somewhere so it isn't lost (logging.exception is your friend), clean what has to be cleaned and terminate with a more friendly error message.
There are very few cases where one would really want to just ignore an exception (I mean pretending nothing wrong or unexpected happened and happily continue). At the very least you will want to notify the user one of the actions failed and why - in your case that might be a top-level loop iterating over a set of sites to scrap, with an inner try/except block catching "expected" error cases, ie:
# config:
config = [
# ('url', {params})
('some.site.tld', {"param1" : value1, "param2" : value2}),
('some.other.tld', {"param1" : value1, "answer" : 42}),
# etc
]
def run():
for url, params in config:
try:
results = scrap(url, **params)
except (SomeKnownError, SomeOtherExceptedException) as e:
# things that are to be expected and mostly harmless
#
# you configured your logger so that warnings only
# go to stderr
logger.warning("failed to scrap %s : %s - skipping", url, e)
except (MoreSeriousError, SomethingIWannaKnowAbout) as e:
# things that are more annoying and you want to know
# about but that shouldn't prevent from continuing
# with the remaining sites
#
# you configured your logger so that exceptions goes
# to both stderr and your email.
logger.exception("failed to scrap %s : %s - skipping", url, e)
else:
do_something_with(results)
Then have a top-level handler around the call to run() that takes care of unexpected errors :
def main(argv):
parse_args()
try:
set_up_everything()
run()
return 0
except Exception as e:
logger.exception("oops, something unexpected happened : %s", e)
return 1
finally:
do_some_cleanup()
if __name__ == "__main__":
sys.exit(main(sys.argv))
Note that the logging module has an SMTPHandler - but since mail can easily fail too you'd better still have a reliable log (stderr and tee to a file ?) locally. The logging module takes some time to learn but it really pays off in the long run.

Safest way to open a file in python 3.4

I was expecting the following would work but PyDev is returning an error:
try fh = open(myFile):
logging.info("success")
except Exception as e:
logging.critical("failed because:")
logging.critical(e)
gives
Encountered "fh" at line 237, column 5. Was expecting: ":"...
I've looked around and cannot find a safe way to open a filehandle for reading in Python 3.4 and report errors properly. Can someone point me in the correct direction please?
You misplaced the :; it comes directly after try; it is better to put that on its own, separate line:
try:
fh = open(myFile)
logging.info("success")
except Exception as e:
logging.critical("failed because:")
logging.critical(e)
You placed the : after the open() call instead.
Instead of passing in e as a separate argument, you can tell logging to pick up the exception automatically:
try:
fh = open(myFile)
logging.info("success")
except Exception:
logging.critical("failed because:", exc_info=True)
and a full traceback will be included in the log. This is what the logging.exception() function does; it'll call logging.error() with exc_info set to true, producing a message at log level ERROR plus a traceback.

In Python epoll can I avoid the errno.EWOULDBLOCK, errno.EAGAIN?

I wrote a epoll wrapper in python, It works fine but recently I found the performance is not not ideal for large package sending. I look down into the code and found there's actually a LOT of error
Traceback (most recent call last):
File "/Users/dawn/Documents/workspace/work/dev/server/sandbox/single_point/tcp_epoll.py", line 231, in send_now
num_bytes = self.sock.send(self.response)
error: [Errno 35] Resource temporarily unavailable
and previously silent it as the document said, so my sending function was done this way:
def send_now(self):
'''send message at once'''
st = time.time()
times = 0
while self.response != '':
try:
num_bytes = self.sock.send(self.response)
l.info('msg wrote %s %d : %r size %r',self.ip,self.port,self.response[:num_bytes],num_bytes)
self.response = self.response[num_bytes:]
except socket.error,e:
if e[0] in (errno.EWOULDBLOCK,errno.EAGAIN):
#here I printed it, but I silent it in normal days
#print 'would block, again %r',tb.format_exc()
break
else:
l.warning('%r %r socket error %r',self.ip,self.port,tb.format_exc())
#must break or cause dead loop
break
except:
#other exceptions
l.warning('%r %r msg write error %r',self.ip,self.port,tb.format_exc())
break
times += 1
et = time.time()
I googled it, and says it caused by temporarily network buffer run out
So how can I manually and efficiently detect this error instead it goes to exception phase?
Because it cause to much time to rasie/handle the exception.
Use select to see if a socket is ready for reading or writing.

How to raise Suds.WebFault from python code?

I am trying to raise a Suds.WebFault from python code. The __init__ method\constructor takes three arguments __init__(self, fault, document). The fault has fault.faultcode and fault.detail members\attributes\properties. I could not find out what class fault belongs to no matte what I tried. How do I raise Suds.WebFault type exception from python code?
Thanks in advance.
Not sure what exactly you are asking but you can throw a web fault using:
import suds
try:
client.service.Method(parameter)
except suds.WebFault, e:
print e
WebFault is defined in suds.__init__.py as:
class WebFault(Exception):
def __init__(self, fault, document):
if hasattr(fault, 'faultstring'):
Exception.__init__(self, u"Server raised fault: '%s'" %
fault.faultstring)
self.fault = fault
self.document = document
Therefore to raise a WebFault with a meaningful message you will need to pass an object as the first param with the message. Document can simply be None unless required.
import suds
class Fault(object):
faultstring = 'my error message'
raise suds.WebFault(Fault(), document=None)
WebFault is only meant to be actually raised when a <Fault> element is returned by the web server. So it's probably a bad idea to raise that yourself.
If you still would like to, I would start looking at the code where the framework raises it: https://github.com/unomena/suds/blob/4e90361e13fb3d74915eafc1f3a481400c798e0e/suds/bindings/binding.py#L182 - and work backwards from there.

Categories