1111# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212# See the License for the specific language governing permissions and
1313# limitations under the License.
14-
14+ import inspect
15+ import logging
16+ import os
17+ import sys
18+ from types import ModuleType
1519
1620import objectbox .c as c
1721import objectbox .transaction
22+ from objectbox .model .idsync import sync_model
1823from objectbox .store_options import StoreOptions
1924import objectbox
2025from objectbox .model .entity import _Entity
26+ from objectbox .model .model import Model
2127from typing import *
2228
29+
2330class Store :
24- def __init__ (self ,
25- model : Optional [objectbox .model .Model ] = None ,
26- directory : Optional [str ] = None ,
27- max_db_size_in_kb : Optional [int ] = None ,
31+ def __init__ (self ,
32+ model : Optional [Union [Model , str ]] = "default" ,
33+ model_json_file : Optional [str ] = None ,
34+ directory : Optional [str ] = None ,
35+ max_db_size_in_kb : Optional [int ] = None ,
2836 max_data_size_in_kb : Optional [int ] = None ,
2937 file_mode : Optional [int ] = None ,
3038 max_readers : Optional [int ] = None ,
@@ -46,8 +54,8 @@ def __init__(self,
4654 async_minor_refill_max_count : Optional [int ] = None ,
4755 async_object_bytes_max_cache_size : Optional [int ] = None ,
4856 async_object_bytes_max_size_to_cache : Optional [int ] = None ,
49- c_store : Optional [c .OBX_store_p ] = None ):
50-
57+ c_store : Optional [c .OBX_store_p ] = None ):
58+
5159 """Opens an ObjectBox database Store
5260
5361 :param model:
@@ -107,13 +115,14 @@ def __init__(self,
107115 Maximum size for an object to be cached.
108116 :param c_store:
109117 Internal parameter for deprecated ObjectBox interface. Do not use it; other options would be ignored if passed.
110- """
111-
118+ """
119+
112120 self ._c_store = None
113121 if not c_store :
114122 options = StoreOptions ()
115123 try :
116124 if model is not None :
125+ model = Store ._sync_model (model , model_json_file )
117126 options .model (model )
118127 if directory is not None :
119128 options .directory (directory )
@@ -160,18 +169,77 @@ def __init__(self,
160169 if async_object_bytes_max_cache_size is not None :
161170 options .async_object_bytes_max_cache_size (async_object_bytes_max_cache_size )
162171 if async_object_bytes_max_size_to_cache is not None :
163- options .async_object_bytes_max_size_to_cache (async_object_bytes_max_size_to_cache )
164-
172+ options .async_object_bytes_max_size_to_cache (async_object_bytes_max_size_to_cache )
173+
165174 except c .CoreException :
166175 options ._free ()
167176 raise
168- self ._c_store = c .obx_store_open (options ._c_handle )
177+ self ._c_store = c .obx_store_open (options ._c_handle )
169178 else :
170179 self ._c_store = c_store
171180
181+ @staticmethod
182+ def _sync_model (model : Optional [Union [Model , str ]],
183+ model_json_file : Optional [str ]) -> Model :
184+ if isinstance (model , str ): # Model name provided; get entities collected via @Entity
185+ metadata_set = objectbox .model .entity .obx_models_by_name .get (model )
186+ if metadata_set is None :
187+ raise ValueError (
188+ f"Model \" { model } \" not found; ensure to set the name attribute on the model class." )
189+ model = Model ()
190+ for metadata in metadata_set :
191+ model .entity (metadata )
192+ elif not isinstance (model , Model ):
193+ raise ValueError ("Model must be a Model object or a string." )
194+
195+ if not model_json_file :
196+ model_json_file = Store ._locate_model_json_file ()
197+
198+ sync_model (model , model_json_file )
199+
200+ return model
201+
202+ @staticmethod
203+ def _locate_model_json_file ():
204+ def get_module_path (module : Optional [ModuleType ]) -> Optional [str ]:
205+ if module and hasattr (module , "__file__" ):
206+ return os .path .dirname (os .path .realpath (module .__file__ ))
207+ return None
208+
209+ def json_file_inside_module_path (module : Optional [ModuleType ]) -> Optional [str ]:
210+ module_path = get_module_path (module )
211+ if module_path :
212+ logging .info ("Using module path to locate objectbox-model.json: " , module_path )
213+ return os .path .join (module_path , "objectbox-model.json" )
214+ return None
215+
216+ # The (direct) calling module seems like a good first choice
217+ this_module = sys .modules [__name__ ]
218+ this_module_path = get_module_path (this_module )
219+ stack = inspect .stack ()
220+ calling_module : Optional [ModuleType ] = None
221+ for stack_element in stack :
222+ module = inspect .getmodule (stack_element [0 ])
223+ if module is not this_module :
224+ path = get_module_path (module )
225+ if not path : # Cannot get the direct caller's path, so do not try further
226+ break
227+ if path != this_module_path : # Not inside the objectbox package
228+ calling_module = module
229+ break
230+ model_json_file = json_file_inside_module_path (calling_module )
231+
232+ if not model_json_file :
233+ # Note: the main module seems less reliable,
234+ # e.g. it resulted in a some pycharm dir when running tests from PyCharm.
235+ model_json_file = json_file_inside_module_path (sys .modules .get ('__main__' ))
236+ if not model_json_file :
237+ model_json_file = "objectbox-model.json"
238+ return model_json_file
239+
172240 def __del__ (self ):
173241 self .close ()
174-
242+
175243 def box (self , entity : _Entity ) -> 'objectbox.Box' :
176244 """
177245 Open a box for an entity.
0 commit comments