Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ruff for linter and formatting #508

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
282 changes: 281 additions & 1 deletion .github/CONTRIBUTING.md

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: mypy
on:
push: [push, pull_request]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: chartboost/ruff-action@v1

mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.11.7
cache: pip

- name: Build docs using blender
run: |
wget -nv https://download.blender.org/release/Blender4.1/blender-4.1.0-linux-x64.tar.xz
tar -xf blender-4.1.0-linux-x64.tar.xz

blender-4.1.0-linux-x64/blender --version
blender-4.1.0-linux-x64/blender -b --python tests/python.py -- -m pip install poetry
blender-4.1.0-linux-x64/blender -b --python tests/python.py -- -m poetry install --with dev
blender-4.1.0-linux-x64/blender -b --python tests/python.py -- -m mypy .


8 changes: 8 additions & 0 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: Ruff
on: [ push, pull_request ]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: chartboost/ruff-action@v1
261 changes: 0 additions & 261 deletions CONTRIBUTING.md

This file was deleted.

14 changes: 8 additions & 6 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@


# zips up the template file
def zip_template():
def zip_template() -> None:
# Define the directory and zip file paths
dir_path = 'molecularnodes/assets/template/Molecular Nodes'
zip_file_path = 'molecularnodes/assets/template/Molecular Nodes.zip'
dir_path = "molecularnodes/assets/template/Molecular Nodes"
zip_file_path = "molecularnodes/assets/template/Molecular Nodes.zip"

# Create a ZipFile object in write mode
with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
with zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED) as zipf:
# Walk the directory tree and add files to the zip file
for root, dirs, files in os.walk(dir_path):
for file in files:
# Get the path of the file
file_path = os.path.join(root, file)
# Add the file to the zip file
zipf.write(file_path, arcname=os.path.relpath(
file_path, start='molecularnodes/assets/template/'))
zipf.write(
file_path,
arcname=os.path.relpath(file_path, start="molecularnodes/assets/template/"),
)


if __name__ == "__main__":
Expand Down
57 changes: 26 additions & 31 deletions docs/build_node_docs.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import os
import pathlib
import sys

import bpy
import griffe
from quartodoc import MdRenderer

import molecularnodes as mn
import griffe
import os
import sys
import pathlib

sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, os.path.abspath(".."))

folder = pathlib.Path(__file__).resolve().parent
file_output_qmd = os.path.join(folder, "nodes/index.qmd")
Expand All @@ -28,12 +30,12 @@ def get_values(sockets):
default = None
if dtype == "Float":
default = round(socket.default_value, 2)
elif dtype in ['Geometry', 'Collection', 'Object']:
elif dtype in ["Geometry", "Collection", "Object"]:
default = None
elif dtype == "Vector":
default = [round(x, 2) for x in socket.default_value]
elif dtype == "Material":
default = '`MN Default`'
default = "`MN Default`"
elif dtype == "Color":
default = col_to_rgb_str(socket.default_value)
else:
Expand All @@ -44,62 +46,55 @@ def get_values(sockets):
name=socket.name,
annotation=dtype,
value=default,
description=socket.description
description=socket.description,
)
)
return param_list


cat = ''
cat = ""
text = griffe.docstrings.dataclasses.DocstringSectionText
params = griffe.docstrings.dataclasses.DocstringSectionParameters

categories = {}
for category, node_list in mn.ui.node_info.menu_items.items():
objects = []
objects.append(
[text(title=None, value=f"## {mn.blender.nodes.format_node_name(category)}")])
objects.append([text(title=None, value=f"## {mn.blender.nodes.format_node_name(category)}")])

for item in node_list:
if isinstance(item, str):
continue

iter_list = [item]

if item['label'] == "custom":
iter_list = item['values']
if item["label"] == "custom":
iter_list = item["values"]

for entry in iter_list:
name = entry['name']
name = entry["name"]
if name.startswith("mn."):
name = entry['backup']
name = entry["backup"]

