Skip to content

Commit 6672d0c

Browse files
committed
feat: interpret menu function file relative to json path
this allows application code to construct an absolute path to the shortcut menu json definition at runtime (using importlib.resources or relative to __file__) while the dynamically loaded python module path ('file' key) is assumed relative to the json path. If an absolute filepath is given in the menu data dict, that is used instead.
1 parent 1239e5e commit 6672d0c

File tree

1 file changed

+24
-16
lines changed

1 file changed

+24
-16
lines changed

NodeGraphQt/base/graph.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -775,14 +775,15 @@ def get_context_menu(self, menu):
775775
"""
776776
return self._context_menu.get(menu)
777777

778-
def _deserialize_context_menu(self, menu, menu_data):
778+
def _deserialize_context_menu(self, menu, menu_data, anchor_path=None):
779779
"""
780780
Populate context menu from a dictionary.
781781
782782
Args:
783783
menu (NodeGraphQt.NodeGraphMenu or NodeGraphQt.NodesMenu):
784784
parent context menu.
785785
menu_data (list[dict] or dict): serialized menu data.
786+
anchor_path (str or None): directory to interpret file paths relative to (optional)
786787
"""
787788
if not menu:
788789
raise ValueError('No context menu named: "{}"'.format(menu))
@@ -792,6 +793,10 @@ def _deserialize_context_menu(self, menu, menu_data):
792793

793794
nodes_menu = self.get_context_menu('nodes')
794795

796+
anchor = Path(anchor_path).resolve()
797+
if anchor.is_file():
798+
anchor = anchor.parent
799+
795800
def build_menu_command(menu, data):
796801
"""
797802
Create menu command from serialized data.
@@ -801,14 +806,16 @@ def build_menu_command(menu, data):
801806
menu object.
802807
data (dict): serialized menu command data.
803808
"""
804-
full_path = os.path.abspath(data['file'])
805-
base_dir, file_name = os.path.split(full_path)
806-
base_name = os.path.basename(base_dir)
807-
file_name, _ = file_name.split('.')
809+
func_path = Path(data['file'])
810+
if not func_path.is_absolute():
811+
func_path = anchor.joinpath(func_path)
812+
813+
base_name = func_path.parent.name
814+
file_name = func_path.stem
808815

809816
mod_name = '{}.{}'.format(base_name, file_name)
810817

811-
spec = importlib.util.spec_from_file_location(mod_name, full_path)
818+
spec = importlib.util.spec_from_file_location(mod_name, func_path)
812819
mod = importlib.util.module_from_spec(spec)
813820
sys.modules[mod_name] = mod
814821
spec.loader.exec_module(mod)
@@ -832,12 +839,12 @@ def build_menu_command(menu, data):
832839
elif item_type == 'menu':
833840
sub_menu = menu.add_menu(menu_data['label'])
834841
items = menu_data.get('items', [])
835-
self._deserialize_context_menu(sub_menu, items)
842+
self._deserialize_context_menu(sub_menu, items, anchor_path)
836843
elif isinstance(menu_data, list):
837844
for item_data in menu_data:
838-
self._deserialize_context_menu(menu, item_data)
845+
self._deserialize_context_menu(menu, item_data, anchor_path)
839846

840-
def set_context_menu(self, menu_name, data):
847+
def set_context_menu(self, menu_name, data, anchor_path=None):
841848
"""
842849
Populate a context menu from serialized data.
843850
@@ -875,11 +882,12 @@ def run_test(graph):
875882
Args:
876883
menu_name (str): name of the parent context menu to populate under.
877884
data (dict): serialized menu data.
885+
anchor_path (str or None): directory to interpret file paths relative to (optional)
878886
"""
879887
context_menu = self.get_context_menu(menu_name)
880-
self._deserialize_context_menu(context_menu, data)
888+
self._deserialize_context_menu(context_menu, data, anchor_path)
881889

882-
def set_context_menu_from_file(self, file_path, menu=None):
890+
def set_context_menu_from_file(self, file_path, menu='graph'):
883891
"""
884892
Populate a context menu from a serialized json file.
885893
@@ -892,16 +900,16 @@ def set_context_menu_from_file(self, file_path, menu=None):
892900
menu (str): name of the parent context menu to populate under.
893901
file_path (str): serialized menu commands json file.
894902
"""
895-
file_path = os.path.abspath(file_path)
903+
file = Path(file_path).resolve()
896904

897905
menu = menu or 'graph'
898-
if not os.path.isfile(file_path):
899-
raise IOError('file doesn\'t exists: "{}"'.format(file_path))
906+
if not file.is_file():
907+
raise IOError('file doesn\'t exist: "{}"'.format(file))
900908

901-
with open(file_path) as f:
909+
with file.open() as f:
902910
data = json.load(f)
903911
context_menu = self.get_context_menu(menu)
904-
self._deserialize_context_menu(context_menu, data)
912+
self._deserialize_context_menu(context_menu, data, file)
905913

906914
def disable_context_menu(self, disabled=True, name='all'):
907915
"""

0 commit comments

Comments
 (0)