techniq/flask-script — pull request #88 by fgimian (2014-02-09 07:44:01+00:00)
Title: Made option_list stay isolated to each Command class to avoid option con...

Hey there Sean, hope you're doing well :)

I discovered a rather nasty bug which is squashed with this commit. The **option_list** variable was set as a **class variable** in the **Command** class (instead of an instance variable). This meant that options for one command would conflict with other commands.

You may reproduce the problem as follows...

**Example Script 1**

``` python
#!/usr/bin/env python
from flask import Flask
from flask.ext.script import Manager
from flask.ext.script.commands import ShowUrls, Clean

app = Flask(__name__)
manager = Manager(app)
manager.add_command('clean', Clean())
manager.add_command('urls', ShowUrls())

if __name__ == '__main__':
manager.run()
```

Now run:

``` python
./manage1.py clean
```

The error output is shown below:

``` bash
(flask)fots@fotsies-ubprecise-01:~/flask/flaskage$ ./manage1.py clean
Traceback (most recent call last):
File "./manage1.py", line 12, in
manager.run()
File "/home/fots/.virtualenv/flask/local/lib/python2.7/site-packages/flask_script/__init__.py", line 405, in run
result = self.handle(sys.argv[0], sys.argv[1:])
File "/home/fots/.virtualenv/flask/local/lib/python2.7/site-packages/flask_script/__init__.py", line 384, in handle
return handle(app, *positional_args, **kwargs)
File "/home/fots/.virtualenv/flask/local/lib/python2.7/site-packages/flask_script/commands.py", line 145, in handle
return self.run(*args, **kwargs)
TypeError: run() got an unexpected keyword argument 'url'
```

As you can see, what's happening here is the **run()** command for the **Clean** command class is seeing the **url** option from the **ShowUrls** class.

**Example Script 2**

``` python
#!/usr/bin/env python
from flask.ext.script.commands import ShowUrls, Clean

clean_command = Clean()

print ('clean_command args before ShowUrls: %r' %
[c.args for c in clean_command.get_options()])

urls_command = ShowUrls()

print ('urls_command args after ShowUrls: %r' %
[c.args for c in urls_command.get_options()])
print ('clean_command args after ShowUrls: %r' %
[c.args for c in clean_command.get_options()])
```

And the output:

``` bash
(flask)fots@fotsies-ubprecise-01:~/flask/flaskage$ ./manage2.py
clean_command args before ShowUrls: []
urls_command args after ShowUrls: [('url',), ('--order',)]
clean_command args after ShowUrls: [('url',), ('--order',)]
```

This shows the problem even more clearly as the instance of the **Clean** command gets a different set of args after **ShowUrls** is imported and used.

# Implications of this change #

This change will require developers to ensure they call the **super constructor function** when overriding the constructor in cases where additional options are used **or self.option_list won't be set**.

e.g.

``` python
class ShowUrls(Command):
def __init__(self, order='rule'):
super(ShowUrls, self).__init__()
...
```

I can't really see a way around this I'm afraid, so developers who interface with Flask-Script may need to re-test their extensions with this new version and ideally add this super call in their constructor one way or another (as good practice).

Cheers
Fotis