From 06bf75267f228787f6e369e86b9dd334ec5d90b5 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 1 Jul 2024 16:25:33 -0500 Subject: [PATCH] Switch to ruff, fix flagged issues --- .github/workflows/ci.yml | 13 +++--- .gitlab-ci.yml | 8 ++-- pycparserext/ext_c_generator.py | 73 +++++++++++++++++---------------- pycparserext/ext_c_lexer.py | 52 ++++++++++++----------- pycparserext/ext_c_parser.py | 30 +++++++------- pyproject.toml | 43 +++++++++++++++++++ setup.cfg | 14 ------- setup.py | 17 ++++---- test/test_pycparserext.py | 15 ++++--- 9 files changed, 150 insertions(+), 115 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.cfg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa12e35..efe0101 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,19 +10,16 @@ on: - cron: '17 3 * * 0' jobs: - flake8: - name: Flake8 + ruff: + name: Ruff runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: '3.8' + - uses: actions/setup-python@v5 - name: "Main Script" run: | - curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/prepare-and-run-flake8.sh - . ./prepare-and-run-flake8.sh ./pycparserext ./test + pip install ruff + ruff check pytest: name: Pytest on Py${{ matrix.python-version }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e499e91..9ee3956 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,11 +11,11 @@ Python 3: reports: junit: test/pytest.xml -Flake8: +Ruff: script: - - curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/prepare-and-run-flake8.sh - - ". ./prepare-and-run-flake8.sh pycparserext test" + - pipx install ruff + - ruff check tags: - - python3 + - docker-runner except: - tags diff --git a/pycparserext/ext_c_generator.py b/pycparserext/ext_c_generator.py index a5747fa..a2bc88d 100644 --- a/pycparserext/ext_c_generator.py +++ b/pycparserext/ext_c_generator.py @@ -1,23 +1,24 @@ +import pycparser.c_ast as c_ast from pycparser.c_generator import CGenerator as CGeneratorBaseBuggy + from pycparserext.ext_c_parser import FuncDeclExt, TypeDeclExt -import pycparser.c_ast as c_ast class CGeneratorBase(CGeneratorBaseBuggy): # bug fix def visit_UnaryOp(self, n): operand = self._parenthesize_unless_simple(n.expr) - if n.op == 'p++': - return '%s++' % operand - elif n.op == 'p--': - return '%s--' % operand - elif n.op == 'sizeof': + if n.op == "p++": + return "%s++" % operand + elif n.op == "p--": + return "%s--" % operand + elif n.op == "sizeof": # Always parenthesize the argument of sizeof since it can be # a name. - return 'sizeof(%s)' % self.visit(n.expr) + return "sizeof(%s)" % self.visit(n.expr) else: # avoid merging of "- - x" or "__real__varname" - return '%s %s' % (n.op, operand) + return "%s %s" % (n.op, operand) class AsmAndAttributesMixin(object): @@ -48,15 +49,15 @@ def _generate_type(self, n, modifiers=None, emit_declname=True): if modifiers is None: modifiers = [] typ = type(n) - #~ print(n, modifiers) + # print(n, modifiers) if typ in (c_ast.TypeDecl, TypeDeclExt): - s = '' + s = "" if n.quals: - s += ' '.join(n.quals) + ' ' + s += " ".join(n.quals) + " " s += self.visit(n.type) - nstr = n.declname if n.declname and emit_declname else '' + nstr = n.declname if n.declname and emit_declname else "" # Resolve modifiers. # Wrap in parens to distinguish pointer to array and pointer to # function syntax. @@ -64,48 +65,48 @@ def _generate_type(self, n, modifiers=None, emit_declname=True): for i, modifier in enumerate(modifiers): if isinstance(modifier, c_ast.ArrayDecl): if i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl): - nstr = '(' + nstr + ')' + nstr = "(" + nstr + ")" # BUG FIX: pycparser ignores quals - dim_quals = (' '.join(modifier.dim_quals) + ' ' - if modifier.dim_quals else '') + dim_quals = (" ".join(modifier.dim_quals) + " " + if modifier.dim_quals else "") - nstr += '[' + dim_quals + self.visit(modifier.dim) + ']' + nstr += "[" + dim_quals + self.visit(modifier.dim) + "]" elif isinstance(modifier, c_ast.FuncDecl): if i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl): - nstr = '(' + nstr + ')' - nstr += '(' + self.visit(modifier.args) + ')' + nstr = "(" + nstr + ")" + nstr += "(" + self.visit(modifier.args) + ")" elif isinstance(modifier, FuncDeclExt): if i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl): - nstr = '(' + nstr + ')' - nstr += '(' + self.visit(modifier.args) + ')' + nstr = "(" + nstr + ")" + nstr += "(" + self.visit(modifier.args) + ")" if modifier.asm is not None: nstr += " " + self.visit(modifier.asm) if modifier.attributes.exprs: nstr += ( - ' __attribute__((' + " __attribute__((" + self.visit(modifier.attributes) - + '))') + + "))") elif isinstance(modifier, c_ast.PtrDecl): # BUG FIX: pycparser ignores quals - quals = ' '.join(modifier.quals) + quals = " ".join(modifier.quals) if quals: - quals = quals + ' ' - nstr = '*' + quals + nstr + quals = quals + " " + nstr = "*" + quals + nstr if hasattr(n, "asm") and n.asm: nstr += self.visit(n.asm) if hasattr(n, "attributes") and n.attributes.exprs: - nstr += ' __attribute__((' + self.visit(n.attributes) + '))' + nstr += " __attribute__((" + self.visit(n.attributes) + "))" if nstr: - s += ' ' + nstr + s += " " + nstr return s elif typ == c_ast.Decl: @@ -115,7 +116,7 @@ def _generate_type(self, n, modifiers=None, emit_declname=True): return self._generate_type(n.type, emit_declname=emit_declname) elif typ == c_ast.IdentifierType: - return ' '.join(n.names) + ' ' + return " ".join(n.names) + " " elif typ in (c_ast.ArrayDecl, c_ast.PtrDecl, c_ast.FuncDecl, FuncDeclExt): return self._generate_type( @@ -127,7 +128,7 @@ def _generate_type(self, n, modifiers=None, emit_declname=True): def _generate_decl(self, n): """ Generation from a Decl node. """ - s = '' + s = "" def funcspec_to_str(i): if isinstance(i, c_ast.Node): @@ -136,14 +137,14 @@ def funcspec_to_str(i): return i if n.funcspec: - s = ' '.join(funcspec_to_str(i) for i in n.funcspec) + ' ' + s = " ".join(funcspec_to_str(i) for i in n.funcspec) + " " if n.storage: - s += ' '.join(n.storage) + ' ' + s += " ".join(n.storage) + " " s += self._generate_type(n.type) return s def visit_AttributeSpecifier(self, n): - return ' __attribute__((' + self.visit(n.exprlist) + '))' + return " __attribute__((" + self.visit(n.exprlist) + "))" class GnuCGenerator(AsmAndAttributesMixin, CGeneratorBase): @@ -154,10 +155,10 @@ def visit_TypeOfExpression(self, n): return "%s(%s)" % (n.typeof_keyword, self.visit(n.expr)) def visit_TypeList(self, n): - return ', '.join(self.visit(ch) for ch in n.types) + return ", ".join(self.visit(ch) for ch in n.types) def visit_RangeExpression(self, n): - return '%s ... %s' % (self.visit(n.first), self.visit(n.last)) + return "%s ... %s" % (self.visit(n.first), self.visit(n.last)) class GNUCGenerator(GnuCGenerator): @@ -169,13 +170,13 @@ def __init__(self): class OpenCLCGenerator(AsmAndAttributesMixin, CGeneratorBase): def visit_FileAST(self, n): - s = '' + s = "" from pycparserext.ext_c_parser import PreprocessorLine for ext in n.ext: if isinstance(ext, (c_ast.FuncDef, PreprocessorLine)): s += self.visit(ext) else: - s += self.visit(ext) + ';\n' + s += self.visit(ext) + ";\n" return s def visit_PreprocessorLine(self, n): diff --git a/pycparserext/ext_c_lexer.py b/pycparserext/ext_c_lexer.py index 0d740c9..638d92a 100644 --- a/pycparserext/ext_c_lexer.py +++ b/pycparserext/ext_c_lexer.py @@ -1,4 +1,6 @@ from pycparser.c_lexer import CLexer as CLexerBase + + try: from pycparser.ply.lex import TOKEN except ImportError: @@ -8,16 +10,16 @@ class GnuCLexer(CLexerBase): # support '3i' for imaginary literal floating_constant = ( - '((((' - + CLexerBase.fractional_constant+')' - + CLexerBase.exponent_part+'?)|([0-9]+' - + CLexerBase.exponent_part+'))i?[FfLl]?)') + "((((" + + CLexerBase.fractional_constant+")" + + CLexerBase.exponent_part+"?)|([0-9]+" + + CLexerBase.exponent_part+"))i?[FfLl]?)") @TOKEN(floating_constant) - def t_FLOAT_CONST(self, t): + def t_FLOAT_CONST(self, t): # noqa: N802 return t - t_pppragma_ignore = ' \t<>.-{}();+-*/$%@&^~!?:,0123456789=' + t_pppragma_ignore = " \t<>.-{}();+-*/$%@&^~!?:,0123456789=" class GNUCLexer(GnuCLexer): @@ -30,21 +32,21 @@ def __init__(self, *args, **kwargs): class OpenCLCLexer(CLexerBase): - tokens = CLexerBase.tokens + ('LINECOMMENT',) + tokens = CLexerBase.tokens + ("LINECOMMENT",) states = ( # ('comment', 'exclusive'), # ('preproc', 'exclusive'), - ('ppline', 'exclusive'), # unused - ('pppragma', 'exclusive'), # unused + ("ppline", "exclusive"), # unused + ("pppragma", "exclusive"), # unused ) - def t_LINECOMMENT(self, t): - r'\/\/([^\n]+)\n' + def t_LINECOMMENT(self, t): # noqa: N802 + r"\/\/([^\n]+)\n" t.lexer.lineno += t.value.count("\n") # overrides pycparser, must have same name - def t_PPHASH(self, t): - r'[ \t]*\#([^\n]|\\\n)+[^\n\\]\n' + def t_PPHASH(self, t): # noqa: N802 + r"[ \t]*\#([^\n]|\\\n)+[^\n\\]\n" t.lexer.lineno += t.value.count("\n") return t @@ -61,25 +63,25 @@ def add_lexer_keywords(cls, keywords): _COMMON_KEYWORDS = [ - '__attribute__', '__attribute', - '__asm__', '__asm', 'asm'] + "__attribute__", "__attribute", + "__asm__", "__asm", "asm"] _GNU_KEYWORDS = [ - '__typeof__', 'typeof', '__typeof', - '__real__', '__imag__', - '__builtin_types_compatible_p', - '__const', - '__restrict__', '__restrict', - '__inline__', '__inline', - '__extension__', - '__volatile', '__volatile__'] + "__typeof__", "typeof", "__typeof", + "__real__", "__imag__", + "__builtin_types_compatible_p", + "__const", + "__restrict__", "__restrict", + "__inline__", "__inline", + "__extension__", + "__volatile", "__volatile__"] add_lexer_keywords(GnuCLexer, _COMMON_KEYWORDS + _GNU_KEYWORDS) # These will be added as unadorned keywords and keywords with '__' prepended _CL_BASE_KEYWORDS = [ - 'kernel', 'constant', 'global', 'local', 'private', - 'read_only', 'write_only', 'read_write'] + "kernel", "constant", "global", "local", "private", + "read_only", "write_only", "read_write"] _CL_KEYWORDS = _COMMON_KEYWORDS _CL_KEYWORDS += _CL_BASE_KEYWORDS + ["__"+kw for kw in _CL_BASE_KEYWORDS] diff --git a/pycparserext/ext_c_parser.py b/pycparserext/ext_c_parser.py index 83862a8..26de7bc 100644 --- a/pycparserext/ext_c_parser.py +++ b/pycparserext/ext_c_parser.py @@ -1,7 +1,9 @@ from __future__ import division -import pycparser.c_parser import pycparser.c_ast as c_ast +import pycparser.c_parser + + try: import pycparser.ply.yacc as yacc except ImportError: @@ -11,12 +13,12 @@ class CParserBase(pycparser.c_parser.CParser): def __init__(self, **kwds): - kwds['lexer'] = self.lexer_class - kwds['lextab'] = 'pycparserext.lextab' - kwds['yacctab'] = 'pycparserext.yacctab' + kwds["lexer"] = self.lexer_class + kwds["lextab"] = "pycparserext.lextab" + kwds["yacctab"] = "pycparserext.yacctab" pycparser.c_parser.CParser.__init__(self, **kwds) - def parse(self, text, filename='', debuglevel=0, + def parse(self, text, filename="", debuglevel=0, initial_type_symbols=frozenset()): self.clex.filename = filename self.clex.reset_lineno() @@ -103,7 +105,7 @@ def __iter__(self): if self.clobbered_regs is not None: yield self.clobbered_regs - attr_names = ('asm_keyword',) + attr_names = ("asm_keyword",) class PreprocessorLine(c_ast.Node): @@ -139,7 +141,7 @@ def __iter__(self): if self.declaration is not None: yield self.declaration - attr_names = ('typeof_keyword',) + attr_names = ("typeof_keyword",) class TypeOfExpression(c_ast.Node): @@ -158,7 +160,7 @@ def __iter__(self): if self.expr is not None: yield self.expr - attr_names = ('typeof_keyword',) + attr_names = ("typeof_keyword",) class RangeExpression(c_ast.Node): @@ -350,7 +352,7 @@ def p_asm_keyword(self, p): """ p[0] = p[1] if p[2]: - p[0] += ' ' + p[2] + p[0] += " " + p[2] def p_asm_volatile_opt(self, p): """ asm_volatile_opt : unified_volatile @@ -386,7 +388,7 @@ def p_asm_label_opt(self, p): class _AsmAndAttributesMixin(_AsmMixin, _AttributesMixin): # {{{ /!\ names must match C parser to override - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + @parameterized(("id", "ID"), ("typeid", "TYPEID"), ("typeid_noparen", "TYPEID")) def p_xxx_declarator_1(self, p): """ xxx_declarator : direct_xxx_declarator asm_label_opt attributes_opt """ @@ -412,7 +414,7 @@ def p_xxx_declarator_1(self, p): p[0] = p[1] - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + @parameterized(("id", "ID"), ("typeid", "TYPEID"), ("typeid_noparen", "TYPEID")) def p_xxx_declarator_2(self, p): """ xxx_declarator : pointer direct_xxx_declarator asm_label_opt \ attributes_opt @@ -450,7 +452,7 @@ def p_xxx_declarator_2(self, p): p[0] = self._type_modify_decl(decl, p[1]) - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + @parameterized(("id", "ID"), ("typeid", "TYPEID"), ("typeid_noparen", "TYPEID")) def p_direct_xxx_declarator_6(self, p): """ direct_xxx_declarator : direct_xxx_declarator \ LPAREN parameter_type_list RPAREN \ @@ -603,8 +605,8 @@ class OpenCLCParser(_AsmAndAttributesMixin, CParserBase): { "%s%d" % (base_name, count) for base_name in [ - 'char', 'uchar', 'short', 'ushort', 'int', 'uint', - 'long', 'ulong', 'float', 'double', 'half'] + "char", "uchar", "short", "ushort", "int", "uint", + "long", "ulong", "float", "double", "half"] for count in [2, 3, 4, 8, 16] } | { diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..bb15751 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,43 @@ +[tool.ruff] +target-version = "py38" +line-length = 85 + +preview = true +[tool.ruff.lint] +extend-select = [ + "B", # flake8-bugbear + "C", # flake8-comprehensions + "E", # pycodestyle + "F", # pyflakes + "I", # flake8-isort + "N", # pep8-naming + "NPY", # numpy + "Q", # flake8-quotes + "W", # pycodestyle +] +extend-ignore = [ + "C90", # McCabe complexity + "E221", # multiple spaces before operator + "E226", # missing whitespace around arithmetic operator + "E402", # module-level import not at top of file +] +[tool.ruff.lint.flake8-quotes] +docstring-quotes = "double" +inline-quotes = "double" +multiline-quotes = "double" + +[tool.ruff.lint.per-file-ignores] +"pycparserext/ext_c_generator.py" = ["N802"] +"test/test_pycparserext.py" = ["N802"] + +[tool.ruff.lint.isort] +combine-as-imports = true + +known-first-party = [ + "pycparser", +] +known-local-folder = [ + "pycparserext", +] +lines-after-imports = 2 + diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 5d048b6..0000000 --- a/setup.cfg +++ /dev/null @@ -1,14 +0,0 @@ -[flake8] -ignore = E126,E127,E128,E123,E226,E241,E242,N802,E265,W503 -max-line-length=85 - -per-file-ignores= - pycparserext/ext_c_lexer.py:Q - pycparserext/ext_c_parser.py:Q - pycparserext/ext_c_generator.py:Q - -inline-quotes = " -docstring-quotes = """ -multiline-quotes = """ - -# enable-flake8-bugbear diff --git a/setup.py b/setup.py index 2335da3..f498f23 100644 --- a/setup.py +++ b/setup.py @@ -3,19 +3,20 @@ from setuptools import setup + setup(name="pycparserext", version="2021.1", description="Extensions for pycparser", long_description=open("README.rst", "r").read(), classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Intended Audience :: Other Audience', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Programming Language :: Python', - 'Topic :: Utilities', + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Other Audience", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Programming Language :: Python", + "Topic :: Utilities", ], python_requires="~=3.8", diff --git a/test/test_pycparserext.py b/test/test_pycparserext.py index bf03c3b..9c0c19c 100644 --- a/test/test_pycparserext.py +++ b/test/test_pycparserext.py @@ -1,4 +1,5 @@ from __future__ import print_function + import pytest @@ -24,8 +25,8 @@ def _compare_asts(first, second): def _round_trip_matches(src): - from pycparserext.ext_c_parser import GnuCParser from pycparserext.ext_c_generator import GnuCGenerator + from pycparserext.ext_c_parser import GnuCParser p = GnuCParser() @@ -405,8 +406,8 @@ def test_pointer_reproduction(): return 0; } """ - import pycparserext.ext_c_parser as ext_c_parser import pycparserext.ext_c_generator as ext_c_generator + import pycparserext.ext_c_parser as ext_c_parser parser = ext_c_parser.GnuCParser() ast = parser.parse(src) @@ -424,8 +425,8 @@ def test_no_added_attr(): return 0; } """ - import pycparserext.ext_c_parser as ext_c_parser import pycparserext.ext_c_generator as ext_c_generator + import pycparserext.ext_c_parser as ext_c_parser parser = ext_c_parser.GnuCParser() ast = parser.parse(src) @@ -442,8 +443,8 @@ def test_double_pointer(): void func_with_p2pp(const char *, Error **); """ - import pycparserext.ext_c_parser as ext_c_parser import pycparserext.ext_c_generator as ext_c_generator + import pycparserext.ext_c_parser as ext_c_parser parser = ext_c_parser.GnuCParser() ast = parser.parse(src) @@ -579,9 +580,10 @@ def test_typeof_reproduction(): """ assert _round_trip_matches(src) - import pycparserext.ext_c_parser as ext_c_parser from pycparser.c_ast import NodeVisitor + import pycparserext.ext_c_parser as ext_c_parser + # key is type of visit, value is # [actual # __typeof__, expected # __typeof__, # actual # typeof, expected # typeof, @@ -618,8 +620,9 @@ def visit_TypeOfExpression(self, node): def test_typedef(): from pycparser import c_ast - from pycparserext.ext_c_parser import GnuCParser + from pycparserext.ext_c_generator import GnuCGenerator + from pycparserext.ext_c_parser import GnuCParser p = GnuCParser()