I am trying to wrap a .NET library in nice pythonic wrappers for use in IronPython.
A pattern used often in this library is a PersistenceBlock to make database CRUD operations clean and 'all or nothing':
try:
Persistence.BeginNewTransaction()
# do stuff here
Persistence.CommitTransaction()
except Exception, e:
Persistence.AbortTransaction()
Log.Error(e)
finally:
Persistence.CloseTransaction()
I would like to wrap this in a class that allows this kind of code:
with PersistenceBlock:
# do stuff here
this is what I've come up with:
class PersistenceBlock():
def __init__(self):
def __enter__(self):
return self
def __exit__(self, exctype, excinst, exctb):
try:
Persistence.BeginNewTransaction()
yield
Persistence.CommitTransaction()
except:
Persistence.AbortTransaction()
Log.Error(e)
finally
Persistence.CloseTransaction()
Is this a proper implementation of PEP343? What might I be missing?
The main thing that is throwing me is that Persistence is a static .NET class, and so there is not 'instance' to manage in the normal sense.
I have tried searching, but the word 'with' overwhelms the resutls :(
You can find the docs via searching for context manager protocol - that's the protocol all objects supposed to work with the with statement should implement.
Context managers (i.e. the __enter__ method) does not need to return anything - only if you want to use the with ... as ... syntax. In your __exit__ method you'll have to do some proper error checks: re-raise the exceptions if there are any and commit if there aren't. Maybe like this:
class PersistenceContext():
def __enter__(self):
# when opening the block, open a transaction
Persistence.BeginNewTransaction()
def __exit__(self, exctype, excinst, exctb):
if excinst is None:
# all went well - commit & close
Persistence.CommitTransaction()
Persistence.CloseTransaction()
else:
# something went wrong - abort, close and raise the error
Persistence.AbortTransaction()
Persistence.CloseTransaction()
raise exctype, excinst, exctb
For completeness, you could also use the contextmanager decorator to implement your context using a simple generator:
import contextlib
#contextlib.contextmanager
def PersisenceContext():
try:
yield Persistence.BeginNewTransaction()
except Exception:
Persistence.AbortTransaction()
raise
else:
Persistence.CommitTransaction()
finally:
Persistence.CloseTransaction()
Related
For example, my function call make look like the following:
def parse(self, text):
...
return self.parse_helper(text)
#staticmethod
def parser_helper(text):
...
return normalize(text)
#staticmethod
def normalize(text):
...
try:
...
except:
raise ValueError('normalize failed.')
If the 'parse' is the function to be provided to users to call, if an exception occurs in normalize(), the whole program terminates. To avoid this, to let users decide what to do when exception occurs, do I have to and try ... except blocks into both 'parser_helper' and 'parse', and let use to use try...except when 'parse' is called?
What's the normal practice of handling this? If there are a few more layers of function calls embedded other than 3 as shown below, do I have to use try ... except block in each layer of function, in order to transfer the handling of exception to the end users at the very top?
The practice to pass a parameter to decide wether to raise or be silent regarding an exception, is something that exists, for example in os.makedirs
You could do
def parse(self, text, error_silent=False):
...
try:
return self.parse_helper(text)
except ValueError as e:
if error_silent:
return None
raise e
Some IO operations produce some set of errors. It is important, that it is not one exception, but set. So, we have set for socket errors, set for file io. How to handle group of exceptions without intersection for different io operations?
For example, OSError handles file io errors and some(?) Socket errors.
I have only one solution: wrap io operations with try-except and raise user-defined exception.
def foo():
try:
# some file io
except:
raise MyFileIOException(reason=sys.exc_info())
try:
# some socket io
except:
raise MySocketIOException(reason=sys.exc_info())
def bar():
try:
foo()
except MyFileIOException as exc:
# handle
except MySocketIOException as exc:
# handle
Is there some better and elegant solution?
I think, you have written a decent exception-handler. Moreover, it depends upon scenario. Say for eg. If you are making a function in library, then it's advisable not to introduce any application/domain specific exception types in the library function and simply raise raw/native exception types. When application code will use that function, it's its responsibility to declare its own set of exception types and wrap the exception generated in library into its own type.
eg. Library's code
def WriteToFile(strFile, strCnt):
objFile = open(strFile, "w")
objFile.write(strCnt)
objFile.close()
StudentRecord Application code
import library # import our library in which SaveStudent is present
import sys
def SaveStudent(objStudent):
try:
WriteToFile("studentRec.txt", str(objStudent))
except IOError as e:
raise StudentSaveFailedException, None, sys.exc_info()[2]
Now let this exception be handled in the application's Exception-handler component, which will meaningfully handle this exception and take necessary corrective actions.
Though I know that it's a naive explaination, but this is the strategy used in larger application, which uses several in-house and 3rd party libraries for proper exception handling.
Hope it helps you.
I'm ended with:
def decorator(f):
#functools.wraps(f)
#asyncio.coroutine
def wrapper(*args, **kwargs):
try:
return (yield from f(*args, **kwargs))
except asyncio.CancelledError:
raise
except:
raise CustomError(reason=sys.exc_info())
return wrapper
I'm trying to add SimPy simulation to a project I'm working on and I have some confusion about version 3's release/request.
I was able to implement resources using a 'with'-block without trouble but in my situation I want to request/release a resource without using a 'with'-block.
However, I cannot find an example of this using SimPy 3. I read the documentation/source regarding resources but still can't get it quite right. Could someone explain how to properly:
...
Request a Resource with the method: 'request()'
...
Release that Resource with the method: 'release()'
...
Thanks, and sorry for the bother.
PS: I'm intending to use Resources.resource
If you want to use a resource without a with block (and you know you won’t get interrupted), its just:
req = resource.request()
yield req
# do stuff
resource.release(req)
Using with on an object calls __enter__ when you enter the with block, and __exit__ when you leave. So when you do
res = resource.Resource()
with res.request() as req:
# stuff
You're really calling __enter__ on a Request object, doing #stuff then calling __exit__:
class Request(base.Put):
def __exit__(self, exc_type, value, traceback):
super(Request, self).__exit__(exc_type, value, traceback)
self.resource.release(self)
class Put(Event): # base.Put
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
# If the request has been interrupted, remove it from the queue:
if not self.triggered:
self.resource.put_queue.remove(self)
So, the with block is equivalent to this:
res = resource.Resource(...)
req = res.request()
#stuff
if not req.triggered:
res.put_queue.remove(req)
res.release(req)
However, the with block is also making sure that the cleanup code is called no matter what exceptions are thrown during #stuff. You'll lose that with the above code.
It's all outlined in PEP343;
with EXPR as VAR:
BLOCK
becomes:
mgr = (EXPR)
exit = type(mgr).__exit__ # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
try:
VAR = value # Only if "as VAR" is present
BLOCK
except:
# The exceptional case is handled here
exc = False
if not exit(mgr, *sys.exc_info()):
raise
# The exception is swallowed if exit() returns true
finally:
# The normal and non-local-goto cases are handled here
if exc:
exit(mgr, None, None, None)
This is exactly how python uses with... as... blocks, but I'm presuming there's some reason you don't want to use these. If that's the case, then you just need the __enter__ and __exit__ functions. The way i think of it, is __enter__ sets everything up, and __exit__ does all the cleanup.
I was reading this article and it was showing this interesting bit of code:
class Car(object):
def _factory_error_handler(self):
try:
yield
except FactoryColorError, err:
stacktrace = sys.exc_info()[2]
raise ValidationError(err.message), None, stacktrace
def _create_customizer_error_handler(self, vin):
try:
yield
except CustomizerError, err:
self._factory.remove_car(vin)
stacktrace = sys.exc_info()[2]
raise ValidationError(err.message), None, stacktrace
def _update_customizer_error_handler(self, vin):
try:
yield
except CustomizerError, err:
stacktrace = sys.exc_info()[2]
raise ValidationError(err.message), None, stacktrace
def create(self, color, stereo):
with self._factory_error_handler():
vin = self._factory.make_car(color)
with self._create_customizer_error_handler(vin):
self._customizer.update_car(vin, stereo)
return vin
def update(self, vin, color, stereo):
with self._factory_error_handler():
self._factory.update_car_color(vin, color)
with self._update_customizer_error_handler(vin):
self._customizer.update_car(vin, stereo)
return
Here, they are yielding with no argument in a try block. And then using it within a with block. Can someone please explain what is going on here?
The code in the post appears to be wrong. It would make sense if the various error handlers were decorated with contextmanager. Note that, in the post, the code imports contextmanager but doesn't use it. This makes me think the person just made a mistake in creating the post and left contextmanager out of that example. (The later examples in the post do use contextmanager.) I think the code as posted will cause an AttributeError, because the various _error_handler functions aren't context managers and don't have the right __enter__ and __exit__ methods.
With contextmanager, the code makes sense based on the documentation:
At the point where the generator yields, the block nested in the with statement is executed. The generator is then resumed after the block is exited. If an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occurred.
Firstly, I'm not sure if my approach is proper, so I'm open to a variety of suggestions.
If try/except statements are frequently repeated in code, are there any good ways to shorten them or avoid fully writing them out?
try:
# Do similar thing
os.remove('/my/file')
except OSError, e:
# Same exception handing
pass
try:
# Do similar thing
os.chmod('/other/file', 0700)
except OSError, e:
#Same exception handling
pass
For example, for one line actions you could define a exception handling wrapper and then pass a lambda function:
def may_exist(func):
"Work with file which you are not sure if exists."""
try:
func()
except OSError, e:
# Same exception handling
pass
may_exist(lambda: os.remove('/my/file'))
may_exist(lambda: os.chmod('/other/file', 0700))
Does this 'solution' just make things less clear? Should I just fully write out all the try/except statements?
The best way to abstract exception handling is with a context manager:
from contextlib import contextmanager
#contextmanager
def common_handling():
try:
yield
finally:
# whatever your common handling is
then:
with common_handling():
os.remove('/my/file')
with common_handling():
os.chmod('/other/file', 0700)
This has the advantage that you can put full statements, and more than one of them, in each common_handling block.
Keep in mind though, your need to use the same handling over and over again feels a lot like over-handling exceptions. Are you sure you need to do this much?
It would probably be cleaner to make may_exist a decorator:
from functools import wraps
def may_exist(func):
#wraps(func):
def wrapper(*args, **kwds):
try:
return func(*args, **kwds)
except OSError:
pass
return wrapper
Then you can either do:
may_exist(os.remove)('/my/file')
may_exist(os.chmod)('/other/file', 0700)
for a one-off call, or:
remove_if_exists = may_exist(os.remove)
...
remove_if_exists('somefile')
if you use it a lot.
I think your generic solution is ok, but I wouldn't use those lambdas at the bottom. I'd recommend passing the function and arguments like this
def may_exist(func, *args):
"Work with file which you are not sure if exists."""
try:
func(args)
except OSError, e:
# Same exception handling
pass
may_exist(os.remove, '/my/file')
may_exist(os.chmod, '/other/file', '0700')
Would something like this work:
def may_exist(func, *func_args):
try:
func(*func_args)
except OSError as e:
pass