Публикации с меткой «opster»

Александр Соловьёв

Opster

Two months ago I've released little command line parsing library for Python called opster (actually it was called finaloption then, but I've renamed it because of remark from native speaker ;-)). What's the reason to write one more command line parser when Python already has getopt and optparse in standard library and not so long time ago argparse and optfunc were released?

Well... as usually, because I think that they are going wrong way and doing wrong things. Of course, IMO (but what matters if not opinion? :P).

It started to happen when Zed Shaw wrote big article on Python warts and mentioned that Lamson has much better command line parsing solution than argparse/optparse. I was interested in this topic a little at the time and I looked at the code. It would be lie to say that I liked it. In fact I thought that this is a heresy of the same level as optparse. ;-)

I've written in Twitter that it's funny to say that Lamson has superior command system and got some amount of sarcasm from Zed and clear understanding that Zed see nothing bad when:

  • command functions should be defined in a single module
  • default settings are defined by calling separate function inside a command function
  • specifying option in command line with mistake wouldn't raise error
  • formatting of help text on options is done by hands

So I thought that world needs Mercurial's command system. ;) And I've rewritten it as library, keeping main idea.

Small example of usage:

from opster import command

@command(usage='[-l HOST] DIR')
def main(dirname,
         listen=('l', 'localhost', 'ip to listen on'),
         port=('p', 8000, 'port to listen on'),
         daemonize=('d', False, 'daemonize process'),
         pid_file=('', '', 'name of file to write process ID to')):
    '''Command with option declaration as keyword arguments

    Otherwise it's the same as previous command
    '''
    print locals()

if __name__ == '__main__':
    main()

I think that you should understand what's going on here. For example, option is required to have long name (keyword argument name), possibly short name (using '' will discard short name), some default value and help string. Default value determines what should be done to incoming data:

  • string - nothing happens, incoming value will remain as string
  • int - incoming value is parsed by calling int()
  • list - incoming value is appended to the list
  • function is called with incoming value and output is used
  • True/False/None - option needs no argument, just switching default value in opposite value

After wrapping with @command your function main() can be called:

  • without arguments at all; it will parse sys.argv in this case
  • with argument named argv, which needs to be list of strings (same as sys.argv[1:])
  • with usual arguments/keyword arguments, which are defined in function

I think it may be not obvious that you will get clean values in your function (for example, port will contain value 8000), and not some strange three-tuples.

And you get such help for free:

piranha@gto ~/dev/misc/opster>./test_opts.py --help
test_opts.py [-l HOST] DIR

Command with option declaration as keyword arguments

    Otherwise it's the same as previous command

options:

 -l --listen     ip to listen on (default: localhost)
 -p --port       port to listen on (default: 8000)
 -d --daemonize  daemonize process
    --pid-file   name of file to write process ID to
 -h --help       show help

Furthermore, underscores in argument names are converted to hyphens to support conventions of command line. ;)

I should mention that option names (and subcommand names, if you're using them) can be shortened: i.e. you can say --pi instead of --pid-file.

If I'm going to compare opster with something, this should be optfunc by Simon Willison. Most noticeable differences are syntax of command definitions and subcommand support. Actually optfunc has subcommand support, but it's pretty incomplete.

Opster uses getopt inside to parse options and that's the reason why it's somewhat bigger than optfunc (which is essentially optparse wrapper). Opster's internal API - options are list of four-tuples (short name, long name, default value, help string) - is exactly the same as Mercurial's API for defining options. This means that such code will work (taken from test_cmd.py):

import opster

config_opts=[('c', 'config', 'webshops.ini', 'config file to use')]

@opster.command(options=config_opts)
def initdb(config):
    """Initialize database"""
    pass

@opster.command(options=config_opts)
def runserver(listen=('l', 'localhost', 'ip to listen on'),
              port=('p', 5000, 'port to listen on'),
              **opts):
    """Run development server"""
    print locals()

if __name__ == '__main__':
    opster.dispatch()

Interesting thing happens in definition of runserver, help and output of which looks like this:

piranha@gto ~/dev/misc/opster> ./test_cmd.py help runs
test_cmd.py runserver [OPTIONS]

Run development server

options:

 -l --listen  ip to listen on (default: localhost)
 -p --port    port to listen on (default: 5000)
 -c --config  config file to use (default: webshops.ini)
 -h --help    display help

piranha@gto ~/dev/misc/opster> ./test_cmd.py runs
{'port': 5000, 'opts': {'config': 'webshops.ini'}, 'listen': 'localhost'}

You can factor out common options and pass them to @command decorator, keeping your pants DRY. ;-)

So... Read documentation and use it! :) Any feedback, questions, suggestions and patches are highly welcome. ;-)

Александр Соловьёв

Opster

