Skip to main content

provides a drop-in replacement for click, extending it to work in various OOP contexts

Project description

objclick

pypi python versions documentation pipeline status

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 classcommand or a classgroup via super() in a subclass.

v0.1.0 (2020-10-01)

  • Initial release.

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

objclick-0.1.1.tar.gz (14.1 kB view details)

Uploaded Source

Built Distribution

objclick-0.1.1-py3-none-any.whl (10.1 kB view details)

Uploaded Python 3

File details

Details for the file objclick-0.1.1.tar.gz.

File metadata

  • Download URL: objclick-0.1.1.tar.gz
  • Upload date:
  • Size: 14.1 kB
  • Tags: Source
  • Uploaded using 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

File hashes

Hashes for objclick-0.1.1.tar.gz
Algorithm Hash digest
SHA256 344a4cc65a2c4d0cbd543c57133e2190dab66005a8fc561632a2abf009692d6b
MD5 6c17330daa61e634e030dc0562678f8b
BLAKE2b-256 9c275393b6b97edceea10b7c82935a81a6d62787174cbcdd5a55312bbbcba273

See more details on using hashes here.

File details

Details for the file objclick-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: objclick-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 10.1 kB
  • Tags: Python 3
  • Uploaded using 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

File hashes

Hashes for objclick-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1327851512565a46d9a54c6a650ed494f15667670c4be5af86dc74476fffdef7
MD5 4f2746afc85a5e6b0d07dee0311d07a9
BLAKE2b-256 7bfa757892c585116ab50ef414d390be68434ab8f1905675f6f745568776e9d6

See more details on using hashes here.

Supported by

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