Twisted script issue

569 Views Asked by At

I have written a Twisted bin file that is deployed on /usr/bin during application deployement, based on the Axiom example provided elsewhere on StackOverflow (I don't remember where), the project can be found here.

My problem is that, during the python setup.py install process, the installed bin file is different than the one from Axiom package:

/usr/bin/axiomatic

#!/code/venv/bin/python
from axiom.scripts import axiomatic
axiomatic.main()

/usr/bin/myapp

#!/code/venv/bin/python
# EASY-INSTALL-DEV-SCRIPT: 'MyApp==0.0.1','myapp'
__requires__ = 'MyApp==0.0.1'
__import__('pkg_resources').require('MyApp==0.0.1')
exec(compile(open(__file__).read(), __file__, 'exec'))

and the latter doesn't work when invoking it from the bash shell: myapp start

I get the following error: unknow command myapp

If I use python setup.py develop instead of python setup.py install everything works smoothly.


I have setup a little test application that starts a tcp service on port 1234:

  • the command twistd finger works, the service starts
  • the command fingerize start (different name on purpose, to be sure not calling the wrong one) doesn't work

Here is the code:

bin/fingerize

#!/usr/bin/python
from finger import tap
tap.main()

twisted/plugins/finger_plugin.py

from twisted.application.service import ServiceMaker
Finger = ServiceMaker('Finger', 'finger.plugins', 'blah', 'finger')

finger/plugins.py

from twisted.application import internet
from twisted.internet import endpoints
from twisted.python import usage
from twisted.internet import protocol


class Options(usage.Options):
    """ """


def makeService(options):
    from twisted.internet import reactor

    endpoint = endpoints.TCP4ServerEndpoint(reactor, 1234)
    return internet.StreamServerEndpointService(
        endpoint,
        protocol.Factory())

finger/tap.py

import sys

from twisted.python import usage
from twisted.scripts import twistd


class Start(twistd.ServerOptions):
    run = staticmethod(twistd.run)

    def subCommands(self):
        raise AttributeError()

    subCommands = property(subCommands)

    def parseOptions(self, args):
        print(sys.argv)
        print(args)
        a = self.getArguments(args)
        print(a)
        sys.argv[1:] = a
        print(sys.argv)
        print('Starting finger service...')
        self.run()

    def getArguments(self, args):
        args.extend(['--pidfile', self.parent.pid()])
        args.extend(['finger'])
        return args


class Options(usage.Options):
    def subCommands():
        def get(self):
            yield ('start', None, Start, 'Launch finger service')

        return get,

    subCommands = property(*subCommands())

    def pid(self):
        return '/tmp/finger.pid'


def main(argv=None):
    o = Options()
    try:
        o.parseOptions(argv)
    except usage.UsageError, e:
        raise SystemExit(str(e))

setup.py

from setuptools import find_packages
from setuptools import setup

METADATA = dict(
    name='Finger',
    version='0.0.1',
    packages=find_packages(),
    scripts=['bin/fingerize'],
    install_requires=[
        'Twisted >= 15.5.0',
    ],
    include_package_data=True,
    zip_safe=False,
)

setup(**METADATA)

And when I call fingerize start I get: /code/test/bin/fingerize: Unknown command: finger (test is a virtualenv)

1

There are 1 best solutions below

8
On

Since you are using setuptools in your setup.py, you can use the newer entry_points keyword, like so:

entry_points={ 'console_scripts': [ 'fingerize=finger.tap:main', ], },

instead of the scripts keyword.

Note if python setup.py install is run in a virtualenv, it will put the script in env/bin/fingerize (assuming your virtualenv folder is env).

A nice way to make such "python applications" then available globally (for some deployment purpose), while their inner mechanics doesn't interfere with your system python applciations, is to use pipsi (pip install pipsi) (made by the guy who developed flask). It's designed to expose just the project's executables to the system and have everything else tucked away in its own virtualenv.

You can see more on how to write/configure/distribute python packages at the packaging.python.org guide.