Code Conventions#
We have a few in-house conventions we try to stick to when developing KnowIt. Updating and enforcing these conventions is an ongoing task.
Layout#
KnowIt.knowit.KnowIt
is the top-level module that calls and runs all lower-level operations. It is assumed that the user only interacts directly with the toolkit through this module.
Below
KnowIt.knowit.KnowIt
are the three main modulesKnowIt.data
,KnowIt.trainer
, andKnowIt.interpret
along with sub- and side classes and methods in between.
Logging#
We use a global
logger
variable set up inKnowIt.knowit.KnowIt
. All other scripts can create a logger variable and log messages at three levels, as depicted below.from helpers.logger import get_logger
logger = get_logger()logger.info(‘Here is some info to log.’)
logger.warning(‘Here is some important info to log.’)
logger.error(‘Here is something wrong. I will describe it and then abort.’)
exit(101)
Importing#
Do not import anything that is not used by the script in which it is imported.
When importing methods or modules (either internal or external to KnowIt), always use the
from x import y, z
format and then usey
andz
in your code, do notimport x
and then usex.y
andx.z
.
All imports happen at the top of a script, not in between the main body of code.
Classes#
Use classes and inheritance only where sensible (e.g. to contain re-usable objects that are meant to be maintained and passed around the code). Methods are fine if you only want to prevent code duplication.
If a method in a class can be static, make it a static method with
@staticmethod
decorator.
Annotating#
For all method definitions, annotate the input and output argument types.
Use kwargs sparingly. We use kwargs to pass user arguments and default values at the top levels. Lower level methods should use named arguments, with as few default values as possible.
Usually if a variable has the value
None
it means that it is not provided (if it is an argument) or not created yet (if it is an attribute).
Documentation#
All methods must have docstring in numpy format.
If a script contains a sizable set of functions and/or classes, provide an overview about what it includes and how the parts fit together in the header of the script.
Don’t add docstrings inside the
__init__()
class constructor. Rather include it in the class definition right above it.
If you want a particular class or method to be included in the auto-generated API reference docs, include it in the local (same directory as the current .py script)
__init__.py
file.
Every script has the following top-matter:
“”” Overview of contents… “””
__copyright__ = ‘Copyright (c) 2025 North-West University (NWU), South Africa.’
__licence__ = ‘Apache 2.0; see LICENSE file for details.’
__author__ = ‘tiantheunissen@gmail.com, randlerabe@gmail.com’
__description__ = ‘One-liner of the contents.’
# external imports
…
# internal imports
…
logger = get_logger()
Naming conventions#
Use descriptive names.
Script names are always written in lower-case letters seperated by
_
.
Class names are written in camel case without spaces between words.
Method names are written the same as scripts.
Use a single
_
in front of a method name to indicate that it is not meant to be used outside the current local context (within current class or script).
Variable names for strings representing file paths use the suffix ‘_path’, and variable names for strings representing directory paths use the suffix ‘_dir’.
Helper functions#
Some functions have the potential to be useful throughout the codebase. We keep these in specific scripts, as defined below.
When specific file- or directory paths are to be constructed, define it in
env.env_paths
and import it from there.
Ad-hoc methods that load, dump, or convert stored files or directories should be defined in
helpers.file_dir_procs
and imported from there.
As far as possible we try to only define default arguments in
setup.setup_action_args
.