<h1 id='fdN9CAJbdjc'>Pants / PyOxidizer / PyProject.toml</h1>

<h2 id='fdN9CAWj0ms'>Initial project</h2>

<p id='fdN9CAZ6a1y' class='line'>Based on <a href="https://github.com/pypa/sampleproject">sampleproject</a><br>I've created a fork <a href="https://github.com/alexiswl/sampleproject">here</a>.</p>

<pre id='fdN9CAGaUiH' class='prettyprint'>git clone https://github.com/alexiswl/sampleproject</pre>

<h2 id='fdN9CA6zMJm'>Create entrypoint</h2>

<p id='fdN9CATrPR6' class='line'>If our end goal is to have a binary with a simple goal, we need to have a main, so<br>let's update our simple.py to catch main</p>

<pre id='fdN9CAcCAgA' class='prettyprint'>import sys<br><br>def add_one(number):<br>    return number + 1<br><br>if __name__ == "__main__":<br>  print(add_one(int(sys.argv[1])))</pre>

<h2 id='fdN9CAQYisz'>Install pants</h2>

<pre id='fdN9CAiB1IC' class='prettyprint'>/bin/bash -c "$(curl -fsSL https://static.pantsbuild.org/setup/one_step_setup.sh)"</pre>

<h3 id='fdN9CA63eGQ'>Configure pants global options</h3>

<blockquote id='fdN9CAih9YF'>pants.toml</blockquote>

<p id='fdN9CAvN0Zy' class='line'>We set allow the pyoxidizer experimentl package, don't generate the setup.py and<br>use the --release arg for pyoxidizer building</p>

<pre id='fdN9CAHCi0p' class='prettyprint'>[GLOBAL]<br>pants_version = "2.14.0"<br><br>backend_packages = [<br>  "pants.backend.experimental.python.packaging.pyoxidizer",<br>  "pants.backend.python"<br>]<br><br>[setup-py-generation]<br>generate_setup_default = false<br><br>[pyoxidizer]<br>args = [ "--release" ]</pre>

<h3 id='fdN9CAtyZ3H'>Making the pants BUILD command</h3>

<blockquote id='fdN9CAYV3jV'>BUILD</blockquote>

<pre id='fdN9CAiveAT' class='prettyprint'># Copyright 2020 Pants project contributors.<br># Licensed under the Apache License, Version 2.0 (see LICENSE).<br><br># This target sets the metadata for all the Python non-test files in this directory.<br>resource(<br>  name="sampleproject_res",<br>  source="pyproject.toml"<br>)<br><br># Create pex binary<br>pex_binary(<br>    name="pex_binary",<br>    entry_point="sample.simple",<br>    dependencies=[":sampleproject_res"]<br>)<br><br># Required for pyox binary<br>python_distribution(<br>    name="sample_dist",<br>    dependencies=[<br>     ":sampleproject_res"<br>    ],<br>    provides=python_artifact(<br>            name="sampleproject",<br>            version="2.0.0"<br>    )<br>)<br><br># Build pyoxidizer binary<br>pyoxidizer_binary(<br>    name="pyox_binary",<br>    entry_point="sample.simple",<br>    dependencies=[":sample_dist"]<br>)</pre>

<h2 id='fdN9CA1vYfR'>First attempt at creating the python distribution wheel</h2>

<pre id='fdN9CAIm3J6' class='prettyprint'>./pants \<br>  --level=debug \<br>  --no-pantsd \<br>  --no-local-cache \<br>  package ":sample_dist"</pre>

<p id='fdN9CAEgbsN' class='line'>Which creates the output files:</p>

<div class="" style="" data-section-style='5'><ul id='fdN9CANJkEb'><li id='fdN9CAO7ehS' class='' value='1'><span id='fdN9CAO7ehS'>dist/backend_shim-0.0.0-py3-none-any.whl</span>

<br/></li><li id='fdN9CAIaX0b' class=''><span id='fdN9CAIaX0b'>dist/backend_shim-0.0.0.tar.gz</span>

<br/></li></ul></div><p id='fdN9CA3jSUr' class='line'>But if we look inside the wheel, it doesn't seem to have picked up any of the<br>requirements from the setup.py (the pyproject.toml should read setup.py - should it not?)</p>

