objclick
objclick provides a drop-in replacement for
click, extending it to work in
various OOP contexts.
click is a very nice package for quickly and easily defining composable
command-line interfaces in a declarative manner by stacking decorators on
top of functions that implement the "main" functions of a commands.
However, by design, it does not play well in OOP contexts. In particular,
it is not so easy to promote an instance method of a class to a click
command. This package attempts to rectify that by providing wrappers around
click that also play well with classes in some cases.
To give a motivating example, say you have a base class that implements a CLI:
>>> import objclick as click
>>> import abc
>>> class BaseService(metaclass=abc.ABCMeta):
... def explain(self):
... """Explain this service; must be implemented by subclasses."""
...
... @click.command()
... def main(self):
... print('Hello, let me tell you about myself.')
... self.explain()
...
This class must now be subclassed with an implementation of explain, but
the subclass need not re-implement the rest of the CLI:
>>> class MyService(BaseService):
... def explain(self):
... print(f'I am an instance of {self.__class__.__name__}.')
...
Since MyService.main is an instance method, we cannot simply call
MyService.main() to run the "main" function of CLI. Just like with a
normal instance method of a class, we must instantiate the class first and
call the method on the instance:
>>> service = MyService()
>>> service.main([], standalone_mode=False)
Hello, let me tell you about myself.
I am an instance of MyService.
(note:
standalone_mode
is a standard argument to click main functions that is useful for testing
commands.)
The inititial version of this package is still experimental, but it implements a number of other useful cases.
One such case is given by the classgroup decorator. This allows defining
a command group on a classmethod-like method that is bound to the class
rather than an instance of the class. In the common case where a
classmethod implements an alternative constructor for a class, if the
classgroup returns an instance of the class it's define on, this instance
will be passed as the self argument to any instance methods that are added
as subcommands of the group.
For example, here is command group that takes a --config option, as a
configuration is needed to instantiate the Service class. All subcommands
of Service.main can then access the configuration:
>>> import objclick as click
>>> import json, pprint
>>> class Service:
... def __init__(self, config):
... """Instantiate `Service` with a configuration dict."""
...
... self.config = config
...
... @click.classgroup()
... @click.option('--config', type=click.File())
... def main(cls, config=None):
... if config is not None:
... with config as f:
... config = json.load(f)
... else:
... config = {}
...
... print(f'Starting up {cls.__name__}...')
... return cls(config)
...
... @main.command()
... def show_config(self):
... print('Config:', end=' ')
... pprint.pprint(self.config)
...
Now the CLI defined by Service can be invoked like:
>>> import tempfile
>>> config = {'option1': 'a', 'option2': 'b'}
>>> with tempfile.NamedTemporaryFile(mode='w') as f:
... json.dump(config, f)
... f.flush()
... # like `service.py --config <config-file> show-config`
... args = ['--config', f.name, 'show-config']
... Service.main(args, standalone_mode=False)
...
Starting up Service...
Config: {'option1': 'a', 'option2': 'b'}
objclick Changelog
v0.1.1 (2020-10-02)
Bug fixes
- Fixed a bug with calling the callback of a
classcommandor aclassgroupviasuper()in a subclass.
v0.1.0 (2020-10-01)
- Initial release.
Metadata
Release files for objclick 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| objclick-0.1.1.tar.gz | 14.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| objclick-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.2 kB
Release files / objclick-0.1.1.tar.gz
| Download URL | objclick-0.1.1.tar.gz |
|---|---|
| Size | 14.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
344a4cc65a2c4d0cbd543c57133e2190dab66005a8fc561632a2abf009692d6b
|
|
BLAKE2b-256 checksum How to use checksums |
9c275393b6b97edceea10b7c82935a81a6d62787174cbcdd5a55312bbbcba273
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.0 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.7.5
|
Release files / objclick-0.1.1-py3-none-any.whl
| Download URL | objclick-0.1.1-py3-none-any.whl |
|---|---|
| Size | 10.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1327851512565a46d9a54c6a650ed494f15667670c4be5af86dc74476fffdef7
|
|
BLAKE2b-256 checksum How to use checksums |
7bfa757892c585116ab50ef414d390be68434ab8f1905675f6f745568776e9d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.0 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.7.5
|