{"id":65438,"date":"2022-06-30T09:00:53","date_gmt":"2022-06-30T09:00:53","guid":{"rendered":"https:\/\/www.cryptocabaret.com\/?p=65438"},"modified":"2022-06-30T09:00:53","modified_gmt":"2022-06-30T09:00:53","slug":"package-a-new-python-module-in-4-steps","status":"publish","type":"post","link":"https:\/\/www.cryptocabaret.com\/?p=65438","title":{"rendered":"Package a new Python module in 4 steps"},"content":{"rendered":"<p><span class=\"field field--name-title field--type-string field--label-hidden\">Package a new Python module in 4 steps<\/span><br \/>\n<span class=\"field field--name-uid field--type-entity-reference field--label-hidden\"><a title=\"View user profile.\" href=\"https:\/\/opensource.com\/users\/sumantro\" class=\"username\">Sumantro Mukherjee<\/a><\/span><br \/>\n<span class=\"field field--name-created field--type-created field--label-hidden\">Thu, 06\/30\/2022 &#8211; 03:00<\/span><\/p>\n<div data-drupal-selector=\"rate-node-70081\" class=\"rate-widget-thumbs-up\" title=\"Register or Login to like.\">\n<div class=\"rate-thumbs-up-btn-up vote-pending\"><a href=\"https:\/\/opensource.com\/user\/register\">Register<\/a> or <a href=\"https:\/\/opensource.com\/user\/login?current=\/rss.xml\">Login<\/a> to like<\/div>\n<div class=\"rate-score\"><a href=\"https:\/\/opensource.com\/user\/register\">Register<\/a> or <a href=\"https:\/\/opensource.com\/user\/login?current=\/rss.xml\">Login<\/a> to like<\/div>\n<\/div>\n<div class=\"clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item\">\n<p>When you install an application, you&#8217;re usually installing a package that contains the executable code for an application and important files such as documentation, icons, and so on. On Linux, applications are commonly packaged as RPM or DEB files, and users install them with the <code>dnf<\/code> or <code>apt<\/code> commands, depending on the Linux distribution. However, new Python modules are released virtually every day, so you could easily encounter a module that hasn&#8217;t yet been packaged. And that&#8217;s exactly why the <code>pyp2rpm<\/code> command exists.<\/p>\n<p>Recently, I tried to install a module called python-concentration. It didn&#8217;t go well:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ <span class=\"kw2\">sudo<\/span> dnf <span class=\"kw2\">install<\/span> python-concentration<br>\nUpdating Subscription Management repositories.<br>\nLast metadata expiration check: <span class=\"nu0\">1<\/span>:<span class=\"nu0\">23<\/span>:<span class=\"nu0\">32<\/span> ago on Sat <span class=\"nu0\">11<\/span> Jun <span class=\"nu0\">2022<\/span> 06:<span class=\"nu0\">37<\/span>:<span class=\"nu0\">25<\/span>.<br>\nNo match <span class=\"kw1\">for<\/span> argument: python-concentration<br>\nError: Unable to <span class=\"kw2\">find<\/span> a match: python-concentration<\/div><\/div><\/pre>\n<p>It\u2019s a PyPi package, but it&#8217;s not yet available as an RPM package. The good news is that you can build an RPM yourself with a relatively simple process using <code>pyp2rpm<\/code>.<\/p>\n<p>You&#8217;ll need two directories to get started:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ <span class=\"kw2\">mkdir<\/span> rpmbuild<br>\n$ <span class=\"kw3\">cd<\/span> rpmbuild <span class=\"sy0\">&amp;&amp;<\/span> <span class=\"kw2\">mkdir<\/span> SPECS<\/div><\/div><\/pre>\n<p>You&#8217;ll also need to install <code>pyp2rpm<\/code>:<\/p>\n<pre>\n<span class=\"geshifilter\"><code class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span><span class=\"kw2\">sudo<\/span> dnf <span class=\"kw2\">install<\/span> pyp2rpm<\/code><\/span><\/pre>\n<\/p>\n<div class=\"embedded-resource-list callout-float-right\">\n<div class=\"field field--name-title field--type-string field--label-hidden field__item\">More Python resources<\/div>\n<div class=\"field field--name-links field--type-link field--label-hidden field__items\">\n<div class=\"field__item\"><a href=\"https:\/\/www.redhat.com\/en\/topics\/middleware\/what-is-ide?intcmp=7016000000127cYAAQ\">What is an IDE?<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/downloads\/cheat-sheet-python-37-beginners?intcmp=7016000000127cYAAQ\">Cheat sheet: Python 3.7 for beginners<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/resources\/python\/gui-frameworks?intcmp=7016000000127cYAAQ\">Top Python GUI frameworks<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/downloads\/7-essential-pypi-libraries?intcmp=7016000000127cYAAQ\">Download: 7 essential PyPI libraries<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/developers.redhat.com\/?intcmp=7016000000127cYAAQ\">Red Hat Developers<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/tags\/python?intcmp=7016000000127cYAAQ\">Latest Python articles<\/a><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<h2>1. Generate the spec file<\/h2>\n<p>The foundation of any RPM package is a file called the <i>spec file<\/i>. This file contains all the information about how to build the package, which dependencies it needs, the version of the application it provides, what files it installs, and more. When pointed to a Python module, <code>pyp2rpm<\/code> generates a spec file for it, which you can use to build an RPM.<\/p>\n<p>Using python-concentration as an arbitrary example, here&#8217;s how to generate a spec file:<\/p>\n<pre>\n<span class=\"geshifilter\"><code class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span>pyp2rpm concentration <span class=\"sy0\">&gt;<\/span> ~<span class=\"sy0\">\/<\/span>rpmbuild<span class=\"sy0\">\/<\/span>SPECS<span class=\"sy0\">\/<\/span>concentration.spec<\/code><\/span><\/pre>\n<p>And here&#8217;s the file it generates:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"co0\"># Created by pyp2rpm-3.3.8<\/span><br><span class=\"sy0\">%<\/span>global pypi_name concentration<br><span class=\"sy0\">%<\/span>global pypi_version 1.1.5<br><br>\nName: \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 python-<span class=\"sy0\">%<\/span><span class=\"br0\">{<\/span>pypi_name<span class=\"br0\">}<\/span><br>\nVersion: \u00a0 \u00a0 \u00a0 \u00a0<span class=\"sy0\">%<\/span><span class=\"br0\">{<\/span>pypi_version<span class=\"br0\">}<\/span><br>\nRelease: \u00a0 \u00a0 \u00a0 \u00a0<span class=\"nu0\">1<\/span><span class=\"sy0\">%<\/span><span class=\"br0\">{<\/span>?dist<span class=\"br0\">}<\/span><br>\nSummary: \u00a0 \u00a0 \u00a0 \u00a0Get work <span class=\"kw1\">done<\/span> when you need to, goof off when you don<span class=\"st_h\">'t<br><br>\nLicense: \u00a0 \u00a0 \u00a0 \u00a0None<br>\nURL: \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0None<br>\nSource0: \u00a0 \u00a0 \u00a0 \u00a0%{pypi_source}<br>\nBuildArch: \u00a0 \u00a0 \u00a0noarch<br><br>\nBuildRequires: \u00a0python3-devel<br>\nBuildRequires: \u00a0python3dist(setuptools)<br><br>\n%description<br>\nConcentration [![PyPI version]( [![Test Status]( [![Lint Status]( [![codecov](<br><br>\n%package -n \u00a0 \u00a0 python3-%{pypi_name}<br>\nSummary: \u00a0 \u00a0 \u00a0 \u00a0%{summary}<br>\n%{?python_provide:%python_provide python3-%{pypi_name}}<br><br>\nRequires: \u00a0 \u00a0 \u00a0 (python3dist(hug) &gt;= 2.6.1 with python3dist(hug) \nRequires: \u00a0 \u00a0 \u00a0 python3dist(setuptools)<br>\n%description -n python3-%{pypi_name}<br>\nConcentration [![PyPI version]( [![Test Status]( [![Lint Status]( [![codecov](<br><br><br>\n%prep<br>\n%autosetup -n %{pypi_name}-%{pypi_version}<br><br>\n%build<br>\n%py3_build<br><br>\n%install<br>\n%py3_install<br><br>\n%files -n python3-%{pypi_name}<br>\n%license LICENSE<br>\n%doc README.md<br>\n%{_bindir}\/concentration<br>\n%{python3_sitelib}\/%{pypi_name}<br>\n%{python3_sitelib}\/%{pypi_name}-%{pypi_version}-py%{python3_version}.egg-info<br><br>\n%changelog<br>\n* \u00a0- 1.1.5-1<br>\n- Initial package.<\/span><\/div><\/div><\/pre>\n<h2><b>2. Run rpmlint<\/b><\/h2>\n<p>To ensure that the spec file is up to standards, run the <code>rpmlint<\/code> command on the file:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ rpmlint ~<span class=\"sy0\">\/<\/span>rpmbuild<span class=\"sy0\">\/<\/span>SPEC<span class=\"sy0\">\/<\/span>concentration.spec<br>\nerror: bad <span class=\"kw2\">date<\/span> <span class=\"kw1\">in<\/span> <span class=\"sy0\">%<\/span>changelog: - 1.1.5-<span class=\"nu0\">1<\/span><br><span class=\"nu0\">0<\/span> packages and <span class=\"nu0\">1<\/span> specfiles checked; <span class=\"nu0\">0<\/span> errors, <span class=\"nu0\">0<\/span> warnings.<\/div><\/div><\/pre>\n<p>It seems the changelog entry requires a date.<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"sy0\">%<\/span>changelog<br><span class=\"sy0\">*<\/span> Sat Jun <span class=\"nu0\">11<\/span> <span class=\"nu0\">2022<\/span> Tux <span class=\"sy0\">tux<span class=\"sy0\">@<\/span>example.com<span class=\"sy0\">&gt;<\/span> - 1.1.5-<span class=\"nu0\">1<\/span><\/span><\/div><\/div><\/pre>\n<p>Try <code>rpmlint<\/code> again:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ rpmlint ~<span class=\"sy0\">\/<\/span>rpmbuild<span class=\"sy0\">\/<\/span>SPEC<span class=\"sy0\">\/<\/span>concentration.spec<br><span class=\"nu0\">0<\/span> packages and <span class=\"nu0\">1<\/span> specfiles checked; <span class=\"nu0\">0<\/span> errors, <span class=\"nu0\">0<\/span> warnings.<\/div><\/div><\/pre>\n<p>Success!<\/p>\n<h2>3. Download the source code<\/h2>\n<p>To build an RPM package, you must download the code you&#8217;re packaging up. The easy way to do this is to parse your spec file to find the source code&#8217;s location on the Internet.<\/p>\n<p>First, install the <code>spectool<\/code> command with <code>dnf<\/code>:<\/p>\n<pre>\n<span class=\"geshifilter\"><code class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span><span class=\"kw2\">sudo<\/span> dnf <span class=\"kw2\">install<\/span> spectool<\/code><\/span><\/pre>\n<p>Then use it to download the source code:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ <span class=\"kw3\">cd<\/span> ~<span class=\"sy0\">\/<\/span>rpmbuild<br>\n$ spectool <span class=\"re5\">-g<\/span> <span class=\"re5\">-R<\/span> SPEC<span class=\"sy0\">\/<\/span>concentration.spec<br>\nDownloading: https:<span class=\"sy0\">\/\/<\/span>files.pythonhosted.org<span class=\"sy0\">\/<\/span>...concentration-1.1.5.tar.gz<br>\n\u00a0 \u00a0<span class=\"nu0\">6.0<\/span> KiB <span class=\"sy0\">\/<\/span> <span class=\"nu0\">6.0<\/span> KiB \u00a0 \u00a0<span class=\"br0\">[<\/span>=====================================<span class=\"br0\">]<\/span><br>\nDownloaded: concentration-1.1.5.tar.gz<\/div><\/div><\/pre>\n<p>This creates a SOURCES directory and places the source code archive into it.<\/p>\n<h2>4. Build the source package<\/h2>\n<p>Now you have a valid spec file, so it&#8217;s time to build the source package with the <code>rpmbuild<\/code> command. If you don&#8217;t have <code>rpmbuild<\/code> yet, install the rpm-build package with <code>dnf<\/code> (or accept your terminal&#8217;s offer to install that package when you attempt to use the <code>rpmbuild<\/code> command).<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ <span class=\"kw3\">cd<\/span> ~<span class=\"sy0\">\/<\/span>rpmbuild<br>\n$ spectool <span class=\"re5\">-g<\/span> <span class=\"re5\">-R<\/span> SPEC<span class=\"sy0\">\/<\/span>concentration.spec<br>\nDownloading: https:<span class=\"sy0\">\/\/<\/span>files.pythonhosted.org<span class=\"sy0\">\/<\/span>...concentration-1.1.5.tar.gz<br>\n\u00a0 \u00a0<span class=\"nu0\">6.0<\/span> KiB <span class=\"sy0\">\/<\/span> <span class=\"nu0\">6.0<\/span> KiB \u00a0 \u00a0<span class=\"br0\">[<\/span>=====================================<span class=\"br0\">]<\/span><br>\nDownloaded: concentration-1.1.5.tar.gz<\/div><\/div><\/pre>\n<p>The <code>-bs<\/code> option stands for <i>build source<\/i>. This option gives you an <code>src.rpm<\/code> file, an all-purpose package that must be rebuilt for a specific architecture.<\/p>\n<p>Build an installable RPM for your system:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ rpmbuild \u2013rebuild SRPMS<span class=\"sy0\">\/<\/span>python-concentration-1.1.5-<span class=\"nu0\">1<\/span>.el9.src.rpm<br>\nerror: Failed build dependencies:<br>\n\u00a0 \u00a0 \u00a0 \u00a0 python3-devel is needed by python-concentration-1.1.5-<span class=\"nu0\">1<\/span>.el9.noarch<\/div><\/div><\/pre>\n<p>It looks like this package requires the development libraries of Python. Install them to continue with the build. This time the build succeeds and renders a lot more output (which I abbreviate here for clarity):<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ <span class=\"kw2\">sudo<\/span> dnf <span class=\"kw2\">install<\/span> python3-devel <span class=\"re5\">-y<\/span><br>\n$ rpmbuild \u2013rebuild SRPMS<span class=\"sy0\">\/<\/span>python-concentration-1.1.5-<span class=\"nu0\">1<\/span>.el9.src.rpm<br><span class=\"br0\">[<\/span>...<span class=\"br0\">]<\/span><br>\nExecuting<span class=\"br0\">(<\/span>--clean<span class=\"br0\">)<\/span>: <span class=\"sy0\">\/<\/span>bin<span class=\"sy0\">\/<\/span><span class=\"kw2\">sh<\/span> <span class=\"re5\">-e<\/span> <span class=\"sy0\">\/<\/span>var<span class=\"sy0\">\/<\/span>tmp<span class=\"sy0\">\/<\/span>rpm-tmp.TYA7l2<br>\n+ <span class=\"kw3\">umask<\/span> 022<br>\n+ <span class=\"kw3\">cd<\/span> <span class=\"sy0\">\/<\/span>home<span class=\"sy0\">\/<\/span>bogus<span class=\"sy0\">\/<\/span>rpmbuild<span class=\"sy0\">\/<\/span>BUILD<br>\n+ <span class=\"kw2\">rm<\/span> <span class=\"re5\">-rf<\/span> concentration-1.1.5<br>\n+ <span class=\"re2\">RPM_EC<\/span>=<span class=\"nu0\">0<\/span><br>\n++ <span class=\"kw3\">jobs<\/span> <span class=\"re5\">-p<\/span><br>\n+ <span class=\"kw3\">exit<\/span> <span class=\"nu0\">0<\/span><\/div><\/div><\/pre>\n<p>Your RPM package has been built in the RPMS subdirectory. Install it as usual with <code>dnf<\/code>:<\/p>\n<pre>\n<span class=\"geshifilter\"><code class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span><span class=\"kw2\">sudo<\/span> dnf <span class=\"kw2\">install<\/span> RPMS<span class=\"sy0\">\/<\/span>noarch<span class=\"sy0\">\/<\/span>python3-concentration<span class=\"sy0\">*<\/span>rpm<\/code><\/span><\/pre>\n<h2>Why not just use PyPi?<\/h2>\n<p>It&#8217;s not absolutely necessary to make a Python module into an RPM. Installing a module with PyPi is also acceptable, but PyPi adds another package manager to your personal list of things to check and update. When you install an RPM using <code>dnf<\/code>, you have a complete listing of what you&#8217;ve installed on your system. Thanks to <code>pyp2rpm<\/code>, the process is quick, easy, and automatable.<\/p>\n<\/div>\n<div class=\"clearfix text-formatted field field--name-field-article-subhead field--type-text-long field--label-hidden field__item\">\n<p>The pyp2rpm command makes it possible to create an RPM package and automate the process.<\/p>\n<\/div>\n<div class=\"field field--name-field-lead-image field--type-entity-reference field--label-hidden field__item\">\n<article class=\"media media--type-image media--view-mode-caption\">\n<div class=\"field field--name-field-media-image field--type-image field--label-hidden field__item\">  <img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/06\/python-programming-code-keyboard.png\" width=\"520\" height=\"292\" alt=\"Hands on a keyboard with a Python book \" title=\"Hands on a keyboard with a Python book \"><\/div>\n<div class=\"field field--name-field-caption field--type-text-long field--label-hidden caption field__item\"><span class=\"caption__byline\">Image by: <\/span><\/p>\n<p>WOCinTech Chat. Modified by Opensource.com. CC BY-SA 4.0<\/p>\n<\/div>\n<\/article>\n<\/div>\n<div class=\"field field--name-field-tags field--type-entity-reference field--label-hidden field__items\">\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/tags\/python\" hreflang=\"en\">Python<\/a><\/div>\n<\/p><\/div>\n<div class=\"field field--name-field-listicle-title field--type-string field--label-hidden field__item\">What to read next<\/div>\n<div class=\"field field--name-field-listicles field--type-entity-reference field--label-hidden field__items\">\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/article\/20\/3\/pip-linux-mac-windows\" hreflang=\"und\">How to install pip to manage PyPI packages easily<\/a><\/div>\n<\/p><\/div>\n<div class=\"field field--name-field-default-license field--type-list-string field--label-hidden field__item\"><a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/4.0\/\"><br \/>\n        <img decoding=\"async\" alt=\"Creative Commons License\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/06\/cc-by-sa--40.png\" title=\"This work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.\"><\/a>This work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.<\/div>\n<section class=\"field field--name-field-comments field--type-comment field--label-hidden comment-wrapper\">\n<div class=\"comments__count\">\n<div class=\"login\"><a href=\"https:\/\/opensource.com\/user\/register?absolute=1\">Register<\/a> or <a href=\"https:\/\/opensource.com\/user\/login?current=\/rss.xml&amp;absolute=1\">Login<\/a> to post a comment.<\/div>\n<\/p><\/div>\n<\/section>\n<p class=\"wpematico_credit\"><small>Powered by <a href=\"http:\/\/www.wpematico.com\" target=\"_blank\" rel=\"noopener\">WPeMatico<\/a><\/small><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Package a new Python module in 4 steps Sumantro Mukherjee Thu, 06\/30\/2022 &#8211; 03:00 Register or Login to like Register or Login to like When you install an application, you&#8217;re usually installing a package that contains the executable code for an application and important files such as documentation, icons, and so on. On Linux, applications [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":65439,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[307],"tags":[],"class_list":["post-65438","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-open-source"],"_links":{"self":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/posts\/65438","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=65438"}],"version-history":[{"count":0,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/posts\/65438\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/media\/65439"}],"wp:attachment":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=65438"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=65438"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=65438"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}