<pre id='fdN9CAgJ422' class='prettyprint'>$ unzip dist/backend_shim-0.0.0-py3-none-any.whl<br>Archive:  dist/backend_shim-0.0.0-py3-none-any.whl<br>  inflating: backend_shim.py<br>  inflating: backend_shim-0.0.0.dist-info/METADATA<br>  inflating: backend_shim-0.0.0.dist-info/WHEEL<br>  inflating: backend_shim-0.0.0.dist-info/top_level.txt<br>  inflating: backend_shim-0.0.0.dist-info/RECORD<br>  <br>$ cat backend_shim-0.0.0.dist-info/METADATA<br>Metadata-Version: 2.1<br>Name: backend-shim<br>Version: 0.0.0<br><br>$ cat backend_shim-0.0.0.dist-info/top_level.txt<br>backend_shim<br><br>$ cat backend_shim-0.0.0.dist-info/RECORD<br>backend_shim.py,sha256=tMfCMQd30OulboVgGHYa2RHcClFY9pD9ERynjzDVAuE,614<br>backend_shim-0.0.0.dist-info/METADATA,sha256=giO3YLXz2rXq0eKG983qo0Gk0MvEpB9W1hrQ2EDvmYM,57<br>backend_shim-0.0.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92<br>backend_shim-0.0.0.dist-info/top_level.txt,sha256=EKWQsiPSspK-bGKZTmzpG0it55Yl6-Y032orzB86crY,13<br>backend_shim-0.0.0.dist-info/RECORD,,</pre>

<h2 id='fdN9CAFUcur'>Using an internal build file to ensure modules are copied over</h2>

<p id='fdN9CA1iLOO' class='line'>One way to mitigate this appears to be specifying a second build file inside the <code>src/sample</code> directory<br>via the 'tailor' goal and then adding this file to the distribution.</p>

<pre id='fdN9CAU55zk' class='prettyprint'>./pants \<br>  --level=debug \<br>  --no-pantsd \<br>  --no-local-cache \<br>  tailor src/::</pre>

<p id='fdN9CAeojIQ' class='line'><br>This generates a BUILD file under src/sample with just 'python_sources()'.<br>Let's update the name attribute</p>

<blockquote id='fdN9CAjDpLX'>src/sample/BUILD</blockquote>

<pre id='fdN9CA7c8Er' class='prettyprint'>python_sources(<br>    name="lib"<br>)</pre>

<p id='fdN9CAcykX7' class='line'>Let's now add this BUILD to the dependencies for the python distribution in the top level BUILD</p>

<blockquote id='fdN9CADWe0j'>BUILD.python_distrubtion</blockquote>

<pre id='fdN9CAoMN8J' class='prettyprint'># Required for pyox binary<br>python_distribution(<br>    name="sample_dist",<br>    dependencies=[<br>     ":sampleproject_res",<br>     "src/sample:lib"<br>    ],<br>    provides=python_artifact(<br>            name="sampleproject",<br>            version="2.0.0"<br>    )<br>)</pre>

<p id='fdN9CAJmZaf' class='line'>And re-run the pants package :sample_dist command above.</p>

<h3 id='fdN9CAfquIC'>Changes</h3>

<p id='fdN9CAiXSJ4' class='line'>This outputs two files to the dist directory:</p>

<div class="" style="" data-section-style='5'><ul id='fdN9CAxhKeE'><li id='fdN9CAtNSFV' class='' value='1'><span id='fdN9CAtNSFV'>dist/sample-0.0.0-py3-none-any.whl</span>

<br/></li><li id='fdN9CARWRpn' class=''><span id='fdN9CARWRpn'>dist/sample-0.0.0.tar.gz</span>

