You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/azpy/shared/noodely/pathnode.py

411 lines
14 KiB
Python

"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# -------------------------------------------------------------------------
# this has to be at the beginning
from __future__ import division
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# pathnode.py
# simple path objecy based Node Class, for tool creation.
# version: 0.1
# author: Gallowj
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
"""
Module docstring:
A simple path objecy based Node Class, for creating path hierarchies.
"""
__author__ = 'HogJonny'
# -------------------------------------------------------------------------
# built-ins
import os
import copy
import subprocess
import traceback
import string
import logging
from unipath import Path, AbstractPath
# local ly imports
from azpy.shared.noodely.helpers import istext
from azpy.shared.noodely.find_arg import find_arg
from azpy.shared.noodely.synth import synthesize
from azpy.shared.noodely.node import Node
import azpy
from azpy.env_bool import env_bool
from azpy.constants import ENVAR_DCCSI_GDEBUG
from azpy.constants import ENVAR_DCCSI_DEV_MODE
# -------------------------------------------------------------------------
# global space
# To Do: update to dynaconf dynamic env and settings?
_G_DEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
_DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
_MODULENAME = 'azpy.shared.noodely.pathnode'
_log_level = int(20)
if _G_DEBUG:
_log_level = int(10)
_LOGGER = azpy.initialize_logger(_MODULENAME,
log_to_file=False,
default_log_level=_log_level)
_LOGGER.debug('Starting:: {}.'.format({_MODULENAME}))
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Use unicode strings
_base = str # Python 3 str (=unicode), or Python 2 bytes.
if os.path.supports_unicode_filenames:
try:
_base = unicode # Python 2 unicode.
except NameError:
pass
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
class PathNode(Node):
"""doc string"""
# share the debug state
_DEBUG = _G_DEBUG
# class header
_message_header = 'noodly, PathNode(): Message'
# App Launcher paths...
try:
_maya_exe_path = Path(os.environ['MAYAPY'])
except:
_maya_exe_path = Path(r"C:\Program Files\Autodesk\Maya2019\bin\maya.exe")
try:
_notepad_exe_path = Path(os.environ['DEFAULT_TXT_EXE'])
except:
_notepad_exe_path = Path(r"C:\Program Files (x86)\Notepad++\notepad++.exe")
# --BASE-METHODS-------------------------------------------------------
def __new__(cls, path="", root_path=None, *args, **kwargs):
'''docstring'''
# if not isinstance(path, str) and not isinstance(path, Path):
# raise TypeError("{0}, {1}: Accepts paths as str or Path() types!\r"
# "Input data is:{2}\r"
# "".format('noodly, PathNode',
# 'PathNode(filename)', type(path)))
#
self = super(PathNode, cls).__new__(cls)
return self
# --constructor--------------------------------------------------------
def __init__(self, path="", root_path=None, parent_is_root=None,
name_is_path=None, *args, **kwargs):
self._logger = Node._LOGGER
self._node_type = self.__class__.__name__
# a dict to store properties/attrs
# in the event an object is re-built / re-init
# it is important to store anything here that needs retention
self._kwargs_dict = {}
# -- secret keyword -----------------------------------------------
self._temp_node = False
temp_node, kwargs = find_arg(arg_pos_index=None, arg_tag='temp_node',
remove_kwarg=True, in_args=args,
in_kwargs=kwargs) # <-- kwarg only
self._temp_node = temp_node
if self._temp_node:
self.k_wargs_dict['temp_node'] = self._temp_node
# -- Node class args/kwargs ---------------------------------------
node_name, kwargs = find_arg(arg_pos_index=2, arg_tag='node_name',
remove_kwarg=True, in_args=args,
in_kwargs=kwargs) # <-- third arg, kwarg
parent_node, kwargs = find_arg(arg_pos_index=3, arg_tag='parent_node',
remove_kwarg=True, in_args=args,
in_kwargs=kwargs) # <-- fourth arg, kwarg
self._root_path = root_path
self._parent_is_root = parent_is_root
if self._parent_is_root != None:
self._kwargs_dict['parent_is_root'] = self.parent_is_root
if parent_is_root: # <-- do it
self._root_path = parent_node
# make sure the path is a Path
self._path = path
if not isinstance(self._path, Path):
try:
self._path = Path(path)
except:
self._path = Path() # empty path object fallback
self._name_is_path = name_is_path
if self._name_is_path:
self._kwargs_dict['name_is_path'] = self._name_is_path
# this might only work if the file actually exists
self._node_name = node_name
if self._name_is_path:
if self._path.name != None or self._path.name != '':
self._node_name = str(self._path.name)
# Path.__init__(self)
super(PathNode, self).__init__(self._node_name, parent_node,
temp_node=temp_node,
*args, **kwargs)
# -- properties -------------------------------------------------------
@property
def path(self):
return self._path
@path.setter
def path(self, path):
self._path = path
return self._path
@path.getter
def path(self):
return self._path
@property
def root_path(self):
return self._root_path
@root_path.setter
def root_path(self, root_path):
self._root_path = root_path
return self._root_path
@root_path.getter
def root_path(self):
return self._root_path
@property
def parent_is_root(self):
return self._parent_is_root
@parent_is_root.setter
def parent_is_root(self, parent_is_root):
self._parent_is_root = parent_is_root
return self._parent_is_root
@parent_is_root.getter
def parent_is_root(self):
return self._parent_is_root
@property
def name_is_path(self):
return self._name_is_path
# @name_is_path.setter
# def name_is_path(self, name_is_path):
# self._name_is_path = name_is_path
# return self._name_is_path
@name_is_path.getter
def name_is_path(self):
return self._name_is_path
# --method-------------------------------------------------------------
def set_file_path(self, path):
if not isinstance(path, Path):
try:
path = Path(path)
except:
raise TypeError("must be Path compatible")
# retreive a copy of the old _kwargs dict
_kwargs_dict_copy = copy.copy(self._kwargs_dict)
_name_is_uni_hashid = copy.copy(self.name_is_uni_hashid)
# create a new me (self), with new value
# attempt to keep existing attrs/settings
self = PathNode(path=path,
root_path=self.root_path,
parent_is_root=self.parent_is_root,
name_is_path=self.name_is_path,
temp_node=self.temp_node,
node_name=self.node_name,
parent_node=self.parent_node,
name_is_uni_hashid=self.name_is_uni_hashid)
# now we need to restore any custom properties on the replacement object
for key, value in _kwargs_dict_copy.items():
self._kwargs_dict[key] = value
try:
synthesize(self, '{0}'.format(key), value)
except:
code = compile('self._{0} = {1}'.format(key, value), 'synthProp', 'exec')
if Node._DEBUG:
self.logger.error('can not set: self._{0} = {1}'.format(key, value))
# replace myself in the class nodeDict, based on my unihashid
self.cls_node_dict[self.uni_hashid] = self
# return the new version of myself
return self.cls_node_dict[self.uni_hashid]
# ---------------------------------------------------------------------
# --method-------------------------------------------------------------
def start_file(self, filepath=None):
'''opens the file in the prefered os editor for the filetype'''
if filepath == None:
filepath = self.path
if not isinstance(filepath, Path): # <-any subclass of Path works?
filepath = Path(filepath)
self.logger.debug('starting file: {0}'.format(filepath))
try:
os.startfile(filepath)
except IOError as e:
self.logger.error(e)
return filepath
# ---------------------------------------------------------------------
# --method-------------------------------------------------------------
def explore_file(self, filepath=None):
if filepath == None:
filepath = self.path
if not isinstance(filepath, Path):
filepath = Path(filepath)
self.logger.debug('exploring file: {0}'.format(filepath))
if filepath.exists():
try:
subprocess.Popen(r'explorer /select,"{0}"'.format(filepath))
except IOError as e:
self.logger.error(e)
else:
self.logger.error('file does not exist: {0}'.format(filepath))
return filepath
# ---------------------------------------------------------------------
# --method-------------------------------------------------------------
def hierarchy(self, tabLevel=-1):
output = ''
if isinstance(self, RootNode):
if gDebug:
func = inspect.currentframe().f_back.f_code
output += ('{0}Called from:\n'
'{0}{1}\n'.format('\t' * (tabLevel + 1), func))
tabLevel += 1
for i in range(tabLevel):
output += '\t'
output += ('{tab}/------ nodeName:: "{0}"\n'
'{1} |typeInfo:: {2}\n'
'{1} |_uniHashid:: "{3}"\r'
'{1} |path:: "{4}"\n'
'{1} |get_root():: "{5}"\n'
'{1} |getPathFromRoot():: "{6}"\n'
''.format(self.getNodeName(),
'\t' * tabLevel,
self.get_typeInfo(),
self.get_uniHashid(),
self,
self.get_root(),
self.getPathFromRoot(),
tab=tabLevel))
for child in self._children:
output += child.hierarchy(tabLevel)
tabLevel -= 1
return output
# ---------------------------------------------------------------------
# --Class End--------------------------------------------------------------
###########################################################################
# tests(), code block for testing module
# -------------------------------------------------------------------------
def tests():
from node import Node
default_node = Node() # result: Node(node_name='PRIME')
print(default_node)
first_child = PathNode(path=None, node_name='first_child', parent_node=default_node)
print(first_child)
# result: PathNode(temp_node=True, parent_node=Node(node_name='PRIME')).siblingNodeFromHashid('WNPZoKBVpXV16QLz')
# first_child.nodeType
# first_child.parent_node
# first_child.node_name
try:
# PathNode requires a arg 'path' input (should be a path str)
fubar_path_node = PathNode() # <-- this should fail
print (fubar_path_node)
except Exception as err:
print ('\r{0}'.format(err))
print (traceback.format_exc())
foo = PathNode(r'\foo\fooey\kablooey', node_name='foo',
parent_node=default_node)
print(foo)
testes = Path(r'/foo/fooey/kablooey')
print(foo.path.exists())
print(foo.path.parent)
print(foo.path.norm_case())
print(foo.path.absolute())
fooey = PathNode(None, parent_node=foo)
print(fooey)
kablooey = PathNode(r'\foo\fooey\kablooey',
parent_node=default_node,
name_is_path=True)
print(kablooey)
kablooey = kablooey.set_file_path(r'c:\mytemp\fubar.txt')
print(kablooey)
kablooey.start_file()
kablooey.explore_file()
return
# - END, tests() ----------------------------------------------------------
def main():
pass
return
# - END, main() -----------------------------------------------------------
###########################################################################
# --call block-------------------------------------------------------------
if __name__ == "__main__":
print ("# ----------------------------------------------------------------------- #")
print ('~ noodly.PathNode ... Running script as __main__')
print ("# ----------------------------------------------------------------------- #\r")
# run simple tests
tests()