33
44print (f"Launching CoperniFUS v{ version ('coperniFUS' )} " )
55
6- import sys , pathlib , trimesh , pymeshfix , copy , hashlib , base64 , warnings , re
6+ import sys , shutil , os , subprocess , pathlib , trimesh , pymeshfix , copy , hashlib , base64 , warnings , re
77import PyQt6 .QtGui as pyqtg
88import PyQt6 .QtCore as pyqtc
99import PyQt6 .QtWidgets as pyqtw
1010from si_prefix import si_format , si_parse
11+ from datetime import datetime
1112import pyqtgraph .opengl as gl
1213import numpy as np
1314
1718from coperniFUS .modules import _jsonshelve
1819
1920import coperniFUS
20- coperniFUS_location = coperniFUS .__file__
21+ coperniFUS_location = pathlib . Path ( coperniFUS .__file__ )
2122
2223def clean_string (string ):
2324 clean_string = '' .join (filter (str .isalnum , string ))
2425 return clean_string
2526
2627
28+
29+ class GitVersionTrackerInterface (object ):
30+
31+ GITHUB_BASE_URL = 'https://github.com/Tomaubier/CoperniFUS/' # commit/hash
32+
33+ def __init__ (self , repo_path ):
34+
35+ if shutil .which ('git' ) is None :
36+ raise ValueError ('Git command not found' )
37+
38+ self .repo_path = repo_path
39+
40+ def get_current_git_revision_hash (self ) -> str :
41+ return subprocess .check_output (
42+ ['git' , 'rev-parse' , 'HEAD' ],
43+ cwd = self .repo_path
44+ ).decode ('ascii' ).strip ()
45+
46+ def get_commit_datetime (self , commit_hash ):
47+ output = subprocess .check_output (
48+ ['git' , 'show' , '-s' , '--format=%cI' , commit_hash ],
49+ cwd = self .repo_path
50+ ).decode ().strip ()
51+ return datetime .fromisoformat (output )
52+
53+ def get_commit_message (self , commit_hash ):
54+ return subprocess .check_output (
55+ ['git' , 'show' , '-s' , '--format=%B' , commit_hash ],
56+ cwd = self .repo_path
57+ ).decode ('utf-8' ).strip ()
58+
59+
60+ class CacheUpdater (object ):
61+
62+ """
63+ Reference the breaking revisions that require a cache update in CACHE_UPDATER_PROCEDURES.
64+ """
65+
66+ CACHE_UPDATER_PROCEDURES = {
67+ # '2026-08-04T15:00:12+01:00': { # revision date using iso format
68+ # 'commit_hash': 'ad44071b61f825a77c9f12f762cdd89faab817ad',
69+ # 'commit_message': 'trimesh 2d path extrusion bug fix -> bumping mapbox_earcut to v2.0.0',
70+ # },
71+ }
72+
73+ def __init__ (self , cache_data_handler ):
74+ self .cache_data_handler = cache_data_handler
75+ self .git_handler = GitVersionTrackerInterface (coperniFUS_location .parent .parent )
76+
77+ self .cache_updater_procedures = {
78+ commit_datetime : {
79+ ** commit_update_dict ,
80+ 'cache_upgrade_function' : self ._get_updater_function (commit_update_dict ['commit_hash' ])}
81+ for (commit_datetime , commit_update_dict ) in self .CACHE_UPDATER_PROCEDURES .items ()
82+ if self ._is_updater_function_available (commit_update_dict ['commit_hash' ])
83+ }
84+
85+ self .check_for_cache_updates ()
86+
87+ def check_for_cache_updates (self ):
88+
89+ # Get the revision status of the configuration file being loaded
90+ self .cache_last_update_githash = self .cache_data_handler .get_attr (
91+ 'cache_last_update_githash' , default_value = None )
92+ self .cache_last_update_githash_datetime = self .cache_data_handler .get_attr (
93+ 'cache_last_update_githash_datetime' , default_value = None )
94+ if self .cache_last_update_githash is None :
95+ warnings .warn ('No git hash was found in this configuration file -> Assuming it was created using CoperniFUS v0.1.2' )
96+ self .cache_last_update_githash = '8c5f1cab59f03ffb4012c54b98d87270b6fc6320' # '2025-06-18 15:51:54' -> v0.1.2
97+ if self .cache_last_update_githash_datetime is None :
98+ self .cache_last_update_githash_datetime = self .git_handler .get_commit_datetime (
99+ self .cache_last_update_githash
100+ )
101+ else :
102+ self .cache_last_update_githash_datetime = datetime .fromisoformat (self .cache_last_update_githash_datetime ) # decode str -> datetime object
103+
104+ # Get the revision status of the CoperniFUS instance
105+ self .current_coperniFUS_revision_hash = self .git_handler .get_current_git_revision_hash ()
106+ self .current_coperniFUS_revision_datetime = self .git_handler .get_commit_datetime (
107+ self .current_coperniFUS_revision_hash
108+ )
109+
110+ # Outdated CoperniFUS instance
111+ if self .cache_last_update_githash_datetime > self .current_coperniFUS_revision_datetime :
112+ warning .warn (f'This configuration file appears to have been created using a more recent revision of CoperniFUS.\n \t -> Config. file { self .current_coperniFUS_revision_datetime } (git hash: { self .cache_last_update_githash } )\n \t -> CoperniFUS: { self .cache_last_update_githash_datetime } (git hash: { self .current_coperniFUS_revision_hash } )\n Some features might be missing in the version installed on this system. Please update CoperniFUS by pulling a newer version from its GitHub repo ({ self .git_handler .GITHUB_BASE_URL } ), or ignore this warning if you know what you are doing! :)' )
113+
114+ # Outdated cache file
115+ if self .cache_last_update_githash_datetime < self .current_coperniFUS_revision_datetime :
116+ available_cache_updates_procedures = self ._get_available_cache_updates (self .cache_last_update_githash_datetime )
117+
118+ # Check for updates available
119+ if len (available_cache_updates_procedures ) > 0 :
120+ legacy_cache_fpath = self .cache_data_handler .cache_dir / '_legacy_config_files' / f'{ self .cache_data_handler .cached_settings_fname .split ("." )[0 ]} _{ self .cache_last_update_githash } .json'
121+ os .makedirs (self .cache_data_handler .cache_dir / '_legacy_config_files' , exist_ok = True )
122+ shutil .copy (self .cache_data_handler .cached_settings_fpath , legacy_cache_fpath )
123+ print (f'\n > { len (available_cache_updates_procedures )} cache updates available -> the current version of the cache file has been backed-up to\n \t -> { str (legacy_cache_fpath )} ' )
124+
125+ for commit_datetime , commit_update_dict in available_cache_updates_procedures .items ():
126+ print (f'\n === Running { commit_datetime } cache update ({ commit_update_dict ['commit_message' ]} ) ===' )
127+ commit_update_dict ['cache_upgrade_function' ]()
128+
129+ # Udpate cache_last_update_githash is procedure successful
130+ self .cache_data_handler .set_attr ('cache_last_update_githash' , self .current_coperniFUS_revision_hash )
131+ self .cache_data_handler .set_attr ('cache_last_update_githash_datetime' , self .current_coperniFUS_revision_datetime .isoformat ())
132+
133+ def _get_updater_func_name (self , commit_hash ):
134+ return f'updater_func_{ commit_hash } '
135+
136+ def _is_updater_function_available (self , commit_hash ):
137+ if hasattr (self , self ._get_updater_func_name (commit_hash )):
138+ return True
139+ else :
140+ return False
141+
142+ def _get_updater_function (self , commit_hash ):
143+ return getattr (self , self ._get_updater_func_name (commit_hash ))
144+
145+ def _get_available_cache_updates (self , cache_last_update_datetime : datetime ):
146+ available_cache_update_procedures = {
147+ commit_datetime : commit_update_dict
148+ for commit_datetime , commit_update_dict in self .cache_updater_procedures .items ()
149+ if datetime .fromisoformat (commit_datetime ) > cache_last_update_datetime
150+ }
151+ return available_cache_update_procedures
152+
153+ # === ADD UPDATER FUNCTIONS HERE ===
154+
155+ def updater_func_ad44071b61f825a77c9f12f762cdd89faab817ad (self ):
156+ # implement cache update procedure here
157+ pass
158+
159+
27160class CachedDataHandler :
28161
29162 def __init__ (self , cache_dir_name = '.cachedDir' , cached_settings_fname = None ):
@@ -63,6 +196,12 @@ def __init__(self, cache_dir_name='.cachedDir', cached_settings_fname=None):
63196
64197 print (f'\n > Info: Cached configuration file location: { self .cached_settings_fpath } ' )
65198
199+ # === Run cache updater ===
200+ try :
201+ self ._updater = CacheUpdater (self )
202+ except Exception as e :
203+ warnings .warn (f'{ str (e )} -> Skipping cache version checks' )
204+
66205 def is_cached_filename_already_defined (self , cache_fname ):
67206 """ Checks the availibility of a cache_fname (regardless of the file extension). """
68207 directory_path = pathlib .Path (self .cache_dir )
@@ -74,7 +213,7 @@ def is_cached_filename_already_defined(self, cache_fname):
74213
75214 @property
76215 def cached_settings_fpath (self ):
77- """ FIle path of the cached file. """
216+ """ File path of the cached file. """
78217 return self .cache_dir / self .cached_settings_fname
79218
80219 def _attribute_str_id (self , attribute_id ):
0 commit comments