Tue, 19 Nov 2019 18:53:58 +0100
Syntax Checker:
- added a syntax checker for JSON files
- added a syntax checker for YAML files
--- a/docs/changelog Thu Nov 14 19:39:07 2019 +0100 +++ b/docs/changelog Tue Nov 19 18:53:58 2019 +0100 @@ -6,6 +6,9 @@ -- added a menu entry to show the local and device time side-by-side -- added a PyBoard menu entry to flash a new firmware -- added a menu entry to open the firmware download page of a device +- Syntax Checker + -- added a syntax checker for JSON files + -- added a syntax checker for YAML files - install script -- added the "--yes" flag to allow installation of all missing prerequisites with pip without asking
--- a/eric6.e4p Thu Nov 14 19:39:07 2019 +0100 +++ b/eric6.e4p Tue Nov 19 18:53:58 2019 +0100 @@ -319,10 +319,12 @@ <Source>eric6/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py</Source> <Source>eric6/Plugins/CheckerPlugins/SyntaxChecker/__init__.py</Source> <Source>eric6/Plugins/CheckerPlugins/SyntaxChecker/jsCheckSyntax.py</Source> + <Source>eric6/Plugins/CheckerPlugins/SyntaxChecker/jsonCheckSyntax.py</Source> <Source>eric6/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/__init__.py</Source> <Source>eric6/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/checker.py</Source> <Source>eric6/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/messages.py</Source> <Source>eric6/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/translations.py</Source> + <Source>eric6/Plugins/CheckerPlugins/SyntaxChecker/yamlCheckSyntax.py</Source> <Source>eric6/Plugins/CheckerPlugins/Tabnanny/Tabnanny.py</Source> <Source>eric6/Plugins/CheckerPlugins/Tabnanny/TabnannyDialog.py</Source> <Source>eric6/Plugins/CheckerPlugins/Tabnanny/__init__.py</Source>
--- a/eric6/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheck.py Thu Nov 14 19:39:07 2019 +0100 +++ b/eric6/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheck.py Tue Nov 19 18:53:58 2019 +0100 @@ -2,7 +2,6 @@ # Copyright (c) 2011 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> # -# pylint: disable=C0103 """ Module implementing the syntax check for Python 2/3. @@ -202,7 +201,7 @@ def worker(inputQueue, outputQueue): """ - Module function acting as the parallel worker for the style check. + Module function acting as the parallel worker for the syntax check. @param inputQueue input queue (multiprocessing.Queue) @param outputQueue output queue (multiprocessing.Queue)
--- a/eric6/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckService.py Thu Nov 14 19:39:07 2019 +0100 +++ b/eric6/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckService.py Tue Nov 19 18:53:58 2019 +0100 @@ -240,6 +240,38 @@ self.__serviceError(self.tr("JavaScript batch check"), msg) self.batchJobDone(fx, lang) + def serviceErrorYAML(self, fx, lang, fn, msg): + """ + Public method handling service errors for YAML. + + @param fx service name (string) + @param lang language (string) + @param fn file name (string) + @param msg message text (string) + """ + if fx in ['YAMLSyntax', 'batch_YAMLSyntax']: + if fx == 'YAMLSyntax': + self.__serviceError(fn, msg) + else: + self.__serviceError(self.tr("YAML batch check"), msg) + self.batchJobDone(fx, lang) + + def serviceErrorJSON(self, fx, lang, fn, msg): + """ + Public method handling service errors for JSON. + + @param fx service name (string) + @param lang language (string) + @param fn file name (string) + @param msg message text (string) + """ + if fx in ['JSONSyntax', 'batch_JSONSyntax']: + if fx == 'JSONSyntax': + self.__serviceError(fn, msg) + else: + self.__serviceError(self.tr("JSON batch check"), msg) + self.batchJobDone(fx, lang) + def batchJobDone(self, fx, lang): """ Public slot handling the completion of a batch job. @@ -247,9 +279,13 @@ @param fx service name (string) @param lang language (string) """ - if fx in ['Python2Syntax', 'batch_Python2Syntax', - 'Python3Syntax', 'batch_Python3Syntax', - 'JavaScriptSyntax', 'batch_JavaScriptSyntax']: + if fx in [ + 'Python2Syntax', 'batch_Python2Syntax', + 'Python3Syntax', 'batch_Python3Syntax', + 'JavaScriptSyntax', 'batch_JavaScriptSyntax', + 'YAMLSyntax', 'batch_YAMLSyntax', + 'JSONSyntax', 'batch_JSONSyntax', + ]: if lang in self.queuedBatches: self.queuedBatches.remove(lang) # prevent sending the signal multiple times
--- a/eric6/Plugins/CheckerPlugins/SyntaxChecker/jsCheckSyntax.py Thu Nov 14 19:39:07 2019 +0100 +++ b/eric6/Plugins/CheckerPlugins/SyntaxChecker/jsCheckSyntax.py Tue Nov 19 18:53:58 2019 +0100 @@ -2,19 +2,12 @@ # Copyright (c) 2014 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> # -# pylint: disable=C0103 """ -Module implementing the syntax check for Python 2/3. +Module implementing the syntax check for JavaScript. """ -from __future__ import unicode_literals - -try: # Only for Py2 - import Queue as queue -except ImportError: - import queue - +import queue import os import sys import multiprocessing @@ -144,7 +137,7 @@ def worker(inputQueue, outputQueue): """ - Module function acting as the parallel worker for the style check. + Module function acting as the parallel worker for the syntax check. @param inputQueue input queue (multiprocessing.Queue) @param outputQueue output queue (multiprocessing.Queue)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/Plugins/CheckerPlugins/SyntaxChecker/jsonCheckSyntax.py Tue Nov 19 18:53:58 2019 +0100 @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the syntax check for JSON. +""" + +import queue +import multiprocessing + + +def initService(): + """ + Initialize the service and return the entry point. + + @return the entry point for the background client + @rtype func + """ + return jsonSyntaxCheck + + +def initBatchService(): + """ + Initialize the batch service and return the entry point. + + @return the entry point for the background client + @rtype func + """ + return jsonSyntaxBatchCheck + + +def normalizeCode(codestring): + """ + Function to normalize the given code. + + @param codestring code to be normalized + @type str + @return normalized code + @rtype str + """ + codestring = codestring.replace("\r\n", "\n").replace("\r", "\n") + + if codestring and codestring[-1] != '\n': + codestring = codestring + '\n' + + return codestring + + +def jsonSyntaxCheck(file, codestring): + """ + Function to check a JSON source file for syntax errors. + + @param file source filename + @type str + @param codestring string containing the code to check + @type str + @return dictionary with the keys 'error' and 'warnings' which + hold a list containing details about the error/ warnings + (file name, line number, column, codestring (only at syntax + errors), the message, a list with arguments for the message) + @rtype dict + """ + return __jsonSyntaxCheck(file, codestring) + + +def jsonSyntaxBatchCheck(argumentsList, send, fx, cancelled, maxProcesses=0): + """ + Module function to check syntax for a batch of files. + + @param argumentsList list of arguments tuples as given for yamlSyntaxCheck + @type list + @param send reference to send function + @type func + @param fx registered service name + @type str + @param cancelled reference to function checking for a cancellation + @type func + @param maxProcesses number of processes to be used + @type int + """ + if maxProcesses == 0: + # determine based on CPU count + try: + NumberOfProcesses = multiprocessing.cpu_count() + if NumberOfProcesses >= 1: + NumberOfProcesses -= 1 + except NotImplementedError: + NumberOfProcesses = 1 + else: + NumberOfProcesses = maxProcesses + + # Create queues + taskQueue = multiprocessing.Queue() + doneQueue = multiprocessing.Queue() + + # Submit tasks (initially two time number of processes + initialTasks = 2 * NumberOfProcesses + for task in argumentsList[:initialTasks]: + taskQueue.put(task) + + # Start worker processes + for _ in range(NumberOfProcesses): + multiprocessing.Process( + target=worker, args=(taskQueue, doneQueue) + ).start() + + # Get and send results + endIndex = len(argumentsList) - initialTasks + for i in range(len(argumentsList)): + resultSent = False + wasCancelled = False + + while not resultSent: + try: + # get result (waiting max. 3 seconds and send it to frontend + filename, result = doneQueue.get() + send(fx, filename, result) + resultSent = True + except queue.Empty: + # ignore empty queue, just carry on + if cancelled(): + wasCancelled = True + break + + if wasCancelled or cancelled(): + # just exit the loop ignoring the results of queued tasks + break + + if i < endIndex: + taskQueue.put(argumentsList[i + initialTasks]) + + # Tell child processes to stop + for _ in range(NumberOfProcesses): + taskQueue.put('STOP') + + +def worker(inputQueue, outputQueue): + """ + Module function acting as the parallel worker for the syntax check. + + @param inputQueue input queue + @type multiprocessing.Queue + @param outputQueue output queue + @type multiprocessing.Queue + """ + for filename, args in iter(inputQueue.get, 'STOP'): + source = args[0] + result = __jsonSyntaxCheck(filename, source) + outputQueue.put((filename, result)) + + +def __jsonSyntaxCheck(file, codestring): + """ + Function to check a YAML source file for syntax errors. + + @param file source filename + @type str + @param codestring string containing the code to check + @type str + @return dictionary with the keys 'error' and 'warnings' which + hold a list containing details about the error/ warnings + (file name, line number, column, codestring (only at syntax + errors), the message, a list with arguments for the message) + @rtype dict + """ + import json + + codestring = normalizeCode(codestring) + + try: + json.loads(codestring) + except json.JSONDecodeError as exc: + line = exc.lineno + column = exc.colno + error = exc.msg + + cline = min(len(codestring.splitlines()), int(line)) - 1 + code = codestring.splitlines()[cline] + return [{'error': (file, line, column, code, error)}] + + return [{}]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric6/Plugins/CheckerPlugins/SyntaxChecker/yamlCheckSyntax.py Tue Nov 19 18:53:58 2019 +0100 @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the syntax check for YAML. +""" + +import queue +import multiprocessing + + +def initService(): + """ + Initialize the service and return the entry point. + + @return the entry point for the background client + @rtype func + """ + return yamlSyntaxCheck + + +def initBatchService(): + """ + Initialize the batch service and return the entry point. + + @return the entry point for the background client + @rtype func + """ + return yamlSyntaxBatchCheck + + +def normalizeCode(codestring): + """ + Function to normalize the given code. + + @param codestring code to be normalized + @type str + @return normalized code + @rtype str + """ + codestring = codestring.replace("\r\n", "\n").replace("\r", "\n") + + if codestring and codestring[-1] != '\n': + codestring = codestring + '\n' + + return codestring + + +def yamlSyntaxCheck(file, codestring): + """ + Function to check a YAML source file for syntax errors. + + @param file source filename + @type str + @param codestring string containing the code to check + @type str + @return dictionary with the keys 'error' and 'warnings' which + hold a list containing details about the error/ warnings + (file name, line number, column, codestring (only at syntax + errors), the message, a list with arguments for the message) + @rtype dict + """ + return __yamlSyntaxCheck(file, codestring) + + +def yamlSyntaxBatchCheck(argumentsList, send, fx, cancelled, maxProcesses=0): + """ + Module function to check syntax for a batch of files. + + @param argumentsList list of arguments tuples as given for yamlSyntaxCheck + @type list + @param send reference to send function + @type func + @param fx registered service name + @type str + @param cancelled reference to function checking for a cancellation + @type func + @param maxProcesses number of processes to be used + @type int + """ + if maxProcesses == 0: + # determine based on CPU count + try: + NumberOfProcesses = multiprocessing.cpu_count() + if NumberOfProcesses >= 1: + NumberOfProcesses -= 1 + except NotImplementedError: + NumberOfProcesses = 1 + else: + NumberOfProcesses = maxProcesses + + # Create queues + taskQueue = multiprocessing.Queue() + doneQueue = multiprocessing.Queue() + + # Submit tasks (initially two time number of processes + initialTasks = 2 * NumberOfProcesses + for task in argumentsList[:initialTasks]: + taskQueue.put(task) + + # Start worker processes + for _ in range(NumberOfProcesses): + multiprocessing.Process( + target=worker, args=(taskQueue, doneQueue) + ).start() + + # Get and send results + endIndex = len(argumentsList) - initialTasks + for i in range(len(argumentsList)): + resultSent = False + wasCancelled = False + + while not resultSent: + try: + # get result (waiting max. 3 seconds and send it to frontend + filename, result = doneQueue.get() + send(fx, filename, result) + resultSent = True + except queue.Empty: + # ignore empty queue, just carry on + if cancelled(): + wasCancelled = True + break + + if wasCancelled or cancelled(): + # just exit the loop ignoring the results of queued tasks + break + + if i < endIndex: + taskQueue.put(argumentsList[i + initialTasks]) + + # Tell child processes to stop + for _ in range(NumberOfProcesses): + taskQueue.put('STOP') + + +def worker(inputQueue, outputQueue): + """ + Module function acting as the parallel worker for the syntax check. + + @param inputQueue input queue + @type multiprocessing.Queue + @param outputQueue output queue + @type multiprocessing.Queue + """ + for filename, args in iter(inputQueue.get, 'STOP'): + source = args[0] + result = __yamlSyntaxCheck(filename, source) + outputQueue.put((filename, result)) + + +def __yamlSyntaxCheck(file, codestring): + """ + Function to check a YAML source file for syntax errors. + + @param file source filename + @type str + @param codestring string containing the code to check + @type str + @return dictionary with the keys 'error' and 'warnings' which + hold a list containing details about the error/ warnings + (file name, line number, column, codestring (only at syntax + errors), the message, a list with arguments for the message) + @rtype dict + """ + try: + from yaml import safe_load_all, MarkedYAMLError + except ImportError: + error = "pyyaml not available. Install it via the PyPI interface." + return [{'error': (file, 0, 0, '', error)}] + + codestring = normalizeCode(codestring) + + try: + for _obj in safe_load_all(codestring): + # do nothing with it, just to get parse errors + pass + except MarkedYAMLError as exc: + if exc.problem_mark: + line = exc.problem_mark.line + 1 + column = exc.problem_mark.column + else: + line, column = 0, 0 + error = exc.problem + cline = min(len(codestring.splitlines()), int(line)) - 1 + code = codestring.splitlines()[cline] + return [{'error': (file, line, column, code, error)}] + + return [{}]
--- a/eric6/Plugins/PluginSyntaxChecker.py Thu Nov 14 19:39:07 2019 +0100 +++ b/eric6/Plugins/PluginSyntaxChecker.py Tue Nov 19 18:53:58 2019 +0100 @@ -88,6 +88,26 @@ lambda fn, problems: self.syntaxCheckService.syntaxChecked.emit(fn, problems), self.syntaxCheckService.serviceErrorJavaScript) + + # YAML syntax check via Python3 + self.syntaxCheckService.addLanguage( + 'YAML', 'Python3', path, + 'yamlCheckSyntax', + lambda: [], # No options + lambda: ['.yml', '.yaml'], + lambda fn, problems: + self.syntaxCheckService.syntaxChecked.emit(fn, problems), + self.syntaxCheckService.serviceErrorYAML) + + # JSON syntax check via Python3 + self.syntaxCheckService.addLanguage( + 'JSON', 'Python3', path, + 'jsonCheckSyntax', + lambda: [], # No options + lambda: ['.json'], + lambda fn, problems: + self.syntaxCheckService.syntaxChecked.emit(fn, problems), + self.syntaxCheckService.serviceErrorJSON) def __initialize(self): """
--- a/eric6/QScintilla/Editor.py Thu Nov 14 19:39:07 2019 +0100 +++ b/eric6/QScintilla/Editor.py Tue Nov 19 18:53:58 2019 +0100 @@ -1740,7 +1740,7 @@ filename, self.text(0), self) language = "Python{0}".format(pyVer) if language in ['Python2', 'Python3', 'MicroPython', 'Ruby', - 'JavaScript']: + 'JavaScript', 'YAML', 'JSON']: self.filetype = language else: self.filetype = ""
--- a/eric6/Utilities/BackgroundService.py Thu Nov 14 19:39:07 2019 +0100 +++ b/eric6/Utilities/BackgroundService.py Tue Nov 19 18:53:58 2019 +0100 @@ -126,7 +126,7 @@ connection = self.connections.get(lang) if connection is None: if fx != 'INIT': - # Avoid growing recursion deep which could itself result in an + # Avoid growing recursion depth which could itself result in an # exception QTimer.singleShot( 0,