Cli

Run an application via a CLI command

  1. Overview
  2. application
  3. classes
  4. modules
  5. bindings
  6. additional_configs
  7. class_overrides
  8. overrides
  9. now
  10. utcnow

Overview

This context converts a clearskies application into a CLI command. Here’s a simple example:

#!/usr/bin/env python
import clearskies

def my_function():
    return "Hello World!"

cli = clearskies.contexts.Cli(my_function)
cli()

Which you can then run as expected:

$ ./example.py
Hello World!

Routing is still supported, with routes and route parameters becoming CLI args:

#!/usr/bin/env python
import clearskies

def my_function(name):
    return f"Hello {name}!"

cli = clearskies.contexts.Cli(
    clearskies.endpoints.Callable(
        my_function,
        url="/hello/:name",
        return_standard_response=False,
    )
)
cli()

With a url of /hello/:name you would invoke like so:

./example.py hello Bob
Hello Bob!

If the endpoint expects a request method you can provide it by setting the -X or --request_method= kwargs. So for tihs example:

#!/usr/bin/env python
import clearskies

def my_function(name):
    return f"Hello {name}!"

cli = clearskies.contexts.Cli(
    clearskies.endpoints.Callable(
        my_function,
        url="/hello/:name",
        request_methods=["POST"],
    )
)
cli()

And then calling it successfully:

./example.py hello Bob --request_method=POST

./example.py hello Bob -X POST

You can pass data as a json string with the -d flag or set individual named arguments. The following example just reflects the request data back to the client:

#!/usr/bin/env python
import clearskies

def my_function(request_data):
    return request_data

cli = clearskies.contexts.Cli(
    clearskies.endpoints.Callable(
        my_function,
    )
)
cli()

And these three calls are identical:

./example.py -d '{"hello": "world"}'

echo '{"hello": "world"}' | ./test.py

./test.py --hello=world

Although note that the first two are going to be preferred over the third, simply because with the third there’s simply no way to specify the type of a variable. As a result, you may run into issues with strict type checking on endpoints.

application

Required

The application to execute.

This can be a callable, an endpoint, or an endpoint group. If passed a callable, the callable can request any standard or defined dependencies and should return the desired response. It can also raise any exception from clearskies.exceptions.

classes

Optional

Record any class that should be made available for injection.

All classes that come in here become available via their injection name, which is calculated by converting the class name from TitleCase to snake_case. e.g. the following class:

class MyClass:
    pass

gets an injection name of my_class. Also, clearskies will only resolve and reject based on type hints if those classes are first added via add_classes. See the following example:

from clearskies.di import Di

class MyClass:
    name = "Simple Demo"

di = Di(classes=[MyClass])
# equivalent: di.add_classes(MyClass), di.add_classes([MyClass])

def my_function(my_class):
    print(my_class.name)

def my_function_with_type_hinting(the_name_no_longer_matters: MyClass):
    print(my-class.name)

# both print 'Simple Demo'
di.call_function(my_function)
di.call_function(my_function_with_type_hinting)

modules

Optional

Add a module to the dependency injection container.

clearskies will iterate through the module, adding all imported classes into the dependency injection container.

So, consider the following file structure inside a module:

my_module/
    __init__.py
    my_sub_module/
        __init__.py
        my_class.py

Assuming that the submodule and class are imported at each level (e.g. my_module/init.py imports my_sub_module, and my_sub_module/init.py imports my_class.py) then you can:

from clearksies.di import Di
import my_module

di = Di()
di.add_modules([
    my_module
])  # also equivalent: di.add_modules(my_module), or Di(modules=[my_module])


def my_function(my_class):
    pass


di.call_function(my_function)

my_function will be called and my_class will automatically be populated with an instance of my_module.sub_module.my_class.MyClass.

Note that MyClass will be able to declare its own dependencies per normal dependency injection rules. See the main docblock in the clearskies.di.Di class for more details about how all the pieces work together.

bindings

Optional

Provide a specific value for name-based injection.

This method attaches a value to a specific dependency injection name.

import clearskies.di

di = clearskies.di.Di()
di.add_binding("my_name", 12345)
# equivalent:
# di = clearskies.di.Di(bindings={"my_name": 12345})


def my_function(my_name):
    print(my_name)  # prints 12345


di.call_function(my_function)

additional_configs

Optional

Add an additional config instance to the dependency injection container.

Additional config class provide an additional way to provide dependencies into the dependency injection system. For more details about how to use them, see both base classes:

  1. clearskies.di.additional_config.AdditionalConfig
  2. clearskies.di.additional_config_auto_import.AdditionalConfigAutoImport

To use this method:

import clearskies.di


class MyConfig(clearskies.di.AdditionalConfig):
    def provide_some_value(self):
        return 2

    def provide_another_value(self, some_value):
        return some_value * 2


di = clearskies.di.Di()
di.add_additional_configs([MyConfig()])
# equivalents:
# di.add_additional_configs(MyConfig())
# di = clearskies.di.Di(additional_configs=[MyConfig()])


def my_function(another_value):
    print(another_value)  # prints 4


di.call_function(my_function)

class_overrides

Optional

Override a class for type-based injection.

This function allows you to replace/mock class provided when relying on type hinting for injection. This is most often (but not exclusively) used for mocking out classes during texting. Note that this only overrides that specific class - not classes that extend it.

Example:

from clearskies.import Di

class TypeHintedClass:
    my_value = 5

class ReplacementClass:
    my_value = 10

di = Di()
di.add_classes(TypeHintedClass)
di.add_class_override(TypeHintedClass, ReplacementClass)
# also di = Di(class_overrides={TypeHintedClass: ReplacementClass})

def my_function(some_value: TypeHintedClass):
    print(some_value.my_value) # prints 10

di.call_function(my_function)

overrides

Optional

now

Optional

Set the current time which will be passed along to any dependency arguments named now.

utcnow

Optional