Cli
Run an application via a CLI command
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:
- clearskies.di.additional_config.AdditionalConfig
- 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