Skip to main content

Easily make your app extensible by you or others via use of setuptools entrypoints.

Project description

Safdie

Easily make your app extensible by you or others via use of setuptools entrypoints.

  • Free software: MIT license

I've written roughly the same module system for ten or so command-line apps over the last few years, and by now I've landed on a pattern that I've found pretty flexible and useful. Here, I've packed it into a module so both you and I can avoid re-inventing it every time we have a new project.

Installation

pip install safdie

You can also install the in-development version with:


pip install https://github.com/coddingtonbear/safdie/archive/master.zip

Quickstart

The below example isn't particularly useful, but does demonstrate a fully-working use of this.

  1. Create your commands as subclasses of safdie.BaseCommand and write whatever command classes you need:
# Module Path: my_app.commands
from safdie import BaseCommand

class MyCommand(BaseCommand):
    def handle(self):
        print("Do whatever you need to do here")
  1. Create your program's main command-line function:
# Module Path: my_app.cli
from safdie import SafdieRunner, BaseCommand

def main():
    # This will look up the command and run it's `handle` function.
    SafdieRunner("myapp.commands").run()
  1. In setuptools entrypoints, declare your entrypoints for both your command-line entrypoint and each of your commands:
   setup(
       ...
       entrypoints={
           "console_scripts": [
               "my_command_line_app = my_app.cli:main",
           ],
           "myapp.commands": {
               "somecommand = my_app.commands:MyCommand",
           }
       }
   )
  1. Install your app with python setup.py install

Now you can run my_command_line_app somecommand to execute your function.

Tips

Adding arguments

Maybe you want to add a command-line flag to your app; you can add those by subclassing SafdieRunner and defining an override for add_arguments as shown below:

from argparse import ArgumentParser
from safdie import SafdieRunner


class MyRunner(SafdieRunner):
    def add_arguments(self, parser: ArgumentParser) -> None:
        parser.add_argument("--something", action="store_true")


def main():
    MyRunner("myapp.commands").run()

Customizing your argument parser

By default, Safdie will generate a new argument parser for you, but maybe you want to use Gooey or some other Argparse-compatible parser? You can provide the class to use for generating the argument parser by specifying the parser_class command-line argument:

from gooey import GooeyParser, Gooey
from safdie import SafdieRunner


@Gooey
def main():
    SafdieRunner("myapp.commands", parser_class=GooeyParser).run()

Doing something before executing a command

Maybe you want to be able to optionally start a debugger between parsing args and executing the command?

import argparse
from safdie import SafdieRunner
from typing import Any, Dict, Iterable


class MyRunner(SafdieRunner):
    def add_arguments(self, parser: ArgumentParser) -> None:
        parser.add_argument("--debugger", action="store_true')

    def handle(
        self,
        args: argparse.Namespace,
        init_args: Iterable[Any],
        init_kwargs: Dict[str, Any],
        handle_args: Iterable[Any],
        handle_kwargs: Dict[str, Any],
    ) -> Any:
        if args.debugger:
            import debugpy

            debugpy.listen(("0.0.0.0", 5678))
            debugpy.wait_for_client()

        super().handle(
            args,
            init_args,
            init_kwargs,
            handle_args,
            handle_kwargs
        )


def main():
    MyRunner("myapp.commands").run()

Using your own command subclass

In the below example, you have your own command subclass that requires an additional parameter at init-time. Although the example below only uses an extra parameter for __init__, you can also pass extra parameters to handle. See the source for more details.

# Module Path: my_app.commands
from safdie import BaseCommand


class MyAppCommandBase(BaseCommand):
    def __init__(self, some_additional_init_param, *args, **kwargs):
        # Do something with `some_additional_init_param
        super().__init__(*args, **kwargs)


class MyCommand(MyAppBaseCommand):
    def handle(self):
        print("Do whatever you need to do here")
from typing import Any, Dict, Iterable

from safdie import SafdieRunner

from .commands import MyAppCommandBase


class MyRunner(SafdieRunner):
    def handle(
        self,
        args: argparse.Namespace,
        init_args: Iterable[Any],
        init_kwargs: Dict[str, Any],
        handle_args: Iterable[Any],
        handle_kwargs: Dict[str, Any],
    ) -> Any:
        some_value_i_want_to_pass = "Arbitrary"

        init_kwargs['some_additional_init_param'] = (
            some_value_i_want_to_pass
        )

        super().handle(
            args,
            init_args,
            init_kwargs,
            handle_args,
            handle_kwargs
        )

def main():
    MyRunner("myapp.commands", cmd_class=MyAppCommandBase).run()

Why is this named 'Safdie'?

You've probably seen at least a few photos of the famous building named Habitat 67. Moshe Safdie is the man who designed it.

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

safdie-2.0.4.tar.gz (12.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

safdie-2.0.4-py2.py3-none-any.whl (7.6 kB view details)

Uploaded Python 2Python 3

File details

Details for the file safdie-2.0.4.tar.gz.

File metadata

  • Download URL: safdie-2.0.4.tar.gz
  • Upload date:
  • Size: 12.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.9.21

File hashes

Hashes for safdie-2.0.4.tar.gz
Algorithm Hash digest
SHA256 19063536ebf00060e4bac636d4028055ab86efad35682ee90f3e969bbda2e3f0
MD5 133c303f39810506ae22f355b9b38ac7
BLAKE2b-256 08425cfe15deb32400423cdf223878263e985f0b5eb9c267d0939d1f47217cef

See more details on using hashes here.

File details

Details for the file safdie-2.0.4-py2.py3-none-any.whl.

File metadata

  • Download URL: safdie-2.0.4-py2.py3-none-any.whl
  • Upload date:
  • Size: 7.6 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.9.21

File hashes

Hashes for safdie-2.0.4-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 3ee15a95cd9778f6800647302ab921fb54c2cde3b955e569f173d66a373e1a9a
MD5 071e214c2632e12f17c2a2e9442725b9
BLAKE2b-256 12acdd3d148d692197ca0041bd8acec163b9f38abaaa01a7279276e282620f70

See more details on using hashes here.

Supported by

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