API
more-kivy-app
Base class for kivy apps that is crash resistant with builtin configuration.
App
The base App class.
- class more_kivy_app.app.MoreKivyApp(yaml_config_path=None, **kw)
Bases:
App
The base app.
- app_settings = {}
A dict that contains the
tree-config
settings for the app for all the configurable classes. See that module for details.The keys in the dict are configuration names for a class and its values are property values or nested dicts whose keys are class attributes names and values are their values. These attributes are the ones listed in
_config_props_
. Seetree-config
for how configuration works.
- apply_app_settings()
Applies the app config stored in
app_settings
to the application instance. Seetree_config.apply_config()
.
- ask_cannot_close(*largs, **kwargs)
Called by kivy when the user tries to close the app. It only closes if this returns False.
This is only used when the app is started with
run_app()
orrun_app_async()
.
- build(root=None)
Similar to App’s build, but it takes the root widget if provided and if
inspect
is True, it activates kivy’s inspector.- Parameters:
root – The root widget instance.
- Returns:
The root widget
- clean_up()
Called by
run_app()
orrun_app_async()
after the app closes to clean up any remaining resources.By default, if
inspect
is enabled, it cleans up after it.
- property data_path
The install dependent path to the persistent config data directory. It is automatically added to the kivy resource PATH.
if this is running under a PyInstaller installation, it is the path that contains the executable, it’s the data folder next to the app class’s python file.
- dump_app_settings_to_file()
Saves the app config from the app to the config file at
yaml_config_path
. Seetree_config.dump_config()
.
- ensure_file(filename)
Returns the full path to
filename
by searching for it in kivy’s resource directories if it exists. Otherwise, it create the empty file and returns the path to it.
- get_logger()
Returns the logger to use in
handle_exception()
to log messages.Defaults to returning the Kivy Logger.
- handle_exception(msg, exc_info=None, level='error', *largs)
Should be called whenever an exception is caught in the app.
If the app is started with
run_app()
orrun_app_async()
, this is called if kivy encounters an error. Similarly,app_error()
,app_error_async()
, andreport_exception_in_app()
call this on the app upon an exception.It logs the message to the logger using
get_logger()
.- Parameters:
- exception: string or exception object
The caught exception (i.e. the
e
inexcept Exception as e
)- exc_info: stack trace
If not None, the return value of
sys.exc_info()
or a stringified version of it. It is used to log the stack trace.- level: string
The log level to use on the message. Can be
'error'
,'exception'
(with the same meaning), or any other python log level.
- init_load()
Creates any config files and adds
data_path
to the kivy resource PATH when the app is instantiated.
- inspect = False
Enables GUI inspection. If True, it is activated by hitting ctrl-e in the GUI.
- load_app_settings_from_file()
Reads the config from the
yaml_config_path
file and saves it toapp_settings
. Seetree_config.read_config_from_file()
.
- yaml_config_path = None
The full path to the config file used for the app.
If it’s None when
__init__
of the base class is called, it is set toClassName_config.yaml
, whereClassName
is the name of the App class.
- more_kivy_app.app.app_error(app_error_func=None, threaded=True)
A decorator which wraps the function in try…except and calls
MoreKivyApp.handle_exception()
when a exception is raised.E.g.:
@app_error def do_something(): do_something
- more_kivy_app.app.app_error_async(app_error_func=None, threaded=True)
A decorator which wraps the async function in try…except and calls
MoreKivyApp.handle_exception()
when a exception is raised.E.g.:
@app_error async def do_something(): do_something
- more_kivy_app.app.report_exception_in_app(e, exc_info=None, threaded=True)
Takes the error and reports it to
MoreKivyApp.handle_exception()
.- Parameters:
e – The error
exc_info – If not None, the return value of
sys.exc_info()
or a stringified version of it.threaded – If the app should be called in a thread safe manner, e.g. if called from another thread.
- more_kivy_app.app.run_app(cls_or_app)
Entrance method used to start the App. It runs, or instantiates and runs a
MoreKivyApp
type instance.
- async more_kivy_app.app.run_app_async(cls_or_app, async_lib=None)
Entrance method used to start the App. It runs, or instantiates and runs a
MoreKivyApp
type instance.
Configuration
Exports kivy-aware version of the configuration functions.
- class more_kivy_app.config.Configurable
Bases:
object
tree_config
uses a duck typing approach. E.g. when applying config,apply_config()
will callapply_config_property
if it is defined for the objects being configured, otherwise it directly sets the properties.The
Configurable
can be used as a base-class instead of the duck typing approach and it defines all the special config methods. It can also be used as a guide to the names of the special config hooks available when using the duck typing approach.There are only two possible benefits to using
Configurable
. (1) it defines_config_props
/_config_children
which caches the names of the configurable properties / children on a per-class basis. Unlike the duck typing approach that gathers them afresh every time. (2) You can usesuper
for properties you want to use the default handling approach as in the following example.However, the main reason for the class’ existance is to define the configuration API in one place so we can refer to the method names.
Consider a duck typing example and the inheritance based example:
class DuckApp: _config_props_ = ('frame', 'color') frame = 'square' color = 'blue' class ConfigurableApp(Configurable): _config_props_ = ('frame', 'color') frame = 'square' color = 'blue'
Both behave identically with respect to configuration:
>>> read_config_from_object(DuckApp()) {'frame': 'square', 'color': 'blue'} >>> read_config_from_object(ConfigurableApp()) {'frame': 'square', 'color': 'blue'}
However, if we wanted to customize setting the
frame
property, usingConfigurable
will be slighlty simpler since you can call super to set the remaining properties in the default way. E.g.:class DuckApp: _config_props_ = ('frame', 'color') frame = 'square' color = 'blue' def apply_config_property(self, name, value): if name == 'frame': self.frame = value * 2 else: setattr(self, name, value) class ConfigurableApp(Configurable): _config_props_ = ('frame', 'color') frame = 'square' color = 'blue' def apply_config_property(self, name, value): if name == 'frame': self.frame = value * 2 else: super().apply_config_property(name, value)
- property _config_children: Dict[str, str]
A property, which if defined will be used to get all the configurable children instead of using
_config_children_
. It returns the dict of configurable children like_config_children_
.
- _config_children_: Dict[str, str] = {}
A dict of configurable children objects of the class that is used by the configuration API to traverse the configuration “tree” and to configure all these objects.
The keys are the human friendly names of the children and the values are the corresponding property names storing the child.
Each sub/super-class can define this and the children are accumulated across all the sub/super-classes.
- property _config_props: List[str]
A property, which if defined will be used to get all the configurable properties instead of using
_config_props_
. It returns the list of configurable properties like_config_props_
.
- _config_props_: Tuple[str] = ()
A list of configurable property names of the class that is set/read by the configuration API.
Each sub/super-class can define this and the properties are accumulated across all the sub/super-classes.
- apply_config_child(name: str, prop: str, obj: Any, config: Dict[str, Any] | List[Dict[str, Any]]) Any
If defined, it is called to apply the configuration for all the properties of the child, for each child.
When defined, it must either call
apply_config()
which also implicitly dispatchespost_config_applied()
. If you manually configure the child, you also have to callpost_config_applied()
manually.- Parameters:
name – The human friendly name of the child that will be configured. It is the same as the keys in
_config_children_
.prop – The property name storing the child that will be configured. It is the same as the values in
_config_children_
.obj – The configurable child object or a list of objects if the property is a list.
config – The config dict or list of config dicts to be applied to the child(ern).
- apply_config_property(name: str, value: Any) None
If defined, it is called to set the value of a configurable property of the class.
- Parameters:
name – The name of the property.
value – The value of the property.
- get_config_property(name: str) Any
If defined, it is called by the configuration system to get the value of the named property.
- Parameters:
name – The name of the property to get.
- post_config_applied() None
If defined, it is called by the configuration system when it finishes applying all the configuration properties/children of this object.
- more_kivy_app.config.apply_config(obj, config: ~typing.Dict[str, ~typing.Any], get_attr=<built-in function getattr>, set_attr=<built-in function setattr>) None
Takes the config data read with e.g.
read_config_from_object()
orread_config_from_file()
and applies them to the object and its children.Calls
post_config_applied
on the object and/or its children after they are configured if all/any have such a method.- Parameters:
obj – The object to which to apply the config.
config – The config dict.
get_attr – The function to use to get the child value from the object and its children. defaults to
getattr
.set_attr – The function to use to set the property values for the object and its children. defaults to
setattr
.
The object’s config is applied before its children’s config is applied.
E.x.:
class App: _config_props_ = ('name', ) _config_children_ = {'the box': 'box'} name = 'chair' box = None class Box: _config_props_ = ('volume', ) volume = 12 def apply_config_property(self, name, value): print('applying child', name) setattr(self, name, value) def post_config_applied(self): print('done applying child')
then:
>>> app = App() >>> app.box = Box() >>> d = read_config_from_object(app) >>> d {'the box': {'volume': 12}, 'name': 'chair'} >>> d['name'] = 'bed' >>> d['the box']['volume'] = 34 >>> apply_config(app, d) applying child volume done applying child >>> app.name 'bed' >>> app.box.volume 34
- more_kivy_app.config.create_doc_listener(sphinx_app, package_name, filename, *, yaml_dump_str=functools.partial(<function yaml_dumps>, get_yaml_obj=<function get_yaml>))
Creates a listener for the
__config_props__
attributes and dumps the docs of any props listed, tofilename
. If the file already exists, it extends it with new data and overwrites any exiting properties that we see again in this run.To use, in the sphinx conf.py file do something like:
def setup(app): import package create_doc_listener(app, package, 'config_attrs.yaml')
where
package
is the package for which the docs are generated.See the guide for a full example.
- more_kivy_app.config.dump_config(filename: str | ~pathlib.Path, data: ~typing.Dict[str, ~typing.Any], *, yaml_dump_str=functools.partial(<function yaml_dumps>, get_yaml_obj=<function get_yaml>)) None
Dumps config data gotten with e.g.
read_config_from_object()
to a yaml file.- Parameters:
filename – The yaml filename.
data – The config data.
yaml_dump_str – The function to encode the config to yaml. Defaults to
yaml_dumps()
.
E.g.:
>>> class App: >>> _config_props_ = ('name', ) >>> name = 'chair' >>> app = App() >>> d = read_config_from_object(app) >>> dump_config('config.yaml', d) >>> with open('config.yaml') as fh: ... print(fh.read())
Which prints
name: chair
.
- more_kivy_app.config.load_apply_save_config(obj, filename: str | ~pathlib.Path, get_attr=<built-in function getattr>, set_attr=<built-in function setattr>, *, yaml_dump_str=functools.partial(<function yaml_dumps>, get_yaml_obj=<function get_yaml>), yaml_load_str=<function yaml_loads>) Dict[str, Any]
Applies the config to the object from the yaml file (if the file doesn’t exist it creates it), and then dumps to the yaml file the current config from the object. It also returns the final config dict.
This can be used to set the object from the config, but also making sure the file contains the current config including any new properties not previously there or properties that changed during config application.
- Parameters:
obj – The configurable object.
filename – The yaml filename.
get_attr – The function to use to get the property/children values from the object. defaults to
getattr
.set_attr – The function to use to set the property values of the object and its children. defaults to
setattr
.yaml_dump_str – The function to encode the config to yaml. Defaults to
yaml_dumps()
.yaml_load_str – The function to parse the yaml string read from the file. Defaults to
yaml_loads()
.
E.x.:
class App: _config_props_ = ('name', ) name = 'chair' class AppV2: _config_props_ = ('name', 'side') name = 'chair' side = 'left'
then:
>>> app = App() >>> app.name = 'tree' >>> load_apply_save_config(app, 'config_app.yaml') {'name': 'tree'} >>> app.name 'tree' >>> # then later for v2 of the app >>> app_v2 = AppV2() >>> app_v2.name 'chair' >>> load_apply_save_config(app_v2, 'config_app.yaml') {'name': 'tree', 'side': 'left'} >>> app_v2.name 'tree' >>> with open('config_app.yaml') as fh: ... print(fh.read())
this prints:
name: tree side: left
- more_kivy_app.config.load_config(obj, filename: str | ~pathlib.Path, get_attr=<built-in function getattr>, *, yaml_dump_str=functools.partial(<function yaml_dumps>, get_yaml_obj=<function get_yaml>), yaml_load_str=<function yaml_loads>) Dict[str, Any]
Loads and decodes the config from a yaml file. If the config file doesn’t exist, it first dumps the config to the file using
read_config_from_object()
anddump_config()
before loading it.- Parameters:
obj – The object from which to dump the config when the files doesn’t exist.
filename – The yaml filename.
get_attr – The function to use to get the property/children values from the object. defaults to
getattr
.yaml_dump_str – The function to encode the config to yaml. Defaults to
yaml_dumps()
.yaml_load_str – The function to parse the yaml string read from the file. Defaults to
yaml_loads()
.
E.g.:
>>> class App: >>> _config_props_ = ('name', ) >>> name = 'chair' >>> load_config(App(), 'app_config.yaml') {'name': 'chair'}
- more_kivy_app.config.read_config_from_file(filename: str | ~pathlib.Path, yaml_load_str=<function yaml_loads>) Dict[str, Any]
Reads and returns the yaml config data dict from a file that was previously dumped with
dump_config()
.- Parameters:
filename – The yaml filename.
yaml_load_str – The function to parse the yaml string read from the file. Defaults to
yaml_loads()
.
E.g.:
>>> class App: >>> _config_props_ = ('name', ) >>> name = 'chair' >>> app = App() >>> d = read_config_from_object(app) >>> dump_config('config.yaml', d) >>> read_config_from_file('config.yaml') {'name': 'chair'}
- more_kivy_app.config.read_config_from_object(obj, get_attr=<built-in function getattr>) Dict[str, Any]
Returns a recursive dict containing all the configuration options of the obj and its configurable children.
- Parameters:
obj – The object from which to get the config.
get_attr – The function to use to get the child value from the object and its children. defaults to
getattr
.
E.x.:
class App: _config_props_ = ('name', ) _config_children_ = {'the box': 'box'} name = 'chair' box = None class Box: _config_props_ = ('volume', ) volume = 12
then:
>>> app = App() >>> app.box = Box() >>> read_config_from_object(app) {'the box': {'volume': 12}, 'name': 'chair'}
- more_kivy_app.config.write_config_props_rst(obj, project, app, exception, filename, rst_filename, get_attr=<built-in function getattr>, *, yaml_dump_str=functools.partial(<function yaml_dumps>, get_yaml_obj=<function get_yaml>))
Walks through all the configurable classes of
obj
, recursively by looking at_config_props_
and_config_children_
and using the type hints of these children properties if they are None (e.g. if obj is a class). For each property it loads their docs from the yaml filefilename
and it generates a rst output file atrst_filename
with all the tokens.For example in the sphinx conf.py file do:
def setup(app): app.connect('build-finished', partial(write_config_props_rst, ProjectApp, project_name, filename='config_prop_docs.yaml', rst_filename='source/config.rst'))
where project_name is the name of project and ProjectApp is the App of the package the contains all the configurable objects.
See the guide for a complete example.
Utilities
- more_kivy_app.utils.yaml_dumps(value: ~typing.Any, *, get_yaml_obj: ~typing.Callable[[], ~ruamel.yaml.main.YAML] = <function get_yaml>) str
Converts the object to yaml.
- Parameters:
value – the object to convert.
get_yaml_obj – A function such as
get_yaml()
that will be called to get a yaml object. Defaults toget_yaml()
.
- Returns:
a string yaml representation.