refactor: porcail api

This commit is contained in:
Swann 2021-04-22 10:04:38 +02:00
parent 0e73af4d49
commit 93d50ac56b
No known key found for this signature in database
GPG Key ID: E1D3641A7C43AACB

View File

@ -59,6 +59,7 @@ background_execution_queue = Queue()
deleyables = [] deleyables = []
stop_modal_executor = False stop_modal_executor = False
def session_callback(name): def session_callback(name):
""" Session callback wrapper """ Session callback wrapper
@ -89,7 +90,7 @@ def initialize_session():
logging.error(f"Can't construct node {node}") logging.error(f"Can't construct node {node}")
elif node_ref.state == FETCHED: elif node_ref.state == FETCHED:
node_ref.resolve() node_ref.resolve()
# Step 2: Load nodes # Step 2: Load nodes
logging.info("Loading nodes") logging.info("Loading nodes")
for node in session.repository.list_ordered(): for node in session.repository.list_ordered():
@ -140,7 +141,8 @@ def on_connection_end(reason="none"):
if isinstance(handler, logging.FileHandler): if isinstance(handler, logging.FileHandler):
logger.removeHandler(handler) logger.removeHandler(handler)
if reason != "user": if reason != "user":
bpy.ops.session.notify('INVOKE_DEFAULT', message=f"Disconnected from session. Reason: {reason}. ") bpy.ops.session.notify(
'INVOKE_DEFAULT', message=f"Disconnected from session. Reason: {reason}. ")
# OPERATORS # OPERATORS
@ -190,8 +192,8 @@ class SessionStartOperator(bpy.types.Operator):
handler.setFormatter(formatter) handler.setFormatter(formatter)
bpy_protocol = io_bpy.get_data_translation_protocol() bpy_protocol = io_bpy.get_data_translation_protocol()
# Check if supported_datablocks are up to date before starting the # Check if supported_datablocks are up to date before starting the
# the session # the session
for impl in bpy_protocol.implementations.values(): for impl in bpy_protocol.implementations.values():
if impl.__name__ not in settings.supported_datablocks: if impl.__name__ not in settings.supported_datablocks:
@ -205,78 +207,61 @@ class SessionStartOperator(bpy.types.Operator):
else: else:
python_binary_path = bpy.app.binary_path_python python_binary_path = bpy.app.binary_path_python
# Host a session # HOST
if self.host: if self.host:
if settings.init_method == 'EMPTY': if settings.init_method == 'EMPTY':
utils.clean_scene() utils.clean_scene()
try: # Start the server locally
# Init repository server = porcelain.serve(port=settings.port,
for scene in bpy.data.scenes: timeout=settings.connection_timeout,
porcelain.add(repo, scene) admin_password=admin_pass,
log_directory=settings.cache_directory)
# Create an empty repository
repo = Repository(
data_protocol=bpy_protocol)
session.host( # Init repository
repository= repo, repo = porcelain.init(bare=False,
id=settings.username, data_protocol=bpy_protocol)
port=settings.port,
timeout=settings.connection_timeout, # Add the existing scenes
password=admin_pass, for scene in bpy.data.scenes:
cache_directory=settings.cache_directory, porcelain.add(repo, scene)
server_log_level=logging.getLevelName(
logging.getLogger().level), porcelain.remote_add(repo,
) 'server',
except Exception as e: '127.0.0.1',
self.report({'ERROR'}, repr(e)) settings.port)
logging.error(f"Error: {e}") porcelain.sync(repo, 'server')
traceback.print_exc() porcelain.push(repo, 'server')
# Join a session # JOIN
else: else:
if not runtime_settings.admin: utils.clean_scene()
utils.clean_scene()
# regular session, no password needed
admin_pass = None
try: repo = porcelain.clone(settings.ip, settings.ip)
session.connect(
repository= repo,
id=settings.username,
address=settings.ip,
port=settings.port,
timeout=settings.connection_timeout,
password=admin_pass
)
except Exception as e:
self.report({'ERROR'}, str(e))
logging.error(str(e))
# Background client updates service # Background client updates service
deleyables.append(timers.ClientUpdate()) # deleyables.append(timers.ClientUpdate())
deleyables.append(timers.DynamicRightSelectTimer()) # deleyables.append(timers.DynamicRightSelectTimer())
deleyables.append(timers.ApplyTimer(timeout=settings.depsgraph_update_rate)) # deleyables.append(timers.ApplyTimer(
# timeout=settings.depsgraph_update_rate))
# deleyables.append(timers.PushTimer( # deleyables.append(timers.PushTimer(
# queue=stagging, # queue=stagging,
# timeout=settings.depsgraph_update_rate # timeout=settings.depsgraph_update_rate
# )) # ))
session_update = timers.SessionStatusUpdate() # session_update = timers.SessionStatusUpdate()
session_user_sync = timers.SessionUserSync() # session_user_sync = timers.SessionUserSync()
session_background_executor = timers.MainThreadExecutor( # session_background_executor = timers.MainThreadExecutor(
execution_queue=background_execution_queue) # execution_queue=background_execution_queue)
session_listen = timers.SessionListenTimer(timeout=0.001) # session_listen = timers.SessionListenTimer(timeout=0.001)
session_listen.register() # session_listen.register()
session_update.register() # session_update.register()
session_user_sync.register() # session_user_sync.register()
session_background_executor.register() # session_background_executor.register()
deleyables.append(session_background_executor) # deleyables.append(session_background_executor)
deleyables.append(session_update) # deleyables.append(session_update)
deleyables.append(session_user_sync) # deleyables.append(session_user_sync)
deleyables.append(session_listen) # deleyables.append(session_listen)
self.report( self.report(
{'INFO'}, {'INFO'},
@ -589,20 +574,20 @@ class SessionApply(bpy.types.Operator):
try: try:
node_ref = session.repository.get_node(self.target) node_ref = session.repository.get_node(self.target)
porcelain.apply(session.repository, porcelain.apply(session.repository,
self.target, self.target,
force=True, force=True,
force_dependencies=self.reset_dependencies) force_dependencies=self.reset_dependencies)
if node_ref.bl_reload_parent: if node_ref.bl_reload_parent:
for parent in session.repository.get_parents(self.target): for parent in session.repository.get_parents(self.target):
logging.debug(f"Refresh parent {parent}") logging.debug(f"Refresh parent {parent}")
porcelain.apply(session.repository, porcelain.apply(session.repository,
parent.uuid, parent.uuid,
force=True) force=True)
except Exception as e: except Exception as e:
self.report({'ERROR'}, repr(e)) self.report({'ERROR'}, repr(e))
traceback.print_exc() traceback.print_exc()
return {"CANCELLED"} return {"CANCELLED"}
return {"FINISHED"} return {"FINISHED"}
@ -628,6 +613,7 @@ class SessionCommit(bpy.types.Operator):
self.report({'ERROR'}, repr(e)) self.report({'ERROR'}, repr(e))
return {"CANCELED"} return {"CANCELED"}
class ApplyArmatureOperator(bpy.types.Operator): class ApplyArmatureOperator(bpy.types.Operator):
"""Operator which runs its self from a timer""" """Operator which runs its self from a timer"""
bl_idname = "session.apply_armature_operator" bl_idname = "session.apply_armature_operator"
@ -699,6 +685,7 @@ class SessionClearCache(bpy.types.Operator):
row = self.layout row = self.layout
row.label(text=f" Do you really want to remove local cache ? ") row.label(text=f" Do you really want to remove local cache ? ")
class SessionPurgeOperator(bpy.types.Operator): class SessionPurgeOperator(bpy.types.Operator):
"Remove node with lost references" "Remove node with lost references"
bl_idname = "session.purge" bl_idname = "session.purge"
@ -743,7 +730,6 @@ class SessionNotifyOperator(bpy.types.Operator):
layout = self.layout layout = self.layout
layout.row().label(text=self.message) layout.row().label(text=self.message)
def invoke(self, context, event): def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self) return context.window_manager.invoke_props_dialog(self)
@ -789,6 +775,7 @@ class SessionSaveBackupOperator(bpy.types.Operator, ExportHelper):
def poll(cls, context): def poll(cls, context):
return session.state == STATE_ACTIVE return session.state == STATE_ACTIVE
class SessionStopAutoSaveOperator(bpy.types.Operator): class SessionStopAutoSaveOperator(bpy.types.Operator):
bl_idname = "session.cancel_autosave" bl_idname = "session.cancel_autosave"
bl_label = "Cancel auto-save" bl_label = "Cancel auto-save"
@ -829,15 +816,13 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
except OSError as e: except OSError as e:
f = open(self.filepath, "rb") f = open(self.filepath, "rb")
db = pickle.load(f) db = pickle.load(f)
if db: if db:
logging.info(f"Reading {self.filepath}") logging.info(f"Reading {self.filepath}")
nodes = db.get("nodes") nodes = db.get("nodes")
logging.info(f"{len(nodes)} Nodes to load") logging.info(f"{len(nodes)} Nodes to load")
# init the factory with supported types # init the factory with supported types
bpy_protocol = DataTranslationProtocol() bpy_protocol = DataTranslationProtocol()
for type in io_bpy.types_to_register(): for type in io_bpy.types_to_register():
@ -846,11 +831,10 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
type_impl_name = 'Bl'+''.join(name) type_impl_name = 'Bl'+''.join(name)
type_module_class = getattr(type_module, type_impl_name) type_module_class = getattr(type_module, type_impl_name)
bpy_protocol.register_type( bpy_protocol.register_type(
type_module_class.bl_class, type_module_class.bl_class,
type_module_class) type_module_class)
graph = Repository() graph = Repository()
for node, node_data in nodes: for node, node_data in nodes:
@ -866,7 +850,7 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
data=node_data['data']) data=node_data['data'])
graph.do_commit(instance) graph.do_commit(instance)
instance.state = FETCHED instance.state = FETCHED
logging.info("Graph succefully loaded") logging.info("Graph succefully loaded")
utils.clean_scene() utils.clean_scene()
@ -879,15 +863,16 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
for node in graph.list_ordered(): for node in graph.list_ordered():
graph[node].apply() graph[node].apply()
return {'FINISHED'} return {'FINISHED'}
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return True return True
def menu_func_import(self, context): def menu_func_import(self, context):
self.layout.operator(SessionLoadSaveOperator.bl_idname, text='Multi-user session snapshot (.db)') self.layout.operator(SessionLoadSaveOperator.bl_idname,
text='Multi-user session snapshot (.db)')
classes = ( classes = (
@ -910,6 +895,7 @@ classes = (
SessionPurgeOperator, SessionPurgeOperator,
) )
def update_external_dependencies(): def update_external_dependencies():
nodes_ids = session.list(filter=io_bpy.bl_file.BlFile) nodes_ids = session.list(filter=io_bpy.bl_file.BlFile)
for node_id in nodes_ids: for node_id in nodes_ids:
@ -919,6 +905,7 @@ def update_external_dependencies():
porcelain.commit(session.repository, node_id) porcelain.commit(session.repository, node_id)
session.push(node_id, check_data=False) session.push(node_id, check_data=False)
def sanitize_deps_graph(remove_nodes: bool = False): def sanitize_deps_graph(remove_nodes: bool = False):
""" Cleanup the replication graph """ Cleanup the replication graph
""" """
@ -950,6 +937,7 @@ def resolve_deps_graph(dummy):
if session and session.state == STATE_ACTIVE: if session and session.state == STATE_ACTIVE:
sanitize_deps_graph(remove_nodes=True) sanitize_deps_graph(remove_nodes=True)
@persistent @persistent
def load_pre_handler(dummy): def load_pre_handler(dummy):
if session and session.state in [STATE_ACTIVE, STATE_SYNCING]: if session and session.state in [STATE_ACTIVE, STATE_SYNCING]:
@ -980,7 +968,7 @@ def depsgraph_evaluation(scene):
if update.id.uuid: if update.id.uuid:
# Retrieve local version # Retrieve local version
node = session.repository.get_node(update.id.uuid) node = session.repository.get_node(update.id.uuid)
# Check our right on this update: # Check our right on this update:
# - if its ours or ( under common and diff), launch the # - if its ours or ( under common and diff), launch the
# update process # update process
@ -994,12 +982,12 @@ def depsgraph_evaluation(scene):
except ReferenceError: except ReferenceError:
logging.debug(f"Reference error {node.uuid}") logging.debug(f"Reference error {node.uuid}")
except ContextError as e: except ContextError as e:
logging.debug(e) logging.debug(e)
except Exception as e: except Exception as e:
logging.error(e) logging.error(e)
else: else:
continue continue
# A new scene is created # A new scene is created
elif isinstance(update.id, bpy.types.Scene): elif isinstance(update.id, bpy.types.Scene):
ref = session.repository.get_node_by_datablock(update.id) ref = session.repository.get_node_by_datablock(update.id)
if ref: if ref:
@ -1008,13 +996,14 @@ def depsgraph_evaluation(scene):
scn_uuid = porcelain.add(session.repository, update.id) scn_uuid = porcelain.add(session.repository, update.id)
porcelain.commit(session.repository, scn_uuid) porcelain.commit(session.repository, scn_uuid)
porcelain.push(session.repository) porcelain.push(session.repository)
def register(): def register():
from bpy.utils import register_class from bpy.utils import register_class
for cls in classes: for cls in classes:
register_class(cls) register_class(cls)
bpy.app.handlers.undo_post.append(resolve_deps_graph) bpy.app.handlers.undo_post.append(resolve_deps_graph)
bpy.app.handlers.redo_post.append(resolve_deps_graph) bpy.app.handlers.redo_post.append(resolve_deps_graph)