Skip to main content

Molot - lightweight build tool for software projects.

Project description

Lightweight build tool for software projects.

Requirements

Molot requires Python 3.6 or above (3.5 should work too, but that’s untested).

Usage

To create a build script, just make a new file build.py in the root of your project. You can make it executable chmod +x build.py to make it easier to run it.

Here’s how to start your build script.

#!/usr/bin/env python3
from molot import * #pylint: disable=unused-wildcard-import

Note that #pylint: disable=unused-wildcard-import is optional but will help keep your editor quiet about unused imports.

Once you’ve defined your targets and all, do this at the end to compile them:

build()

Now you’re ready to run the build script to see the help message:

./build.py

If you only wanted to see the list of targets and environment arguments, you run the built-in target list as follows:

./build.py list

The output will be something like this:

→ Executing target: list
available targets:
<builtin>
    list - lists all available targets

environment arguments:

Not very exciting. Now let’s learn how to add targets and environment arguments.

Targets

Any piece of work that your build script needs to perform is defined as a target. Here’s a trivial example of a target that just runs ls.

@target(
    name='ls',
    description="lists current directory items",
    group='greetings',
    depends=['target1', 'target2']
)
def ls():
    shell("ls")

Parameters are as follows:

  • name - unique name to use when requesting the target (optional; by default the function name will be used)

  • description - short description about what the target does, to be displayed in the help message (optional)

  • group - group name to list target under alphabetically (optional; by default, will be listed under ungrouped)

  • depends - list of other targets that need to be executed first (optional)

Since all the parameters are optional, the shortest definition of the same target can be as follows:

@target()
def ls():
    shell("ls")

There is a basic dependency resolution routine that checks for circular dependencies and finds the first targets to execute before running the one that you requested.

Anyway, here’s how you run your new target:

./build.py ls

Environment Arguments

Environment arguments are intended as a cross between environment variables and arguments. Values can be passed as the former and then overriden as the latter.

Here’s how you define one:

ENV = envarg('ENV', default='dev', description="build environment")

Parameters are as follows:

  • name - unique name for the argument

  • default - default value if none is supplied (optional; by default None)

  • description - short description about what the argument is, to be displayed in the help message (optional)

The argument is evaluated right there (rather than inside of targets), so you can use that variable anywhere once it’s set.

It can either be set as a regular environment variable. For example:

ENV=dev ./build.py sometarget

Alternatively, it can be passed as an argument:

./build.py sometarget --arg ENV=prod

If both are passed simultaneously (not recommended), then argument takes precedence over the environment variable.

Configuration

Molot provides an optional configuration parsing facility.

If you want to specify a configuration YAML file, create a file build.yaml in your project root, same location as your build.py, and fill it with any valid YAML. For example, something like this:

Environments:
    dev:
        Name: development
    prod:
        Name: production

Now you can access these configurations by calling config() from anywhere. First call will do the initial parsing, subsequent ones will just returned a cached dictionary with your configurations.

Therefore, if you want to parse a YAML file with a different name, pass the path to the first call:

config(path=os.path.join(PROJECT_PATH, 'somethingelse.yaml'))

You can either get the whole configuration dictionary or pass a specific path of keys to extract. For example, if you want to get the name for the prod environment:

name = config(['Environments', 'prod', 'Name'])

If the desired key is optional and you don’t want to fail the execution if it’s not there, you can do the following:

name = config(['Environments', 'qa', 'Name'], required=False)

Bootstrap

The build script above assumes Molot is already installed. If not, there are some tricks that you can use to pre-install before the script runs.

For example, you can create a separate file build_boot.py as follows:

from subprocess import run
from importlib.util import find_spec as spec
from pkg_resources import get_distribution as dist

# Preloads Molot build tool.
def preload_molot(ver):
    mod, pkg = 'molot', 'molot'
    spec(mod) and dist(pkg).version == ver or run(['pip3', 'install', f"{pkg}=={ver}"])

Then at the top of your script, you’ll be able to do the following:

#!/usr/bin/env python3
__import__('build_boot').preload_molot('X.Y.Z')
from molot import * #pylint: disable=unused-wildcard-import

This downloads a specific version X.Y.Z if it’s not already installed.

Installer

There is an installer for external packages that you can use to install dependencies only when they’re needed.

from molot.installer import install
install([
    'package1',
    ('module2', 'package2>=1.2.3')
])

Notice that you can pass a list of packages to install in two formats:

  • When the module name (import statement) matches the install package name, you can just pass it as a string, i.e. like 'package1' in the example

  • When they differ or you want to provide a specific version of a package, pass a tuple with the module name first and the install statement second, i.e. like ('module2', 'package2>=1.2.3') in the example

The install() expression checks if the module can be imported (meaning that it’s already installed) and installs it otherwise.

By default, the installer uses pip3 install but if you want to use a different expression (e.g. different version of pip or conda), you can pass it using the INSTALLER environment argument.

INSTALLER="conda install" ./build.py

Contexts

Although you can do all the work within each target, you can also abstract it into “contexts”. While you can use this concept however you like, the intended use was creating an object that extends Context that sets up the arguments, paths and anything else your target needs, and then calling a method on it.

Here’s an example:

PATH = './'
ENV = 'dev'

@target()
def create_foo():
    FooContext(PATH, ENV).create()

@target()
def delete_foo():
    FooContext(PATH, ENV).delete()

from molot.context import Context

class FooContext(Context):

    def __init__(self, path, env):
        self.path = path
        self.env = env

    def create(self):
        self.ensure_dir(self.path)
        # Do something with self.env

    def delete(self):
        self.ensure_dir(self.path)
        # Do something with self.env

It might be a good idea to then extract your contexts into a separate file build_contexts.py and import them in your build.py. That way, your build script is nice and clean with only the targets, meanwhile all your under-the-hood implementation is hidden away in a separate file.

Examples

See examples directory for sample build scripts that demonstrate some features.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

molot-0.3.3.tar.gz (12.9 kB view details)

Uploaded Source

Built Distribution

molot-0.3.3-py3-none-any.whl (11.2 kB view details)

Uploaded Python 3

File details

Details for the file molot-0.3.3.tar.gz.

File metadata

  • Download URL: molot-0.3.3.tar.gz
  • Upload date:
  • Size: 12.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.6.0 requests-toolbelt/0.9.1 tqdm/4.37.0 CPython/3.7.5

File hashes

Hashes for molot-0.3.3.tar.gz
Algorithm Hash digest
SHA256 598497505622c7ed002e49b593755f94e2ffaac13b171ac070d59046b5f5fc5e
MD5 90307ca3b7ffe2b6dead64aa5b422b72
BLAKE2b-256 30613b0d88486c7415cff4c6cff475b9d0d1ab79f9fab7b01480595df720b970

See more details on using hashes here.

File details

Details for the file molot-0.3.3-py3-none-any.whl.

File metadata

  • Download URL: molot-0.3.3-py3-none-any.whl
  • Upload date:
  • Size: 11.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.6.0 requests-toolbelt/0.9.1 tqdm/4.37.0 CPython/3.7.5

File hashes

Hashes for molot-0.3.3-py3-none-any.whl
Algorithm Hash digest
SHA256 37bd802962e44cc7ca3aa3273434f729121596782f200bcf9eeefe4176e2848e
MD5 1e80f598b0068472a9b3d62281d86420
BLAKE2b-256 17a227a713dffb707d302dc2d6da70cf1934c31183e53d3aa96e07ce88a5eed7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page