In object-oriented programming languages, there is a special type of abstraction called an exception. Depending on which language you're using, exception instances can be raised or thrown and caught or excepted. The terminology used is somewhat irrelevant as the general concept remains the same; exceptional behavior has taken place and needs to be dealt with.
Most languages provide a set of standard exceptions that are always available. These exceptions typically build on a base exception class. For instance, in Python, all standard exceptions inherit the base Exception class. This means that developers can also write their own exception types. So if a standard set of exceptions already exists, why bother writing new exceptions?
Exceptional behavior in programs don't just randomly happen. As always, there is a cause and effect. If your program is behaving exactly as it was intended, there is a reason it is doing so. Imagine you have a Python program that only raised Exception exceptions. You could pass some data indicating why the exception occurred to the exception instance. This way the handler could figure out what went wrong.
What if the handler doesn't know what to do with the exception? That is, it doesn't know how to handle the error. The exception handling behavior has been invoked for no good reason. Consider looking up a dictionary key that doesn't exist. In this case, you'd get a KeyError exception. This type of exception has real value because it knows exactly what went wrong. The normal behavior provided a key and expected a value in return. The correct exceptional behavior can now execute because it knows exactly what went wrong.
If the normal behavior doesn't define an exception handler for actions that might raise an exception, any exceptions that are raised will propagate outward in the call stack. So, if we have one function that calls a second function, and that second function raises an exception, the first function will receive the exception if unhandled by the second function. Say the second, nested function raises a KeyError exception. The first, outer function better be prepared to handle KeyError exceptions.
The problem here is that KeyError exceptions may not have any meaning in the first function. If the second function has a responsibility to do a dictionary lookup, it should take on the responsibility of handling KeyError exceptions. The second function could raise a custom exception in the event of a KeyError exception that has some meaning in the context of the first function. It is useful to provide a smaller set of possible exception types as the context grows outward as this gives exceptional behavior more meaning.
Showing posts with label error. Show all posts
Showing posts with label error. Show all posts
Friday, May 14, 2010
Wednesday, October 7, 2009
Form Encode Errors
The FormEncode Python package has grown to be the standard for validating web requests in Python web applications. Rather than implement some custom code that ensures incoming request data is what is expected, FormEncode provides validation functionality that is common. Although the package name suggests that the validation may only occur on form data, this is not the case fortunately. The validation may be applied to any data. The data might not even come from the web, although, that is what its' intended use is.
Parameters of web controllers that make up the application are what the developers of these applications want to validate. This is how external clients are able to get data into the application. Through the parameters of the controllers. It is thus important to ensure that this data is acceptable to the application.
With FormEncode, schemas may be defined by a controller. These schemas are defined on a per controller basis and include the parameters used by the controller. By using this method of parameter validation, each parameter of the controller can be validated at the same time instead of trying to validate each parameter individually.
However, with schemas that have many parameters to validate, more than one parameter may be at fault for the validation failure. In this case, the exception raised by the validation failure has no useful meaning as a whole. What the client needs is an explanation for why each individual field failed. An example of how this can be done is shown below.
Parameters of web controllers that make up the application are what the developers of these applications want to validate. This is how external clients are able to get data into the application. Through the parameters of the controllers. It is thus important to ensure that this data is acceptable to the application.
With FormEncode, schemas may be defined by a controller. These schemas are defined on a per controller basis and include the parameters used by the controller. By using this method of parameter validation, each parameter of the controller can be validated at the same time instead of trying to validate each parameter individually.
However, with schemas that have many parameters to validate, more than one parameter may be at fault for the validation failure. In this case, the exception raised by the validation failure has no useful meaning as a whole. What the client needs is an explanation for why each individual field failed. An example of how this can be done is shown below.
#Example; Better formencode error handling.
import formencode
#A simple validation schema.
class Schema(formencode.Schema):
name=formencode.validators.String(not_empty=True)
#Get meaningful data from the formencode exception.
def format_error(e):
return e.error_dict
#Main.
if __name__=="__main__":
#Attempt to validate.
try:
Schema().to_python({"name":""})
except formencode.Invalid, e:
#Display the better error data.
print format_error(e)
Labels:
error
,
form
,
formencode
,
python
,
validation
Monday, March 9, 2009
Trying to break Python memory consumption
I've been trying to find consistent method to raise a MemoryError exception in Python and have so far been unsuccessful. I'm mostly interested in real-world usage scenarios such has executing huge number arithmetic. Here is what my latest test looks like.
Here, we are simply appending progressively larger integers to a list. Executing this on my Ubuntu laptop results a huge increase in memory consumption. Because we are constructing a massive list object, this would make sense. However, a memory error isn't raised for me. Figuring that my laptop has enough memory to not let any kind of memory mishap take place for awhile, I fired-up a virtual machine with much less memory capabilities and executed the same program. The Python process ends up being killed. No MemoryError exception. What gives?
If I want to handle memory errors in Python, how can I deal with it if the process is terminated before I get a chance to?
#Example; Massive memory consumption.
if __name__=="__main__":
list_obj=[]
cnt=0
while cnt<10**24:
list_obj.append(cnt**2)
cnt+=1
If I want to handle memory errors in Python, how can I deal with it if the process is terminated before I get a chance to?
Labels:
error
,
exceptions
,
memory
,
python
,
ubuntu
,
virtualmachine
Wednesday, February 25, 2009
Interfaces and errors
The basic idea behind defining interfaces and classes that provide those interfaces is to create a contract. This contract specifies that any instances of this class will carry out the behaviour specified within the contract faithfully. It is said that if some instance that provides a given interface, any context in which that instance is used that requires behaviour specified in the contract, the instance is behaving as expected. The instance is error free.
On the other hand, if some behavior is invoked, on some instance, in some required interface context, and the instance does not conform to the contract, the instance does is not behaving correctly.
For example, here is an example of a functional provided interface.
Here we have a simple interface called IDoable. This interface specifies that any classes that provide this interface must implement a do_something() method. The Doer class that provides this interface is valid and functional because it implements the do_something() method.
What about an invalid provided interface? Consider the following non-functional example.

