class Foo1: foo2 = Inject('Foo2') def __init__(self, val): self.val = val def Start(self): print(self.foo2.val)
class Foo2: foo1 = Inject('Foo1') def __init__(self, val): self.val = val def Start(self): print('foo2 {0}, foo1 {1}'.format(self.val, self.foo1.val))
class Test2: qux = Inject('Qux') def __init__(self): self.X = 0 def Run(self): print(self.qux.GetValue())
class ZipHelper: _sys = Inject('SystemHelper') _varMgr = Inject('VarManager') _log = Inject('Logger') def createZipFile(self, dirPath, zipFilePath): assertThat(zipFilePath.endswith('.zip')) dirPath = self._varMgr.expandPath(dirPath) zipFilePath = self._varMgr.expandPath(zipFilePath) self._sys.makeMissingDirectoriesInPath(zipFilePath) self._sys.removeFileIfExists(zipFilePath) self._log.debug("Writing directory '{0}' to zip at '{1}'", dirPath, zipFilePath) self._writeDirectoryToZipFile(zipFilePath, dirPath) def _writeDirectoryToZipFile(self, zipFilePath, dirPath): with zipfile.ZipFile(zipFilePath, 'w', zipfile.ZIP_DEFLATED) as zipf: self._zipAddDir(zipf, dirPath, '') def _zipAddDir(self, zipf, dirPath, zipPathPrefix=None): dirPath = self._varMgr.expandPath(dirPath) assertThat(os.path.isdir(dirPath), 'Invalid directory given at "{0}"'.format(dirPath)) if zipPathPrefix is None: zipPathPrefix = os.path.basename(dirPath) for root, dirs, files in os.walk(dirPath): for file in files: filePath = os.path.join(root, file) zipf.write( filePath, os.path.join(zipPathPrefix, os.path.relpath(filePath, dirPath)))
class ScriptRunner: _log = Inject('Logger') def runWrapper(self, runner): startTime = datetime.now() succeeded = False try: runner() succeeded = True except KeyboardInterrupt as e: self._log.endHeading() self._log.error('Operation aborted by user by hitting CTRL+C') except Exception as e: self._log.endHeading() self._log.error(str(e)) if self._log.currentHeading: self._log.error("Failed while executing '" + self._log.currentHeading + "'") # Only print stack trace if it's a build-script error if not isinstance(e, ProcessErrorCodeException) and not isinstance( e, ProcessTimeoutException): self._log.debug('\n' + traceback.format_exc()) totalSeconds = (datetime.now() - startTime).total_seconds() totalSecondsStr = Util.formatTimeDelta(totalSeconds) if succeeded: self._log.finished('Operation completed successfully. Took ' + totalSecondsStr + '.\n') else: self._log.finished('Operation completed with errors. Took ' + totalSecondsStr + '.\n') return succeeded
class Test2: title = Inject('Title', Assertions.IsInstanceOf(str)) def Run(self): print('title: {0}'.format(self.title))
class Test1: con = Inject('Console', Assertions.HasMethods('WriteLine')) def Run(self): self.con.WriteLine('lorem ipsum')
class VisualStudioHelper: _log = Inject('Logger') _config = Inject('Config') _packageManager = Inject('PackageManager') _unityHelper = Inject('UnityHelper') _varMgr = Inject('VarManager') _sys = Inject('SystemHelper') _vsSolutionGenerator = Inject('VisualStudioSolutionGenerator') def openFile(self, filePath, lineNo, project, platform): if not lineNo or lineNo <= 0: lineNo = 1 if MiscUtil.doesProcessExist('^devenv\.exe$'): self.openFileInExistingVisualStudioInstance(filePath, lineNo) # This works too but doesn't allow going to a specific line #self._sys.executeNoWait('[VisualStudioCommandLinePath] /edit "{0}"'.format(filePath)) else: # Unfortunately, in this case we can't pass in the line number self.openCustomSolution(project, platform, filePath) def openFileInExistingVisualStudioInstance(self, filePath, lineNo): try: dte = win32com.client.GetActiveObject("VisualStudio.DTE.12.0") dte.MainWindow.Activate dte.ItemOperations.OpenFile(self._sys.canonicalizePath(filePath)) dte.ActiveDocument.Selection.MoveToLineAndOffset(lineNo, 1) except Exception as error: raise Exception( "COM Error. This is often triggered when given a bad line number. Details: {0}" .format(win32api.FormatMessage(error.excepinfo[5]))) def openVisualStudioSolution(self, solutionPath, filePath=None): if not self._varMgr.hasKey('VisualStudioIdePath'): assertThat( False, "Path to visual studio has not been defined. Please set <VisualStudioIdePath> within one of your {0} files", ConfigFileName) if self._sys.fileExists('[VisualStudioIdePath]'): self._sys.executeNoWait('"[VisualStudioIdePath]" {0} {1}'.format( self._sys.canonicalizePath(solutionPath), self._sys.canonicalizePath(filePath) if filePath else "")) else: assertThat( False, "Cannot find path to visual studio. Expected to find it at '{0}'" .format(self._varMgr.expand('[VisualStudioIdePath]'))) def updateCustomSolution(self, project, platform): self._vsSolutionGenerator.updateVisualStudioSolution(project, platform) def openCustomSolution(self, project, platform, filePath=None): self.openVisualStudioSolution( self._getCustomSolutionPath(project, platform), filePath) def buildCustomSolution(self, project, platform): solutionPath = self._getCustomSolutionPath(project, platform) if not self._sys.fileExists(solutionPath): self._log.warn( 'Could not find generated custom solution. Generating now.') self._vsSolutionGenerator.updateVisualStudioSolution( project, platform) self._log.heading('Building {0}-{1}.sln'.format(project, platform)) self.buildVisualStudioProject(solutionPath, 'Debug') def buildVisualStudioProject(self, solutionPath, buildConfig): if self._config.getBool('Compilation', 'UseDevenv'): buildCommand = '"[VisualStudioCommandLinePath]" {0} /build "{1}"'.format( solutionPath, buildConfig) else: buildCommand = '"[MsBuildExePath]" /p:VisualStudioVersion=12.0' #if rebuild: #buildCommand += ' /t:Rebuild' buildCommand += ' /p:Configuration="{0}" "{1}"'.format( buildConfig, solutionPath) self._sys.executeAndWait(buildCommand) def _getCustomSolutionPath(self, project, platform): return '[UnityProjectsDir]/{0}/{0}-{1}.sln'.format(project, platform) def updateUnitySolution(self, projectName, platform): """ Simply runs unity and then generates the monodevelop solution file using an editor script This is used when generating the Visual Studio Solution to get DLL references and defines etc. """ self._log.heading( 'Updating unity generated solution for project {0} ({1})'.format( projectName, platform)) self._packageManager.checkProjectInitialized(projectName, platform) # This will generate the unity csproj files which we need to generate Modest3d.sln correctly # It's also necessary to run this first on clean checkouts to initialize unity properly self._unityHelper.runEditorFunction( projectName, platform, 'Projeny.ProjenyEditorUtil.UpdateMonodevelopProject')
class ProcessRunner: _log = Inject('Logger') def execNoWait(self, vals, startDir): params = {} if startDir != None: params['cwd'] = startDir Popen(vals, **params) def waitForProcessOrTimeout(self, commandVals, seconds, startDir=None): params = {} params['stdout'] = subprocess.PIPE params['stderr'] = subprocess.STDOUT if startDir != None: params['cwd'] = startDir proc = Popen(commandVals, **params) # TODO - clean this up so there's only one thread, then # do the timeout logic on the main thread timeout = KillProcessThread(seconds, proc.pid) timeout.run() def enqueueOutput(out, queue): for line in iter(out.readline, b''): queue.put(line) out.close() # We use a queue here instead of just calling stdout.readline() on the main thread # so that we can catch the KeyboardInterrupt event, and force kill the process queue = Queue() thread = threading.Thread(target=enqueueOutput, args=(proc.stdout, queue)) thread.daemon = True # thread dies with the program thread.start() while True: try: try: line = queue.get_nowait() self._log.debug(line.decode('utf-8').rstrip()) except Empty: if not thread.isAlive(): break time.sleep(0.2) except KeyboardInterrupt as e: self._log.error( "Detected KeyboardInterrupt - killing process...") timeout.forceKill() raise e resultCode = proc.wait() timeout.cancel() if timeout.timeOutOccurred: return ResultType.TimedOut if resultCode != 0: return ResultType.Error return ResultType.Success # Note that in this case we pass the command as a string # This is recommended by the python docs here when using shell = True # https://docs.python.org/2/library/subprocess.html#subprocess.Popen def execShellCommand(self, commandStr, startDir=None): params = {} params['stdout'] = subprocess.PIPE params['stderr'] = subprocess.PIPE params['shell'] = True if startDir != None: params['cwd'] = startDir # Would be nice to get back output in real time but I can't figure # out a way to do this # This method should only be used for a few command-prompt specific # commands anyway so not a big loss proc = Popen(commandStr, **params) (stdoutData, stderrData) = proc.communicate() output = stdoutData.decode('utf-8').strip() errors = stderrData.decode('utf-8').strip() if output: for line in output.split('\n'): self._log.debug(line) if errors: self._log.error( 'Error occurred during command "{0}":'.format(commandStr)) for line in errors.split('\n'): self._log.error(' ' + line) exitStatus = proc.returncode if exitStatus != 0: return ResultType.Error return ResultType.Success
class Runner: _scriptRunner = Inject('ScriptRunner') _unityHelper = Inject('UnityHelper') _log = Inject('Logger') _sys = Inject('SystemHelper') _varManager = Inject('VarManager') _zipHelper = Inject('ZipHelper') def __init__(self): self._platform = Platforms.Windows def run(self, args): self._args = args success = self._scriptRunner.runWrapper(self._runInternal) if not success: sys.exit(1) def _runBuilds(self): if self._args.clearOutput: self._log.heading("Clearing output directory") self._sys.clearDirectoryContents('[OutputRootDir]') self._clearTempDirectory() self._copyToTempDirectory() if self._args.buildType == 'all' or self._args.buildType == 'pc': self._log.heading("Building windows") self._platform = Platforms.Windows self._createBuild() self._zipHelper.createZipFile('[OutputRootDir]/Windows', '[OutputRootDir]/Windows.zip') if self._args.buildType == 'all' or self._args.buildType == 'webgl': self._log.heading("Building WebGl") self._platform = Platforms.WebGl self._createBuild() self._sys.copyFile('[WebGlTemplate]', '[OutputRootDir]/WebGl/Web.config') self._zipHelper.createZipFile( '[OutputRootDir]/WebGl', '[OutputRootDir]/GreatEggscapeWebGl.zip') def _clearTempDirectory(self): self._sys.deleteDirectoryIfExists('[TempDir]') self._sys.createDirectory('[TempDir]') self._sys.clearDirectoryContents('[TempDir]') def _copyToTempDirectory(self): self._log.info('Copying to temporary directory') try: self._sys.copyDirectory('[UnityProjectPath]', '[UnityProjectPathTempDir]') except: pass def _runInternal(self): if self._args.runBuilds: self._runBuilds() if self._args.openUnity: self._openUnity() def _createBuild(self): self._log.info("Creating build") self._runEditorFunction('BuildRelease') #self._runEditorFunction('BuildDebug') def _openUnity(self): self._unityHelper.openUnity('[UnityProjectPath]', self._platform) def _runEditorFunction(self, functionName): self._log.info("Calling Builder." + functionName) self._unityHelper.runEditorFunction('[UnityProjectPathTempDir]', 'Builder.' + functionName, self._platform)
class SystemHelper: '''Responsibilities: - Miscellaneous file-handling/path-related operations - Wrapper to execute arbitrary commands ''' _varManager = Inject('VarManager') _log = Inject('Logger') _processRunner = Inject('ProcessRunner') # Use an hour timeout def __init__(self, timeout = 60 * 60): self._timeout = timeout def canonicalizePath(self, pathStr): # Make one standard representation of the given path # This will remove ..\ and also change to always use back slashes since this is what os.path.join etc. uses return self._varManager.expandPath(pathStr) def executeAndWait(self, commandStr, startDir = None): expandedStr = self._varManager.expand(commandStr) self._log.debug("Executing '%s'" % expandedStr) vals = self._splitCommandStr(expandedStr) if startDir != None: startDir = self._varManager.expand(startDir) result = self._processRunner.waitForProcessOrTimeout(vals, self._timeout, startDir) if result == ResultType.Error: raise ProcessErrorCodeException('Command returned with error code while executing: %s' % expandedStr) if result == ResultType.TimedOut: raise ProcessTimeoutException('Timed out while waiting for command: %s' % expandedStr) assertThat(result == ResultType.Success) def executeNoWait(self, commandStr, startDir = None): expandedStr = self._varManager.expand(commandStr) self._log.debug("Executing '{0}'".format(expandedStr)) vals = self._splitCommandStr(expandedStr) if startDir != None: startDir = self._varManager.expand(startDir) self._processRunner.execNoWait(vals, startDir) # This is only used to execute shell-specific commands like copy, mklink, etc. def executeShellCommand(self, commandStr, startDir = None): expandedStr = self._varManager.expand(commandStr) self._log.debug("Executing '%s'" % expandedStr) if startDir != None: startDir = self._varManager.expand(startDir) result = self._processRunner.execShellCommand(expandedStr, startDir) if result == ResultType.Error: raise ProcessErrorCodeException('Command returned with error code while executing: %s' % expandedStr) assertThat(result == ResultType.Success, "Expected success result but found '{0}'".format(result)) def _splitCommandStr(self, commandStr): # Hacky but necessary since shlex.split will otherwise remove our backslashes if platform.platform().startswith('Windows'): commandStr = commandStr.replace(os.sep, os.sep + os.sep) # Convert command to argument list to avoid issues with escape characters, etc. # Based on an answer here: http://stackoverflow.com/questions/12081970/python-using-quotes-in-the-subprocess-popen return shlex.split(commandStr) def executeAndReturnOutput(self, commandStr): self._log.debug("Executing '%s'" % commandStr) return subprocess.getoutput(self._splitCommandStr(commandStr)).strip() def walkDir(self, dirPath): dirPath = self._varManager.expand(dirPath) return os.listdir(dirPath) def getParentDirectoriesWithSelf(self, path): yield path for parentDir in self.getParentDirectories(path): yield parentDir def getParentDirectories(self, path): path = self._varManager.expand(path) lastParentDir = None parentDir = os.path.dirname(path) while parentDir and parentDir != lastParentDir: yield parentDir lastParentDir = parentDir parentDir = os.path.dirname(parentDir) def createDirectory(self, dirPath): dirPath = self._varManager.expand(dirPath) assertThat(not self.directoryExists(dirPath), 'Tried to create a directory that already exists') os.makedirs(dirPath) def makeMissingDirectoriesInPath(self, dirPath): dirPath = self._varManager.expand(dirPath) try: os.makedirs(os.path.dirname(dirPath)) except: pass def copyFile(self, fromPath, toPath): toPath = self._varManager.expand(toPath) fromPath = self._varManager.expand(fromPath) self.makeMissingDirectoriesInPath(toPath) shutil.copy2(fromPath, toPath) def move(self, fromPath, toPath): toPath = self._varManager.expand(toPath) fromPath = self._varManager.expand(fromPath) self.makeMissingDirectoriesInPath(toPath) shutil.move(fromPath, toPath) def IsDir(self, path): return os.path.isdir(self._varManager.expand(path)) def clearDirectoryContents(self, dirPath): dirPath = self._varManager.expand(dirPath) if not os.path.exists(dirPath): return for fileName in os.listdir(dirPath): filePath = os.path.join(dirPath, fileName) if os.path.isfile(filePath): os.unlink(filePath) elif os.path.isdir(filePath): shutil.rmtree(filePath) def deleteDirectoryWaitIfNecessary(self, dirPath): dirPath = self._varManager.expand(dirPath) if not os.path.isdir(dirPath): # Already removed return attemptsLeft = 10 while True: try: shutil.rmtree(dirPath) except Exception as e: self._log.warn('Could not delete directory at "{0}". Waiting to try again...'.format(dirPath)) time.sleep(5) attemptsLeft -= 1 if attemptsLeft < 0: raise e continue break def deleteDirectory(self, dirPath): dirPath = self._varManager.expand(dirPath) shutil.rmtree(dirPath) def deleteDirectoryIfExists(self, dirPath): dirPath = self._varManager.expand(dirPath) if os.path.exists(dirPath): shutil.rmtree(dirPath) return True return False def deleteEmptyDirectoriesUnder(self, dirPath): dirPath = self._varManager.expandPath(dirPath) if not os.path.isdir(dirPath): return 0 # Can't process long paths on windows if len(dirPath) >= 256: return 0 files = os.listdir(dirPath) numDirsDeleted = 0 for fileName in files: fullpath = os.path.join(dirPath, fileName) if os.path.isdir(fullpath): numDirsDeleted += self.deleteEmptyDirectoriesUnder(fullpath) files = os.listdir(dirPath) if len(files) == 0: self._log.debug("Removing empty folder '%s'" % dirPath) os.rmdir(dirPath) numDirsDeleted += 1 metaFilePath = dirPath + '/../' + os.path.basename(dirPath) + '.meta' if os.path.isfile(metaFilePath): self._log.debug("Removing meta file '%s'" % metaFilePath) os.remove(metaFilePath) return numDirsDeleted def fileExists(self, path): return os.path.isfile(self._varManager.expand(path)) def directoryExists(self, dirPath): return os.path.exists(self._varManager.expand(dirPath)) def copyDirectory(self, fromPath, toPath): fromPath = self._varManager.expand(fromPath) toPath = self._varManager.expand(toPath) self._log.debug("Copying directory '{0}' to '{1}'".format(fromPath, toPath)) shutil.copytree(fromPath, toPath) def readFileAsText(self, path): with self.openInputFile(path) as f: return f.read() def writeFileAsText(self, path, text): with self.openOutputFile(path) as f: f.write(text) def openOutputFile(self, path): path = self._varManager.expand(path) self.makeMissingDirectoriesInPath(path) return open(path, 'w', encoding='utf-8', errors='ignore') def openInputFile(self, path): return open(self._varManager.expand(path), 'r', encoding='utf-8', errors='ignore') def removeFile(self, fileName): os.remove(self._varManager.expand(fileName)) def removeFileIfExists(self, fileName): fullPath = self._varManager.expand(fileName) if os.path.isfile(fullPath): os.remove(fullPath) return True return False def findFilesByPattern(self, startDir, pattern): startDir = self._varManager.expand(startDir) for root, dirs, files in os.walk(startDir): for basename in files: if fnmatch.fnmatch(basename, pattern): filename = os.path.join(root, basename) yield filename def renameFile(self, currentName, newName): os.rename(self._varManager.expand(currentName), self._varManager.expand(newName)) def removeFileWaitIfNecessary(self, fileName): outputPath = self._varManager.expand(fileName) if not os.path.isfile(outputPath): # File already removed return while True: try: os.remove(outputPath) except OSError: self._log.warn('Could not delete file at "{0}". Waiting to try again...'.format(outputPath)) time.sleep(5) continue break def removeByRegex(self, regex): regex = self._varManager.expand(regex) count = 0 for filePath in glob(regex): os.unlink(filePath) count += 1 self._log.debug("Removed %s files matching '%s'" % (count, regex)) def makeMissingDirectoriesInPath(self, dirPath): dirPath = self._varManager.expand(dirPath) self._log.debug("Making missing directories in path '{0}'".format(dirPath)) try: os.makedirs(os.path.dirname(dirPath)) except: pass
class LogStreamConsole: _log = Inject('Logger') _sys = Inject('SystemHelper') _varManager = Inject('VarManager') _config = Inject('Config') def __init__(self, verbose, veryVerbose): self._verbose = verbose self._veryVerbose = veryVerbose self.headingPatterns = self._getPatterns('HeadingPatterns') self.headingMaps = self._getPatternMaps('HeadingPatternMaps') self.goodPatterns = self._getPatterns('GoodPatterns') self.goodMaps = self._getPatternMaps('GoodPatternMaps') self.infoPatterns = self._getPatterns('InfoPatterns') self.infoMaps = self._getPatternMaps('InfoPatternMaps') self.errorPatterns = self._getPatterns('ErrorPatterns') self.errorMaps = self._getPatternMaps('ErrorPatternMaps') self.warningPatterns = self._getPatterns('WarningPatterns') self.warningMaps = self._getPatternMaps('WarningPatternMaps') self.warningPatternsIgnore = self._getPatterns('WarningPatternsIgnore') self.debugPatterns = self._getPatterns('DebugPatterns') self.debugMaps = self._getPatternMaps('DebugPatternMaps') self._useColors = self._config.tryGetBool(False, 'Console', 'UseColors') self._fileStream = None if self._config.tryGetBool(False, 'Console', 'OutputToFilteredLog'): self._fileStream = self._getFileStream() if self._useColors: self._initColors() def _initColors(self): self._defaultColors = ColorConsole.get_text_attr() self._defaultBg = self._defaultColors & 0x0070 self._defaultFg = self._defaultColors & 0x0007 def log(self, logType, message): logType, message = self.classifyMessage(logType, message) if logType is not None: if logType == LogType.HeadingFailed or logType == LogType.Error: self._output(logType, message, sys.stderr, self._useColors) else: self._output(logType, message, sys.stdout, self._useColors) if self._fileStream: self._output(logType, message, self._fileStream, False) def _getFileStream(self): primaryPath = self._varManager.expand('[LogFilteredPath]') if not primaryPath: raise Exception("Could not find path for log file") previousPath = None if self._varManager.hasKey('LogFilteredPreviousPath'): previousPath = self._varManager.expand('[LogFilteredPreviousPath]') # Keep one old build log if os.path.isfile(primaryPath) and previousPath: shutil.copy2(primaryPath, previousPath) return open(primaryPath, 'w', encoding='utf-8', errors='ignore') def _output(self, logType, message, stream, useColors): stream.write('\n') if self._log.hasHeading and logType != LogType.Heading and logType != LogType.HeadingSucceeded and logType != LogType.HeadingFailed: stream.write(' ') if not useColors or logType == LogType.Info: stream.write(message) stream.flush() else: ColorConsole.set_text_attr(self._getColorAttrs(logType)) stream.write(message) stream.flush() ColorConsole.set_text_attr(self._defaultColors) def _getColorAttrs(self, logType): if logType == LogType.Heading or logType == LogType.HeadingSucceeded: return ColorConsole.FOREGROUND_CYAN | self._defaultBg | ColorConsole.FOREGROUND_INTENSITY if logType == LogType.Good: return ColorConsole.FOREGROUND_GREEN | self._defaultBg | ColorConsole.FOREGROUND_INTENSITY if logType == LogType.Warn: return ColorConsole.FOREGROUND_YELLOW | self._defaultBg | ColorConsole.FOREGROUND_INTENSITY if logType == LogType.Error or logType == LogType.HeadingFailed: return ColorConsole.FOREGROUND_RED | self._defaultBg | ColorConsole.FOREGROUND_INTENSITY if logType == LogType.Debug: return ColorConsole.FOREGROUND_BLACK | self._defaultBg | ColorConsole.FOREGROUND_INTENSITY assertThat(False, 'Unrecognized log type "{0}"'.format(logType)) def _getPatternMaps(self, settingName): maps = self._config.tryGetDictionary({}, 'Console', settingName) result = [] for key, value in maps.items(): regex = re.compile(key) logMap = LogMap(regex, value) result.append(logMap) return result def _getPatterns(self, settingName): patternStrings = self._config.tryGetList([], 'Console', settingName) result = [] for pattern in patternStrings: result.append(re.compile('.*' + pattern + '.*')) return result def tryMatchPattern(self, message, maps, patterns): for logMap in maps: if logMap.regex.match(message): return logMap.regex.sub(logMap.sub, message) for pattern in patterns: match = pattern.match(message) if match: groups = match.groups() if len(groups) > 0: return groups[0] return message return None def classifyMessage(self, logType, message): if logType == LogType.Info or logType == LogType.Heading or logType == LogType.HeadingFailed or logType == LogType.HeadingSucceeded or logType == LogType.Good or logType == LogType.Warn or logType == LogType.Error: return logType, message parsedMessage = self.tryMatchPattern(message, self.errorMaps, self.errorPatterns) if parsedMessage: return LogType.Error, parsedMessage if not any(p.match(message) for p in self.warningPatternsIgnore): parsedMessage = self.tryMatchPattern(message, self.warningMaps, self.warningPatterns) if parsedMessage: return LogType.Warn, parsedMessage parsedMessage = self.tryMatchPattern(message, self.headingMaps, self.headingPatterns) if parsedMessage: return LogType.Heading, parsedMessage parsedMessage = self.tryMatchPattern(message, self.goodMaps, self.goodPatterns) if parsedMessage: return LogType.Good, parsedMessage parsedMessage = self.tryMatchPattern(message, self.infoMaps, self.infoPatterns) if parsedMessage: return LogType.Info, parsedMessage if self._verbose: parsedMessage = self.tryMatchPattern(message, self.debugMaps, self.debugPatterns) if parsedMessage: return LogType.Debug, parsedMessage if self._veryVerbose: return LogType.Debug, message return None, message
class Test1: foo = Inject('Foo') bar = InjectOptional('Bar', 5)
class VarManager: _config = Inject('Config') ''' Stores a dictionary of keys to values to replace path variables with ''' def __init__(self, initialParams=None): self._params = initialParams if initialParams else {} self._params['StartCurrentDir'] = os.getcwd() self._params['ExecDir'] = MiscUtil.getExecDirectory().replace( '\\', '/') # We could just call self._config.getDictionary('PathVars') here but # then we wouldn't be able to use fallback (?) and override (!) characters in # our config self._regex = re.compile('^([^\[]*)(\[[^\]]*\])(.*)$') def hasKey(self, key): return key in self._params or self._config.tryGet('PathVars', key) != None def get(self, key): if key in self._params: return self._params[key] return self._config.getString('PathVars', key) def tryGet(self, key): if key in self._params: return self._params[key] return self._config.tryGetString(None, 'PathVars', key) def set(self, key, value): self._params[key] = value def expandPath(self, text, extraVars=None): ''' Same as expand() except it cleans up the path to remove ../ ''' return os.path.realpath(self.expand(text, extraVars)) def expand(self, text, extraVars=None): if not extraVars: extraVars = {} allArgs = self._params.copy() allArgs.update(extraVars) while True: match = self._regex.match(text) if not match: break prefix = match.group(1) var = match.group(2) suffix = match.group(3) var = var[1:-1] if var in allArgs: replacement = allArgs[var] else: replacement = self.get(var) text = prefix + replacement + suffix if '[' in text: raise Exception( "Unable to find all keys in path '{0}'".format(text)) return text
class UnityHelper: _log = Inject('Logger') _sys = Inject('SystemHelper') _varMgr = Inject('VarManager') def __init__(self): pass def onUnityLog(self, logStr): self._log.debug(logStr) def openUnity(self, projectPath, platform): self._sys.executeNoWait( '"[UnityExePath]" -buildTarget {0} -projectPath "{1}"'.format( self._getBuildTargetArg(platform), projectPath)) def runEditorFunction(self, projectPath, editorCommand, platform=Platforms.Windows, batchMode=True, quitAfter=True, extraExtraArgs=''): extraArgs = '' if quitAfter: extraArgs += ' -quit' if batchMode: extraArgs += ' -batchmode -nographics' extraArgs += ' ' + extraExtraArgs self.runEditorFunctionRaw(projectPath, editorCommand, platform, extraArgs) def _getBuildTargetArg(self, platform): if platform == Platforms.Windows: return 'win32' if platform == Platforms.WebPlayer: return 'web' if platform == Platforms.Android: return 'android' if platform == Platforms.WebGl: return 'WebGl' if platform == Platforms.OsX: return 'osx' if platform == Platforms.Linux: return 'linux' if platform == Platforms.Ios: return 'ios' if platform == Platforms.WindowsStoreApp: return 'wsa' assertThat(False, "Unhandled platform {0}".format(platform)) def runEditorFunctionRaw(self, projectPath, editorCommand, platform, extraArgs): logPath = self._varMgr.expandPath(UnityLogFileLocation) logWatcher = LogWatcher(logPath, self.onUnityLog) logWatcher.start() assertThat(self._varMgr.hasKey('UnityExePath'), "Could not find path variable 'UnityExePath'") try: command = '"[UnityExePath]" -buildTarget {0} -projectPath "{1}"'.format( self._getBuildTargetArg(platform), projectPath) if editorCommand: command += ' -executeMethod ' + editorCommand command += ' ' + extraArgs self._sys.executeAndWait(command) except ProcessErrorCodeException as e: raise UnityReturnedErrorCodeException( "Error while running Unity! Command returned with error code." ) except: raise UnityUnknownErrorException( "Unknown error occurred while running Unity!") finally: logWatcher.stop() while not logWatcher.isDone: time.sleep(0.1)