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)
Since you are using setuptools in your
setup.py
, you can use the newerentry_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 inenv/bin/fingerize
(assuming your virtualenv folder isenv
).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.