Forgive me the possibly trivial question, but: How do I run the script published by pybuilder?
I'm trying to follow the official Pybuilder Tutorial.
I've walked through the steps and successfully generated a project that
setup.py
.tar.gz
that can be used by pip install
.That's all very nice, but I still don't see what the actual runnable artifact is?
All that is contained in the target
directory seems to look more or less exactly the same as the content in src
directory + additional reports and installable archives.
The tutorial itself at the end of the "Adding a runnable Script"-section concludes that "the script was picked up". Ok, it was picked up, now how do I run it? At no point does the tutorial demonstrate that we can actually print the string "Hello, World!" on the screen, despite the fact that the whole toy-project is about doing exactly that.
MCVE
Below is a Bash Script that generates the following directory tree with Python source files and build script:
projectRoot
├── build.py
└── src
└── main
├── python
│ └── pkgRoot
│ ├── __init__.py
│ ├── pkgA
│ │ ├── __init__.py
│ │ └── modA.py
│ └── pkgB
│ ├── __init__.py
│ └── modB.py
└── scripts
└── entryPointScript.py
7 directories, 7 files
================================================================================
projectRoot/build.py
--------------------------------------------------------------------------------
from pybuilder.core import use_plugin
use_plugin("python.core")
use_plugin("python.distutils")
default_task = "publish"
================================================================================
projectRoot/src/main/scripts/entryPointScript.py
--------------------------------------------------------------------------------
#!/usr/bin/env python
from pkgRoot.pkgB.modB import b
if __name__ == "__main__":
print(f"Hello, world! 42 * 42 - 42 = {b(42)}")
================================================================================
projectRoot/src/main/python/pkgRoot/pkgA/modA.py
--------------------------------------------------------------------------------
def a(n):
"""Computes square of a number."""
return n * n
================================================================================
projectRoot/src/main/python/pkgRoot/pkgB/modB.py
--------------------------------------------------------------------------------
from pkgRoot.pkgA.modA import a
def b(n):
"""Evaluates a boring quadratic polynomial."""
return a(n) - n
The full script that generates example project (Disclaimer: provided as-is, modifies files and directories, execute at your own risk):
#!/bin/bash
# Creates a very simple hello-world like project
# that can be build with PyBuilder, and describes
# the result.
# Uses BASH heredocs and `cut -d'|' -f2-` to strip
# margin from indented code.
# strict mode
set -eu
# set up directory tree for packages and scripts
ROOTPKG_PATH="projectRoot/src/main/python/pkgRoot"
SCRIPTS_PATH="projectRoot/src/main/scripts"
mkdir -p "$ROOTPKG_PATH/pkgA"
mkdir -p "$ROOTPKG_PATH/pkgB"
mkdir -p "$SCRIPTS_PATH"
# Touch bunch of `__init__.py` files
touch "$ROOTPKG_PATH/__init__.py"
touch "$ROOTPKG_PATH/pkgA/__init__.py"
touch "$ROOTPKG_PATH/pkgB/__init__.py"
# Create module `modA` in package `pkgA`
cut -d'|' -f2- <<__HEREDOC > "$ROOTPKG_PATH/pkgA/modA.py"
|def a(n):
| """Computes square of a number."""
| return n * n
|
__HEREDOC
# Create module `modB` in package `pkgB`
cut -d'|' -f2- <<__HEREDOC > "$ROOTPKG_PATH/pkgB/modB.py"
|from pkgRoot.pkgA.modA import a
|
|def b(n):
| """Evaluates a boring quadratic polynomial."""
| return a(n) - n
|
__HEREDOC
# Create a hello-world script in `scripts`:
cut -d'|' -f2- <<__HEREDOC > "$SCRIPTS_PATH/entryPointScript.py"
|#!/usr/bin/env python
|
|from pkgRoot.pkgB.modB import b
|
|if __name__ == "__main__":
| print(f"Hello, world! 42 * 42 - 42 = {b(42)}")
|
__HEREDOC
# Create a simple `build.py` build script for PyBuilder
cut -d'|' -f2- <<__HEREDOC > "projectRoot/build.py"
|from pybuilder.core import use_plugin
|
|use_plugin("python.core")
|use_plugin("python.distutils")
|
|default_task = "publish"
|
__HEREDOC
#################################################
# Directory tree construction finished, only #
# debug output below this box. #
#################################################
# show the layout of the generater result
tree "projectRoot"
# walk through each python file, show path and content
find "projectRoot" -name "*.py" -print0 | \
while IFS= read -r -d $'\0' pathToFile
do
if [ -s "$pathToFile" ]
then
printf "=%.0s" {1..80} # thick horizontal line
echo ""
echo "$pathToFile"
printf -- "-%.0s" {1..80}
echo ""
cat "$pathToFile"
fi
done
The simplest way that I've found to run the freshly built project was as follows (used from the directory that contains projectRoot
):
virtualenv env
source env/bin/activate
cd projectRoot
pyb
cd target/dist/projectRoot-1.0.dev0/dist/
pip install projectRoot-1.0.dev0.tar.gz
entryPointScript.py
This indeed successfully runs the script with all its dependencies on user-defined packages, and prints:
Hello, world! 42 * 42 - 42 = 1722
but the whole procedure seems quite complicated. For comparison, in an analogous situation in SBT, I would just issue the single
run
command from the SBT-shell -- that's why the seven-step recipe above seems a bit suspicios to me.
Is there something like a pyb run
or pyb exec
plugin that does the same, but does not require from me that I set up all these environments and install anything? What I'm looking for is the analogon of sbt run
in SBT or mvn exec:java
in Maven that will build everything, set up all the classpaths, and then run the class with the main
method, without leaving any traces outside the project directory.
Since there is essentially no difference between the source code and the output of the target, I'm probably missing some obvious way how to run the script. If the PyBuilder
itself is not needed at all for that, it's fine too: all I want is to somehow get Hello, world! 42 * 42 - 42 = 1722
-string printed in the terminal.
Apparently the following workflow:
is exactly what is proposed by the creator of PyBuilder in this talk.
Note that the linked video is from 2014. If someone can propose a more streamlined recently provided solution, I'll of course accept that.