This time, the Doer class does not faithfully carry out the contract specified by the IDoable interface. So if an instance of Doer is used in the context of a required IDoable interface, this will not work as expected.
Depending on the implementation language, the instance may not even have an opportunity to behave in certain contexts because of the lack of one or more required interfaces. Other languages, don't care what type of instances are used in a given context let alone what interfaces they provide. So in either case, the failure of an instance to provide an interface can be taken care of. There will be a compilation error or a runtime error.
This still leaves us with some unanswered questions. In the event of a runtime error, due to the lack of a required interface, is this the result of a design error or should the instance simply not be there. In the case of the design error, chances are that the instance is valid in the context, there is simply a mistake in the implementation of the class. This could in fact be as simple as a missing operation or an invalid operation signature. What about when the instance has no business being in the context which is attempting to invoke behaviour on it? This is obviously not a design flaw in terms of the required interface and the instance that does not provide it. How do you deal with such situations? In interpreted languages, this can be a little trickier. Then, the question is why was this instance placed in the specified context in the first place if it doesn't belong there? We know that all classes that provide the interface in question are implemented perfectly.
There really is no answer. You really have to look at the architectural layers of your application at this point. If instances that cause these types of mishaps belong to the problem domain, you could be in good shape. If hierarchies of problem classes are constructed in such a way as to provide a means to handle these interface errors in the problem domain as well.
Other problem developers can stumble over is the over-reliance on interface conformance. When instances provide the necessary interface in a given context, this doesn't mean that the invoked behavior will execute error-free. Should these errors be handled outside of the problem domain. Again, this is really dependent on your architectural layers and how they are implemented. If rigorous testing results in flawless class implementations in all the problem domain and the application domain, we could then tie the interface errors to the problem domain. Anything else can be considered at the application level and be dealt with accordingly.
Building all these interfaces around the problem domain is a lot of work. Sometimes it is simpler to just define some common exceptions and deal with them, be it application or domain layer.
On the other hand, if some behavior is invoked, on some instance, in some required interface context, and the instance does not conform to the contract, the instance does is not behaving correctly.
For example, here is an example of a functional provided interface.

Here we have a simple interface called IDoable. This interface specifies that any classes that provide this interface must implement a do_something() method. The Doer class that provides this interface is valid and functional because it implements the do_something() method.
What about an invalid provided interface? Consider the following non-functional example.

This time, the Doer class does not faithfully carry out the contract specified by the IDoable interface. So if an instance of Doer is used in the context of a required IDoable interface, this will not work as expected.
Depending on the implementation language, the instance may not even have an opportunity to behave in certain contexts because of the lack of one or more required interfaces. Other languages, don't care what type of instances are used in a given context let alone what interfaces they provide. So in either case, the failure of an instance to provide an interface can be taken care of. There will be a compilation error or a runtime error.
This still leaves us with some unanswered questions. In the event of a runtime error, due to the lack of a required interface, is this the result of a design error or should the instance simply not be there. In the case of the design error, chances are that the instance is valid in the context, there is simply a mistake in the implementation of the class. This could in fact be as simple as a missing operation or an invalid operation signature. What about when the instance has no business being in the context which is attempting to invoke behaviour on it? This is obviously not a design flaw in terms of the required interface and the instance that does not provide it. How do you deal with such situations? In interpreted languages, this can be a little trickier. Then, the question is why was this instance placed in the specified context in the first place if it doesn't belong there? We know that all classes that provide the interface in question are implemented perfectly.
There really is no answer. You really have to look at the architectural layers of your application at this point. If instances that cause these types of mishaps belong to the problem domain, you could be in good shape. If hierarchies of problem classes are constructed in such a way as to provide a means to handle these interface errors in the problem domain as well.
Other problem developers can stumble over is the over-reliance on interface conformance. When instances provide the necessary interface in a given context, this doesn't mean that the invoked behavior will execute error-free. Should these errors be handled outside of the problem domain. Again, this is really dependent on your architectural layers and how they are implemented. If rigorous testing results in flawless class implementations in all the problem domain and the application domain, we could then tie the interface errors to the problem domain. Anything else can be considered at the application level and be dealt with accordingly.
Building all these interfaces around the problem domain is a lot of work. Sometimes it is simpler to just define some common exceptions and deal with them, be it application or domain layer.
Labels:
architecture
,
domain
,
error
,
exceptions
,
interface
Subscribe to:
Posts
(
Atom
)