<br/></li></ul></div><p id='fdN9CA4LiDA' class='line'>These files DO have the <code>sample/simple.py</code> and `sample/init.py .</p>

<pre id='fdN9CAq9cph' class='prettyprint'>$ unzip dist/sample-0.0.0-py3-none-any.whl<br>Archive:  dist/sample-0.0.0-py3-none-any.whl<br>  inflating: sample/__init__.py<br>  inflating: sample/simple.py<br>  inflating: sample-0.0.0.dist-info/METADATA<br>  inflating: sample-0.0.0.dist-info/WHEEL<br>  inflating: sample-0.0.0.dist-info/top_level.txt<br>  inflating: sample-0.0.0.dist-info/RECORD</pre>

<p id='fdN9CAU9OTO' class='line'>However, this doesn't have any of the requirements in the METADATA or WHEEL or RECORD like above.</p>

<h2 id='fdN9CABwmOy'>Move dependencies to pyproject.toml</h2>

<p id='fdN9CAA9pVF' class='line'>If we update pyproject.toml and set the project section we can set the dependencies in here instead.<br>It seems odd that pyproject.toml doesn't read these from setup.py.<br>Let's copy over the dependencies in setup.py into pyproject.toml under the project section.<br>We can also use this as an opportunity to name the package.</p>

<pre id='fdN9CAaujnT' class='prettyprint'>[build-system]<br>...<br><br>[project]<br>name = "sampleproject"<br><br>version = "2.0.0"<br><br>dependencies = [<br>  "peppercorn"<br>]</pre>

<p id='fdN9CAr6dDv' class='line'>Let's try rebuild the distribution now.</p>

<pre id='fdN9CACJTEd' class='prettyprint'>./pants \<br>  --level=debug \<br>  --no-pantsd \<br>  --no-local-cache \<br>  package :sample_dist</pre>

<p id='fdN9CARAZQS' class='line'>Gives us the following outputs</p>

<div class="" style="" data-section-style='5'><ul id='fdN9CA2FKlz'><li id='fdN9CAhcksR' class='' value='1'><span id='fdN9CAhcksR'>dist/sampleproject-2.0.0-py3-none-any.whl</span>

<br/></li><li id='fdN9CAKWydW' class=''><span id='fdN9CAKWydW'>dist/sampleproject-2.0.0.tar.gz</span>

<br/></li></ul></div><p id='fdN9CAw6qVW' class='line'>Let's have a peek in!</p>

<pre id='fdN9CAGate5' class='prettyprint'>$ cat  sampleproject-2.0.0.dist-info/METADATA<br>Metadata-Version: 2.1<br>Name: sampleproject<br>Version: 2.0.0<br>Requires-Dist: peppercorn</pre>

<p id='fdN9CAormPx' class='line'>Hurrah! So the peppercorn dependency now exists in the METADATA!</p>

<h2 id='fdN9CAt4RZh'>Build the pyox binary</h2>

<pre id='fdN9CAygDND' class='prettyprint'>./pants \<br>  --level=debug \<br>  --no-pantsd \<br>  --no-local-cache \<br>  package :pyox_binary</pre>

<p id='fdN9CAs2CzO' class='line'>And launch!</p>

<pre id='fdN9CA5ENOW' class='prettyprint'>time dist/pyox_binary/x86_64-unknown-linux-gnu/release/install/pyox_binary 23434</pre>

<p id='fdN9CAPmvJu' class='line'>Wow</p>

<pre id='fdN9CAzMqud' class='prettyprint'>23435<br><br><br>real    0m0.034s<br>user    0m0.034s<br>sys     0m0.001s</pre>

<p id='fdN9CAVbhT9' class='line'>I know it's a very simple addition... but that is fast! I'm very excited to see how this scales up with more complex projects!<br><br>Anyway, back to the bits that don't quite make sense</p>

<h2 id='fdN9CAnKBWr'>Another point of confusion</h2>

<p id='fdN9CAoIMHw' class='line'>Why do we need the <i>src/sample/BUILD </i>file? We should be able to specify<br>packages under the setuptools package-dir right?<br><br>In theory, setuptools SHOULD be able to find the packages under <i>src/</i>.<br><br>Even if it can't search correctly, we should be able to explicitly add the package directory under</p>

<pre id='fdN9CAXiDLC' class='prettyprint'>[build-system]<br>...<br><br>[project]<br>...<br><br>[tool.setuptools]<br>package-dir = {"" = "src"}<br><br>[tool.setuptools.packages.find]<br>where = [ "src" ]<br>include = [ "sample" ]<br>namespaces = false</pre>

<p id='fdN9CAaWYFD' class='line'>However, if we retry with <i>src/sample:lib</i> removed from the <i>python_distribution </i>in BUILD, this yields the following error -</p>

<pre id='fdN9CASuHob' class='prettyprint'>17:41:37.86 [ERROR] 1 Exception encountered:<br><br>  ProcessExecutionFailure: Process 'Run setuptools.build_meta for //:sample_dist' failed with exit code 1.<br>stdout:<br>running bdist_wheel<br>running build<br>installing to build/bdist.linux-x86_64/wheel<br>running install<br>running install_egg_info<br><br>stderr:<br>/home/alexiswl/.cache/pants/named_caches/pex_root/venvs/s/022089bc/venv/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py:108: _BetaConfiguration: Support for `[tool.setuptools]` in `pyproject.toml` is still *beta*.<br>  warnings.warn(msg, _BetaConfiguration)<br>error: error in 'egg_base' option: 'src' does not exist or is not a directory<br><br><br><br>Use `--keep-sandboxes=on_failure` to preserve the process chroot for inspection.</pre>

<h2 id='fdN9CAQN5t0'>Conclusion</h2>

<p id='fdN9CAQQggi' class='line'>If pyproject.toml won't read setup.cfg, nor setup.py, but<br>also doesn't support <i>[tool.setuptools]</i>, then what is<br>the best way to package up a project like this?<br><br>It doesn't seem right to require a BUILD file inside every package.<br><br>Nevertheless, it's a small compromise for the speed pyoxidizer appears to be showing.<br><br>Hope this tutorial is helpful to others as well!</p>