Two months ago I've released little command line parsing library for Python called opster (actually it was called finaloption then, but I've renamed it because of remark from native speaker ;-)). What's the reason to write one more command line parser when Python already has getopt and optparse in standard library and not so long time ago argparse and optfunc were released?

Well... as usually, because I think that they are going wrong way and doing wrong things. Of course, IMO (but what matters if not opinion? :P).

It started to happen when Zed Shaw wrote big article on Python warts and mentioned that Lamson has much better command line parsing solution than argparse/optparse. I was interested in this topic a little at the time and I looked at the code. It would be lie to say that I liked it. In fact I thought that this is a heresy of the same level as optparse. ;-)

I've written in Twitter that it's funny to say that Lamson has superior command system and got some amount of sarcasm from Zed and clear understanding that Zed see nothing bad when:

  • command functions should be defined in a single module
  • default settings are defined by calling separate function inside a command function
  • specifying option in command line with mistake wouldn't raise error
  • formatting of help text on options is done by hands

So I thought that world needs Mercurial's command system. ;) And I've rewritten it as library, keeping main idea.

Small example of usage:

from opster import command

@command(usage='[-l HOST] DIR')
def main(dirname,
         listen=('l', 'localhost', 'ip to listen on'),
         port=('p', 8000, 'port to listen on'),
         daemonize=('d', False, 'daemonize process'),
         pid_file=('', '', 'name of file to write process ID to')):
    '''Command with option declaration as keyword arguments

    Otherwise it's the same as previous command
    '''
    print locals()

if __name__ == '__main__':
    main()

I think that you should understand what's going on here. For example, option is required to have long name (keyword argument name), possibly short name (using '' will discard short name), some default value and help string. Default value determines what should be done to incoming data:

  • string - nothing happens, incoming value will remain as string
  • int - incoming value is parsed by calling int()
  • list - incoming value is appended to the list
  • function is called with incoming value and output is used
  • True/False/None - option needs no argument, just switching default value in opposite value

After wrapping with @command your function main() can be called:

  • without arguments at all; it will parse sys.argv in this case
  • with argument named argv, which needs to be list of strings (same as sys.argv[1:])
  • with usual arguments/keyword arguments, which are defined in function

I think it may be not obvious that you will get clean values in your function (for example, port will contain value 8000), and not some strange three-tuples.

And you get such help for free:

piranha@gto ~/dev/misc/opster>./test_opts.py --help
test_opts.py [-l HOST] DIR

Command with option declaration as keyword arguments

    Otherwise it's the same as previous command

options:

 -l --listen     ip to listen on (default: localhost)
 -p --port       port to listen on (default: 8000)
 -d --daemonize  daemonize process
    --pid-file   name of file to write process ID to
 -h --help       show help

Furthermore, underscores in argument names are converted to hyphens to support conventions of command line. ;)

I should mention that option names (and subcommand names, if you're using them) can be shortened: i.e. you can say --pi instead of --pid-file.

If I'm going to compare opster with something, this should be optfunc by Simon Willison. Most noticeable differences are syntax of command definitions and subcommand support. Actually optfunc has subcommand support, but it's pretty incomplete.

Opster uses getopt inside to parse options and that's the reason why it's somewhat bigger than optfunc (which is essentially optparse wrapper). Opster's internal API - options are list of four-tuples (short name, long name, default value, help string) - is exactly the same as Mercurial's API for defining options. This means that such code will work (taken from test_cmd.py):

import opster

config_opts=[('c', 'config', 'webshops.ini', 'config file to use')]

@opster.command(options=config_opts)
def initdb(config):
    """Initialize database"""
    pass

@opster.command(options=config_opts)
def runserver(listen=('l', 'localhost', 'ip to listen on'),
              port=('p', 5000, 'port to listen on'),
              **opts):
    """Run development server"""
    print locals()

if __name__ == '__main__':
    opster.dispatch()

Interesting thing happens in definition of runserver, help and output of which looks like this:

piranha@gto ~/dev/misc/opster> ./test_cmd.py help runs
test_cmd.py runserver [OPTIONS]

Run development server

options:

 -l --listen  ip to listen on (default: localhost)
 -p --port    port to listen on (default: 5000)
 -c --config  config file to use (default: webshops.ini)
 -h --help    display help

piranha@gto ~/dev/misc/opster> ./test_cmd.py runs
{'port': 5000, 'opts': {'config': 'webshops.ini'}, 'listen': 'localhost'}

You can factor out common options and pass them to @command decorator, keeping your pants DRY. ;-)

So... Read documentation and use it! :) Any feedback, questions, suggestions and patches are highly welcome. ;-)

Метки