entry_list = []
desc = entry.get('description')
urls = entry.get('video_url')
desc = entry.get("description")
urls = entry.get("video_url")

inputs = params(get_values(
mn.blender.nodes.inputs(bpy.data.node_groups[name])))
outputs = params(get_values(
mn.blender.nodes.outputs(bpy.data.node_groups[name])))
inputs = params(get_values(mn.blender.nodes.inputs(bpy.data.node_groups[name])))
outputs = params(get_values(mn.blender.nodes.outputs(bpy.data.node_groups[name])))

title = mn.blender.nodes.format_node_name(entry.get('label'))
title = mn.blender.nodes.format_node_name(entry.get("label"))
entry_list.append(text(title=None, value=f"### {title}"))
if desc:
entry_list.append(text(title=None, value=desc))
if urls:
if not isinstance(urls, list):
urls = [urls]
[
entry_list.append(
text(title=None, value=f"![]({url}.mp4)")
) for url in urls
]
[entry_list.append(text(title=None, value=f"![]({url}.mp4)")) for url in urls]

if len(inputs.as_dict()['value']) > 0:
if len(inputs.as_dict()["value"]) > 0:
entry_list.append(text(value="\n#### Inputs"))
entry_list.append(inputs)
if len(outputs.as_dict()['value']) > 0:
if len(outputs.as_dict()["value"]) > 0:
entry_list.append(text(value="\n#### Outputs"))
entry_list.append(outputs)

