Full precept documentation¶
Usage¶
Install¶
Install with pip:
pip install precept
Write async console applications¶
Precept comes with many classes and functions to build async applications,
the main class consist of Precept
which you
subclass to create your application. Methods of this class decorated with
Command
are automatically added as sub command to the
application.
Basic example defining an echo command:
from precept import Precept, Command, Argument
class App(Precept):
@Command(
Argument('name'),
)
async def echo(self, name):
print(f'Hello {name}')
Then call from the terminal like this: app echo bob
-> Prints Hello bob
Note
If no command was supplied, main
will be called instead.
Starting the application¶
To create a console application from a precept app you need to add function
that will create an instance of your precept subclass and call start
then add it to setup.py
.
- app.py
def cli(): App().start()
- setup.py
from setuptools import setup setup( entry_points: { 'console_scripts': ['cli = app:cli'] } )
You can also create a global instance of the app and assign the entrypoint to it’s start method
Configs¶
Precept comes with a built in config system, create a subclass of
Config
with members as config. You can nest classes
definition to create sub sections.
Example¶
from precept import Config, Nestable, ConfigProperty
class MyConfig(Config):
my_config = ConfigProperty(comment='comment', config_type=str)
class SubConfig(Nestable):
nested = ConfigProperty(default='Default')
sub_config: SubConfig # A class member will be auto created.
Then you use it in the precept class like so:
from precept import Precept
class MyApp(Precept):
config = MyConfig()
Config file¶
To use the config with files, add a config_file
argument to precept init:
from precept import Precept
class MyApp(Precept):
def __init__(self):
super().__init__(
config_file='config.yml',
)
Precept will automatically add a --config-file
global argument for the user
to override.
It will also add a dump-config
command to dump the default config for first
use.
Note
The config_file
argument can also be a list, in which case the first
file found will be used.
Config format¶
Precept can read and write three config format, default being yaml:
yaml
ini
json, doesn’t support comments.
See also
Concepts¶
These concepts can be used to extend and interact with precept applications.
Events¶
Generic asyncio event consumer system.
The Precept instance and services both comes with an event dispatcher (events
member). You
can subscribe to events and handle them when they happen.
Subscribe to events
Subscribe to events that are dispatched.
app.events.subscribe('cli_started', lambda e: print('Started'))
Dispatch events
Send events with optional payload as keyword arguments.
app.events.dispatch('my_events', something='foo')
cli events¶
These events are available when starting the application with start.
Event |
Description |
---|---|
|
Called before everything else. |
|
Called after parsing the arguments and before the command start, payload with the arguments. |
|
The command has started, this is called at the same time. |
|
The command has stopped and the application will quit after. |
See also
Services¶
Service’s runs alongside the precept application, they are started and stopped
at the same time when calling start()
.
Service events¶
Services comes with three auto events starting with the service name. If a service is called dummy, it will get:
dummy_setup
, called with the running application before starting.dummy_start
, started before the application.dummy_stop
, stopped after the application.
Note
If the application is not started with start
, you need to call the
services methods:
setup_services
start_services
stop_services
See also
Plugins¶
Plugins automatically connect to precept applications during initialisation.
They have one method, setup()
, which takes the application
as argument you can use to set variables.
To add a plugin, you need to set it in setup.py, the entrypoint key needs to
be the snake cased version of prog_name
variable of the precept application.
from setuptools import setup
setup(
entry_points={
'precept_app.plugins': ['my_plugin = plug:plugin']
}
)
See also
Application list¶
These applications are created with precept:
aiocast, stream videos to chromecast devices.
top-drawer, generate valid pypi/npm package name from synonyms.
To add your application to this list, modify docs/applications.rst
and open
a PR.
precept¶
precept package¶
- class precept.Argument(*flags: str, type: Optional[type] = None, help: Optional[str] = None, choices: Optional[Iterable] = None, default: Optional[Any] = None, nargs: Optional[Union[str, int]] = None, action: Optional[str] = None, required: Optional[bool] = None, metavar: Optional[str] = None, dest: Optional[str] = None)[source]¶
Bases:
precept._immutable.ImmutableDict
Argument of a Command, can either be optional or not depending on the flags
- __init__(*flags: str, type: Optional[type] = None, help: Optional[str] = None, choices: Optional[Iterable] = None, default: Optional[Any] = None, nargs: Optional[Union[str, int]] = None, action: Optional[str] = None, required: Optional[bool] = None, metavar: Optional[str] = None, dest: Optional[str] = None)[source]¶
- Parameters
flags – How to call the argument, prefixing with
-
makes the argument a keyword. Example:'-d', '--date'
make the variable available asdate
, but can be supplied as-d
from the cli.type – The type of the variable to cast to, default to str.
help – Description to go along with
choices – The available choices to choose from.
default – The value to take if not supplied.
nargs – Number of times the argument can be supplied.
action – What to do with the argument.
required – Makes a keyword argument required.
metavar – Name in help.
dest – The name of the variable to add the value to once parsed.
- action¶
- choices¶
- default¶
- dest¶
- property flag_key¶
- flags¶
- help¶
- metavar¶
- nargs¶
- register(parser)[source]¶
Register a virtual subclass of an ABC.
Returns the subclass, to allow usage as a class decorator.
- required¶
- type¶
- class precept.AsyncExecutor(loop=None, executor=None, max_workers=None)[source]¶
Bases:
object
Execute functions in a Pool Executor
- __init__(loop=None, executor=None, max_workers=None)[source]¶
- Parameters
loop – asyncio event loop.
executor – Set to use an already existing PoolExecutor, default to a new ThreadPoolExecutor if not supplied.
max_workers – Max workers of the created ThreadPoolExecutor.
- async execute(func, *args, **kwargs)[source]¶
Execute a sync function asynchronously in the executor.
- Parameters
func – Synchronous function.
args – Argument to give to the function.
kwargs – Keyword arguments to give to the function
- Returns
- class precept.Command(*arguments: precept._cli.Argument, name: Optional[str] = None, description: Optional[str] = None, help: Optional[str] = None, auto: bool = False, services: Optional[List[precept._services.Service]] = None)[source]¶
Bases:
object
Command decorator, methods of CliApp subclasses decorated with this gets a sub-command in the parser.
Wrapped methods will gets the arguments by the
Argument
flag.- __init__(*arguments: precept._cli.Argument, name: Optional[str] = None, description: Optional[str] = None, help: Optional[str] = None, auto: bool = False, services: Optional[List[precept._services.Service]] = None)[source]¶
- arguments: Iterable[precept._cli.Argument]¶
- property command_name¶
- description: str¶
- class precept.Config(config_format: precept._configs.ConfigFormat = ConfigFormat.TOML, root_name='config')[source]¶
Bases:
precept._configs.Nestable
Root config class, assign ConfigProperties as class members.
- __init__(config_format: precept._configs.ConfigFormat = ConfigFormat.TOML, root_name='config')[source]¶
- property config_format: precept._configs.ConfigFormat¶
- class precept.ConfigFormat(value)[source]¶
Bases:
precept._tools.AutoNameEnum
Available formats to use with configs.
TOML provided by tomlkit, supports comments and more complex types.
YML provided by ruamel.yaml, supports comments and more complex types.
JSON stdlib, no support for comments and types.
INI stdlib, support for comments.
- INI = 'ini'¶
- JSON = 'json'¶
- TOML = 'toml'¶
- YML = 'yml'¶
- class precept.ConfigProperty(default=None, comment=None, config_type=None, environ_name=None, auto_environ=False, name=None, auto_global=False, global_name=None)[source]¶
Bases:
object
- class precept.Plugin[source]¶
Bases:
object
Plugin’s are automatically added to a precept application upon installation.
Set the entry point in setup to register the plugin:
entry_point = {‘{app_name}.plugins’: [‘plugin = my_plugin:plugin’]}
- name: str = ''¶
- async setup(application)[source]¶
Setup the plugin
- Parameters
application (precept.Precept) – The running precept application.
- Returns
- class precept.Precept(config_file: Optional[Union[str, List[str]]] = None, loop=None, executor=None, executor_max_workers=None, add_dump_config_command=False, help_formatter=<class 'precept._cli.CombinedFormatter'>, logger_level=20, logger_fmt=None, logger_datefmt=None, logger_stream=<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, logger_colors=None, logger_style='%', services: Optional[List[precept._services.Service]] = None, print_version: bool = True)[source]¶
Bases:
object
Auto cli generator, methods decorated with
Command
will have a corresponding sub-command in the cli application.Commands will get the arguments named as the last element of the command flags.
Override main method for root handler, it gets all the global_arguments
- __init__(config_file: Optional[Union[str, List[str]]] = None, loop=None, executor=None, executor_max_workers=None, add_dump_config_command=False, help_formatter=<class 'precept._cli.CombinedFormatter'>, logger_level=20, logger_fmt=None, logger_datefmt=None, logger_stream=<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, logger_colors=None, logger_style='%', services: Optional[List[precept._services.Service]] = None, print_version: bool = True)[source]¶
- Parameters
config_file – Path to the default config file to use. Can be specified with
--config-file
loop – Asyncio loop to use.
executor – concurrent executor to use.
add_dump_config_command – Add a
dump-config
command.help_formatter – The cli formatter to use.
logger_level – Set logger level when setting up logging.
logger_fmt – The format of the logger.
logger_datefmt – Date format of the logger.
logger_stream – The stream to print the logs.
logger_colors – Dictionary with key logger level name and values of bg/fg/style dict.
logger_style – The symbol to use for formatting.
services – List of global services to start with the program.
print_version – Print the version & name of the app before start.
- config: precept._configs.Config = None¶
- config_class = None¶
- property config_path¶
- default_configs: dict = {}¶
- global_arguments = []¶
- async main(**kwargs)[source]¶
Handler when no command has been entered. Gets the globals arguments.
- Parameters
kwargs – Global arguments.
- Returns
- prog_name = ''¶
- async setup_plugins()[source]¶
Load and setup the registered plugins.
To register a plugin, subclass
Plugin
and instantiate then add tosetup.py
entry_points:‘{app_name}.plugins’: [‘my_plugin = plugin_module:plugin’]
- Returns
- async setup_services(command: Optional[precept._cli.Command] = None)[source]¶
Setup the services for the command or the main application.
- Parameters
command – The command that was run.
- Returns
- async start_services(command: Optional[precept._cli.Command] = None)[source]¶
Start the services, automatically called by start.
If the application if run with another method you can call this to start the global services without the command argument.
- Parameters
command – The command that was run.
- Returns
- async stop_services(command: Optional[precept._cli.Command] = None)[source]¶
Stop the services, automatically called by start.
Call this if your application is not run with start and you have running services.
- Parameters
command – The command that was run.
- Returns
- version = '0.0.1'¶
- class precept.Service(events: Optional[precept.events._dispatcher.EventDispatcher] = None)[source]¶
Bases:
object
Service’s runs alongside the main application.
Communicate via events.
- Events
{name}_setup
when added to services.{name}_start
after calling start.{name}_stop
after calling stop.
- __init__(events: Optional[precept.events._dispatcher.EventDispatcher] = None)[source]¶
- name: str = 'service'¶
- async setup(application)[source]¶
Called when added to services.
Use this to set events and other post initialization that may need the application instance.
- Parameters
application (precept.Precept) – Precept application
- Returns
Errors¶
- exception precept.errors.ConfigError[source]¶
Bases:
precept.errors.PreceptError
Error in the config system.
- exception precept.errors.ImmutableError[source]¶
Bases:
precept.errors.PreceptError
Immutable properties cannot change
Subpackages¶
precept.console¶
- class precept.console.Keys[source]¶
Bases:
object
- BACKSPACE = <Key 'backspace'>¶
- CTRL_A = <Key 'ctrl-a'>¶
- CTRL_ALT_A = <Key 'ctrl-alt-a'>¶
- CTRL_ALT_DEL = <Key 'ctrl-alt-del'>¶
- CTRL_B = <Key 'ctrl-b'>¶
- CTRL_C = <Key 'ctrl-c'>¶
- CTRL_D = <Key 'ctrl-d'>¶
- CTRL_E = <Key 'ctrl-e'>¶
- CTRL_F = <Key 'ctrl-f'>¶
- CTRL_Z = <Key 'ctrl-z'>¶
- DELETE = <Key 'delete'>¶
- DOWN = <Key 'down'>¶
- END = <Key 'end'>¶
- ENTER = <Key 'enter'>¶
- ESCAPE = <Key 'escape'>¶
- F1 = <Key 'F1'>¶
- F10 = <Key 'F10'>¶
- F11 = <Key 'F11'>¶
- F12 = <Key 'F12'>¶
- F2 = <Key 'F2'>¶
- F3 = <Key 'F3'>¶
- F4 = <Key 'F4'>¶
- F5 = <Key 'F5'>¶
- F6 = <Key 'F6'>¶
- F7 = <Key 'F7'>¶
- F8 = <Key 'F8'>¶
- F9 = <Key 'F9'>¶
- HOME = <Key 'home'>¶
- INSERT = <Key 'insert'>¶
- LEFT = <Key 'left'>¶
- RIGHT = <Key 'right'>¶
- SPACE = <Key 'space'>¶
- SPECIAL_KEYS = (<Key 'space'>, <Key 'backspace'>, <Key 'enter'>, <Key 'escape'>, <Key 'insert'>, <Key 'end'>, <Key 'home'>, <Key 'delete'>, <Key 'down'>, <Key 'up'>, <Key 'left'>, <Key 'right'>, <Key 'F1'>, <Key 'F2'>, <Key 'F3'>, <Key 'F4'>, <Key 'F5'>, <Key 'F6'>, <Key 'F7'>, <Key 'F8'>, <Key 'F9'>, <Key 'F10'>, <Key 'F11'>, <Key 'F12'>, <Key 'ctrl-c'>, <Key 'ctrl-a'>, <Key 'ctrl-alt-a'>, <Key 'ctrl-alt-del'>, <Key 'ctrl-b'>, <Key 'ctrl-d'>, <Key 'ctrl-e'>, <Key 'ctrl-f'>, <Key 'ctrl-z'>)¶
- UP = <Key 'up'>¶
- keys = {'\x01': <Key 'ctrl-a'>, '\x02': <Key 'ctrl-b'>, '\x03': <Key 'ctrl-c'>, '\x04': <Key 'ctrl-d'>, '\x05': <Key 'ctrl-e'>, '\x06': <Key 'ctrl-f'>, '\r': <Key 'enter'>, '\x1a': <Key 'ctrl-z'>, '\x1b': <Key 'escape'>, '\x1b\x01': <Key 'ctrl-alt-a'>, '\x1bO15~': <Key 'F5'>, '\x1bO17~': <Key 'F6'>, '\x1bO18~': <Key 'F7'>, '\x1bO19~': <Key 'F8'>, '\x1bO20~': <Key 'F9'>, '\x1bO21~': <Key 'F10'>, '\x1bO23~': <Key 'F11'>, '\x1bO24~': <Key 'F12'>, '\x1bOP': <Key 'F1'>, '\x1bOQ': <Key 'F2'>, '\x1bOR': <Key 'F3'>, '\x1bOS': <Key 'F4'>, '\x1b[2~': <Key 'insert'>, '\x1b[3^': <Key 'ctrl-alt-del'>, '\x1b[3~': <Key 'delete'>, '\x1b[A': <Key 'up'>, '\x1b[B': <Key 'down'>, '\x1b[C': <Key 'right'>, '\x1b[D': <Key 'left'>, '\x1b[F': <Key 'end'>, '\x1b[H': <Key 'home'>, ' ': <Key 'space'>, '0': <Key '0'>, '1': <Key '1'>, '2': <Key '2'>, '3': <Key '3'>, '4': <Key '4'>, '5': <Key '5'>, '6': <Key '6'>, '7': <Key '7'>, '8': <Key '8'>, '9': <Key '9'>, 'A': <Key 'A'>, 'B': <Key 'B'>, 'C': <Key 'C'>, 'D': <Key 'D'>, 'E': <Key 'E'>, 'F': <Key 'F'>, 'G': <Key 'G'>, 'H': <Key 'H'>, 'I': <Key 'I'>, 'J': <Key 'J'>, 'K': <Key 'K'>, 'L': <Key 'L'>, 'M': <Key 'M'>, 'N': <Key 'N'>, 'O': <Key 'O'>, 'P': <Key 'P'>, 'Q': <Key 'Q'>, 'R': <Key 'R'>, 'S': <Key 'S'>, 'T': <Key 'T'>, 'U': <Key 'U'>, 'V': <Key 'V'>, 'W': <Key 'W'>, 'X': <Key 'X'>, 'Y': <Key 'Y'>, 'Z': <Key 'Z'>, 'a': <Key 'a'>, 'b': <Key 'b'>, 'c': <Key 'c'>, 'd': <Key 'd'>, 'e': <Key 'e'>, 'f': <Key 'f'>, 'g': <Key 'g'>, 'h': <Key 'h'>, 'i': <Key 'i'>, 'j': <Key 'j'>, 'k': <Key 'k'>, 'l': <Key 'l'>, 'm': <Key 'm'>, 'n': <Key 'n'>, 'o': <Key 'o'>, 'p': <Key 'p'>, 'q': <Key 'q'>, 'r': <Key 'r'>, 's': <Key 's'>, 't': <Key 't'>, 'u': <Key 'u'>, 'v': <Key 'v'>, 'w': <Key 'w'>, 'x': <Key 'x'>, 'y': <Key 'y'>, 'z': <Key 'z'>, '\x7f': <Key 'backspace'>}¶
- precept.console.print_table(data, formatting=None, file=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>)[source]¶
precept.events¶
- class precept.events.Event(name: str, payload: dict)[source]¶
Bases:
object
Event with payload and stop property.
License¶
MIT License
Copyright (c) 2020 Philippe Duval
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Changelog¶
Versions follow semver.
- warning
Expect breaking changes between minor versions prior to
1.0.0
while the api stabilize.
[0.6.7]¶
Fixed¶
- bug
Fixed Python 10 support.
[0.6.6]¶
Fixed¶
- bug
Fixed reading toml config file with more keys than defined in the config class.
- bug
Fixed default config property
auto_environ
to false.
[0.6.5]¶
Fixed¶
- bug
Fix Toml dump config of type
bool
with a default and a comment with less than 40 characters. #22
- bug
Fix Toml dump config of type
str
with no comment and no default. #23
- bug
Use repr in case of error instead of
__class__.__name__
which might exist if the given type is wrong.
[0.6.4]¶
Fixed¶
- bug
Fix config nestable not updated from file.
[0.6.3]¶
Fixed¶
- bug
Fix config type.
- construction
Add KeyboardInterrupt event in case some resources need to be cleaned up.
- hammer
Add global name argument to config property for use with auto global.
- construction
Add raw_args to cli attributes.
- hammer
Allow for start arguments to be a string command.
[0.6.2]¶
Fixed¶
- bug
Fix config multi instance
[0.6.1]¶
Fixed¶
- bug
Fix config order of reading (argument > code > file).
- bug
Fix comment below variables in toml config format.
- bug
Fix plugin registration for running loops instantiation.
- bug
Fix config_class instantiation
- bug
Fix config from cli arguments with default value.
- bug
Fix toml null values.
[0.6.0]¶
Added¶
- sparkles
Add TOML config format.
Changed¶
- hammer
Change default config format to TOML.
- bug
Change config_format to property auto updating serializer.
[0.5.1]¶
Fixed¶
- hammer
Removed star imports.
[0.5.0]¶
Changed¶
- hammer
Allow configs to be set on the instance.
- hammer
Add
print_version
option.
Added¶
- sparkles
Add services to run alongside applications and commands.
- sparkles
Add plugin system.
[0.4.0]¶
Changed¶
- construction
Executor default to ThreadPoolExecutor.
- construction
Export AsyncExecutor.
Added¶
- sparkles
Add Executor wraps
- construction
Add Executor max workers argument
- sparkles
Add Event & Dispatcher
- sparkles
Add cli events.
[0.3.1]¶
Fixed¶
- bug
Fix multiple logging handlers registered during tests.
- construction
Add options for configuring the logger.
[0.3.0]¶
Changed¶
- feet
Moved console related functions to console package.
- feet
Moved keyhandler to console package.
Added¶
- sparkles
console.progress_bar
- sparkles
Add auto arguments from command functions.
- sparkles
Add auto global argument for config.
- construction
Add
symbols
argument toconsole.spinner
Fixed¶
- bug
Config get_root return self if root.
[0.2.1]¶
Fixed¶
- hammer
Allow config instance to be set directly.
- hammer
Add root config class docstring as the first config comment.
[0.2.0]¶
Changed¶
- boom
Rename
CliApp
->Precept
.
- hocho
Removed old ConfigProp.
- hocho
Removed
Precept.configs
, nowPrecept.config
with new config api.
Added¶
- sparkles
Command help from docstring.
- construction
Add help formatter argument.
- sparkles
Nested commands support, wrap a class into a
Command
.
- sparkles
Config class
Comment support
Ini/yaml/json format choices
value from environ
nestable config attribute lookup
config factory.
- construction
Errors classes:
PreceptError
,ConfigError
,ImmutableError
[0.1.1]¶
Fixed¶
KeyHandler fixes:
Remove no handler print.
Add Keys class with most specials keys.
Handle ctrl-c by default.
Fix single argument casing (auto convert to snake_case).
[0.1.0]¶
Added¶
Added global
--log-file
optionAdded
execute_with_lock
toAsyncExecutor
(CliApp.executor
)Added
add_dump_config_command
, addsprog dump-config outfile
command to output the currently used config file.Added
ImmutableDict
, immutable mapping with auto attribute lookup from__init__
arguments.Added
goto_xy
,getch
.Added
KeyHandler
, reads each sys.stdin keys as they come and apply a function according to the key.
Changed¶
Default
main
command onCliApp
now async.Removed
_
prefix fromprog_name
,global_arguments
,default_configs
,version
.Argument
andconfigs
are nowImmutableDict
.Removed
kwargs
fromArgument
, allparser.add_argument
parameters now available as key argument.config_file
can be a list of paths to check for existence in order.
[0.0.2]¶
Fixed¶
Added
help
toCommand
Set
spinner
default background toNone
[0.0.1]¶
Initial release
Contributing to Precept¶
Getting Started¶
Fork and clone the repo
Create, activate & install dependencies
$ python -m venv venv $ . venv/bin/activate $ pip install -r requirements.txt
Submit a PR with your changes.
Coding style¶
Linters¶
Commit messages¶
Prefix your commit messages with an emoji according to this list:
Commit type |
Emoji |
---|---|
Initial commit |
|
Version tag |
|
New feature |
|
Bugfix |
|
Metadata |
|
Documentation |
|
Documenting source code |
|
Performance |
|
Cosmetic |
|
Tests |
|
Adding a test |
|
General update |
|
Improve format/structure |
|
Move code |
|
Refactor code |
|
DRY up code |
|
Removing code/files |
|
Continuous Integration |
|
Security |
|
Upgrading dependencies |
|
Downgrading dependencies |
|
Lint |
|
Translation |
|
Text |
|
Critical hotfix |
|
Deploying stuff |
|
Fixing on MacOS |
|
Fixing on Linux |
|
Fixing on Windows |
|
Adding CI build system |
|
Analytics or tracking code |
|
Removing a dependency |
|
Adding a dependency |
|
Docker |
|
Configuration files |
|
Bundles update |
|
Merging branches |
|
Bad code / need improv. |
|
Reverting changes |
|
Breaking changes |
|
Code review changes |
|
Accessibility |
|
Move/rename repository |
|
Other |