safdie


Namesafdie JSON
Version 2.0.4 PyPI version JSON
download
home_pagehttps://github.com/coddingtonbear/safdie
SummaryEasily make your app extensible by you or others via use of setuptools entrypoints.
upload_time2025-03-09 18:53:50
maintainerNone
docs_urlNone
authorAdam Coddington
requires_python>=3.6
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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:

```python
# Module Path: my_app.commands
from safdie import BaseCommand

class MyCommand(BaseCommand):
    def handle(self):
        print("Do whatever you need to do here")

```

2. Create your program's main command-line function:

```python
# 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()

```

3. In setuptools entrypoints, declare your entrypoints for both your command-line entrypoint and each of your commands:

```python
   setup(
       ...
       entrypoints={
           "console_scripts": [
               "my_command_line_app = my_app.cli:main",
           ],
           "myapp.commands": {
               "somecommand = my_app.commands:MyCommand",
           }
       }
   )
```

4. 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:

```python
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:

```python
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?

```python
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.

```python
# 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")
```

```python
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](https://en.wikipedia.org/wiki/Habitat_67). [Moshe Safdie](https://en.wikipedia.org/wiki/Moshe_Safdie) is the man who designed it.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/coddingtonbear/safdie",
    "name": "safdie",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.6",
    "maintainer_email": null,
    "keywords": null,
    "author": "Adam Coddington",
    "author_email": "me@adamcoddington.net",
    "download_url": "https://files.pythonhosted.org/packages/08/42/5cfe15deb32400423cdf223878263e985f0b5eb9c267d0939d1f47217cef/safdie-2.0.4.tar.gz",
    "platform": null,
    "description": "# Safdie\n\nEasily make your app extensible by you or others via use of setuptools entrypoints.\n\n* Free software: MIT license\n\nI'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.\n\n## Installation\n\n```\npip install safdie\n```\n\nYou can also install the in-development version with:\n\n```\n\npip install https://github.com/coddingtonbear/safdie/archive/master.zip\n\n```\n\n## Quickstart\n\nThe below example isn't particularly useful, but does demonstrate a fully-working use of this.\n\n1. Create your commands as subclasses of `safdie.BaseCommand` and write whatever command classes you need:\n\n```python\n# Module Path: my_app.commands\nfrom safdie import BaseCommand\n\nclass MyCommand(BaseCommand):\n    def handle(self):\n        print(\"Do whatever you need to do here\")\n\n```\n\n2. Create your program's main command-line function:\n\n```python\n# Module Path: my_app.cli\nfrom safdie import SafdieRunner, BaseCommand\n\ndef main():\n    # This will look up the command and run it's `handle` function.\n    SafdieRunner(\"myapp.commands\").run()\n\n```\n\n3. In setuptools entrypoints, declare your entrypoints for both your command-line entrypoint and each of your commands:\n\n```python\n   setup(\n       ...\n       entrypoints={\n           \"console_scripts\": [\n               \"my_command_line_app = my_app.cli:main\",\n           ],\n           \"myapp.commands\": {\n               \"somecommand = my_app.commands:MyCommand\",\n           }\n       }\n   )\n```\n\n4. Install your app with `python setup.py install`\n\nNow you can run `my_command_line_app somecommand` to execute your function.\n\n## Tips\n\n### Adding arguments\n\nMaybe 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:\n\n```python\nfrom argparse import ArgumentParser\nfrom safdie import SafdieRunner\n\n\nclass MyRunner(SafdieRunner):\n    def add_arguments(self, parser: ArgumentParser) -> None:\n        parser.add_argument(\"--something\", action=\"store_true\")\n\n\ndef main():\n    MyRunner(\"myapp.commands\").run()\n```\n\n### Customizing your argument parser\n\nBy 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:\n\n```python\nfrom gooey import GooeyParser, Gooey\nfrom safdie import SafdieRunner\n\n\n@Gooey\ndef main():\n    SafdieRunner(\"myapp.commands\", parser_class=GooeyParser).run()\n```\n\n### Doing something before executing a command\n\nMaybe you want to be able to optionally start a debugger between parsing args and executing the command?\n\n```python\nimport argparse\nfrom safdie import SafdieRunner\nfrom typing import Any, Dict, Iterable\n\n\nclass MyRunner(SafdieRunner):\n    def add_arguments(self, parser: ArgumentParser) -> None:\n        parser.add_argument(\"--debugger\", action=\"store_true')\n\n    def handle(\n        self,\n        args: argparse.Namespace,\n        init_args: Iterable[Any],\n        init_kwargs: Dict[str, Any],\n        handle_args: Iterable[Any],\n        handle_kwargs: Dict[str, Any],\n    ) -> Any:\n        if args.debugger:\n            import debugpy\n\n            debugpy.listen((\"0.0.0.0\", 5678))\n            debugpy.wait_for_client()\n\n        super().handle(\n            args,\n            init_args,\n            init_kwargs,\n            handle_args,\n            handle_kwargs\n        )\n\n\ndef main():\n    MyRunner(\"myapp.commands\").run()\n```\n\n### Using your own command subclass\n\nIn 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.\n\n```python\n# Module Path: my_app.commands\nfrom safdie import BaseCommand\n\n\nclass MyAppCommandBase(BaseCommand):\n    def __init__(self, some_additional_init_param, *args, **kwargs):\n        # Do something with `some_additional_init_param\n        super().__init__(*args, **kwargs)\n\n\nclass MyCommand(MyAppBaseCommand):\n    def handle(self):\n        print(\"Do whatever you need to do here\")\n```\n\n```python\nfrom typing import Any, Dict, Iterable\n\nfrom safdie import SafdieRunner\n\nfrom .commands import MyAppCommandBase\n\n\nclass MyRunner(SafdieRunner):\n    def handle(\n        self,\n        args: argparse.Namespace,\n        init_args: Iterable[Any],\n        init_kwargs: Dict[str, Any],\n        handle_args: Iterable[Any],\n        handle_kwargs: Dict[str, Any],\n    ) -> Any:\n        some_value_i_want_to_pass = \"Arbitrary\"\n\n        init_kwargs['some_additional_init_param'] = (\n            some_value_i_want_to_pass\n        )\n\n        super().handle(\n            args,\n            init_args,\n            init_kwargs,\n            handle_args,\n            handle_kwargs\n        )\n\ndef main():\n    MyRunner(\"myapp.commands\", cmd_class=MyAppCommandBase).run()\n```\n\n## Why is this named 'Safdie'?\n\nYou've probably seen at least a few photos of the famous building named [Habitat 67](https://en.wikipedia.org/wiki/Habitat_67). [Moshe Safdie](https://en.wikipedia.org/wiki/Moshe_Safdie) is the man who designed it.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Easily make your app extensible by you or others via use of setuptools entrypoints.",
    "version": "2.0.4",
    "project_urls": {
        "Homepage": "https://github.com/coddingtonbear/safdie",
        "Issue Tracker": "https://github.com/coddingtonbear/safdie/issues"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "12acdd3d148d692197ca0041bd8acec163b9f38abaaa01a7279276e282620f70",
                "md5": "071e214c2632e12f17c2a2e9442725b9",
                "sha256": "3ee15a95cd9778f6800647302ab921fb54c2cde3b955e569f173d66a373e1a9a"
            },
            "downloads": -1,
            "filename": "safdie-2.0.4-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "071e214c2632e12f17c2a2e9442725b9",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": ">=3.6",
            "size": 7565,
            "upload_time": "2025-03-09T18:53:48",
            "upload_time_iso_8601": "2025-03-09T18:53:48.910886Z",
            "url": "https://files.pythonhosted.org/packages/12/ac/dd3d148d692197ca0041bd8acec163b9f38abaaa01a7279276e282620f70/safdie-2.0.4-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "08425cfe15deb32400423cdf223878263e985f0b5eb9c267d0939d1f47217cef",
                "md5": "133c303f39810506ae22f355b9b38ac7",
                "sha256": "19063536ebf00060e4bac636d4028055ab86efad35682ee90f3e969bbda2e3f0"
            },
            "downloads": -1,
            "filename": "safdie-2.0.4.tar.gz",
            "has_sig": false,
            "md5_digest": "133c303f39810506ae22f355b9b38ac7",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6",
            "size": 12182,
            "upload_time": "2025-03-09T18:53:50",
            "upload_time_iso_8601": "2025-03-09T18:53:50.333777Z",
            "url": "https://files.pythonhosted.org/packages/08/42/5cfe15deb32400423cdf223878263e985f0b5eb9c267d0939d1f47217cef/safdie-2.0.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-03-09 18:53:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "coddingtonbear",
    "github_project": "safdie",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "safdie"
}
        
Elapsed time: 1.39183s