class MFileChecker(TypificChecker): rulific_decision_map = { 'copyright': True, 'eol': True, 'first_line_comment': False, 'max_line_length': False, 'no_dos_eol': True, 'no_last_eol': True, 'no_rcs_keywords': False, 'no_tab_indent': True, 'no_trailing_space': True, } typific_info = TypificCheckerInfo(comment_line_re=None, ada_RM_spec_p=False, copyright_box_r_edge_re='%') @property def file_type(self): # This Typific checker was created mostly for matlab files, # but as it happens, right now, the same extension is also # used for other types of files. Since the checking we are # doing for it is fairly generic, just use a generic name # for the file type as well. return '.m files (matlab, Obj-C, mathematica...)' def run_external_checker(self): # Nothing to do. pass
class ShFileChecker(TypificChecker): rulific_decision_map = { "bidi": True, "copyright": False, "eol": True, "first_line_comment": False, "max_line_length": False, "no_dos_eol": True, "no_last_eol": True, "no_rcs_keywords": False, "no_tab_indent": False, "no_trailing_space": True, } typific_info = TypificCheckerInfo(comment_line_re=None, ada_RM_spec_p=False, copyright_box_r_edge_re=None) @property def file_type(self): return "sh script" def run_external_checker(self): try: p = Run(["/bin/sh", "-n", self.filename]) if p.status != 0: return p.out except OSError as e: # pragma: no cover (see below) # Can only really happen if sh is not installed on the host # machine. Near-zero probability, but we keep this handler # so as to generate an error message rather than a traceback. return "Failed to run sh: %s" % e
class PerlFileChecker(TypificChecker): rulific_decision_map = { 'copyright': False, 'eol': True, 'first_line_comment': False, 'max_line_length': False, 'no_dos_eol': True, 'no_last_eol': True, 'no_rcs_keywords': False, 'no_tab_indent': False, 'no_trailing_space': True, } typific_info = TypificCheckerInfo(comment_line_re=None, ada_RM_spec_p=False, copyright_box_r_edge_re=None) @property def file_type(self): return 'perl script' def run_external_checker(self): try: p = Run(['/usr/bin/perl', '-c', self.filename]) if p.status != 0: return p.out except OSError as e: # pragma: no cover (see below) # Can only really happen if perl is not installed on the host # machine. Fairly low probability, but we keep this handler # so as to generate an error message rather than a traceback. return 'Failed to run perl: %s' % e
class TexiFileChecker(TypificChecker): rulific_decision_map = { "bidi": False, "copyright": True, "eol": True, "first_line_comment": False, "max_line_length": False, "no_dos_eol": True, "no_last_eol": True, "no_rcs_keywords": False, "no_tab_indent": False, "no_trailing_space": True, } typific_info = TypificCheckerInfo(comment_line_re=None, ada_RM_spec_p=False, copyright_box_r_edge_re="o") @property def file_type(self): return "texi" def run_external_checker(self): # Nothing to do. pass
class YamlFileChecker(TypificChecker): rulific_decision_map = { 'copyright': False, 'eol': False, 'first_line_comment': False, 'max_line_length': False, 'no_dos_eol': False, 'no_last_eol': False, 'no_rcs_keywords': False, 'no_tab_indent': False, 'no_trailing_space': False, } typific_info = TypificCheckerInfo(comment_line_re=None, ada_RM_spec_p=False, copyright_box_r_edge_re=None) @property def file_type(self): return 'YaML file' def run_external_checker(self): try: with open(self.filename, 'rb') as fd: yaml.load(fd) except yaml.YAMLError as exc: return 'Error: %s: %s' % (self.filename, str(exc))
class YamlFileChecker(TypificChecker): rulific_decision_map = { "bidi": True, "copyright": False, "eol": False, "first_line_comment": False, "max_line_length": False, "no_dos_eol": False, "no_last_eol": False, "no_rcs_keywords": False, "no_tab_indent": False, "no_trailing_space": False, } typific_info = TypificCheckerInfo(comment_line_re=None, ada_RM_spec_p=False, copyright_box_r_edge_re=None) @property def file_type(self): return "YaML file" def run_external_checker(self): try: with open(self.filename, "rb") as fd: yaml.safe_load(fd) except yaml.YAMLError as exc: return "Error: %s: %s" % (self.filename, str(exc))
class IncompleteFileChecker(TypificChecker): rulific_decision_map = dict((checker.RULE_CONFIG_NAME, False) for checker in ALL_RULIFIC_CHECKERS[:-1]) typific_info = TypificCheckerInfo(comment_line_re='#', ada_RM_spec_p=False, copyright_box_r_edge_re=None) file_type = 'Python script'
class BadFileChecker(TypificChecker): # Do provide a complete rulific_decision_map attribute, though, # as the contents of that dictionary is checked during # the object's initialization. rulific_decision_map = dict((checker.RULE_CONFIG_NAME, False) for checker in ALL_RULIFIC_CHECKERS) # Same the typific_info attribute... typific_info = TypificCheckerInfo(comment_line_re='#', ada_RM_spec_p=False, copyright_box_r_edge_re=None)
class JavascriptFileChecker(TypificChecker): rulific_decision_map = { 'copyright': False, 'eol': True, 'first_line_comment': False, 'max_line_length': False, 'no_dos_eol': True, 'no_last_eol': True, 'no_rcs_keywords': False, 'no_tab_indent': False, 'no_trailing_space': True, } typific_info = TypificCheckerInfo(comment_line_re=None, ada_RM_spec_p=False, copyright_box_r_edge_re=None) # The list of modules for which JsDoc annotations are allowed # (we default is to reject them). MODULES_WITH_JSDOC = ('qmachine', 'modeling', 'web-components') @property def file_type(self): return 'Javascript' def run_external_checker(self): # For these kinds of files, the external check should be # disabled if there is a "No_Style_Check" comment starting # either the first or the second line. with open(self.filename) as f: for lineno in (1, 2): line = f.readline() if line and re.match('^// No_Style_Check$', line) is not None: # ??? VERBOSE... return jslint_cmd = ['gjslint'] if not any(x in self.config.module_name for x in self.MODULES_WITH_JSDOC): jslint_cmd.append('--nojsdoc') jslint_cmd.append(self.filename) try: p = Run(jslint_cmd) if p.status != 0: return p.out except OSError as e: return 'Failed to run gjslint: %s' % e
class GNATInfoFileChecker(TypificChecker): rulific_decision_map = { 'copyright': False, 'eol': True, 'first_line_comment': False, 'max_line_length': True, 'no_dos_eol': True, 'no_last_eol': True, 'no_rcs_keywords': False, 'no_tab_indent': False, 'no_trailing_space': True, } typific_info = TypificCheckerInfo(comment_line_re=None, ada_RM_spec_p=False, copyright_box_r_edge_re=None) @property def file_type(self): return 'GNAT_info' def run_external_checker(self): pass
def typific_info(self): return TypificCheckerInfo(comment_line_re='--*$', ada_RM_spec_p=(self.file_type == RT_SPEC), copyright_box_r_edge_re=None)
class JavaFileChecker(TypificChecker): rulific_decision_map = { "bidi": True, "copyright": True, "eol": True, "first_line_comment": True, "max_line_length": False, "no_dos_eol": True, "no_last_eol": True, "no_rcs_keywords": True, "no_tab_indent": True, "no_trailing_space": True, } typific_info = TypificCheckerInfo(comment_line_re=r"(/\*|//).*", ada_RM_spec_p=False, copyright_box_r_edge_re=None) # Our style file, located besides this module. style_filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), "java.xml") @property def file_type(self): return "java" def run_external_checker(self): env = { # This sets the Java max heap size manually. By default, Java # allocates a value that causes a heap memory error on kwai. # For N909-043. "JAVA_ARGS": "-Xmx1024m" } # Create a properties, file, as we as can't pass -D arguments to # checkstyle without modifying it. (prop_fd, prop_filename) = mkstemp("tmp-style_checker-") try: with closing(os.fdopen(prop_fd, "w")) as f: f.write("basedir=%s\n" % os.path.dirname(os.path.abspath(self.filename))) p = Run( [ "checkstyle", "-p", prop_filename, "-c", self.style_filename, "-f", "plain", self.filename, ], env=env, ignore_environ=False, ) if p.status != 0: # Return the program's output minus a few useless lines. out = "\n".join([ line for line in p.out.splitlines() if not re.match("(Starting audit|Audit done)", line) ]) return out except OSError as e: return "Failed to run checkstyle: %s" % e finally: os.unlink(prop_filename)
class RstFileChecker(TypificChecker): rulific_decision_map = { 'copyright': False, 'eol': True, 'first_line_comment': False, 'max_line_length': False, 'no_dos_eol': True, 'no_last_eol': True, 'no_rcs_keywords': False, 'no_tab_indent': False, 'no_trailing_space': False, } typific_info = TypificCheckerInfo(comment_line_re=None, ada_RM_spec_p=False, copyright_box_r_edge_re=None) @property def file_type(self): return 'ReST file' def run_external_checker(self): # Check that user user didn't forget the second colon in # what looks like a directive, but isn't. For instance... # # .. note: someting # # ... which looks like the user actually meant to be: # # .. note:: someting # # The syntax for directives is the sequence of: # - '..' # - the directive type # (case-insentive words, which are alphanumerics, plus # ISOLATED internal hyphens, underscores, plus signs, # colons, and periods; no whitespace) # - '::' # - arguments (optional) # # Allowing isolated colons in the directive type means that # we cannot simply use a single colon as the delimiter for # the directive type. So what we do is the following: For # lines that start with '..' and a space, extract everything # after it until the last colon on the line. If somewhere in # there, we find two colons, then that's the boundary of # the directive type, and the user properly used two colons, # so all good. If we don't find any occurence of '::', then # the user likely made a typo. directive_matcher = re.compile(r'^\.\.\s+([-_+:.a-zA-Z0-9]+:)', re.IGNORECASE) errors = [] with open(self.filename) as f: for line_no, line in enumerate(f, 1): m = directive_matcher.match(line) if m is None: # This line does not have a directive continue if m.group(1).startswith('_'): # This is a hyperlink target, not a directive. continue if '::' in m.group(1): # This line has a directive, and the syntax looks correct. continue # It looks like this line has a directive, and that # the user may have made a typo. loc = "%s:%d" % (self.filename, line_no) errors.extend([ "%s: invalid directive syntax (':' should be '::')" % loc, "%s %s" % (' ' * len(loc), line.rstrip()), "%s %s" % (' ' * len(loc), ' ' * len(m.group(0)[:-1]) + '^') ]) if errors: return '\n'.join(errors)
class JavaFileChecker(TypificChecker): rulific_decision_map = { 'copyright': True, 'eol': True, 'first_line_comment': True, 'max_line_length': False, 'no_dos_eol': True, 'no_last_eol': True, 'no_rcs_keywords': True, 'no_tab_indent': True, 'no_trailing_space': True, } typific_info = TypificCheckerInfo(comment_line_re=r'(/\*|//).*', ada_RM_spec_p=False, copyright_box_r_edge_re=None) # Our style file, located besides this module. style_filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'java.xml') @property def file_type(self): return 'java' def run_external_checker(self): env = { # This sets the Java max heap size manually. By default, Java # allocates a value that causes a heap memory error on kwai. # For N909-043. 'JAVA_ARGS': '-Xmx1024m' } # Create a properties, file, as we as can't pass -D arguments to # checkstyle without modifying it. (prop_fd, prop_filename) = mkstemp('tmp-style_checker-') try: os.write( prop_fd, 'basedir=%s\n' % os.path.dirname(os.path.abspath(self.filename))) os.close(prop_fd) p = Run([ 'checkstyle', '-p', prop_filename, '-c', self.style_filename, '-f', 'plain', self.filename ], env=env, ignore_environ=False) if p.status != 0: # Return the program's output minus a few useless lines. out = '\n'.join([ line for line in p.out.splitlines() if not re.match('(Starting audit|Audit done)', line) ]) return out except OSError as e: return 'Failed to run checkstyle: %s' % e finally: os.unlink(prop_filename)
class PythonFileChecker(TypificChecker): rulific_decision_map = { 'copyright': False, 'eol': True, 'first_line_comment': False, 'max_line_length': False, 'no_dos_eol': True, 'no_last_eol': True, 'no_rcs_keywords': False, 'no_tab_indent': False, 'no_trailing_space': True, } typific_info = TypificCheckerInfo(comment_line_re='##*', ada_RM_spec_p=False, copyright_box_r_edge_re=None) @property def file_type(self): if self.filename.endswith('.plan'): return PLAN_FILE_TYPE elif self.filename.endswith('.frag.py'): return PYTHON_FRAGMENT_FILE_TYPE else: return PYTHON_FILE_TYPE def run_external_checker(self): # Check the first two lines of the file, and scan for certain # specific keywords indicating possible special treatment for # this file. python_fragment_p = False with open(self.filename) as f: for lineno in (1, 2): line = f.readline() if line and re.match('^# No_Style_Check$', line) is not None: # ??? VERBOSE... return elif line and re.match('^# Style_Check:Python_Fragment', line, re.I) is not None: python_fragment_p = True try: p = Run(['pycodestyle', '--config=' + os.path.join(get_prefix_dir(), 'etc', 'pycodestyle.cfg'), self.filename]) if p.status != 0 or p.out: return p.out except OSError as e: return 'Failed to run pycodestyle: %s' % e if not python_fragment_p and self.__run_pyflakes(): try: p = Run(['pyflakes', self.filename]) if p.status != 0 or p.out: return p.out except OSError as e: return 'Failed to run pyflakes: %s' % e def __run_pyflakes(self): """Return True iff we should run pyflakes to validate our file. """ # We want to run pyflakes on every file, except that there are # kinds of file that legitimately fail this checker. Exclude # those. return self.file_type not in (PYTHON_FRAGMENT_FILE_TYPE, PLAN_FILE_TYPE)
class PythonFileChecker(TypificChecker): rulific_decision_map = { "bidi": True, "copyright": False, "eol": True, "first_line_comment": False, "max_line_length": False, "no_dos_eol": True, "no_last_eol": True, "no_rcs_keywords": False, "no_tab_indent": False, "no_trailing_space": True, } typific_info = TypificCheckerInfo(comment_line_re="##*", ada_RM_spec_p=False, copyright_box_r_edge_re=None) @property def file_type(self): if self.filename.endswith(".plan"): return PLAN_FILE_TYPE elif self.filename.endswith(".frag.py"): return PYTHON_FRAGMENT_FILE_TYPE else: return PYTHON_FILE_TYPE def run_external_checker(self): # Check the first two lines of the file, and scan for certain # specific keywords indicating possible special treatment for # this file. python_fragment_p = False with open(self.filename) as f: for _unused_lineno in (1, 2): line = f.readline() if line and re.match("^# No_Style_Check$", line) is not None: # ??? VERBOSE... return elif (line and re.match("^# Style_Check:Python_Fragment", line, re.I) is not None): python_fragment_p = True extend_ignore = list(EXTEND_IGNORE_LIST_ALL) if python_fragment_p or self.__incomplete_python_file_p(): extend_ignore.extend(EXTEND_IGNORE_LIST_INCOMPLETE_PYTHON) p = Run([ sys.executable, "-m", "flake8", f"--max-line-length={MAX_LINE_LENGTH}", "--extend-ignore={}".format(",".join(extend_ignore)), self.filename, ]) if p.status != 0 or p.out: return p.out def __incomplete_python_file_p(self): """Return True if checking a Python file which may not be self-sufficient. This method checks our file_type attributes, to determine whether we expect the file to be checked to be complete and self-sufficient. If not, return True. Otherwise, return False. See EXTEND_IGNORE_LIST_INCOMPLETE_PYTHON for more information about the intent behind identifying such files. """ return self.file_type in (PYTHON_FRAGMENT_FILE_TYPE, PLAN_FILE_TYPE)