Its mainly used to define the additional scripts you'll be using in your package. Here's a snippet from the official documentation on python-packaging:
#!/usr/bin/env python
import funniest
print funniest.joke()
Then we can declare the script in setup() like this:
setup(
...
scripts=['bin/funniest-joke'],
...
)
Answer from worker_bee on Stack OverflowWhen we install the package, setuptools will copy the script to our PATH and make it available for general use. This has advantage of being generalizeable to non-python scripts, as well: funniest-joke could be a shell script, or something completely different.
setup.py is a Python file, the presence of which is an indication that the module/package you are about to install has likely been packaged and distributed with Distutils, which is the standard for distributing Python Modules.
This allows you to easily install Python packages. Often it's enough to write:
$ pip install .
pip will use setup.py to install your module. Avoid calling setup.py directly.
It helps to install a python package foo on your machine (can also be in virtualenv) so that you can import the package foo from other projects and also from [I]Python prompts.
It does the similar job of pip, easy_install etc.,
Using setup.py
Let's start with some definitions:
- Package - A folder/directory that contains
__init__.pyfile. - Module - A valid python file with
.pyextension. - Distribution - How one package relates to other packages and modules.
Let's say you want to install a package named foo. Then you do,
$ git clone https://github.com/user/foo
$ cd foo
$ python setup.py install
Instead, if you don't want to actually install it but still would like to use it. Then do,
$ python setup.py develop
This command will create symlinks to the source directory within site-packages instead of copying things. Because of this, it is quite fast (particularly for large packages).
Creating setup.py
If you have your package tree like,
foo
βββ foo
β βββ data_struct.py
β βββ __init__.py
β βββ internals.py
βββ README
βββ requirements.txt
βββ setup.py
Then, you do the following in your setup.py script so that it can be installed on some machine:
from setuptools import setup
setup(
name='foo',
version='1.0',
description='A useful module',
author='Man Foo',
author_email='foomail@foo.example',
packages=['foo'], #same as name
install_requires=['wheel', 'bar', 'greek'], #external packages as dependencies
)
Instead, if your package tree is more complex like the one below:
foo
βββ foo
β βββ data_struct.py
β βββ __init__.py
β βββ internals.py
βββ README
βββ requirements.txt
βββ scripts
β βββ cool
β βββ skype
βββ setup.py
Then, your setup.py in this case would be like:
from setuptools import setup
setup(
name='foo',
version='1.0',
description='A useful module',
author='Man Foo',
author_email='foomail@foo.example',
packages=['foo'], #same as name
install_requires=['wheel', 'bar', 'greek'], #external packages as dependencies
scripts=[
'scripts/cool',
'scripts/skype',
]
)
Add more stuff to (setup.py) & make it decent:
from setuptools import setup
with open("README", 'r') as f:
long_description = f.read()
setup(
name='foo',
version='1.0',
description='A useful module',
license="MIT",
long_description=long_description,
author='Man Foo',
author_email='foomail@foo.example',
url="http://www.foopackage.example/",
packages=['foo'], #same as name
install_requires=['wheel', 'bar', 'greek'], #external packages as dependencies
scripts=[
'scripts/cool',
'scripts/skype',
]
)
The long_description is used in pypi.org as the README description of your package.
And finally, you're now ready to upload your package to PyPi.org so that others can install your package using pip install yourpackage.
At this point there are two options.
- publish in the temporary test.pypi.org server to make oneself familiarize with the procedure, and then publish it on the permanent pypi.org server for the public to use your package.
- publish straight away on the permanent pypi.org server, if you are already familiar with the procedure and have your user credentials (e.g., username, password, package name)
Once your package name is registered in pypi.org, nobody can claim or use it. Python packaging suggests the twine package for uploading purposes (of your package to PyPi). Thus,
the first step is to locally build the distributions using:
# prereq: wheel (pip install wheel) $ python setup.py sdist bdist_wheelthen using
twinefor uploading either totest.pypi.orgorpypi.org:$ twine upload --repository testpypi dist/* username: *** password: ***
It will take few minutes for the package to appear on test.pypi.org. Once you're satisfied with it, you can then upload your package to the real & permanent index of pypi.org simply with:
$ twine upload dist/*
Optionally, you can also sign the files in your package with a GPG by:
$ twine upload dist/* --sign
Bonus Reading:
See a sample
setup.pyfrom a real project here:torchvision-setup.pyPEP 517, setuptools
why twine? using twine
I'm deep in packaging hell at the moment, because I'm shipping Cython modules which other Cython modules will depend on at compile time. It's an unusual use-case, Python packaging is deeply broken, so okay, I'll have pain.
But: what is even going on when I execute
python setup.py install
Is the file executed top-to-bottom as normal, until it enters the setup() function? Does setuptools do anything unseemly/magical? When does the "install" argument get read from the command line? What determines what order the dependencies start getting installed?
Is the file executed top-to-bottom as normal, until it enters the setup() function?
Yes, of course; it's just a Python script.
Does setuptools do anything unseemly/magical?
It isn't specially recognized by the Python interpreter, or anything like that, if that's what you mean. I'll leave you to judge whether any of what distutils or setuptools do is is black, black magic.
When does the "install" argument get read from the command line? But: what is even going on when I execute
python setup.py install?
setup.py is just a script. It, by convention, includes a call to the setup() function defined in distutils (or setuptools, etc.). This is a totally normal Python function call. The arguments that you pass are metadata about the package, what Python and non-Python files should be included, how any native code should be built, what entry point wrappers should be installed, and so forth.
If I had to guess without looking at the source, I would guess that something like argparse is used to actually do the work of reading "install" from the command line. These arguments are available as sys.argv throughout a script's execution.
Documentation for setuptools is here. The source (including the implementation of setup()) is available here.
Welcome to Golang! Just kidding! Most setup scripts use distutils (or setuptools) in conjunction with their setup.py script. Here is probably the best example I can find :
https://docs.python.org/3.3/distutils/introduction.html#a-simple-example
Also if you are developing for windows take a look at:
bdist_wininst
Edit: I unfortunetly not quite sure of the order in which packages are installed I imagine it retains the order of how it is listed in the setup script???
Complete walkthrough of writing setup.py scripts here. (with some examples)
If you'd like a real-world example, I could point you towards the setup.py scripts of a couple major projects. Django's is here, pyglet's is here. You can just browse the source of other projects for a file named setup.py for more examples.
These aren't simple examples; the tutorial link I gave has those. These are more complex, but also more practical.
Minimal example
from setuptools import setup, find_packages
setup(
name="foo",
version="1.0",
packages=find_packages(),
)
More info in docs