Expand All @@ -116,10 +111,10 @@ def get_values(sockets):
"""

for category, object in categories.items():
with open(os.path.join(folder, f'nodes/{category}.qmd'), 'w') as file:
with open(os.path.join(folder, f"nodes/{category}.qmd"), "w") as file:
file.write(header)
for doc in object:
section = ''
section = ""
for sec in doc:
file.write(ren.render(sec))
file.write("\n\n")
Expand Down
10 changes: 3 additions & 7 deletions docs/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@
import os


def main():

def main() -> None:
python = os.path.realpath(sys.executable)

commands = [
f'{python} -m pip install .',
f'{python} -m pip install quartodoc'
]
commands = [f"{python} -m pip install .", f"{python} -m pip install quartodoc"]

for command in commands:
subprocess.run(command.split(' '))
subprocess.run(command.split(" "))


if __name__ == "__main__":
Expand Down
5 changes: 2 additions & 3 deletions molecularnodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"warning": "",
"doc_url": "https://bradyajohnston.github.io/MolecularNodes/",
"tracker_url": "https://github.com/BradyAJohnston/MolecularNodes/issues",
"category": "Import"
"category": "Import",
}

auto_load.init()
Expand All @@ -40,8 +40,7 @@
def register():
auto_load.register()
bpy.types.NODE_MT_add.append(MN_add_node_menu)
bpy.types.Object.mn = bpy.props.PointerProperty(
type=MolecularNodesObjectProperties)
bpy.types.Object.mn = bpy.props.PointerProperty(type=MolecularNodesObjectProperties)
for func in universe_funcs:
try:
bpy.app.handlers.load_post.append(func)
Expand Down
Binary file modified molecularnodes/assets/template/Molecular Nodes.zip
Binary file not shown.
48 changes: 37 additions & 11 deletions molecularnodes/auto_load.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import os
import bpy
import sys
import typing
import inspect
import pkgutil
Expand All @@ -18,13 +16,15 @@
modules = None
ordered_classes = None


def init():
global modules
global ordered_classes

modules = get_all_submodules(Path(__file__).parent)
ordered_classes = get_ordered_classes_to_register(modules)


def register():
for cls in ordered_classes:
bpy.utils.register_class(cls)
Expand All @@ -35,6 +35,7 @@ def register():
if hasattr(module, "register"):
module.register()


def unregister():
for cls in reversed(ordered_classes):
bpy.utils.unregister_class(cls)
Expand All @@ -49,13 +50,16 @@ def unregister():
# Import modules
#################################################


def get_all_submodules(directory):
return list(iter_submodules(directory, directory.name))


def iter_submodules(path, package_name):
for name in sorted(iter_submodule_names(path)):
yield importlib.import_module("." + name, package_name)


def iter_submodule_names(path, root=""):
for _, module_name, is_package in pkgutil.iter_modules([str(path)]):
if is_package:
Expand All @@ -69,29 +73,34 @@ def iter_submodule_names(path, root=""):
# Find classes to register
#################################################


def get_ordered_classes_to_register(modules):
return toposort(get_register_deps_dict(modules))


def get_register_deps_dict(modules):
my_classes = set(iter_my_classes(modules))
my_classes_by_idname = {cls.bl_idname : cls for cls in my_classes if hasattr(cls, "bl_idname")}
my_classes_by_idname = {cls.bl_idname: cls for cls in my_classes if hasattr(cls, "bl_idname")}

deps_dict = {}
for cls in my_classes:
deps_dict[cls] = set(iter_my_register_deps(cls, my_classes, my_classes_by_idname))
return deps_dict


def iter_my_register_deps(cls, my_classes, my_classes_by_idname):
yield from iter_my_deps_from_annotations(cls, my_classes)
yield from iter_my_deps_from_parent_id(cls, my_classes_by_idname)


def iter_my_deps_from_annotations(cls, my_classes):
for value in typing.get_type_hints(cls, {}, {}).values():
dependency = get_dependency_from_annotation(value)
if dependency is not None:
if dependency in my_classes:
yield dependency


def get_dependency_from_annotation(value):
if blender_version >= (2, 93):
if isinstance(value, bpy.props._PropertyDeferred):
Expand All @@ -102,6 +111,7 @@ def get_dependency_from_annotation(value):
return value[1]["type"]
return None


def iter_my_deps_from_parent_id(cls, my_classes_by_idname):
if bpy.types.Panel in cls.__bases__:
parent_idname = getattr(cls, "bl_parent_id", None)
Expand All @@ -110,38 +120,54 @@ def iter_my_deps_from_parent_id(cls, my_classes_by_idname):
if parent_cls is not None:
yield parent_cls


def iter_my_classes(modules):
base_types = get_register_base_types()
for cls in get_classes_in_modules(modules):
if any(base in base_types for base in cls.__bases__):
if not getattr(cls, "is_registered", False):
yield cls


def get_classes_in_modules(modules):
classes = set()
for module in modules:
for cls in iter_classes_in_module(module):
classes.add(cls)
return classes


def iter_classes_in_module(module):
for value in module.__dict__.values():
if inspect.isclass(value):
yield value


def get_register_base_types():
return set(getattr(bpy.types, name) for name in [
"Panel", "Operator", "PropertyGroup",
"AddonPreferences", "Header", "Menu",
"Node", "NodeSocket", "NodeTree",
"UIList", "RenderEngine",
"Gizmo", "GizmoGroup",
])
return set(
getattr(bpy.types, name)
for name in [
"Panel",
"Operator",
"PropertyGroup",
"AddonPreferences",
"Header",
"Menu",
"Node",
"NodeSocket",
"NodeTree",
"UIList",
"RenderEngine",
"Gizmo",
"GizmoGroup",
]
)


# Find order to register to solve dependencies
#################################################


def toposort(deps_dict):
sorted_list = []
sorted_values = set()
Expand All @@ -153,5 +179,5 @@ def toposort(deps_dict):
sorted_values.add(value)
else:
unsorted.append(value)
deps_dict = {value : deps_dict[value] - sorted_values for value in unsorted}
deps_dict = {value: deps_dict[value] - sorted_values for value in unsorted}
return sorted_list
Loading
Loading