I need to install a python module in the site packages that also will be used as a command line application. Suppose I have a module like:
app.py
def main():
print 'Dummy message'
if __name__ == '__main__':
main()
setup.py
import distutils
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
if __name__ == '__main__':
setup(name = 'dummy',
version = '1.0',
packages = ['dummy'],
)
Creating the dist by:
setup.py sdist
Install:
setup.py install
And now I would like to use it as a command line application by opening the command window and typing just: dummy
Is it possible to create such application under windows without to carry out registering system pat variables and so on ...
You can use the options in setup.py to declare command line scripts. Please refer to this article. On Windows, the script will be created in "C:\Python26\Scripts" (if you didn't change the path) - lots of tools store their scripts there (e.g. "easy_install", "hg", ...).
Put the following in dummy.cmd:
python.exe -m dummy
Or is it dummy.app...
Oh well, it's one of those.
Related
In order to make pip or jupyter available from command-line on Windows no matter the current working directory with just pip ... or jupyter ..., Python on Windows seems to use this method:
put C:\Python37\Scripts in the PATH
create a small 100KB .exe file C:\Python37\Scripts\pip.exe or jupyter.exe that probably does not much more than calling a Python script with the interpreter (I guess?)
Then doing pip ... or jupyter ... in command-line works, no matter the current directory.
Question: how can I create a similar 100KB mycommand.exe that I could put in a directory which is in the PATH, so that I could do mycommand from anywhere in command-line?
Note: I'm not speaking about pyinstaller, cxfreeze, etc. (that I already know and have used before); I don't think these C:\Python37\Scripts\ exe files use this.
Assuming you are using setuptools to package this application, you need to create a console_scripts entry point as documented here:
https://packaging.python.org/guides/distributing-packages-using-setuptools/#console-scripts
For a single top-level module it could look like the following:
setup.py
#!/usr/bin/env python3
import setuptools
setuptools.setup(
py_modules=['my_top_level_module'],
entry_points={
'console_scripts': [
'mycommand = my_top_level_module:my_main_function',
],
},
# ...
name='MyProject',
version='1.0.0',
)
my_top_level_module.py
#!/usr/bin/env python3
def my_main_function():
print("Hi!")
if __name__ == '__main__':
my_main_function()
And then install it with a command such as:
path/to/pythonX.Y -m pip install path/to/MyProject
I have a Python command line application that I have been distributing on PyPI in the manner described here: https://gehrcke.de/2014/02/distributing-a-python-command-line-application/
In short, that means I'm using setuptools with the entry_points option in my setup.py file:
import programs
setup(
name='my_package',
entry_points={
'gui_scripts': [
'program1 = programs.program1:main',...
]
})
My package is uploaded to PyPI, and can be installed using pip. Normal behavior on the command line is that program1 launches the GUI.
The problem is, I would like to support the Anaconda distribution of Python. If I pip install and try to run program1 using using Anaconda, I get this warning:
This program needs access to the screen.
Please run with a Framework build of python, and only when you are logged in on the main display of your Mac.
The executable script lives here:
~/anaconda2/bin/program1
And here is its text:
#!/Users/***/anaconda2/bin/python
import re
import sys
from programs.program1 import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())
And it is importing program1 from this location:
/Users/***/anaconda2/lib/python2.7/site-packages/programs/program1.pyc
How can I get program_1 to run as an executable using Anaconda?
So I'm trying to create a setup.py file do deploy a test framework in python.
The library has dependencies in pexpect and easy_install. So, after installing easy_install, I need to install s3cmd which is a tool to work with Amazon's S3.
However, to configure s3cmd I use pexpect, but if you want to run setup.py from a fresh VM, so we run into an ImportError:
import subprocess
import sys
import pexpect # pexpect is not installed ... it will be
def install_s3cmd():
subprocess.call(['sudo easy_install s3cmd'])
# now use pexpect to configure s3cdm
child = pexpect.spawn('s3cmd --configure')
child.expect ('(?i)Access Key')
# ... more code down there
def main():
subprocess.call(['sudo apt-get install python-setuptools']) # installs easy_install
subprocess.call(['sudo easy_install pexpect']) # installs pexpect
install_s3cmd()
# ... more code down here
if __name__ == "__main__":
main()
I know of course I could create a another file, initial_setup.py to have easy_install and pexpect installed, before using setup.py, but my question is: Is there a way to import pexpect before having it installed? The library will be installed before using it, but does the Python interpreter will accept the import pexpect command?
It won't accept it like that, but Python allows you to import things everywhere, not only in the global scope. So you can postpone the import until the time when you really need it:
def install_s3cmd():
subprocess.call(['easy_install', 's3cmd'])
# assuming that by now it's already been installed
import pexpect
# now use pexpect to configure s3cdm
child = pexpect.spawn('s3cmd --configure')
child.expect ('(?i)Access Key')
# ... more code down there
EDIT: there is a peculiarity with using setuptools this way, since the .pth file will not be reloaded until Python relaunches. You can enforce reloading though (found here):
import subprocess, pkg_resources
subprocess.call(['easy_install', 'pexpect'])
pkg_resources.get_distribution('pexpect').activate()
import pexpect # Now works
(Unrelated: I'd rather assume that the script itself is called with the needed privileges, not use sudo in it. That will be useful with virtualenv.)
How can I import or read the VERSION from the setup.py file so that I can log the version at runtime.
This way I can make sure that the results obtained are from this particular version of my package.
The following is the contents of my setup.py file (simplified to have the necessary part)
import distutils.core
VERSION = '0.1.0'
LICENSE = 'GPLv2'
distutils.core.setup(**KWARGS)
When I try to do :
import setup
I get the following error:
distutils.core.setup(**KWARGS)
usr/lib/python2.6/distutils/core.pyc in setup(**attrs)
ok = dist.parse_command_line()
except DistutilsArgError, msg:
raise SystemExit, gen_usage(dist.script_name) + "\nerror: %s" % msg
if DEBUG:
SystemExit:
error: no commands supplied
There is a way to get the version from your setup script:
python setup.py --version
But I’m not sure I understand what you mean with “log the version at runtime”; the setup script is normally not installed with your modules, so people use other ways to put a version number in their code, like a __version__ attribute in their module or __init__.py file.
In yor example, setup is excecuted automatically, you have to replace:
distutils.core.setup(**KWARGS)
with:
if __name__ == '__main__':
distutils.core.setup(**KWARGS)
Like this, setup is only executed if you actually run the setup.py
I'm just getting into packaging with setuptools, and it seems that the recommended way to install a python script along with one's module is to specify a script name that calls the name of a function, like this:
setup(
# ...
entry_points = {
"console_scripts": [
"script_name": "project.main:main",
],
}
)
This clearly precludes the standard way of making a python module executable, which is (last time I checked, which was a while ago) to use if __name__ == "__main__": do_stuff(). Does setuptools support this style, or should I switch to defining a main function and specifying it in entry_points?
It is: "script_name = project.main:do_stuff with setuptools
Setuptools creates scripts named script_name that imports and runs the function project.main:do_stuff, not run the script directly. You should re-read this part (alternate link, if you use Distribute) of the setuptools docs again to understand why it's this way. The script it creates contains if __name__ == "__main__" still. So yes, this is still the defacto way of making it execute.
This is a copy of easy_install installed with setuptools
#!/usr/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'distribute==0.6.14','console_scripts','easy_install'
__requires__ = 'distribute==0.6.14'
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.exit(
load_entry_point('distribute==0.6.14', 'console_scripts', 'easy_install')()
)
I think it's best to define an entry point and a script similar to easy_install. That imports and uses the entry point, like you show in your example if __name__ == "__main__": do_stuff(). It's great for debugging and early testing, also if you use distutils, there is no need to add/change anything. You can also have another app to call do_stuff() to access your app with out the overhead of running it in the shell, which is what setuptools is doing, distutils copy's the script.