aboutsummaryrefslogtreecommitdiff
path: root/content/weblog/2019-01-31_python-package-to-pypi/index.md
blob: f0b14e057093e507ceda9758a565ba0fbeee5d89 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
+++
title = "Packaging and distributing python apps and modules"
date = 2019-01-31T08:10:00Z
+++

There may come a time after some hacking and playing around with python that
you feel like the piece of code you just created needs to be shared with the
world, and so you might be thinking "Man, wouldn't it be sweet if anyone could
install my app/module just by typing `pip install stuffimade'`. Well, it is
actually easier than you might think (it certainly was in my case).

<!-- more -->

It basically boils down to these five things:

1. Make an account on pypi.org
1. Come up with an original name, that hasn't been taken on pypi (arguably the
   hardest part of these instructions).
1. Prepare a setup py file to package your app/module, choose a license, maybe
   (preferably) write a readme.
1. Package it.
1. Upload it.

The first step is really simple, just go the website (pypi.org) and click on
the link that says "Register" on the top right corner. Be sure to not forget
your password, especially after uploading your package, since you will need
everything that you want to upload a new version of your package.

The second step is all up to you, just use the search function in the website
to make sure that your package name hasn't been taken.

After having created your account on pypi.org, and decided on a name for your
app/package, you'll proceed to create the necessary package files. Your package
should consist of a folder with your actual module or app, inside which should
also be a `__init__.py` file (it can be empty if you don't need to set any
variables or anything), a `LICENSE` file with your license of choice (e.g. MIT,
BSD, GPL, etc.), and a `README.rst` or `README.md` file containing a detailed
description of your app/module.

Your directory structure should look something like this

```
/my-app-package
  /my-app
    __init__.py
    (other files and directories)
  setup.py
  LICENSE
  README.md
```

Now, if your app is going to have files other than python files, and the
aforementioned files, you might to add a `MANIFEST.in` in the root of you
package directory specifying the files to include. This is true, for example,
for Django apps, since they might contain template and static files (html, css,
js, etc.) that the packaging tool we are going to use might not pick up. As an
example I'll show you the `MANIFEST.in` file of my w3blog package

```
include LICENSE
include README.md
recursive-include weblog/static *
recursive-include weblog/templates *
recursive-include weblog/locale *
```

There I specified to, just in case, include the `LICENSE` and `README.md`
files, and also include static files, templates, and the message files for
localization, by recursively searching the directories of said files.

Now on to the setup.py file. For this I am also going to use my project's file
as an example

```py
#!/usr/bin/env python3
import os
from setuptools import find_packages, setup

with open(os.path.join(os.path.dirname(__file__), 'README.md')) as readme:
    README = readme.read()

# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

setup(
    name='w3blog',
    version='0.5.2',
    packages=find_packages(),
    include_package_data=True,
    license='BSD License',
    description='A simple blog engine for Django with multilingual capabilities.',
    long_description=README,
    url='https://www.yaroslavps.com/',
    author='Yaroslav de la Peña Smirnov',
    author_email='contact@yaroslavps.com',
    classifiers=[
        'Environment :: Web Environment',
        'Framework :: Django',
        'Framework :: Django :: 1.11',
        'Framework :: Django :: 2.0',
        'Framework :: Django :: 2.1',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: BSD License',
        'Operating System :: OS Independent',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Topic :: Internet :: WWW/HTTP',
        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
    ],
)
```

Of course, you should customize this file according to your app's details, for
example, your classifiers list might look shorter, like this

```py
    classifiers=[
        'License :: OSI Approved :: BSD License',
        'Operating System :: OS Independent',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
    ],
```

It will depend on which version of python you want to support, your license,
some other required packages, etc. The rest of the setup function parameters
should be self explanatory.

Once you have you package files ready (don't forget about your readme and
license), you'll need to proceed to package them, i.e. generate the
distribution files. For that, we'll need to install a couple of packages, we
cant install them inside a virtual environment, or you could install them
system wide

```sh
$ sudo pip3 install --upgrade setuptools wheel twine
```

Now just run the following command from the same directory where setup.py is
located

```sh
$ python3 setup.py sdist bdist_wheel
```

After running that command, you should now have a dist subdirectory with a
.tar.gz archive and a .whl file. Those are the files that you'll need to
upload. The twine package that we just installed is going to take care of that
for us. Remember that you already should have an account on pypi.org to upload
your package.

So for example, when I just uploaded the first version of w3blog, and each time
that I upload a new version I run a command (from the same directory as
setup.py) that looks like this

```sh
$ python3 twine upload dist/w3blog-0.5.2*
```

Just replace "w3blog-0.5.2*" with your appropriate package name-version. After
running that command, twine is going to ask you for your username and password,
thence it is going to upload it to pypi. Once it is successfully uploaded, it
should be a matter of seconds or minutes before you, and everybody else in the
world with an internet connection (and capable of running python, of course),
can install your package!

If you need more detailed information about this process, check out the
official documentation at
[https://packaging.python.org/tutorials/packaging-projects/](https://packaging.python.org/tutorials/packaging-projects/)