Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Python (http://www.velocityreviews.com/forums/f43-python.html)
-   -   adding a simulation mode (http://www.velocityreviews.com/forums/t947809-adding-a-simulation-mode.html)

andrea crotti 07-04-2012 09:42 AM

adding a simulation mode
 
I'm writing a program which has to interact with many external
resources, at least:
- mysql database
- perforce
- shared mounts
- files on disk

And the logic is quite complex, because there are many possible paths to
follow depending on some other parameters.
This program even needs to run on many virtual machines at the same time
so the interaction is another thing I need to check...

Now I successfully managed to mock the database with sqlalchemy and only
the fields I actually need, but I now would like to simulate also
everything else.

I would like for example that if I simulate I can pass a fake database,
a fake configuration and get the log of what exactly would happen. But
I'm not sure how to implement it now.. One possibility would be to have
a global variable (PRETEND_ONLY = False) that if set should be checked
before every potentially system-dependent command.

For example

copytree(src, dest) becomes:
if not PRETEND_ONLY:
copytree(src, dest)

But I don't like it too much because I would have to add a lot of
garbage around..

Another way is maybe to set the sys.excepthook to something that catchs
all the exceptions that would be thrown by these comands, but I'm not
sure is a good idea either..

Any suggestion?

Steven D'Aprano 07-04-2012 01:41 PM

Re: adding a simulation mode
 
On Wed, 04 Jul 2012 10:42:56 +0100, andrea crotti wrote:

> I'm writing a program which has to interact with many external
> resources, at least:
> - mysql database
> - perforce
> - shared mounts
> - files on disk
>
> And the logic is quite complex, because there are many possible paths to
> follow depending on some other parameters. This program even needs to
> run on many virtual machines at the same time so the interaction is
> another thing I need to check...
>
> Now I successfully managed to mock the database with sqlalchemy and only
> the fields I actually need, but I now would like to simulate also
> everything else.
>
> I would like for example that if I simulate I can pass a fake database,
> a fake configuration and get the log of what exactly would happen. But
> I'm not sure how to implement it now.. One possibility would be to have
> a global variable (PRETEND_ONLY = False) that if set should be checked
> before every potentially system-dependent command.


I think a better way would be to use a mock database, etc. For each thing
which you want to simulate, create a class that has the same interface
but a simulated implementation.

Then, have your code accept the thing as an argument.

E.g. instead of having a hard-coded database connection, allow the
database connection to be set (perhaps as an argument, perhaps as a
config option, etc.).

There are libraries to help with mocks, e.g.:

http://garybernhardt.github.com/python-mock-comparison/


> For example
>
> copytree(src, dest) becomes:
> if not PRETEND_ONLY:
> copytree(src, dest)


Ewww :(

Mocking the file system is probably the hardest part, because you
generally don't have a "FileSystem" object available to be replaced. In
effect, your program has one giant global variable, the file system.
Worse, it's not even a named variable, it's hard-coded everywhere you use
it.

I don't know of any good solution for that. I've often thought about it,
but don't have an answer.

I suppose you could monkey-patch a bunch of stuff:

if ONLY_PRETEND:
open = my_mock_open
copytree = my_mock_copytree
# etc.
main() # run your application



but that would also be painful.


--
Steven

andrea crotti 07-04-2012 01:59 PM

Re: adding a simulation mode
 
2012/7/4 Steven D'Aprano <steve+comp.lang.python@pearwood.info>:
>
> Then, have your code accept the thing as an argument.
>
> E.g. instead of having a hard-coded database connection, allow the
> database connection to be set (perhaps as an argument, perhaps as a
> config option, etc.).
>
> There are libraries to help with mocks, e.g.:
>
> http://garybernhardt.github.com/python-mock-comparison/


Ah yes this part is already done, I pass an object to the entry point
of the program which represents the database connection, which looks
like this:

class MockMysqlEngine(MySqlEngine):
# TODO: make the engine more generic would avoid this dirty hack
def __init__(self, *args, **kwargs):
# self.engine =
create_engine('sqlite:////home/andrea/testdb.sqlite', echo=True)
self.engine = create_engine('sqlite://', echo=True)
self.meta = MetaData(bind=self.engine)
self.session_maker = sessionmaker(bind=self.engine)


Now I populate statically the schema and populate with some test data
too, but I'm also implementing a weay to just pass some CSV files so
that other people can easily write some test cases with some other
possible database configurations.

(And I use mock for a few other things)


>
>
>> For example
>>
>> copytree(src, dest) becomes:
>> if not PRETEND_ONLY:
>> copytree(src, dest)

>
> Ewww :(
>
> Mocking the file system is probably the hardest part, because you
> generally don't have a "FileSystem" object available to be replaced. In
> effect, your program has one giant global variable, the file system.
> Worse, it's not even a named variable, it's hard-coded everywhere you use
> it.
>
> I don't know of any good solution for that. I've often thought about it,
> but don't have an answer.
>
> I suppose you could monkey-patch a bunch of stuff:
>
> if ONLY_PRETEND:
> open = my_mock_open
> copytree = my_mock_copytree
> # etc.
> main() # run your application
>
>
>
> but that would also be painful.
>


Yes there is no easy solution apparently.. But I'm also playing
around with vagrant and virtual machine generations, suppose I'm able
to really control what will be on the machine at time X, creating it
on demand with what I need, it might be a good way to solve my
problems (a bit overkill and slow maybe though).

I'll try the sys.excepthook trick first, any error should give me an
exception, so if I catch them all I think it might work already..

andrea crotti 07-04-2012 02:19 PM

Re: adding a simulation mode
 
> Yes there is no easy solution apparently.. But I'm also playing
> around with vagrant and virtual machine generations, suppose I'm able
> to really control what will be on the machine at time X, creating it
> on demand with what I need, it might be a good way to solve my
> problems (a bit overkill and slow maybe though).
>
> I'll try the sys.excepthook trick first, any error should give me an
> exception, so if I catch them all I think it might work already..



I actually thought that the sys.excepthook would be easy but it's not
so trivial apparently:
This simple sample never reaches the print("here"), because even if
the exception is catched it still quits with return code=1.

I also tried to catch the signal but same result, how do I make it
continue and just don't complain?

The other option if of course to do a big try/except, but I would
prefer the excepthook solution..


import sys
from shutil import copy

def my_except_hook(etype, value, tb):
print("got an exception of type", etype)


if __name__ == '__main__':
sys.excepthook = my_except_hook
copy('sdflsdk')
print("here")

Paul Rubin 07-04-2012 05:01 PM

Re: adding a simulation mode
 
andrea crotti <andrea.crotti.0@gmail.com> writes:
> copytree(src, dest) becomes:
> if not PRETEND_ONLY:
> copytree(src, dest)
>
> But I don't like it too much because I would have to add a lot of
> garbage around..


I've had good results writing the module under test in the style of a
java applet, i.e. one of its args is a class instance representing the
"outside world", and ALL interaction that you might want to simulate is
done through this object:

def your_prog(parent):
conn = parent.db.make_connection(...)
blah = parent.copytree(...)

Then you make "real" and "mock" versions of the external interface, and
pass in an appropriate instance.


All times are GMT. The time now is 08:56 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.