{"id":7222,"date":"2021-07-29T10:00:00","date_gmt":"2021-07-29T09:00:00","guid":{"rendered":"https:\/\/www.blopig.com\/blog\/?p=7222"},"modified":"2021-08-03T16:02:30","modified_gmt":"2021-08-03T15:02:30","slug":"making-your-python-tool-as-easy-to-install-as-possible","status":"publish","type":"post","link":"https:\/\/www.blopig.com\/blog\/2021\/07\/making-your-python-tool-as-easy-to-install-as-possible\/","title":{"rendered":"Making your python tool as easy to install as possible"},"content":{"rendered":"\n<p>Have you ever tried to use someone else&#8217;s code and spent a whole day trying to install it? Have you ever decided not to use a tool because installing it was a massive pain? Both of those have happened to me and, to be honest, it is a massive shame. The authors may spend large amounts of time developing these tools and in the end, no one uses them because they can&#8217;t get them to work. So I have decided to try and make all code I develop as easy and painless as possible to install and use. <\/p>\n\n\n\n<!--more-->\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>Say you have just developed a new deep-learning tool in python (lets call it <a href=\"https:\/\/github.com\/brennanaba\/ABlooper\">ABlooper<\/a>). To maximize usage, you want to make it as easy to install as possible. What could be easier than this? \ud83d\ude0d<\/p>\n<pre><code class=\"language-bash\">$ pip install ABlooper\n<\/code><\/pre>\n<p>Okay, so now that we know where we want to get to, the question is how to get there.<\/p>\n<p>Python actually makes it quite straightforward for us to achieve this with packages such as <code>setuptools<\/code>, <code>build<\/code> and <code>twine<\/code>. It even provides a server for people to easily share python packages.<\/p>\n<h4>Package structure and required files<\/h4>\n<p>The first thing we need to do is organise our code in the right way. I would recommend the following structure for a python package. This is just a skeleton however and I am sure many people would disagree on some of the details. But this will work, so unless you have a better way of doing it, it is good enough.<\/p>\n<pre><code>ABlooper\/\n\u251c\u2500\u2500 ABlooper\/\n\u2502   \u251c\u2500\u2500 some_code.py\n\u2502   \u251c\u2500\u2500 more_code.py\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u2514\u2500\u2500 data\/\n\u2502       \u2514\u2500\u2500 model_weights\n\u251c\u2500\u2500 LICENSE\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 MANIFEST.in\n\u2514\u2500\u2500 setup.py\n<\/code><\/pre>\n<p>If you are planning on sharing your code you should probably already have  some code files, a <code>README.md<\/code> and a <code>LICENSE<\/code>. If the code you are sharing is machine learning based, you will probably have weights stored in a non-python file. We will have to explicitly tell <code>setuptools<\/code> to include these in the package.<\/p>\n<p>The key file that will be doing most of the work is <code>setup.py<\/code>. Here is an template of what it should look like:<\/p>\n<pre><code class=\"language-python\">from setuptools import setup, find_packages\n\nwith open(&quot;README.md&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as readme:\n    long_description = readme.read()\nsetup(\n    name='ABlooper',     # Name of the package. This is what people will be installing\n    version='1.0.0',     # Version. Usually if you are not planning on making any major changes after this 1.0.0 is a good way to go.\n    description='Set of functions to predict CDR structure',     # Short description\n    license='BSD 3-clause license',  \n    maintainer='Brennan Abanades',\n    long_description=long_description,     # This just makes your README.md the description shown on pypi\n    long_description_content_type='text\/markdown',\n    maintainer_email='youremail@stats.ox.ac.uk',\n    include_package_data=True,     # If you have extra (non .py) data this should be set to True \n    packages=find_packages(include=('ABlooper', 'ABlooper.*')),     # Where to look for the python package\n    install_requires=[     # All Requirements\n        'numpy',\n        'torch&amp;gt;=1.6',\n    ],\n)\n<\/code><\/pre>\n<p>Not all of the parameters used here a necessary, and there are many additional keywords that can be added to the <code>setup<\/code> function. A full list of keywords can be found <a href=\"https:\/\/setuptools.readthedocs.io\/en\/latest\/references\/keywords.html\">here<\/a>. For example, you can make a python function runnable from the command line using the <code>entry_points<\/code> keyword:<\/p>\n<pre><code class=\"language-python\">entry_points={'console_scripts': ['ABlooper=ABlooper.more_code:main']},\n<\/code><\/pre>\n<p>This way users will not even need to know any python to use your code!<\/p>\n<p>There are two other files left to discuss:<\/p>\n<ul>\n<li><code>__init__.py<\/code> &#8211; Is used to let python know that the directory is a python package and can be an empty file.<\/li>\n<li><code>MANIFEST.in<\/code> &#8211; Enumerates all additional (non-python) data needed to run the package. In our case this would contain one line: <code>include ABlooper\/data\/*<\/code>.<\/li>\n<\/ul>\n<h4>Uploading to PyPi<\/h4>\n<p>At this point, you nearly have all you need to publish your python package. The last missing step is to create a PyPi account. This takes two seconds and can be done from <a href=\"https:\/\/pypi.org\/account\/register\/\">here<\/a>. Once that is done, you just have to run the following commands from the package base directory (the first ABlooper) to build and upload your package:<\/p>\n<pre><code class=\"language-bash\">$ pip install --upgrade build twine\n$ python3 -m build\n$ twine upload dist\/*\n<\/code><\/pre>\n<p>The <code>dist<\/code> directory will be generated by <code>build<\/code> and you will be asked to fill in your username and password when running <code>twine<\/code>.<\/p>\n<h4>Reap the rewards<\/h4>\n<p>And we are done!!\nNow anyone, anywhere, (assuming they have python installed and a good internet connection) will be able to easily install your package! All they have to do is: \ud83d\ude0d<\/p>\n<pre><code class=\"language-bash\">$ pip install ABlooper\n<\/code><\/pre>\n<p>And if you added an entry point, they can also run your python functions by simply calling:<\/p>\n<pre><code class=\"language-bash\">$ ABlooper antibody.pdb\n<\/code><\/pre>\n<p>Now no one can say that they didn&#8217;t use your code because they couldn&#8217;t get it to work. If you got this far, I hope this was not a complete waste of your time.<\/p>\n<\/div>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Have you ever tried to use someone else&#8217;s code and spent a whole day trying to install it? Have you ever decided not to use a tool because installing it was a massive pain? Both of those have happened to me and, to be honest, it is a massive shame. The authors may spend large [&hellip;]<\/p>\n","protected":false},"author":71,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","wikipediapreview_detectlinks":true,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"ngg_post_thumbnail":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[29,296,14,227],"tags":[412,414,152],"ppma_author":[552],"class_list":["post-7222","post","type-post","status-publish","format-standard","hentry","category-code","category-hints-and-tips","category-howto","category-python-code","tag-how-to","tag-pip","tag-python"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"authors":[{"term_id":552,"user_id":71,"is_guest":0,"slug":"brennan","display_name":"Brennan Abanades Kenyon","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/5c85dcbb5b1499e82ecfc264ec387c8302ac238c786e68cc5c92e9c21904d260?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/posts\/7222","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/users\/71"}],"replies":[{"embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/comments?post=7222"}],"version-history":[{"count":6,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/posts\/7222\/revisions"}],"predecessor-version":[{"id":7266,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/posts\/7222\/revisions\/7266"}],"wp:attachment":[{"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/media?parent=7222"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/categories?post=7222"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/tags?post=7222"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.blopig.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=7222"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}