# 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": "",
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": "",
"keywords": "",
"author": "Adam Coddington",
"author_email": "me@adamcoddington.net",
"download_url": "https://files.pythonhosted.org/packages/c0/f5/2ca88fb8f0433a82ba12ae8610d9f1d71b9083b64911c7d030e42d3fece4/safdie-2.0.1.tar.gz",
"platform": "",
"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\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Easily make your app extensible by you or others via use of setuptools entrypoints.",
"version": "2.0.1",
"project_urls": {
"Homepage": "https://github.com/coddingtonbear/safdie",
"Issue Tracker": "https://github.com/coddingtonbear/safdie/issues"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "59d9f4d9667509f30448a4d10f3359ec3b67d8cd3bcba88eb68a76f9ded0426a",
"md5": "cd84e2ed6fbdcd0c6afb57b6c7ba23d7",
"sha256": "ee9e2ff0a382b88060d6a00824560a77a609ff2f37f0298db49ebecc5e0a083e"
},
"downloads": -1,
"filename": "safdie-2.0.1-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "cd84e2ed6fbdcd0c6afb57b6c7ba23d7",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": ">=3.6",
"size": 7539,
"upload_time": "2021-08-23T15:48:16",
"upload_time_iso_8601": "2021-08-23T15:48:16.730506Z",
"url": "https://files.pythonhosted.org/packages/59/d9/f4d9667509f30448a4d10f3359ec3b67d8cd3bcba88eb68a76f9ded0426a/safdie-2.0.1-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "c0f52ca88fb8f0433a82ba12ae8610d9f1d71b9083b64911c7d030e42d3fece4",
"md5": "2e334e68f9ce1c9ad94bd93bf00463b6",
"sha256": "4b2cb5e442054b5f5c2062b89cc374db76fc343e03516658cf40c4cde353a4b9"
},
"downloads": -1,
"filename": "safdie-2.0.1.tar.gz",
"has_sig": false,
"md5_digest": "2e334e68f9ce1c9ad94bd93bf00463b6",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 12010,
"upload_time": "2021-08-23T15:48:18",
"upload_time_iso_8601": "2021-08-23T15:48:18.718312Z",
"url": "https://files.pythonhosted.org/packages/c0/f5/2ca88fb8f0433a82ba12ae8610d9f1d71b9083b64911c7d030e42d3fece4/safdie-2.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2021-08-23 15:48:18",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "coddingtonbear",
"github_project": "safdie",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "safdie"
}