Package: balerin
BalerinYour application will dance with Balerin!Balerin is a python package startup orchestrator. it can handle loading all
packages of an application at startup time respecting package dependencies.Loading all application packages at startup has many benefits:Revealing all syntax errors and invalid imports on startup, preventing runtime failure.Initializing all required objects at startup, providing better performance at runtime.Triggering all first level objects of a module (ex. decorators) without the need to
manually trigger them.Better code maintainability by letting you distribute the code on their suitable
packages and preventing the need for a centralized location for nonsense
imports (ex. models, hooks, managers, ...).Auto registration of celery tasks without the need to name all task modules a
specific name (usually tasks).Populating application level caches at startup to boost performance at runtime.Can I Use Balerin With?Yes, you can. Balerin can be used in conjunction with all major frameworks
(Flask,FastAPI,Sanic,AIOHTTP,Bottle,Pyramid,Tornado,Django)
and many more. You can also use it on bare python applications without a
framework, it's all up to you.What about Pyrin?It's a good question.Pyrinhas builtin support for
Balerin, so you can just use Pyrin without the need to prepare Balerin on your own.InstallingInstall using pip:pip install balerinUsage ExampleThere are two ways to use Balerin in your project:Basic: Loading all packages based on filesystem order.
(the order could be changed between each run).Pro: Loading all packages respecting their dependencies on each other.
(the order will be always the same on every run).Sample Project Structure:root_dirmy_appaccounting__init__.pyapi.pycustomers__init__.pymodels.py__init__.pyapi.pymodels.pystart.pyBasic Usage:my_app/__init__.py:importosfromflaskimportFlaskfrombalerinimportPackagingManagerapp=Flask('my_app')working_directory=os.path.abspath(os.getcwd())root_package=os.path.join(working_directory,'my_app')balerin=PackagingManager(root_package,context=dict(important=True,app=app))start.py:frommy_appimportbalerin,appbalerin.load_components()app.run()Pro Usage:In order to load packages respecting their dependencies on each other, you must define
a package class in__init__.pyfile of each package that has dependency on other packages.
The package class must be a subclass of Balerin'sPackageclass:my_app/accounting/__init__.py:frombalerinimportPackageclassAccountingPackage(Package):# the name of the package.# example: `my_app.api`.# should get it from `__name__` for each package.NAME=__name__# list of all packages that this package is dependent# on them and should be loaded after all of them.# example: ['my_app.logging', 'my_app.api.public']# this can be left as an empty list if there is no dependencies.DEPENDS=['my_app.customers']# specifies that this package is enabled and must be loaded.ENABLED=True# name of a module inside this package that should be loaded before all other modules.# for example: 'manager'# this can be left as None if there is no such file in this package needing early loading.COMPONENT_NAME=NoneNow you can load your packages respecting their dependencies on each other, using
the sample code inBasic Usagesection.How to Choose Between Basic or Pro Usages:In most cases, you don't need to use thePro Usagestyle. unless your application
architecture is based onDependency InjectionandIoCconcepts. so, when in doubt, go
withBasic Usage.Initialization Arguments of PackagingManager Class:root: it can be a single or multiple paths to different packages of your application.
Balerin will load all sub-packages of each path respectively.base_component: specifies a module name which must be loaded before all other modules
in each package if available. for examplemanager. this value could be
overridden in eachPackageclass usingCOMPONENT_NAMEattribute.verbose: specifies that loading info should be printed on each step.
defaults to True if not provided.ignored_packages: list of package names that must be ignored from loading. package names
must be fully qualified. for example:my_app.accounting.public.
notice that if a package that has sub-packages added to ignore list,
all of its sub-packages will be ignored automatically even if not
present in ignore list.ignored_modules: list of module names that must be ignored from loading.
module names could be fully qualified or just the module name itself.
for example:my_app.customers.modelsormodels.
notice that if only module name is provided, then all modules matching
the provided name will be ignored from loading.ignored_detector: a function to be used to detect if a package or module should be ignored.
it must take two arguments, the first is the fully qualified name
and the second is a boolean value indicating that the input is a module.
it also should take optional keyword arguments as context. it should
return a boolean value.
for example:my_detector(name, is_module, **context)module_loader: a function to be used to load custom attributes of a module.
it should take two arguments, a name and a module instance.it also should take optional keyword arguments as context.
the output will be ignored.
for example:my_loader(name, module, **context)context: a dict containing all shared contexts to be used for example
insideignored_detectorandmodule_loaderfunctions.PackagingManager Public Interface:Once you create an object ofPackagingManagerclass, you can call
these methods on the created object:load_components: load all packages inside the root path.load: load a single module with provided name.get_loaded_packages: get a list of all loaded package names.is_package_loaded: get a value indicating that given package is loaded.get_context: get a dict of all shared contexts.importosfrombalerinimportPackagingManagerworking_directory=os.path.abspath(os.getcwd())root_package=os.path.join(working_directory,'my_app')balerin=PackagingManager(root_package,context=dict(important=True))balerin.load_components()balerin.load('my_app.accounting.api')loaded_packages=balerin.get_loaded_packages()is_package_loaded=balerin.is_package_loaded('my_app.accounting')context=balerin.get_context()HintBalerinis an albanian word and means dancer.