From 2b692432c2611e21144f6d27762e4211756a3853 Mon Sep 17 00:00:00 2001 From: Joseph HENRY Date: Mon, 1 Dec 2025 15:34:07 +0100 Subject: [PATCH] black format --- .python-version | 1 + Types.py | 209 ++++++++++++++--------------- __init__.py | 31 +++-- functions.py | 197 ++++++++++++++++------------ operators.py | 342 +++++++++++++++++++++++++++++++----------------- properties.py | 54 ++++---- pyproject.toml | 12 ++ ui.py | 5 +- utils.py | 227 +++++++++++++++++++------------- uv.lock | 113 ++++++++++++++++ 10 files changed, 739 insertions(+), 452 deletions(-) create mode 100644 .python-version create mode 100644 pyproject.toml create mode 100644 uv.lock diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/Types.py b/Types.py index 8be72be..5905656 100644 --- a/Types.py +++ b/Types.py @@ -5,82 +5,75 @@ from bpy.props import * from .properties import CustomShelfProps -class Types(): - def initialyse(self, label) : - package = __package__.replace('_','').replace('-','').lower() - idname = label.replace(' ','_').replace('-','_').lower() +class Types: + def initialyse(self, label): + package = __package__.replace("_", "").replace("-", "").lower() + idname = label.replace(" ", "_").replace("-", "_").lower() self.bl_label = title(label) - self.bl_idname = '%s.%s'%(package, idname) - - #self.bl_props = bpy.context.scene.CustomShelf - #self.prefs = "" - + self.bl_idname = "%s.%s" % (package, idname) + # self.bl_props = bpy.context.scene.CustomShelf + # self.prefs = "" class CSHELF_OT_shelf_popup(Types, Operator): - #props = None - bl_options = {'REGISTER', 'UNDO'} + # props = None + bl_options = {"REGISTER", "UNDO"} @property - def props(self) : - #print(getattr(bpy.context.window_manager.CustomShelf,self.prop)) + def props(self): + # print(getattr(bpy.context.window_manager.CustomShelf,self.prop)) return getattr(bpy.context.window_manager.CustomShelf, self.prop) def get_props(self): properties = self.props.bl_rna.properties - #props = - return {k:v for k,v in self.args.items() if k in properties} + # props = + return {k: v for k, v in self.args.items() if k in properties} - def set_props(self,prop,value): - setattr(bpy.context.window_manager.CustomShelf,prop,value) + def set_props(self, prop, value): + setattr(bpy.context.window_manager.CustomShelf, prop, value) - def initialyse(self,label,info) : - super().initialyse(CSHELF_OT_shelf_popup,label) + def initialyse(self, label, info): + super().initialyse(CSHELF_OT_shelf_popup, label) self.args = dic_to_args(info) - - Props = type("props",(PropertyGroup,),{'__annotations__' : dict(self.args)}) + Props = type("props", (PropertyGroup,), {"__annotations__": dict(self.args)}) bpy.utils.register_class(Props) setattr(CustomShelfProps, arg_name(label), PointerProperty(type=Props)) - #self.props = getattr(bpy.context.window_manager,self.bl_idname) + # self.props = getattr(bpy.context.window_manager,self.bl_idname) self.info = info self.prop = arg_name(label) + # print(self.args) + # properties = self.get_props().bl_rna.properties + # props = {k:v for k,v in self.args.items() if k in properties or hasattr(v,"bl_idname")} - #print(self.args) - - - - #properties = self.get_props().bl_rna.properties - #props = {k:v for k,v in self.args.items() if k in properties or hasattr(v,"bl_idname")} - - return #props + return # props def invoke(self, context, event): - if event.alt : - for t in [t for t in bpy.data.texts if t.filepath == self.script] : + if event.alt: + for t in [t for t in bpy.data.texts if t.filepath == self.script]: bpy.data.texts.remove(t) text = bpy.data.texts.load(self.script) areas = [] text_editor = None - for area in context.screen.areas : - if area.type == 'TEXT_EDITOR' : + for area in context.screen.areas: + if area.type == "TEXT_EDITOR": text_editor = area - else : + else: areas.append(area) - if not text_editor : - bpy.ops.screen.area_split(direction = "VERTICAL") + if not text_editor: + bpy.ops.screen.area_split(direction="VERTICAL") - for area in context.screen.areas : - if area not in areas : + for area in context.screen.areas: + if area not in areas: text_editor = area text_editor.type = "TEXT_EDITOR" text_editor.spaces[0].show_syntax_highlight = True @@ -88,82 +81,77 @@ class CSHELF_OT_shelf_popup(Types, Operator): text_editor.spaces[0].show_line_numbers = True context_copy = context.copy() - context_copy['area'] = text_editor - #bpy.ops.text.properties(context_copy) - #bpy.ops.view3d.toolshelf(context_copy) + context_copy["area"] = text_editor + # bpy.ops.text.properties(context_copy) + # bpy.ops.view3d.toolshelf(context_copy) text_editor.spaces[0].text = text return {"FINISHED"} - else : - if self.args : - #set default value for collection - for k,v in self.info.items() : - if isinstance(v,dict) and v.get('collection') : + else: + if self.args: + # set default value for collection + for k, v in self.info.items(): + if isinstance(v, dict) and v.get("collection"): collection = getattr(bpy.data, id_type[v["collection"]]) - setattr(self.props, k,collection.get(v["value"])) - - + setattr(self.props, k, collection.get(v["value"])) return context.window_manager.invoke_props_dialog(self) - else : + else: return self.execute(context) - - def bl_report(self,*args) : - if len(args) and args[0] in ('INFO','ERROR', 'WARNING') : - return self.report({args[0]},' '.join(args[1:])) - else : + def bl_report(self, *args): + if len(args) and args[0] in ("INFO", "ERROR", "WARNING"): + return self.report({args[0]}, " ".join(args[1:])) + else: print(*args) - def execute(self,context): + def execute(self, context): info, lines = read_info(self.script) values = dict(info) - for arg in self.args : - value = getattr(self.props,arg) + for arg in self.args: + value = getattr(self.props, arg) - if hasattr(value,"name") : - values[arg] = {'value': value.name} + if hasattr(value, "name"): + values[arg] = {"value": value.name} - elif isinstance(info.get(arg),dict) : - values[arg] = {'value': value} + elif isinstance(info.get(arg), dict): + values[arg] = {"value": value} - else : + else: values[arg] = value - info_str = "info = %s\n\n"%values - lines_str = '\n'.join(lines) + info_str = "info = %s\n\n" % values + lines_str = "\n".join(lines) script = f"{info_str}\n\n{lines_str}" - exec(script, {'info': values, 'print': self.bl_report}) + exec(script, {"info": values, "print": self.bl_report}) - return {'FINISHED'} + return {"FINISHED"} - def draw(self,context) : + def draw(self, context): layout = self.layout bl_props = context.scene.CustomShelf - #print(self.get_props()) + # print(self.get_props()) - for k in self.args : - #filter + for k in self.args: + # filter - row = layout.row(align = True) + row = layout.row(align=True) row.label(text=title(k)) - row.prop(self.props,k,text='') - #row.prop_search(self.props,k,self.props,k+"_prop_search",text='') - #op = self.args[k+"_prop_search"] - #row.operator(op.bl_idname,text="",icon ="COLLAPSEMENU" ) + row.prop(self.props, k, text="") + # row.prop_search(self.props,k,self.props,k+"_prop_search",text='') + # op = self.args[k+"_prop_search"] + # row.operator(op.bl_idname,text="",icon ="COLLAPSEMENU" ) - - def check(self,context) : + def check(self, context): return True - """ class ShelfPropSearch(Types,Operator): bl_property = "enum" @@ -194,61 +182,60 @@ class ShelfPropSearch(Types,Operator): """ -class CSHELF_PT_shelf_panel(Types,Panel): +class CSHELF_PT_shelf_panel(Types, Panel): bl_space_type = "VIEW_3D" bl_region_type = "UI" - #bl_category = "SHELF" + # bl_category = "SHELF" settings = {} scripts = [] @staticmethod - def search_filter(search,str) : + def search_filter(search, str): return search.lower() in str.lower() @classmethod def poll(self, context): - bl_props = getattr(context.scene.CustomShelf,self.cat) + bl_props = getattr(context.scene.CustomShelf, self.cat) prefs = bpy.context.preferences.addons[__package__].preferences search = bl_props.search - shelf = [self.search_filter(search,s["text"]) for s in self.scripts] + shelf = [self.search_filter(search, s["text"]) for s in self.scripts] - #print("settings",self.settings) + # print("settings",self.settings) is_taged = True - if self.settings and self.settings.get('tags') : + if self.settings and self.settings.get("tags"): tag = prefs.tag_filter - if not tag : + if not tag: tag = os.getenv("CUSTOM_SHELF_TAG") - is_taged = tag in self.settings['tags'] + is_taged = tag in self.settings["tags"] return any(shelf) and is_taged - - def draw(self,context) : + def draw(self, context): layout = self.layout - bl_props = getattr(context.scene.CustomShelf,self.cat) + bl_props = getattr(context.scene.CustomShelf, self.cat) search = bl_props.search - for script in self.scripts : + for script in self.scripts: args = script.copy() - if not self.search_filter(search,script["text"]) : continue + if not self.search_filter(search, script["text"]): + continue - if bl_props.filter and script["text"].startswith('_') : continue + if bl_props.filter and script["text"].startswith("_"): + continue args["text"] = title(script["text"]) - #print(script) + # print(script) layout.operator(**args) - class CSHELF_PT_shelf_header(Panel): bl_label = "Custom Shelf" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' + bl_space_type = "VIEW_3D" + bl_region_type = "UI" bl_options = {"HIDE_HEADER"} - def draw(self, context): layout = self.layout bl_props = bpy.context.scene.CustomShelf @@ -256,16 +243,20 @@ class CSHELF_PT_shelf_header(Panel): cat_props = getattr(bl_props, self.cat) - row = layout.row(align = True) + row = layout.row(align=True) - row.operator_menu_enum("customshelf.set_tag_filter",'tag_filter',icon= "DOWNARROW_HLT",text='') - row.operator("customshelf.refresh", text='',emboss=True,icon= "FILE_REFRESH") - #row.separator() + row.operator_menu_enum( + "customshelf.set_tag_filter", "tag_filter", icon="DOWNARROW_HLT", text="" + ) + row.operator("customshelf.refresh", text="", emboss=True, icon="FILE_REFRESH") + # row.separator() - row.prop(cat_props, "filter",text='',emboss=True,icon= "FILTER") - #row.separator() + row.prop(cat_props, "filter", text="", emboss=True, icon="FILTER") + # row.separator() - row.prop(cat_props,"search",icon = "VIEWZOOM",text='') - #row.separator() + row.prop(cat_props, "search", icon="VIEWZOOM", text="") + # row.separator() - row.operator("customshelf.open_shelf_folder", text='',emboss=True,icon= "FILE").path = self.path + row.operator( + "customshelf.open_shelf_folder", text="", emboss=True, icon="FILE" + ).path = self.path diff --git a/__init__.py b/__init__.py index 98fd004..7181dd7 100644 --- a/__init__.py +++ b/__init__.py @@ -8,23 +8,24 @@ bl_info = { "doc_url": "https://gitlab.com/autour-de-minuit/blender/custom_shelf/-/blob/main/README.md", "tracker_url": "https://gitlab.com/autour-de-minuit/blender/custom_shelf/-/issues", "support": "COMMUNITY", - "category": "User"} - + "category": "User", +} if "bpy" in locals(): import importlib + importlib.reload(operators) importlib.reload(panels) importlib.reload(functions) importlib.reload(properties) -from . utils import report -from . functions import read_shelves +from .utils import report +from .functions import read_shelves from . import operators from . import properties from . import ui -from .properties import CustomShelfSettings,CustomShelfProps +from .properties import CustomShelfSettings, CustomShelfProps import bpy import os @@ -41,19 +42,22 @@ bl_classes = [ operators.CSHELF_OT_open_shelf_folder, operators.CSHELF_OT_add_script, operators.CSHELF_OT_set_tag_filter, - ui.CSHELF_MT_text_editor + ui.CSHELF_MT_text_editor, ] + def draw_menu(self, context): - self.layout.menu('CSHELF_MT_text_editor') + self.layout.menu("CSHELF_MT_text_editor") def register(): - for bl_class in bl_classes : + for bl_class in bl_classes: bpy.utils.register_class(bl_class) - + bpy.types.Scene.CustomShelf = bpy.props.PointerProperty(type=CustomShelfSettings) - bpy.types.WindowManager.CustomShelf = bpy.props.PointerProperty(type=CustomShelfProps) + bpy.types.WindowManager.CustomShelf = bpy.props.PointerProperty( + type=CustomShelfProps + ) bpy.types.TEXT_MT_editor_menus.append(draw_menu) @@ -72,10 +76,11 @@ def register(): read_shelves() + def unregister(): # unregister panel : - for panel in CustomShelfSettings.panel_list : - try : + for panel in CustomShelfSettings.panel_list: + try: bpy.utils.unregister_class(panel) except Exception: pass @@ -84,5 +89,5 @@ def unregister(): del bpy.types.Scene.CustomShelf del bpy.types.WindowManager.CustomShelf - for bl_class in bl_classes : + for bl_class in bl_classes: bpy.utils.unregister_class(bl_class) diff --git a/functions.py b/functions.py index f5fad7b..818258e 100644 --- a/functions.py +++ b/functions.py @@ -1,197 +1,222 @@ from .utils import * from bpy.props import * -from bpy.types import Panel,Operator -from .properties import CustomShelfSettings,CustomShelfPrefs +from bpy.types import Panel, Operator +from .properties import CustomShelfSettings, CustomShelfPrefs from .Types import * -SHELF_DIR = join(dirname(__file__),'shelves') +SHELF_DIR = join(dirname(__file__), "shelves") -def operator(idname,args) : - op_execute = args['execute'] +def operator(idname, args): + op_execute = args["execute"] + def execute(self, context): - op_execute(context,self) + op_execute(context, self) return {"FINISHED"} - package = __package__.replace('_','').replace('-','').lower() + package = __package__.replace("_", "").replace("-", "").lower() - args['bl_idname'] = "%s.%s"%(package,idname) - args['bl_label'] = title(idname) - args['execute'] = execute + args["bl_idname"] = "%s.%s" % (package, idname) + args["bl_label"] = title(idname) + args["execute"] = execute - op = type('operator',(Operator,),args) + op = type("operator", (Operator,), args) bpy.utils.register_class(op) return op -def register_bl_class(bl_class,args) : - a = type('test',(bl_class,),{}) - a.initialyse(a,*args) + +def register_bl_class(bl_class, args): + a = type("test", (bl_class,), {}) + a.initialyse(a, *args) bpy.utils.register_class(a) return a -def filter_dir(dir_list) : - return [d for d in dir_list if d.is_dir() and not d.name.startswith('_')] + +def filter_dir(dir_list): + return [d for d in dir_list if d.is_dir() and not d.name.startswith("_")] + def get_shelves_folder(): prefs = bpy.context.preferences.addons[__package__].preferences shelves_path = prefs.global_shelves additionnal_shelves_path = [s.path for s in prefs.additionnal_shelves] - return [p for p in additionnal_shelves_path+[shelves_path] if exists(p)] + return [p for p in additionnal_shelves_path + [shelves_path] if exists(p)] - -def get_dirs(paths) : - if isinstance(paths,(tuple,list)) : +def get_dirs(paths): + if isinstance(paths, (tuple, list)): dirs = [] for dir in paths: - if not isinstance(dir, str) : + if not isinstance(dir, str): dir = dir.path dirs += filter_dir(scandir(dir)) return dirs - else : + else: return filter_dir(scandir(paths)) + def get_categories(): return [d.name for d in get_dirs(get_shelves_folder())] -def get_tabs() : + +def get_tabs(): return [d.name for d in get_dirs(get_dirs(get_shelves_folder()))] -def get_category_path(category) : - category_dir = [f for f in get_shelves_folder() if category in [d.name for d in get_dirs(f)]] - if category_dir : - return join(category_dir[0],category) + +def get_category_path(category): + category_dir = [ + f for f in get_shelves_folder() if category in [d.name for d in get_dirs(f)] + ] + if category_dir: + return join(category_dir[0], category) + def get_category_tabs(category): return [d.name for d in get_dirs(get_category_path(category))] -def read_shelves() : +def read_shelves(): tag_filter_items = [] # unregister panel : for panel in CustomShelfSettings.panel_list: print(panel) if panel.__name__ not in get_tabs(): - try : + try: bpy.utils.unregister_class(panel) except Exception: pass CustomShelfSettings.panel_list.remove(panel) - - for cat in get_dirs(get_shelves_folder()) : - cat_name = cat.name.replace('_',' ').title() + for cat in get_dirs(get_shelves_folder()): + cat_name = cat.name.replace("_", " ").title() header_info = { - "bl_region_type" : "UI",#petit ajout pour voir si foncitonne... - "bl_category" : cat_name, - "bl_idname" : "%s_PT_%s_header"%(__package__.upper(),cat.name),# -> "%s.%s_header" makes _PT_ warning - "cat" : cat.name, - "path" : cat.path + "bl_region_type": "UI", # petit ajout pour voir si foncitonne... + "bl_category": cat_name, + "bl_idname": "%s_PT_%s_header" + % (__package__.upper(), cat.name), # -> "%s.%s_header" makes _PT_ warning + "cat": cat.name, + "path": cat.path, } - header = type("Header",(CSHELF_PT_shelf_header,),header_info) + header = type("Header", (CSHELF_PT_shelf_header,), header_info) bpy.utils.register_class(header) - - #enable bool prefs + # enable bool prefs # panel_props = {t.name : BoolProperty(default = False) for t in get_dirs(cat.path)}# give "should be an annotation" field warning ## need annoattion type : https://blender.stackexchange.com/questions/118118/blender-2-8-field-property-declaration-and-dynamic-class-creation - panel_props = {'__annotations__': {t.name : BoolProperty(default = False) for t in get_dirs(cat.path)}} - PanelProps = type("CustomShelfPrefs",(PropertyGroup,),panel_props) + panel_props = { + "__annotations__": { + t.name: BoolProperty(default=False) for t in get_dirs(cat.path) + } + } + PanelProps = type("CustomShelfPrefs", (PropertyGroup,), panel_props) bpy.utils.register_class(PanelProps) setattr(CustomShelfPrefs, cat.name, PointerProperty(type=PanelProps)) - #search and filter + # search and filter props = { - "search" : StringProperty(options = {"TEXTEDIT_UPDATE"}), - "filter" : BoolProperty(default = True) + "search": StringProperty(options={"TEXTEDIT_UPDATE"}), + "filter": BoolProperty(default=True), } # Props = type("props",(PropertyGroup,), props)# annotation error - Props = type("props",(PropertyGroup,),{'__annotations__': props}) + Props = type("props", (PropertyGroup,), {"__annotations__": props}) bpy.utils.register_class(Props) - setattr(CustomShelfSettings,cat.name,PointerProperty(type = Props)) + setattr(CustomShelfSettings, cat.name, PointerProperty(type=Props)) - #enable bool prefs - #Prefs = type("CustomShelfPrefs",(PropertyGroup,),{}) - #bpy.utils.register_class(Prefs) + # enable bool prefs + # Prefs = type("CustomShelfPrefs",(PropertyGroup,),{}) + # bpy.utils.register_class(Prefs) - #setattr(CustomShelfPrefs,cat.name,PointerProperty(type = Prefs)) + # setattr(CustomShelfPrefs,cat.name,PointerProperty(type = Prefs)) - for tab in get_dirs(cat.path) : - #get_settings - settings_json = join(tab.path,'settings.json') + for tab in get_dirs(cat.path): + # get_settings + settings_json = join(tab.path, "settings.json") tab_settings = {} - if exists(settings_json) : + if exists(settings_json): tab_settings = read_json(settings_json) - if not tab_settings or not tab_settings.get('tags') : continue - for tag in tab_settings['tags'] : - if tag not in tag_filter_items : + if not tab_settings or not tab_settings.get("tags"): + continue + for tag in tab_settings["tags"]: + if tag not in tag_filter_items: tag_filter_items.append(tag) panel_info = { - "bl_region_type" : "UI",#Added, maybe need it in 2.8+... - "bl_category" : cat_name, - "bl_idname" : "%s_PT_%s_%s"%(__package__.upper(),cat.name,tab.name),#"%s.%s_%s" -> makes _PT_ warning - "bl_label" : tab.name.replace('_',' ').title(), - "cat" : cat.name, - "settings" :tab_settings + "bl_region_type": "UI", # Added, maybe need it in 2.8+... + "bl_category": cat_name, + "bl_idname": "%s_PT_%s_%s" + % ( + __package__.upper(), + cat.name, + tab.name, + ), # "%s.%s_%s" -> makes _PT_ warning + "bl_label": tab.name.replace("_", " ").title(), + "cat": cat.name, + "settings": tab_settings, } - space_info = {k:v for k,v in tab_settings.items() if k in ("bl_space_type","bl_region_type")} + space_info = { + k: v + for k, v in tab_settings.items() + if k in ("bl_space_type", "bl_region_type") + } panel_info.update(space_info) - panel = type("Panel",(CSHELF_PT_shelf_panel,),panel_info) + panel = type("Panel", (CSHELF_PT_shelf_panel,), panel_info) bpy.utils.register_class(panel) CustomShelfSettings.panel_list.append(panel) scripts = [] - for script in scandir(tab.path) : - if not script.name.endswith('.py'): + for script in scandir(tab.path): + if not script.name.endswith(".py"): continue print(script.path) script_name = splitext(script.name)[0] - info,lines = read_info(script.path) + info, lines = read_info(script.path) - info["description"] = info.get('description',"") + info["description"] = info.get("description", "") - icon = info.get('icon', "WORDWRAP_OFF") + icon = info.get("icon", "WORDWRAP_OFF") - popup = type(script_name, (CSHELF_OT_shelf_popup,), - {"script": script.path, - 'bl_description': info["description"] - }) + popup = type( + script_name, + (CSHELF_OT_shelf_popup,), + {"script": script.path, "bl_description": info["description"]}, + ) popup.initialyse(popup, script_name, info) bpy.utils.register_class(popup) - scripts.append({"operator" : popup.bl_idname,"text" : script_name,"icon" : icon}) + scripts.append( + {"operator": popup.bl_idname, "text": script_name, "icon": icon} + ) - panel.scripts = sorted(scripts,key = lambda x :x['text']) + panel.scripts = sorted(scripts, key=lambda x: x["text"]) - #Register tag filter enum - #setattr(Pre, v) + # Register tag filter enum + # setattr(Pre, v) prefs = bpy.context.preferences.addons[__package__].preferences tag_filter_items.sort() - tag_filter_items.insert(0, '__clear__') + tag_filter_items.insert(0, "__clear__") - prefs['tag_filter_items'] = tag_filter_items + prefs["tag_filter_items"] = tag_filter_items - #bpy.utils.unregister_class(CustomShelfPrefs) - #bpy.utils.register_class(CustomShelfPrefs) - #CustomShelfSettings.folders = list(get_shelves_folder().keys()) + # bpy.utils.unregister_class(CustomShelfPrefs) + # bpy.utils.register_class(CustomShelfPrefs) + # CustomShelfSettings.folders = list(get_shelves_folder().keys()) - #folder_enum = EnumProperty(items = lambda s,c : [(f,f,"") for f in CustomShelfSettings.folders]) - #setattr(CustomShelfSettings,'folders_enum',folder_enum) + # folder_enum = EnumProperty(items = lambda s,c : [(f,f,"") for f in CustomShelfSettings.folders]) + # setattr(CustomShelfSettings,'folders_enum',folder_enum) diff --git a/operators.py b/operators.py index d4d2aae..e6b48fb 100644 --- a/operators.py +++ b/operators.py @@ -7,232 +7,323 @@ from .properties import CustomShelfSettings class CSHELF_OT_refresh(Operator): bl_idname = "customshelf.refresh" - bl_label = 'Refresh Shelves' + bl_label = "Refresh Shelves" - def execute(self,context) : + def execute(self, context): read_shelves() - return {'FINISHED'} + return {"FINISHED"} -def get_tag_items(self,context) : +def get_tag_items(self, context): prefs = bpy.context.preferences.addons[__package__].preferences - return [(i,i.title(),"") for i in prefs['tag_filter_items']] + return [(i, i.title(), "") for i in prefs["tag_filter_items"]] + class CSHELF_OT_set_tag_filter(Operator): bl_idname = "customshelf.set_tag_filter" - bl_label = 'Refresh Shelves' + bl_label = "Refresh Shelves" - tag_filter : EnumProperty(items = get_tag_items) + tag_filter: EnumProperty(items=get_tag_items) def execute(self, context): prefs = bpy.context.preferences.addons[__package__].preferences tag_filter = self.tag_filter - if tag_filter == '__clear__' : + if tag_filter == "__clear__": tag_filter = "" prefs.tag_filter = tag_filter - return {'FINISHED'} + return {"FINISHED"} class CSHELF_OT_add_shelf_folder(Operator): bl_idname = "customshelf.add_shelves_folder" - bl_label = 'Refresh Shelves' + bl_label = "Refresh Shelves" def execute(self, context): prefs = bpy.context.preferences.addons[__package__].preferences path = prefs.additionnal_shelves.add() - return {'FINISHED'} + return {"FINISHED"} + class CSHELF_OT_remove_shelf_folder(Operator): bl_idname = "customshelf.remove_shelves_folder" - bl_label = 'Refresh Shelves' + bl_label = "Refresh Shelves" - index : IntProperty() + index: IntProperty() def execute(self, context): prefs = bpy.context.preferences.addons[__package__].preferences prefs.additionnal_shelves.remove(self.index) - return {'FINISHED'} - - + return {"FINISHED"} class CSHELF_OT_open_shelf_folder(Operator): bl_idname = "customshelf.open_shelf_folder" - bl_label = 'Run Function' + bl_label = "Run Function" - path : StringProperty() + path: StringProperty() - def execute(self,context) : + def execute(self, context): open_folder(self.path) - return {'FINISHED'} + return {"FINISHED"} -class CSHELF_OT_add_script(Operator) : +class CSHELF_OT_add_script(Operator): bl_idname = "customshelf.add_script" - bl_label = 'Add script to a shelf' + bl_label = "Add script to a shelf" - add_category : BoolProperty() - add_tab : BoolProperty() - new_category : StringProperty() - new_tab : StringProperty() + add_category: BoolProperty() + add_tab: BoolProperty() + new_category: StringProperty() + new_tab: StringProperty() - name : StringProperty() - description : StringProperty() - icon : StringProperty() - show_icons : BoolProperty() + name: StringProperty() + description: StringProperty() + icon: StringProperty() + show_icons: BoolProperty() @classmethod def poll(cls, context): - return context.area.type == 'TEXT_EDITOR' and context.space_data.text + return context.area.type == "TEXT_EDITOR" and context.space_data.text - def add_folder(self,context,op) : + def add_folder(self, context, op): folder = self.folder.add() folder.name = self.name - #CustomShelfSettings + # CustomShelfSettings - def remove_folder(self,context,op) : + def remove_folder(self, context, op): bl_props = context.scene.CustomShelf index = self.folders.find(self.folders_enum) self.folders.remove(index) - def get_all_icons (self) : + def get_all_icons(self): ui_layout = bpy.types.UILayout icons = ui_layout.bl_rna.functions["prop"].parameters["icon"].enum_items.keys() - prefixes = ('BRUSH_','MATCAP_','COLORSET_') - exception = ('BRUSH_DATA') + prefixes = ("BRUSH_", "MATCAP_", "COLORSET_") + exception = "BRUSH_DATA" return [i for i in icons if not i.startswith(prefixes) or i in exception] - def get_icons(self,context,op) : + def get_icons(self, context, op): icons = [ - ["SCENE_DATA","RENDERLAYERS","MATERIAL_DATA","GROUP_UVS","TEXTURE","WORLD","SPEAKER","TEXT","NODETREE","NODE_INSERT_OFF","PARTICLES","SORTALPHA"], - ["MODIFIER","MOD_WAVE","MOD_SUBSURF","MOD_FLUIDSIM","MOD_OCEAN","BLANK1", - "ARMATURE_DATA","BONE_DATA","GROUP_BONE"], - ["SEQUENCE","CAMERA_DATA","SCENE","BLANK1", - "FILE_NEW","CONSOLE","BLENDER","APPEND_BLEND"], - ["GROUP","MESH_CUBE","MESH_PLANE","MESH_CIRCLE","MESH_UVSPHERE","MESH_GRID","EMPTY_DATA","OUTLINER_DATA_MESH", - "LIGHT_SUN","LIGHT_SPOT","LIGHT"], - ["TRIA_RIGHT_BAR","REC","PLAY","PREV_KEYFRAME","NEXT_KEYFRAME","PAUSE","X","ADD","REMOVE","RESTRICT_VIEW_OFF","RESTRICT_VIEW_ON","RESTRICT_SELECT_OFF",], - ["BRUSH_DATA","GREASEPENCIL","LINE_DATA","PARTICLEMODE","SCULPTMODE_HLT","WPAINT_HLT","TPAINT_HLT","VIEWZOOM","HAND","KEY_HLT","KEY_DEHLT",], - ["PLUGIN","SCRIPT","PREFERENCES", - "ACTION","SOLO_OFF", - "RNDCURVE","SNAP_ON", - "FORCE_WIND","COPY_ID","EYEDROPPER","AUTO","UNLOCKED","LOCKED", - "UNPINNED","PINNED","PLUGIN","HELP","GHOST_ENABLED","GHOST_DISABLED","COLOR", - "LINKED","UNLINKED","LINKED","ZOOM_ALL", - "FREEZE","STYLUS_PRESSURE","FILE_TICK", - "QUIT","RECOVER_LAST","TIME","PREVIEW_RANGE", - "OUTLINER","NLA","EDITMODE_HLT", - "BOIDS","RNA","CAMERA_STEREO"], + [ + "SCENE_DATA", + "RENDERLAYERS", + "MATERIAL_DATA", + "GROUP_UVS", + "TEXTURE", + "WORLD", + "SPEAKER", + "TEXT", + "NODETREE", + "NODE_INSERT_OFF", + "PARTICLES", + "SORTALPHA", + ], + [ + "MODIFIER", + "MOD_WAVE", + "MOD_SUBSURF", + "MOD_FLUIDSIM", + "MOD_OCEAN", + "BLANK1", + "ARMATURE_DATA", + "BONE_DATA", + "GROUP_BONE", + ], + [ + "SEQUENCE", + "CAMERA_DATA", + "SCENE", + "BLANK1", + "FILE_NEW", + "CONSOLE", + "BLENDER", + "APPEND_BLEND", + ], + [ + "GROUP", + "MESH_CUBE", + "MESH_PLANE", + "MESH_CIRCLE", + "MESH_UVSPHERE", + "MESH_GRID", + "EMPTY_DATA", + "OUTLINER_DATA_MESH", + "LIGHT_SUN", + "LIGHT_SPOT", + "LIGHT", + ], + [ + "TRIA_RIGHT_BAR", + "REC", + "PLAY", + "PREV_KEYFRAME", + "NEXT_KEYFRAME", + "PAUSE", + "X", + "ADD", + "REMOVE", + "RESTRICT_VIEW_OFF", + "RESTRICT_VIEW_ON", + "RESTRICT_SELECT_OFF", + ], + [ + "BRUSH_DATA", + "GREASEPENCIL", + "LINE_DATA", + "PARTICLEMODE", + "SCULPTMODE_HLT", + "WPAINT_HLT", + "TPAINT_HLT", + "VIEWZOOM", + "HAND", + "KEY_HLT", + "KEY_DEHLT", + ], + [ + "PLUGIN", + "SCRIPT", + "PREFERENCES", + "ACTION", + "SOLO_OFF", + "RNDCURVE", + "SNAP_ON", + "FORCE_WIND", + "COPY_ID", + "EYEDROPPER", + "AUTO", + "UNLOCKED", + "LOCKED", + "UNPINNED", + "PINNED", + "PLUGIN", + "HELP", + "GHOST_ENABLED", + "GHOST_DISABLED", + "COLOR", + "LINKED", + "UNLINKED", + "LINKED", + "ZOOM_ALL", + "FREEZE", + "STYLUS_PRESSURE", + "FILE_TICK", + "QUIT", + "RECOVER_LAST", + "TIME", + "PREVIEW_RANGE", + "OUTLINER", + "NLA", + "EDITMODE_HLT", + "BOIDS", + "RNA", + "CAMERA_STEREO", + ], ] self.icons = icons - - def set_icon(self,context,op) : - #bl_props = context.scene.CustomShelf + def set_icon(self, context, op): + # bl_props = context.scene.CustomShelf self.icon = op.icon self.icons = [] - - def draw(self,context) : + def draw(self, context): bl_props = context.scene.CustomShelf layout = self.layout row = layout.row() - row.operator("customshelf.get_icons",text= '',icon = self.icon) - row.prop(self,"name",text ="") + row.operator("customshelf.get_icons", text="", icon=self.icon) + row.prop(self, "name", text="") - col = layout.column(align = True) + col = layout.column(align=True) + for icon_list in self.icons: + i = 0 + for icon in icon_list: + if not i % 12: + row = col.row(align=True) - for icon_list in self.icons : - i=0 - for icon in icon_list : - if not i%12 : - row = col.row(align= True) - - row.operator("customshelf.set_icon", icon=icon, emboss=False, text="").icon=icon + row.operator( + "customshelf.set_icon", icon=icon, emboss=False, text="" + ).icon = icon i += 1 - row = col.row(align= True) + row = col.row(align=True) layout.prop(self, "description", text="") layout.separator() - #Category Row - folder_row = layout.row(align = True) - folder_row.label(text="", icon='FILE_FOLDER') + # Category Row + folder_row = layout.row(align=True) + folder_row.label(text="", icon="FILE_FOLDER") folder_row.separator() - if not self.add_category : - folder_row.prop(bl_props,"category_enum",expand = True) + if not self.add_category: + folder_row.prop(bl_props, "category_enum", expand=True) - else : + else: - folder_row.prop(self,"new_category",text='') + folder_row.prop(self, "new_category", text="") - folder_row.prop(self,"add_category",icon = 'ADD',text='') + folder_row.prop(self, "add_category", icon="ADD", text="") # Tabs row - tab_row = layout.row(align = True) - tab_row.label(text='', icon='MENU_PANEL') + tab_row = layout.row(align=True) + tab_row.label(text="", icon="MENU_PANEL") tab_row.separator() - if not self.add_tab : + if not self.add_tab: category_tabs = get_category_tabs(bl_props.category_enum) - for t in [t for t in category_tabs if t in self.tabs] : - tab_row.prop_enum(bl_props,"tab_enum",t) + for t in [t for t in category_tabs if t in self.tabs]: + tab_row.prop_enum(bl_props, "tab_enum", t) - else : - tab_row.prop(self,"new_tab",text='') + else: + tab_row.prop(self, "new_tab", text="") - tab_row.prop(self,"add_tab",icon = 'ADD',text='') - #folder_row.operator("customshelf.remove_folder",icon = 'REMOVE',text='') + tab_row.prop(self, "add_tab", icon="ADD", text="") + # folder_row.operator("customshelf.remove_folder",icon = 'REMOVE',text='') - def write_script(self,f) : - keys = ['icon','description'] + def write_script(self, f): + keys = ["icon", "description"] keys += [k for k in self.info if k not in keys] - f.write("info = "+dic_to_str(self.info,keys)) + f.write("info = " + dic_to_str(self.info, keys)) print(self.lines) - f.write('\n'.join(self.lines)) + f.write("\n".join(self.lines)) - - def execute(self,context) : + def execute(self, context): preferences = context.preferences bl_props = context.scene.CustomShelf addon_prefs = preferences.addons[__package__].preferences - if self.new_category : + if self.new_category: category_path = get_category_path(self.new_category) if not exists(category_path): mkdir(new_category) - else : + else: category_path = get_category_path(bl_props.category_enum) - if self.new_tab : + if self.new_tab: tab_path = join(category_path, self.new_tab) if not exists(tab_path): mkdir(tab_path) - else : - tab_path = join(category_path,bl_props.tab_enum) + else: + tab_path = join(category_path, bl_props.tab_enum) + script_name = self.name.replace(" ", "_").replace("-", "_") - script_name = self.name.replace(' ','_').replace('-','_') - - script_path = join(tab_path,script_name+'.py') + script_path = join(tab_path, script_name + ".py") if os.path.exists(script_path): os.remove(script_path) + self.info["icon"] = self.icon + self.info["description"] = self.description - self.info['icon'] = self.icon - self.info['description'] = self.description - - with open(script_path,"w") as f : + with open(script_path, "w") as f: self.write_script(f) line_index = self.active_text.current_line_index @@ -246,21 +337,21 @@ class CSHELF_OT_add_script(Operator) : return {"FINISHED"} - def check(self,context) : + def check(self, context): return True - def invoke(self,context,event) : + def invoke(self, context, event): bl_props = context.scene.CustomShelf self.active_text = context.space_data.text - self.info,self.lines = read_info([l.body for l in self.active_text.lines]) + self.info, self.lines = read_info([l.body for l in self.active_text.lines]) icon = "LONGDISPLAY" - if self.info.get('icon') : + if self.info.get("icon"): icon = self.info["icon"] description = "Some description" - if self.info.get('description') : + if self.info.get("description"): description = self.info["description"] self.icon = icon @@ -272,25 +363,32 @@ class CSHELF_OT_add_script(Operator) : self.new_shelf = "" self.icons = [] - operator("get_icons", {'execute': self.get_icons}) - operator("set_icon", {'execute': self.set_icon, '__annotations__': {'icon' : StringProperty()}}) - operator("add_folder", {'execute': self.add_folder}) - #operator("remove_folder",{'execute':self.remove_folder}) + operator("get_icons", {"execute": self.get_icons}) + operator( + "set_icon", + {"execute": self.set_icon, "__annotations__": {"icon": StringProperty()}}, + ) + operator("add_folder", {"execute": self.add_folder}) + # operator("remove_folder",{'execute':self.remove_folder}) self.tabs = get_tabs() self.categories = get_categories() - CustomShelfSettings.category_enum = EnumProperty(items=[(i,i,"") for i in self.categories]) - CustomShelfSettings.tab_enum = EnumProperty(items=[(i,i,"") for i in self.tabs]) + CustomShelfSettings.category_enum = EnumProperty( + items=[(i, i, "") for i in self.categories] + ) + CustomShelfSettings.tab_enum = EnumProperty( + items=[(i, i, "") for i in self.tabs] + ) - if self.active_text.filepath : + if self.active_text.filepath: tab_path = dirname(self.active_text.filepath) category = basename(dirname(tab_path)) tab = basename(tab_path) - if category in self.categories : + if category in self.categories: bl_props.category_enum = category - if tab in self.tabs : + if tab in self.tabs: bl_props.tab_enum = tab return context.window_manager.invoke_props_dialog(self, width=500) diff --git a/properties.py b/properties.py index 099d7b2..c2cc90f 100644 --- a/properties.py +++ b/properties.py @@ -2,47 +2,53 @@ from .utils import * from bpy.props import * - -class CustomShelfSettings(bpy.types.PropertyGroup) : +class CustomShelfSettings(bpy.types.PropertyGroup): panel_list = [] - #category_enum = EnumProperty(items = ) - #tab_enum = EnumProperty(items = lambda s,c : [(f,f,"") for f in s.tabs]) + # category_enum = EnumProperty(items = ) + # tab_enum = EnumProperty(items = lambda s,c : [(f,f,"") for f in s.tabs]) - #search = StringProperty(options = {"TEXTEDIT_UPDATE"}) - #filter = BoolProperty(default = True) - #folders = PointerProperty(type= CustomShelfFolders) - #variables = bpy.props.PointerProperty(type= CustomShelfVariables) + # search = StringProperty(options = {"TEXTEDIT_UPDATE"}) + # filter = BoolProperty(default = True) + # folders = PointerProperty(type= CustomShelfFolders) + # variables = bpy.props.PointerProperty(type= CustomShelfVariables) -class CustomShelfProps(bpy.types.PropertyGroup) : + +class CustomShelfProps(bpy.types.PropertyGroup): pass -class AdditionnalShelves(bpy.types.PropertyGroup) : - path: StringProperty(name="Path",subtype = 'FILE_PATH') + +class AdditionnalShelves(bpy.types.PropertyGroup): + path: StringProperty(name="Path", subtype="FILE_PATH") class CustomShelfPrefs(bpy.types.AddonPreferences): bl_idname = __package__ - global_path = join(dirname(__file__),'shelves') - global_shelves : StringProperty(name="Shelves Path",subtype = 'DIR_PATH',default =global_path) - additionnal_shelves : CollectionProperty(type = AdditionnalShelves) - tag_filter : StringProperty() - + global_path = join(dirname(__file__), "shelves") + global_shelves: StringProperty( + name="Shelves Path", subtype="DIR_PATH", default=global_path + ) + additionnal_shelves: CollectionProperty(type=AdditionnalShelves) + tag_filter: StringProperty() def draw(self, context): prefs = bpy.context.preferences.addons[__package__].preferences layout = self.layout layout.prop(self, "global_shelves") - row = layout.row(align = True) - row.operator("customshelf.add_shelves_folder",icon="ADD",text="") - row.label(text='Additionnal Shelves folder') + row = layout.row(align=True) + row.operator("customshelf.add_shelves_folder", icon="ADD", text="") + row.label(text="Additionnal Shelves folder") - for i,shelf in enumerate(prefs.additionnal_shelves) : - row = layout.row(align = True) + for i, shelf in enumerate(prefs.additionnal_shelves): + row = layout.row(align=True) row.prop(shelf, "path") - row.operator("customshelf.remove_shelves_folder",icon="REMOVE",text="").index = i + row.operator( + "customshelf.remove_shelves_folder", icon="REMOVE", text="" + ).index = i - row = layout.row(align = True) + row = layout.row(align=True) row.prop(self, "tag_filter") - row.operator_menu_enum("customshelf.set_tag_filter",'tag_filter',icon= "DOWNARROW_HLT",text='') + row.operator_menu_enum( + "customshelf.set_tag_filter", "tag_filter", icon="DOWNARROW_HLT", text="" + ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e3f8a24 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[project] +name = "custom-shelf" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] + +[dependency-groups] +dev = [ + "black>=25.11.0", +] diff --git a/ui.py b/ui.py index 74d0b89..155da12 100644 --- a/ui.py +++ b/ui.py @@ -1,4 +1,3 @@ - import bpy @@ -7,9 +6,7 @@ class CSHELF_MT_text_editor(bpy.types.Menu): def draw(self, context): layout = self.layout - layout.operator("customshelf.add_script", text='Add Script', icon="FILE_NEW") - - + layout.operator("customshelf.add_script", text="Add Script", icon="FILE_NEW") """ diff --git a/utils.py b/utils.py index 531a374..b9ded5e 100644 --- a/utils.py +++ b/utils.py @@ -1,192 +1,231 @@ import bpy import os -from os import listdir,mkdir,scandir -from os.path import join,dirname,splitext,isdir,exists,basename +from os import listdir, mkdir, scandir +from os.path import join, dirname, splitext, isdir, exists, basename from bpy.types import Operator, Panel, PropertyGroup from bpy.props import * import subprocess import json -id_type = {"Action" : "actions", "Armature":"armatures", "Brush":"brushes", "CacheFile":"cache_files", "Camera":"cameras", - "Curve":"curves", "FreestyleLineStyle":"linestyles", "GreasePencil":"grease_pencil", "Group":"groups", - "Image":"images", "Key":"shape_keys", "Light":"lights", "Lattice":"lattices", "Library":"librairies", "Mask":"masks", - "Material":"materials", "Mesh":"meshes", "MetaBall":"metaballs", "MovieClip":"movieclips", "NodeTree":"node_groups", - "Object":"objects", "PaintCurve":"paint_curves", "Palette":"palettes", "ParticleSettings":"particles", - "Scene":"scenes", "Screen":"screens", "Sound":"sounds", "Speaker":"speakers", "Text":"texts", "Texture":"textures", - "Collection":"collections", "VectorFont":"fonts", "WindowManager":"window_managers", "World":"worlds"} +id_type = { + "Action": "actions", + "Armature": "armatures", + "Brush": "brushes", + "CacheFile": "cache_files", + "Camera": "cameras", + "Curve": "curves", + "FreestyleLineStyle": "linestyles", + "GreasePencil": "grease_pencil", + "Group": "groups", + "Image": "images", + "Key": "shape_keys", + "Light": "lights", + "Lattice": "lattices", + "Library": "librairies", + "Mask": "masks", + "Material": "materials", + "Mesh": "meshes", + "MetaBall": "metaballs", + "MovieClip": "movieclips", + "NodeTree": "node_groups", + "Object": "objects", + "PaintCurve": "paint_curves", + "Palette": "palettes", + "ParticleSettings": "particles", + "Scene": "scenes", + "Screen": "screens", + "Sound": "sounds", + "Speaker": "speakers", + "Text": "texts", + "Texture": "textures", + "Collection": "collections", + "VectorFont": "fonts", + "WindowManager": "window_managers", + "World": "worlds", +} - -def report(var,message,type='INFO') : +def report(var, message, type="INFO"): print([a for a in locals()]) print([a for a in globals()]) + if "self" in var: + var["self"].report({type}, message) - if 'self' in var : - var['self'].report({type}, message) - - else : - print('') + else: + print("") print(type) print(message) + def read_json(path): jsonFile = path if os.path.exists(jsonFile): - try : + try: with open(jsonFile) as data_file: - return (json.load(data_file)) - except : - print("the file %s not json readable"%jsonFile) + return json.load(data_file) + except: + print("the file %s not json readable" % jsonFile) return - -def search_filter(search,str) : +def search_filter(search, str): return search.lower() in str.lower() -def title(string) : - return string.replace('_',' ').replace('-',' ').title() -def arg_name(string) : - return string.replace(' ','_').replace('-','_').lower().strip('_') +def title(string): + return string.replace("_", " ").replace("-", " ").title() -def open_folder(path) : + +def arg_name(string): + return string.replace(" ", "_").replace("-", "_").lower().strip("_") + + +def open_folder(path): try: os.startfile(path) except: - subprocess.Popen(['xdg-open', path]) + subprocess.Popen(["xdg-open", path]) def dic_to_args(dic): import collections + args = collections.OrderedDict() prop_type = { str: StringProperty, bool: BoolProperty, float: FloatProperty, - int : IntProperty + int: IntProperty, } - for k, v in dic.items() : - if k in ('icon','description') or not isinstance(k,str): + for k, v in dic.items(): + if k in ("icon", "description") or not isinstance(k, str): continue - if isinstance(v,str) : - if '/' in v or '\\' in v : - args[k] = StringProperty(default=v,subtype = "FILE_PATH") - else : + if isinstance(v, str): + if "/" in v or "\\" in v: + args[k] = StringProperty(default=v, subtype="FILE_PATH") + else: args[k] = StringProperty(default=v) - elif isinstance(v,bool) : + elif isinstance(v, bool): args[k] = BoolProperty(default=v) - elif isinstance(v, float) : - args[k] = FloatProperty(default=v,precision=3) + elif isinstance(v, float): + args[k] = FloatProperty(default=v, precision=3) - elif isinstance(v,int) : + elif isinstance(v, int): args[k] = IntProperty(default=v) - elif isinstance(v, dict) : - if v.get("items") and isinstance(v["items"],(tuple,list)) : - args[k] = EnumProperty(items = [(i,i,"") for i in v["items"]]) + elif isinstance(v, dict): + if v.get("items") and isinstance(v["items"], (tuple, list)): + args[k] = EnumProperty(items=[(i, i, "") for i in v["items"]]) - elif v.get("collection") and isinstance(v["collection"],str) : - if v["collection"] not in id_type : - print("Collection %s not supported must be in %s"%(v["collection"],id_type)) - #args[k] = PointerProperty(type = getattr(bpy.types,v["collection"])) - else : - args[k] = PointerProperty(type = getattr(bpy.types,v["collection"]),poll = v.get('poll')) + elif v.get("collection") and isinstance(v["collection"], str): + if v["collection"] not in id_type: + print( + "Collection %s not supported must be in %s" + % (v["collection"], id_type) + ) + # args[k] = PointerProperty(type = getattr(bpy.types,v["collection"])) + else: + args[k] = PointerProperty( + type=getattr(bpy.types, v["collection"]), poll=v.get("poll") + ) else: - args[k] = prop_type[type(v['default'])](**v) - - + args[k] = prop_type[type(v["default"])](**v) return args -def read_info(script_path) : +def read_info(script_path): import collections - if isinstance(script_path,list) : - lines =script_path - else : - with open(script_path,encoding="utf-8") as f: + if isinstance(script_path, list): + lines = script_path + else: + with open(script_path, encoding="utf-8") as f: lines = f.read().splitlines() - info = {} - for i,l in enumerate(lines) : - if i >= 10 : - return info,lines + info = {} + for i, l in enumerate(lines): + if i >= 10: + return info, lines - if l.startswith("info") : + if l.startswith("info"): info_start = i info_str = l - while info_str.endswith(',') or not info_str.endswith('}') or i == len(lines)-1: - i+=1 - info_str+= lines[i] + while ( + info_str.endswith(",") + or not info_str.endswith("}") + or i == len(lines) - 1 + ): + i += 1 + info_str += lines[i] info_end = i - opening_brace_index = info_str.find('{') + opening_brace_index = info_str.find("{") - try : + try: info = eval(info_str[opening_brace_index:]) - except : - print('The info header in the file %s cannot be read'%script_path) + except: + print("The info header in the file %s cannot be read" % script_path) break - info = [(k,v) for k,v in info.items()] - info.sort(key = lambda x : info_str.find(x[0])) + info = [(k, v) for k, v in info.items()] + info.sort(key=lambda x: info_str.find(x[0])) info = collections.OrderedDict(info) - del lines[info_start:info_end+1] + del lines[info_start : info_end + 1] break # remove backspace - for i in range(len(lines)) : - if lines[0] : + for i in range(len(lines)): + if lines[0]: break - else : + else: lines.pop(0) - for i in range(len(lines)) : - if lines[-1] : + for i in range(len(lines)): + if lines[-1]: break - else : + else: lines.pop(-1) - return info,lines + return info, lines -def dic_to_str(dic,keys= None,spaces = 4) : - if not keys : + +def dic_to_str(dic, keys=None, spaces=4): + if not keys: keys = sorted(dic) - line = '{\n' - for k in keys : + line = "{\n" + for k in keys: v = dic[k] - line += ' '*spaces + line += " " * spaces - if isinstance(k,str) : - line +="'%s'"%k - else : - line += "%s"%k + if isinstance(k, str): + line += "'%s'" % k + else: + line += "%s" % k - line += ' : ' + line += " : " - if isinstance(v,str) : - line +="'%s'"%v - else : - line += "%s"%v + if isinstance(v, str): + line += "'%s'" % v + else: + line += "%s" % v - line += ',\n' + line += ",\n" - line += '}\n\n' + line += "}\n\n" return line diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..9c8d7cd --- /dev/null +++ b/uv.lock @@ -0,0 +1,113 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "black" +version = "25.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/ad/33adf4708633d047950ff2dfdea2e215d84ac50ef95aff14a614e4b6e9b2/black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08", size = 655669, upload-time = "2025-11-10T01:53:50.558Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/12/5c35e600b515f35ffd737da7febdb2ab66bb8c24d88560d5e3ef3d28c3fd/black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac", size = 1772831, upload-time = "2025-11-10T02:03:47Z" }, + { url = "https://files.pythonhosted.org/packages/1a/75/b3896bec5a2bb9ed2f989a970ea40e7062f8936f95425879bbe162746fe5/black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96", size = 1608520, upload-time = "2025-11-10T01:58:46.895Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b5/2bfc18330eddbcfb5aab8d2d720663cd410f51b2ed01375f5be3751595b0/black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd", size = 1682719, upload-time = "2025-11-10T01:56:55.24Z" }, + { url = "https://files.pythonhosted.org/packages/96/fb/f7dc2793a22cdf74a72114b5ed77fe3349a2e09ef34565857a2f917abdf2/black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409", size = 1362684, upload-time = "2025-11-10T01:57:07.639Z" }, + { url = "https://files.pythonhosted.org/packages/ad/47/3378d6a2ddefe18553d1115e36aea98f4a90de53b6a3017ed861ba1bd3bc/black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b", size = 1772446, upload-time = "2025-11-10T02:02:16.181Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4b/0f00bfb3d1f7e05e25bfc7c363f54dc523bb6ba502f98f4ad3acf01ab2e4/black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd", size = 1607983, upload-time = "2025-11-10T02:02:52.502Z" }, + { url = "https://files.pythonhosted.org/packages/99/fe/49b0768f8c9ae57eb74cc10a1f87b4c70453551d8ad498959721cc345cb7/black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993", size = 1682481, upload-time = "2025-11-10T01:57:12.35Z" }, + { url = "https://files.pythonhosted.org/packages/55/17/7e10ff1267bfa950cc16f0a411d457cdff79678fbb77a6c73b73a5317904/black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c", size = 1363869, upload-time = "2025-11-10T01:58:24.608Z" }, + { url = "https://files.pythonhosted.org/packages/67/c0/cc865ce594d09e4cd4dfca5e11994ebb51604328489f3ca3ae7bb38a7db5/black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170", size = 1771358, upload-time = "2025-11-10T02:03:33.331Z" }, + { url = "https://files.pythonhosted.org/packages/37/77/4297114d9e2fd2fc8ab0ab87192643cd49409eb059e2940391e7d2340e57/black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545", size = 1612902, upload-time = "2025-11-10T01:59:33.382Z" }, + { url = "https://files.pythonhosted.org/packages/de/63/d45ef97ada84111e330b2b2d45e1dd163e90bd116f00ac55927fb6bf8adb/black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda", size = 1680571, upload-time = "2025-11-10T01:57:04.239Z" }, + { url = "https://files.pythonhosted.org/packages/ff/4b/5604710d61cdff613584028b4cb4607e56e148801ed9b38ee7970799dab6/black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664", size = 1382599, upload-time = "2025-11-10T01:57:57.427Z" }, + { url = "https://files.pythonhosted.org/packages/00/5d/aed32636ed30a6e7f9efd6ad14e2a0b0d687ae7c8c7ec4e4a557174b895c/black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b", size = 204918, upload-time = "2025-11-10T01:53:48.917Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "custom-shelf" +version = "0.1.0" +source = { virtual = "." } + +[package.dev-dependencies] +dev = [ + { name = "black" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [{ name = "black", specifier = ">=25.11.0" }] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, +] + +[[package]] +name = "pytokens" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/8d/a762be14dae1c3bf280202ba3172020b2b0b4c537f94427435f19c413b72/pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a", size = 17644, upload-time = "2025-11-05T13:36:35.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/25/d9db8be44e205a124f6c98bc0324b2bb149b7431c53877fc6d1038dddaf5/pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3", size = 12195, upload-time = "2025-11-05T13:36:33.183Z" }, +]