Well I guess this library deserves its own page :) Because, before being the name of this website and behind all this stuff, aspyct used to be the name of a small, ahead-of-time library.
If you’d like to read more about the aspyct library and download it, please refer to the old aspyct wiki.
The development of it is currently discontinued. Why that? Because what aspyct allows you to do is actually quite easy with python! Basically, aspyct did two things:
wrap functions with custom behavior, like transaction handling or security enforcement;
cut through modules and classes to apply these behaviors.
Aspyct was and will always be a major step in my developer’s life, so it makes me a little sad to say that it’s not really added value! But still, it’s true, so let’s see what python can do for us :)
Using python decorators
The first thing is really easy to do with decorators. Let me tell you about them.
# Basically, a decorator is a function that returns a function.# They usually take another function as argument, however, and wrap the latter# with some behavior.# Create our decoratordefsimple_decorator(func):# This is were your neurons should start heating up...defwrapper():'''Prints "before" and "after" around `func`'''print("before")func()print("after")returnwrapper# Still alive? See, was easy ;)# All we did is create a function that call the given `func`,# and return this new function# Now decorate a function with the '@' sign@simple_decoratordeftest():print("inside")
Now try & run this test function.
>>>test()beforeinsideafter# By the way, what do you think is the result of this line? Why?>>>test.__name__# And when you got it, have a look at functools.wraps ;)
So what can we do with that? Imagine we need to handle database transactions: before the method is called, start a transaction. If everything goes right, commit, otherwise rollback. We could create a class for this.
classTransactionHandler(object):defwrap(self,func):defwrapper(*args,**kwargs):# Standard python varargs notationself.start_transaction()try:func(*args,**kwargs)self.commit_transaction()exceptExceptionase:self.rollback_transaction()raiseereturnwrapperdefstart_transaction(self):print("Starting transaction")defcommit_transaction(self):print("Commit")defrollback_transaction(self):print("Rollback")# And later...tx_handler=TransactionHandler()@tx_handler.wrapdefsave_into_db(value):# Verify that the data is correct.# In our case, it must be true, otherwise an error occursifnotvalue:raiseValueError("Must be true")else:print("Saving value")# Imagine we save this into database
Interesting point: you can stack up several decorators on a single function, or also use decorators on methods (i.e. “class functions”). By the way, what do you think is @instancemethod, @property et al?
classMyClass:@second_decorator@first_decoratordefmy_method(self):# Do something useful...# Notice the weird order "second, first"?# That's because the decorator closest to the function is applied first.
So why use decorators rather than aspyct? Well for this kind of purpose aspyct was more like a hammer to kill a fly. A lot of logic was put into doing not a lot of things, with the inherent bugs and additional debugging complexity.
The second thing is a bit more tricky to accomplish, but is based on a simple fact: you can replace functions and methods at runtime. This is called “monkey patching”. Let’s reuse our previous simple_decorator, with a slight modification
defsimple_decorator(func):# This is were your neurons should start heating up...defwrapper(*args,**kwargs):'''Prints "before" and "after" around `func`'''print("before")func(*args,**kwargs)print("after")returnwrapperdefmy_function():print("inside")# And do the monkey patch!my_function=simple_decorator(my_function)# Actually, that's exactly the equivalent of decorating my_function# This can be applied to methods as well, with a little twistclassMyClass:defmy_method(self):print("inside")# Suspense...MyClass.my_method=simple_decorator(MyClass.my_method)# For methods, it's important that the `self` argument be passed to function# Therefore, we need the *args, **kwargs from the decorator