11#
22# This file is part of BDC-DB.
3- # Copyright (C) 2022 INPE.
3+ # Copyright (C) 2025 INPE.
44#
55# This program is free software: you can redistribute it and/or modify
66# it under the terms of the GNU General Public License as published by
1818
1919"""Database management extension for Brazil Data Cube applications and services."""
2020
21- import importlib .resources
22- from importlib .metadata import entry_points
21+ from importlib .util import find_spec
2322from pathlib import Path
2423from typing import Dict , Iterable , List
2524
3029
3130from . import config as _config
3231from .db import db as _db
32+ from .utils import entry_points
3333
3434
3535def alembic_include_object (object , name , type_ , reflected , compare_to ): # pragma: no cover
@@ -95,6 +95,9 @@ def init_app(self, app, **kwargs):
9595 entry_point_jsonschemas (str): Custom entry point group for JSONSchemas
9696 engine_options (dict): Custom SQLAlchemy Engine Options for instance object.
9797 """
98+ if "bdc-db" in app .extensions :
99+ return # Ignore, already initialized.
100+
98101 self .init_db (app , ** kwargs )
99102
100103 # Load package namespaces
@@ -108,15 +111,15 @@ def init_app(self, app, **kwargs):
108111
109112 # prepare the configuration for multiple named branches
110113 # according to each package entry point
111- script_location = str ( importlib . resources . path ( ' bdc_db' , ' alembic' ) )
114+ script_location = resource_path ( " bdc_db. alembic" )
112115
113116 entrypoints = entry_points (group = "bdc_db.alembic" )
114117
115- version_locations = [
116- ( base_entry . name , str ( importlib . resources . path (
117- base_entry .name , base_entry . attr
118- ))) for base_entry in entrypoints
119- ]
118+ version_locations = []
119+ for entry in entrypoints :
120+ entry_name = f" { entry .name } . { entry . attr } "
121+
122+ version_locations . append (( entry . name , resource_path ( entry_name )))
120123
121124 if ('bdc_db' , script_location ) in version_locations : # pragma: no cover
122125 version_locations .remove (('bdc_db' , str (script_location )))
@@ -154,18 +157,21 @@ def init_app(self, app, **kwargs):
154157 # Add BDC-DB extension to Flask extension list
155158 app .extensions ['bdc-db' ] = self
156159
157- def init_db (self , app , entry_point_group : str = 'bdc_db.models' , engine_options = None , ** kwargs ):
160+ def init_db (self , app , entry_point_group : str = 'bdc_db.models' , engine_options = None , uri : str = None , ** kwargs ):
158161 """Initialize Flask-SQLAlchemy extension.
159162
160163 Args:
161164 app: Flask application
162165 entry_point_group: Entrypoint definition to load models
163166 engine_options: DB instance engine options
167+ uri: the URI to override SQLAlchemy database uri from environment. Defaults to env ``SQLALCHEMY_DATABASE_URI``.
164168 kwargs: optional Arguments to Flask-SQLAlchemy.
165169 """
170+ uri = uri or _config .SQLALCHEMY_DATABASE_URI
171+
166172 # Setup SQLAlchemy
167173 app .config .setdefault ('SQLALCHEMY_DATABASE_URI' ,
168- _config . SQLALCHEMY_DATABASE_URI )
174+ uri )
169175
170176 app .config .setdefault ('SQLALCHEMY_TRACK_MODIFICATIONS' ,
171177 _config .SQLALCHEMY_TRACK_MODIFICATIONS )
@@ -271,4 +277,18 @@ def register_scripts(self, module_name: str, trigger_name: str, path: str):
271277 """Register trigger command to BDC-DB."""
272278 self .scripts .setdefault (module_name , dict ())
273279
274- self .scripts [module_name ][trigger_name ] = path
280+ self .scripts [module_name ][trigger_name ] = path
281+
282+
283+ def resource_path (entry_name : str ):
284+ """Resolve the module path.
285+
286+ Note:
287+ This method returns None when not found or its invalid.
288+ """
289+ spec = find_spec (entry_name )
290+
291+ if spec and spec .submodule_search_locations :
292+ path = list (spec .submodule_search_locations )[0 ]
293+
294+ return str (path )
0 commit comments