In this tutorial, we will create a Python application that can be installed directly from pip
that will show the 10 latest blog posts from this website
(the one you are reading this on!).
If you just want to avoid all of this hard work and publish a python script directly, you can always make use of my
makepip
package right from the command line.
Getting started
Make sure you have registered at Pypy and have an account, we will need this to upload our package once we’re done.
Now create a directory to work from:
mkdir -p ~/src/tmp/aogl_feed && cd $_
In our new directory, make sure to have a python virtual environment to make life a little simpler.
virtualenv -p python3 venv
And activate it:
source venv/bin/activate
Now make sure that we have all the neccessary things installed to complete this tutorial successfully.
python -m pip install --upgrade pip setuptools wheel
python -m pip install tqdm
python -m pip install twine
Creating our structure and files
At this stage we should create our directory structure for our package:
Because our package will be quite simple to demonstrate what it takes, create the following 4 files:
LICENCE
README.md
aogl/
__init__.py
__main__.py
aogo.py
setup.py
In the licence file, you can place the following (customise it as you need):
Copyright (c) 2020 Andrew O https://andrewodendaal.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
For the readme file, we will add this:
A python package to retrieve the latest 10 blog posts from https://ataiva.com
In the setup.py file, we configure all our project-specific information:
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name='aogl',
version='0.1',
author="Andrew Odendaal",
author_email="[email protected]",
description="A python package to retrieve the latest 10 blog posts from https://ataiva.com",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/ao/aogl_pip",
packages=["aogl"],
entry_points = {
"console_scripts": ['aogl = aogl.aogl:main']
},
install_requires=[
"requests",
"feedparser"
],
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
)
Create a repository to store everything
You may notice that we have listed the url
key to point to https://github.com/ao/aogl_pip
, which doesn’t yet exist, so let’s go and create that.
Go to Github and create a new repository.
We will name ours aogl_pip
to match with what we told setup.py
it would be.
We don’t want to initialise with a README, as we already have one. Click Create repository
to proceed.
We can now push to our new repository, but we aren’t quite ready yet. So let’s finish up our local setup.
Add our main code
At this point, we can create our aoms.py
file mentioned above and populate it with the following code:
#!/usr/bin/env python
import feedparser, requests
response = requests.get("/feed")
feed = feedparser.parse(response.content)
feed.entries = feed.entries[0:9]
for entry in feed.entries:
print(entry.title)
print(entry.links[0].href)
print()
Also, make sure to set it to be executable:
chmod +x aogl.py
Let us also create the blank aogl/__init__.py
file and the aogl/__main__.py
file which contains the following code:
from .aogl import main
main()
As we have used a couple dependencies ourselves, will should install them to our virtual environment as follows:
pip install requests
pip install feedparser
Distributing these dependencies is easy when we allow people to install our app through pip
, because we have specified these dependencies in the setup.py
file, remember this block?
install_requires=[
"requests",
"feedparser"
],
Now when we run python aogl.py
, our script prints out the 10 latest blog posts for us.
Build our package
It’s now time to build our package and push it to Pypi.
We do this by running:
python setup.py bdist_wheel
This creates a whole lot of files for us, the output looks something like this:
running sdist
running egg_info
creating aogl.egg-info
writing aogl.egg-info/PKG-INFO
writing dependency_links to aogl.egg-info/dependency_links.txt
writing entry points to aogl.egg-info/entry_points.txt
writing top-level names to aogl.egg-info/top_level.txt
writing manifest file 'aogl.egg-info/SOURCES.txt'
reading manifest file 'aogl.egg-info/SOURCES.txt'
writing manifest file 'aogl.egg-info/SOURCES.txt'
running check
creating aogl-0.1
creating aogl-0.1/aogl
creating aogl-0.1/aogl.egg-info
copying files to aogl-0.1...
copying README.md -> aogl-0.1
copying setup.py -> aogl-0.1
copying aogl/__init__.py -> aogl-0.1/aogl
copying aogl/__main__.py -> aogl-0.1/aogl
copying aogl/aogl.py -> aogl-0.1/aogl
copying aogl.egg-info/PKG-INFO -> aogl-0.1/aogl.egg-info
copying aogl.egg-info/SOURCES.txt -> aogl-0.1/aogl.egg-info
copying aogl.egg-info/dependency_links.txt -> aogl-0.1/aogl.egg-info
copying aogl.egg-info/entry_points.txt -> aogl-0.1/aogl.egg-info
copying aogl.egg-info/top_level.txt -> aogl-0.1/aogl.egg-info
Writing aogl-0.1/setup.cfg
creating dist
Creating tar archive
removing 'aogl-0.1' (and everything under it)
running bdist_wheel
running build
running build_py
creating build
creating build/lib
creating build/lib/aogl
copying aogl/__init__.py -> build/lib/aogl
copying aogl/aogl.py -> build/lib/aogl
copying aogl/__main__.py -> build/lib/aogl
installing to build/bdist.macosx-10.15-x86_64/wheel
running install
running install_lib
creating build/bdist.macosx-10.15-x86_64
creating build/bdist.macosx-10.15-x86_64/wheel
creating build/bdist.macosx-10.15-x86_64/wheel/aogl
copying build/lib/aogl/__init__.py -> build/bdist.macosx-10.15-x86_64/wheel/aogl
copying build/lib/aogl/aogl.py -> build/bdist.macosx-10.15-x86_64/wheel/aogl
copying build/lib/aogl/__main__.py -> build/bdist.macosx-10.15-x86_64/wheel/aogl
running install_egg_info
Copying aogl.egg-info to build/bdist.macosx-10.15-x86_64/wheel/aogl-0.1-py3.7.egg-info
running install_scripts
adding license file "LICENCE" (matched pattern "LICEN[CS]E*")
creating build/bdist.macosx-10.15-x86_64/wheel/aogl-0.1.dist-info/WHEEL
creating 'dist/aogl-0.1-py3-none-any.whl' and adding 'build/bdist.macosx-10.15-x86_64/wheel' to it
adding 'aogl/__init__.py'
adding 'aogl/__main__.py'
adding 'aogl/aogl.py'
adding 'aogl-0.1.dist-info/LICENCE'
adding 'aogl-0.1.dist-info/METADATA'
adding 'aogl-0.1.dist-info/WHEEL'
adding 'aogl-0.1.dist-info/entry_points.txt'
adding 'aogl-0.1.dist-info/top_level.txt'
adding 'aogl-0.1.dist-info/RECORD'
removing build/bdist.macosx-10.15-x86_64/wheel
Test our package
Let’s take a look at what was created; using the tree
command, we limit the output to 2 levels of depth:
tree -L 2
.
├── LICENCE
├── README.md
├── aogl
│ ├── __init__.py
│ ├── __main__.py
│ └── aogl.py
├── aogl.egg-info
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ ├── dependency_links.txt
│ ├── entry_points.txt
│ └── top_level.txt
├── build
│ ├── bdist.macosx-10.15-x86_64
│ └── lib
├── dist
│ ├── aogl-0.1-py3-none-any.whl
│ └── aogl-0.1.tar.gz
├── setup.py
└── venv
├── bin
├── include
└── lib
We can see there is a build
directory, which contains build package information.
The aogl.egg.info
directory contains all dependency and package information.
There is also a dist
directory, which contains our *.whl
, which is a Wheel file.
This wheel can be installed directory with pip
if we wanted to, by running pip install dist/aogl-0.1-py3-none-any.whl
.
We will actually do this to make sure that everything works as expected before we publish our new code to the world.
It works pretty well!
Let’s uninstall this local pip, so that we are able to install it from Pypi once it’s successfully pushed.
pip uninstall aogl
Upload our code to Pypi
Next we will create a file under our home directory called ~/.pypirc
and configure it:
[distutils]
index-servers=pypi
[pypi]
repository = https://upload.pypi.org/legacy/
username = aogl
My username happens to be the same as the package I’m currently building, so make sure to adjust the username
value to whatever your registered username is on the Pypi website.
Now we can use twine
to upload our wheel.
python -m twine upload dist/*
If all was successful, we should see this:
Uploading distributions to https://upload.pypi.org/legacy/
Enter your password:
Uploading aogl-0.1-py3-none-any.whl
100%|█████████████████████████████████████████████| 5.89k/5.89k [00:00<00:00, 7.29kB/s]
NOTE: Try --verbose to see response content.
HTTPError: 403 Client Error: Invalid or non-existent authentication information. See https://pypi.org/help/#invalid-auth for details for url: https://upload.pypi.org/legacy/
(venv) ➜ aogl_feed python -m twine upload dist/*
Uploading distributions to https://upload.pypi.org/legacy/
Enter your password:
Uploading aogl-0.1-py3-none-any.whl
100%|█████████████████████████████████████████████| 5.89k/5.89k [00:03<00:00, 1.96kB/s]
Uploading aogl-0.1.tar.gz
100%|█████████████████████████████████████████████| 4.37k/4.37k [00:01<00:00, 2.93kB/s]
View at:
https://pypi.org/project/aogl/0.1/
Our package is now available at https://pypi.org/project/aogl/0.1/
Push our code to Github
Don’t forget to push the code to Github, so that we can update new versions later.
git init
git add LICENCE README.md aogl/ setup.py
git commit -m 'Pushing code for aogl version 0.1'
git remote add origin https://github.com/ao/aogl_pip.git
git push -u origin master
Test everything as the world would see it
Finally we get to test out installing our new package using pip off of Pypi!
pip install aogl
It installed successfully!
aogl
Our wonderful new contribution to the world has returned a list of the latest 10 blog posts!
Excellent! Job done.
Remember, you can also use the <a rel="noreferrer noopener" aria-label="makepip (opens in a new tab)" href="https://pypi.org/project/makepip/" target="_blank">makepip</a>
package to do all of this automatically for you!