{"id":22889500,"url":"https://github.com/mononobi/balerin","last_synced_at":"2025-07-18T23:05:10.417Z","repository":{"id":57413830,"uuid":"447388392","full_name":"mononobi/balerin","owner":"mononobi","description":"A python package startup orchestrator, respecting package dependencies.","archived":false,"fork":false,"pushed_at":"2023-09-04T13:16:05.000Z","size":95,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-05-07T14:06:50.711Z","etag":null,"topics":["auto-import","balerin","package-loader","package-manager","package-orchestrator","packaging","pyrin","python","startup","startup-import","syntax-error"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mononobi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-01-12T22:24:13.000Z","updated_at":"2022-12-15T07:16:11.000Z","dependencies_parsed_at":"2022-09-04T23:14:02.172Z","dependency_job_id":null,"html_url":"https://github.com/mononobi/balerin","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mononobi%2Fbalerin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mononobi%2Fbalerin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mononobi%2Fbalerin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mononobi%2Fbalerin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mononobi","download_url":"https://codeload.github.com/mononobi/balerin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252892502,"owners_count":21820648,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["auto-import","balerin","package-loader","package-manager","package-orchestrator","packaging","pyrin","python","startup","startup-import","syntax-error"],"created_at":"2024-12-13T21:43:51.016Z","updated_at":"2025-05-07T14:06:58.025Z","avatar_url":"https://github.com/mononobi.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Balerin\n## Your application will dance with Balerin!\n\nBalerin is a python package startup orchestrator. it can handle the loading of all\napplication packages on startup respecting package dependencies.\n\nLoading all application packages on startup has many benefits:\n\n- Revealing all syntax errors and invalid imports on startup, preventing runtime failure.\n- Initializing all required objects on startup, providing better performance at runtime.\n- Triggering all first level objects of a module (ex. decorators) without the need to \n  manually trigger them.\n- Better code maintainability by letting you distribute the code on their suitable \n  packages and preventing the need for a centralized location for nonsense \n  imports (ex. models, hooks, managers, ...).\n- Auto registration of celery tasks without the need to name all task modules a \n  specific name (usually tasks).\n- Populating application level caches on startup to boost performance at runtime.\n\n## Can I Use Balerin With?\n\nYes, you can. Balerin can be used in conjunction with all major frameworks \n([Flask](https://github.com/pallets/flask), [FastAPI](https://github.com/tiangolo/fastapi), \n [Sanic](https://github.com/sanic-org/sanic), [AIOHTTP](https://github.com/aio-libs/aiohttp), \n [Bottle](https://github.com/bottlepy/bottle), [Pyramid](https://github.com/Pylons/pyramid),\n [Tornado](https://github.com/tornadoweb/tornado), [Django](https://github.com/django/django))\nand many more. You can also use it on bare python applications without a \nframework, it's all up to you.\n\n### What about Pyrin?\n\nIt's a good question. [Pyrin](https://github.com/mononobi/pyrin) has builtin support for \nBalerin, so you can just use Pyrin without the need to prepare Balerin on your own.\n\n## Installing\n\n**Install using pip**:\n\n**`pip install balerin`**\n\n## Usage Example\n\n**There are two ways to use Balerin in your project:**\n\n- *`Basic`*: Loading all packages based on filesystem order.\n  (the order could be changed between each run).\n- *`Pro`*: Loading all packages respecting their dependencies on each other.\n  (the order will be always the same on every run).\n\n**Sample Project Structure:**\n\n- root_dir\n  - my_app\n    - accounting\n      - `__init__.py`\n      - `api.py`\n    - customers\n      - `__init__.py`\n      - `models.py`\n    - `__init__.py`\n    - `api.py`\n    - `models.py`\n  - `start.py`\n\n### Basic Usage:\n\n**`my_app/__init__.py:`**\n\n```python\nimport os\n\nfrom flask import Flask\nfrom balerin import PackagingManager\n\n\napp = Flask('my_app')\nworking_directory = os.path.abspath(os.getcwd())\nroot_package = os.path.join(working_directory, 'my_app')\nbalerin = PackagingManager(root_package, context=dict(important=True, app=app))\n```\n\n**`start.py:`**\n\n```python\nfrom my_app import balerin, app\n\nbalerin.load_components()\napp.run()\n```\n\n### Pro Usage:\n\nIn order to load packages respecting their dependencies on each other, you must define \na package class in `__init__.py` file of each package that has dependency on other packages.\nThe package class must be a subclass of Balerin's `Package` class:\n\n**`my_app/accounting/__init__.py:`**\n\n```python\nfrom balerin import Package\n\n\nclass AccountingPackage(Package):\n    # the name of the package.\n    # example: `my_app.api`.\n    # should get it from `__name__` for each package.\n    NAME = __name__\n\n    # list of all packages that this package is dependent\n    # on them and should be loaded after all of them.\n    # example: ['my_app.logging', 'my_app.api.public']\n    # this can be left as an empty list if there is no dependencies.\n    DEPENDS = ['my_app.customers']\n\n    # specifies that this package is enabled and must be loaded.\n    ENABLED = True\n\n    # name of a module inside this package that should be loaded before all other modules.\n    # for example: 'manager'\n    # this can be left as None if there is no such file in this package needing early loading.\n    COMPONENT_NAME = None\n```\n\nNow you can load your packages respecting their dependencies on each other, using \nthe sample code in **`Basic Usage`** section.\n\n### How to Choose Between Basic or Pro Usages:\n\nIn most cases, you don't need to use the `Pro Usage` style. unless your application \narchitecture is based on `Dependency Injection` and `IoC` concepts. so, when in doubt, go \nwith `Basic Usage`.\n\n### Initialization Arguments of PackagingManager Class:\n\n- `root`: it can be a single or multiple paths to different packages of your application.\n          Balerin will load all sub-packages of each path respectively.\n- `base_component`: specifies a module name which must be loaded before all other modules \n                    in each package if available. for example *manager*. this value could be \n                    overridden in each *Package* class using *COMPONENT_NAME* attribute.\n- `verbose`: specifies that loading info should be printed on each step.\n             defaults to True if not provided.\n- `ignored_packages`: list of package names that must be ignored from loading. package names \n                      must be fully qualified. for example: *my_app.accounting.public*. \n                      notice that if a package that has sub-packages added to ignore list, \n                      all of its sub-packages will be ignored automatically even if not \n                      present in ignore list.\n- `ignored_modules`: list of module names that must be ignored from loading. \n                     module names could be fully qualified or just the module name itself.\n                     for example: *my_app.customers.models* or *models*.\n                     notice that if only module name is provided, then all modules matching \n                     the provided name will be ignored from loading.\n- `ignored_detector`: a function to be used to detect if a package or module should be ignored.\n                      it must take two arguments, the first is the fully qualified name \n                      and the second is a boolean value indicating that the input is a module. \n                      it should also take optional keyword arguments as context. it should \n                      return a boolean value.\n                      for example: *my_detector(name, is_module, \\*\\*context)*\n- `module_loader`: a function to be used to load custom attributes of a module. \n                   it should take two arguments, a name and a module instance.  \n                   it should also take optional keyword arguments as context. \n                   the output will be ignored. \n                   for example: *my_loader(name, module, \\*\\*context)*\n- `context`: a dict containing all shared contexts to be used for example \n             inside *ignored_detector* and *module_loader* functions.\n\n### PackagingManager Public Interface:\n\nOnce you create an object of `PackagingManager` class, you can call \nthese methods on the created object:\n\n- `load_components`: load all packages inside the root path.\n- `load`: load a single module with provided name.\n- `get_loaded_packages`: get a list of all loaded package names.\n- `is_package_loaded`: get a value indicating that given package is loaded.\n- `get_context`: get a dict of all shared contexts.\n\n```python\nimport os\n\nfrom balerin import PackagingManager\n\n\nworking_directory = os.path.abspath(os.getcwd())\nroot_package = os.path.join(working_directory, 'my_app')\nbalerin = PackagingManager(root_package, context=dict(important=True))\n\nbalerin.load_components()\nbalerin.load('my_app.accounting.api')\nloaded_packages = balerin.get_loaded_packages()\nis_package_loaded = balerin.is_package_loaded('my_app.accounting')\ncontext = balerin.get_context()\n```\n\n## Hint\n\n`Balerin` is an albanian word and means dancer.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmononobi%2Fbalerin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmononobi%2Fbalerin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmononobi%2Fbalerin/lists"}