.net .NET C# .sort 1.2 2009 2010 404 error admin ajax amazon analytics and apache api archlinux asp.net async asynchronous autocomplete bash blender blog blogengine blogs book bootstrap bot bpython buildout byteflow bzr C c plus plus C++ cache cbv Chaco checkio chrome ci ckeditor class based views clojure closure cms cms с удобной админкой code coding style collectd COM comet competition conference ConfigParser contest Context continuous integration CouchDB coverage CppCMS cpyext cpython crud csrf CSS ctypes curl custom model fields cx_freeze cython database db dbm dbqueries debian debug debugging decorator decorators deploy deployment descriptor design dev devconf developers development diveintopython Django django 1.2 django 1.3 django advent django framework django template django trunk django weblog django-admin-tools django-cms django-compressor django-hosts django-piston django-registration django-sphinx django.admin djangoadvent djangocms djangodash doc documentation drupal e-legion eclipse EGit emacs encoding Enthought epoll erlang event exception ExtJS fabric facebook fastcgi finaloption fixtures fonts forms formset fp framework freebsd freeswitch fs2web ftp fun funcparserlib functional gae gamin gandi generic views gettext gevent gil git github gitosis Google Google App Engine google picasa Google Translate google wave Google Web Toolkit grab grablab greenlet gtd gui haskell hg hgshelve highlighter host hosting how-to howto html html5lib Hudson humor i18n icfpc ide idiomatic image-scripting improvements Internet interpreter ipython ironpython izmenimsya.ru jabber java javascript jenkins jetbrains JIT job jquery json jstree jython kde kiev kiyv kyivpy l10n ldap library libs Life Links linux Linux & Unix LLVM logging logs lxml Mac OS X magic mail markdown Matplotlib Mayavi maybe mediavirus meetup memcache Memcached memory messages metaclass middleware migration mikrotik mkd model models mod_python mod_wsgi mongodb monitoring mptt musicmans.ru musicx mvc my-projects mysql netCDF networkx newforms newforms-admin news nginx Nhibernate nix nose NoSQL numpy oop open source OpenID openoffice opster optimization oracle orm os pagination parsing path patterns pdf PDF-принтер PEP PEP8 performance performance optimization perl personality photo php picture-driven computing PIL pinax pingback pip plasma plone plugin plugins postgresql programming progress bar psycopg2 py2exe pybb pybbm pycamp pycharm pycon pycow pycurl pydev pygtk pylons PyNGL pypy pyqt PyQt4 pyrad pyramid PySide Python Python 2.5 python 2.7 python 3 python c api python speed python-mssql python3 pywinauto Qt Qt4 queue rabbitmq radius raw sql re redis redsolution redsolution cms regexp regular expressions release repoze.bfg RequestContext reusable apps robokassa rss ru ruby ruby-on-rails sample satchmo scalability SciPy scraping screencast search selenium self.error seo server setattr settings setuptools shell sikuli sms snippet socket.io software sorting south sphinx spider sql sqlalchemy sqlite ssh startup step-by-step subdomain subversion svn SyntaxHighlighter system tags tdd tddspry teh drama template templates templatetags test testing thinkpad threading threads tips tips and tricks tools tornadio tornado tornado server tricks tutorial tweepy twisted twitter typography uapycon Ubuntu ucsvlog uml Uncategorized unicode unit test unit testing UnitTest Unladen Swallow upload urllib urls utf-8 uwsgi validation vcs versioning video vim virtualenv Visual Studio vkontakte voip wave web web-devel web-services web-разработка webdev webfaction webkit webpy websockets webtest widget widgets Win API windows Wirbel work wrapper wsgi wxPython wxWidgets wysiwyg xapian xml xmonad xmpp xpath yandex youtube zip zomg zope [cdata[cbv]] [cdata[ci]] [cdata[class based views]] [cdata[continuous integration]] [cdata[django framework]] [cdata[django-sphinx]] [cdata[django]] [cdata[nginx]] [cdata[python]] [cdata[virtualenv]] [cdata[программирование]] автоматизация администрирование администрирование django админка алгоритмы архитектура атрибуты базы данных Без рубрики безопасность библиотеки блоге бот веб-разработка видео Визуализация данных вконтакте Все записи гвидо ван россум граббер графика графы декоратор декораторы дескриптор дескрипторы документация заметки игра жизнь идея интересное киев Клиентам книги конференция личное математика метаклассы модели модули монады морфология мысли невозможное новости о облачные вычисления обо мне Обработка данных оптимизация оптимизация кода Основная лента основы парсинг парсинг сайтов перевод песочница Питон поебень поиск правила кодирования программирование Проектирование производительность работа рабочее размышлизмы Разное разработка разработка приложений разработки регулярные выражения сайт событие события ссылки статьи тестирование тесты Тюмень убунтариум фигня философия формы форум Хабрахабр хакинг хостинг шаблоны шаблоны проектирования эксперимент Эксперименты юмор я пиарюсь Яндекс