5

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)

kitensei
  • 2,510
  • 2
  • 42
  • 68

1 Answers1

0

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.

Ivo
  • 5,378
  • 2
  • 18
  • 18
  • I have tried to use both `scripts` and `entry_points` but I still get the same error. I am aware of the virtualenv scripts dir. – kitensei Mar 08 '16 at 09:38
  • could you try `/code/test/bin/fingerize` and `fingerize`? They could be different. – Ivo Mar 08 '16 at 09:48
  • I have tested right now and I have the exact same issue. If you run the script as provided does it works for you? – kitensei Mar 09 '16 at 10:05
  • Found it. `find_packages()` won't work without an `__init__.py`. run `touch finger/__init__.py` and you should be golden. Alternatively on Python 3, you could use `packages=['finger']`. – Ivo Mar 10 '16 at 00:56
  • This does not work either, I still have the `__init__.py` in my finger module dir. If the code works for you, can you send me a full [ssccee](http://sscce.org/) example to test ? Maybe I am missing something really basic/stupid. (Also to be sure, are you running `fingerize start` ? because just typing `fingerize` works, it is the subCommand that is not working) – kitensei Mar 10 '16 at 14:33
  • @kitensei, all I did was literally copy paste all of your files into a new virtualenv, implement my change suggested above, add a `__init__.py`, run `python setup.py install`, and the `fingerize` command ran without error. I have not looked at whether the actual functional logic of your code works, because that was not really what the question seemed to be about. – Ivo Mar 10 '16 at 15:16
  • you ran the `fingerize start` ? If you did, I may have another problem linked to my python install maybe... – kitensei Mar 10 '16 at 15:33
  • *just thinking about it, could it be a python 2.7.5 issue* ? – kitensei Mar 10 '16 at 16:16