pathnode.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. """
  6. # -------------------------------------------------------------------------
  7. # this has to be at the beginning
  8. from __future__ import division
  9. # -------------------------------------------------------------------------
  10. # -------------------------------------------------------------------------
  11. # pathnode.py
  12. # simple path objecy based Node Class, for tool creation.
  13. # version: 0.1
  14. # author: Gallowj
  15. # -------------------------------------------------------------------------
  16. # -------------------------------------------------------------------------
  17. """
  18. Module docstring:
  19. A simple path objecy based Node Class, for creating path hierarchies.
  20. """
  21. __author__ = 'HogJonny'
  22. # -------------------------------------------------------------------------
  23. # built-ins
  24. import os
  25. import copy
  26. import subprocess
  27. import traceback
  28. import string
  29. import logging
  30. from unipath import Path, AbstractPath
  31. # local ly imports
  32. from azpy.shared.noodely.helpers import istext
  33. from azpy.shared.noodely.find_arg import find_arg
  34. from azpy.shared.noodely.synth import synthesize
  35. from azpy.shared.noodely.node import Node
  36. import azpy
  37. from azpy.env_bool import env_bool
  38. from azpy.constants import ENVAR_DCCSI_GDEBUG
  39. from azpy.constants import ENVAR_DCCSI_DEV_MODE
  40. # -------------------------------------------------------------------------
  41. # global space
  42. # To Do: update to dynaconf dynamic env and settings?
  43. _DCCSI_GDEBUG = env_bool(ENVAR_DCCSI_GDEBUG, False)
  44. _DCCSI_DEV_MODE = env_bool(ENVAR_DCCSI_DEV_MODE, False)
  45. _MODULENAME = 'azpy.shared.noodely.pathnode'
  46. _log_level = int(20)
  47. if _DCCSI_GDEBUG:
  48. _log_level = int(10)
  49. _LOGGER = azpy.initialize_logger(_MODULENAME,
  50. log_to_file=False,
  51. default_log_level=_log_level)
  52. _LOGGER.debug('Starting:: {}.'.format({_MODULENAME}))
  53. # -------------------------------------------------------------------------
  54. # -------------------------------------------------------------------------
  55. # Use unicode strings
  56. _base = str # Python 3 str (=unicode), or Python 2 bytes.
  57. if os.path.supports_unicode_filenames:
  58. try:
  59. _base = unicode # Python 2 unicode.
  60. except NameError:
  61. pass
  62. # -------------------------------------------------------------------------
  63. # -------------------------------------------------------------------------
  64. class PathNode(Node):
  65. """doc string"""
  66. # share the debug state
  67. _DEBUG = _DCCSI_GDEBUG
  68. # class header
  69. _message_header = 'noodly, PathNode(): Message'
  70. # App Launcher paths...
  71. try:
  72. _maya_exe_path = Path(os.environ['MAYAPY'])
  73. except:
  74. _maya_exe_path = Path(r"C:\Program Files\Autodesk\Maya2019\bin\maya.exe")
  75. try:
  76. _notepad_exe_path = Path(os.environ['DEFAULT_TXT_EXE'])
  77. except:
  78. _notepad_exe_path = Path(r"C:\Program Files (x86)\Notepad++\notepad++.exe")
  79. # --BASE-METHODS-------------------------------------------------------
  80. def __new__(cls, path="", root_path=None, *args, **kwargs):
  81. '''docstring'''
  82. # if not isinstance(path, str) and not isinstance(path, Path):
  83. # raise TypeError("{0}, {1}: Accepts paths as str or Path() types!\r"
  84. # "Input data is:{2}\r"
  85. # "".format('noodly, PathNode',
  86. # 'PathNode(filename)', type(path)))
  87. #
  88. self = super(PathNode, cls).__new__(cls)
  89. return self
  90. # --constructor--------------------------------------------------------
  91. def __init__(self, path="", root_path=None, parent_is_root=None,
  92. name_is_path=None, *args, **kwargs):
  93. self._logger = Node._LOGGER
  94. self._node_type = self.__class__.__name__
  95. # a dict to store properties/attrs
  96. # in the event an object is re-built / re-init
  97. # it is important to store anything here that needs retention
  98. self._kwargs_dict = {}
  99. # -- secret keyword -----------------------------------------------
  100. self._temp_node = False
  101. temp_node, kwargs = find_arg(arg_pos_index=None, arg_tag='temp_node',
  102. remove_kwarg=True, in_args=args,
  103. in_kwargs=kwargs) # <-- kwarg only
  104. self._temp_node = temp_node
  105. if self._temp_node:
  106. self.k_wargs_dict['temp_node'] = self._temp_node
  107. # -- Node class args/kwargs ---------------------------------------
  108. node_name, kwargs = find_arg(arg_pos_index=2, arg_tag='node_name',
  109. remove_kwarg=True, in_args=args,
  110. in_kwargs=kwargs) # <-- third arg, kwarg
  111. parent_node, kwargs = find_arg(arg_pos_index=3, arg_tag='parent_node',
  112. remove_kwarg=True, in_args=args,
  113. in_kwargs=kwargs) # <-- fourth arg, kwarg
  114. self._root_path = root_path
  115. self._parent_is_root = parent_is_root
  116. if self._parent_is_root != None:
  117. self._kwargs_dict['parent_is_root'] = self.parent_is_root
  118. if parent_is_root: # <-- do it
  119. self._root_path = parent_node
  120. # make sure the path is a Path
  121. self._path = path
  122. if not isinstance(self._path, Path):
  123. try:
  124. self._path = Path(path)
  125. except:
  126. self._path = Path() # empty path object fallback
  127. self._name_is_path = name_is_path
  128. if self._name_is_path:
  129. self._kwargs_dict['name_is_path'] = self._name_is_path
  130. # this might only work if the file actually exists
  131. self._node_name = node_name
  132. if self._name_is_path:
  133. if self._path.name != None or self._path.name != '':
  134. self._node_name = str(self._path.name)
  135. # Path.__init__(self)
  136. super(PathNode, self).__init__(self._node_name, parent_node,
  137. temp_node=temp_node,
  138. *args, **kwargs)
  139. # -- properties -------------------------------------------------------
  140. @property
  141. def path(self):
  142. return self._path
  143. @path.setter
  144. def path(self, path):
  145. self._path = path
  146. return self._path
  147. @path.getter
  148. def path(self):
  149. return self._path
  150. @property
  151. def root_path(self):
  152. return self._root_path
  153. @root_path.setter
  154. def root_path(self, root_path):
  155. self._root_path = root_path
  156. return self._root_path
  157. @root_path.getter
  158. def root_path(self):
  159. return self._root_path
  160. @property
  161. def parent_is_root(self):
  162. return self._parent_is_root
  163. @parent_is_root.setter
  164. def parent_is_root(self, parent_is_root):
  165. self._parent_is_root = parent_is_root
  166. return self._parent_is_root
  167. @parent_is_root.getter
  168. def parent_is_root(self):
  169. return self._parent_is_root
  170. @property
  171. def name_is_path(self):
  172. return self._name_is_path
  173. # @name_is_path.setter
  174. # def name_is_path(self, name_is_path):
  175. # self._name_is_path = name_is_path
  176. # return self._name_is_path
  177. @name_is_path.getter
  178. def name_is_path(self):
  179. return self._name_is_path
  180. # --method-------------------------------------------------------------
  181. def set_file_path(self, path):
  182. if not isinstance(path, Path):
  183. try:
  184. path = Path(path)
  185. except:
  186. raise TypeError("must be Path compatible")
  187. # retreive a copy of the old _kwargs dict
  188. _kwargs_dict_copy = copy.copy(self._kwargs_dict)
  189. _name_is_uni_hashid = copy.copy(self.name_is_uni_hashid)
  190. # create a new me (self), with new value
  191. # attempt to keep existing attrs/settings
  192. self = PathNode(path=path,
  193. root_path=self.root_path,
  194. parent_is_root=self.parent_is_root,
  195. name_is_path=self.name_is_path,
  196. temp_node=self.temp_node,
  197. node_name=self.node_name,
  198. parent_node=self.parent_node,
  199. name_is_uni_hashid=self.name_is_uni_hashid)
  200. # now we need to restore any custom properties on the replacement object
  201. for key, value in _kwargs_dict_copy.items():
  202. self._kwargs_dict[key] = value
  203. try:
  204. synthesize(self, '{0}'.format(key), value)
  205. except:
  206. code = compile('self._{0} = {1}'.format(key, value), 'synthProp', 'exec')
  207. if Node._DEBUG:
  208. self.logger.error('can not set: self._{0} = {1}'.format(key, value))
  209. # replace myself in the class nodeDict, based on my unihashid
  210. self.cls_node_dict[self.uni_hashid] = self
  211. # return the new version of myself
  212. return self.cls_node_dict[self.uni_hashid]
  213. # ---------------------------------------------------------------------
  214. # --method-------------------------------------------------------------
  215. def start_file(self, filepath=None):
  216. '''opens the file in the prefered os editor for the filetype'''
  217. if filepath == None:
  218. filepath = self.path
  219. if not isinstance(filepath, Path): # <-any subclass of Path works?
  220. filepath = Path(filepath)
  221. self.logger.debug('starting file: {0}'.format(filepath))
  222. try:
  223. os.startfile(filepath)
  224. except IOError as e:
  225. self.logger.error(e)
  226. return filepath
  227. # ---------------------------------------------------------------------
  228. # --method-------------------------------------------------------------
  229. def explore_file(self, filepath=None):
  230. if filepath == None:
  231. filepath = self.path
  232. if not isinstance(filepath, Path):
  233. filepath = Path(filepath)
  234. self.logger.debug('exploring file: {0}'.format(filepath))
  235. if filepath.exists():
  236. try:
  237. subprocess.Popen(r'explorer /select,"{0}"'.format(filepath))
  238. except IOError as e:
  239. self.logger.error(e)
  240. else:
  241. self.logger.error('file does not exist: {0}'.format(filepath))
  242. return filepath
  243. # ---------------------------------------------------------------------
  244. # --method-------------------------------------------------------------
  245. def hierarchy(self, tabLevel=-1):
  246. output = ''
  247. if isinstance(self, RootNode):
  248. if gDebug:
  249. func = inspect.currentframe().f_back.f_code
  250. output += ('{0}Called from:\n'
  251. '{0}{1}\n'.format('\t' * (tabLevel + 1), func))
  252. tabLevel += 1
  253. for i in range(tabLevel):
  254. output += '\t'
  255. output += ('{tab}/------ nodeName:: "{0}"\n'
  256. '{1} |typeInfo:: {2}\n'
  257. '{1} |_uniHashid:: "{3}"\r'
  258. '{1} |path:: "{4}"\n'
  259. '{1} |get_root():: "{5}"\n'
  260. '{1} |getPathFromRoot():: "{6}"\n'
  261. ''.format(self.getNodeName(),
  262. '\t' * tabLevel,
  263. self.get_typeInfo(),
  264. self.get_uniHashid(),
  265. self,
  266. self.get_root(),
  267. self.getPathFromRoot(),
  268. tab=tabLevel))
  269. for child in self._children:
  270. output += child.hierarchy(tabLevel)
  271. tabLevel -= 1
  272. return output
  273. # ---------------------------------------------------------------------
  274. # --Class End--------------------------------------------------------------
  275. ###########################################################################
  276. # tests(), code block for testing module
  277. # -------------------------------------------------------------------------
  278. def tests():
  279. from node import Node
  280. default_node = Node() # result: Node(node_name='PRIME')
  281. print(default_node)
  282. first_child = PathNode(path=None, node_name='first_child', parent_node=default_node)
  283. print(first_child)
  284. # result: PathNode(temp_node=True, parent_node=Node(node_name='PRIME')).siblingNodeFromHashid('WNPZoKBVpXV16QLz')
  285. # first_child.nodeType
  286. # first_child.parent_node
  287. # first_child.node_name
  288. try:
  289. # PathNode requires a arg 'path' input (should be a path str)
  290. fubar_path_node = PathNode() # <-- this should fail
  291. print (fubar_path_node)
  292. except Exception as err:
  293. print ('\r{0}'.format(err))
  294. print (traceback.format_exc())
  295. foo = PathNode(r'\foo\fooey\kablooey', node_name='foo',
  296. parent_node=default_node)
  297. print(foo)
  298. testes = Path(r'/foo/fooey/kablooey')
  299. print(foo.path.exists())
  300. print(foo.path.parent)
  301. print(foo.path.norm_case())
  302. print(foo.path.absolute())
  303. fooey = PathNode(None, parent_node=foo)
  304. print(fooey)
  305. kablooey = PathNode(r'\foo\fooey\kablooey',
  306. parent_node=default_node,
  307. name_is_path=True)
  308. print(kablooey)
  309. kablooey = kablooey.set_file_path(r'c:\mytemp\fubar.txt')
  310. print(kablooey)
  311. kablooey.start_file()
  312. kablooey.explore_file()
  313. return
  314. # - END, tests() ----------------------------------------------------------
  315. def main():
  316. pass
  317. return
  318. # - END, main() -----------------------------------------------------------
  319. ###########################################################################
  320. # --call block-------------------------------------------------------------
  321. if __name__ == "__main__":
  322. print ("# ----------------------------------------------------------------------- #")
  323. print ('~ noodly.PathNode ... Running script as __main__')
  324. print ("# ----------------------------------------------------------------------- #\r")
  325. # run simple tests
  326. tests()