diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9178565..7b491fd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,33 +1,38 @@ +ci: + autofix_commit_msg: 'chore: auto fixes from pre-commit hooks' + autoupdate_commit_msg: 'chore: pre-commit automatic update' + autoupdate_schedule: monthly + repos: - repo: https://github.com/ansys/pre-commit-hooks rev: v0.5.1 hooks: - - id: add-license-headers - args: - - --start_year=2023 + - id: add-license-headers + args: + - --start_year=2023 -- repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.9.2 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.12.4 hooks: - - id: ruff - args: [--fix] - - id: ruff-format + - id: ruff-check + args: [--fix] + - id: ruff-format - repo: https://github.com/codespell-project/codespell rev: v2.3.0 hooks: - id: codespell additional_dependencies: - - tomli + - tomli exclude: > - (?x)^( - .*\.etp| - .*\.vsw| - .*\.scade| - .*\.xscade| - .*\.sct - )$ + (?x)^( + .*\.etp| + .*\.vsw| + .*\.scade| + .*\.xscade| + .*\.sct + )$ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 @@ -37,3 +42,31 @@ repos: - id: check-yaml - id: trailing-whitespace exclude: (?x)^(?:.*\.tot|.*\.swan.?)$ + +- repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.33.2 + hooks: + - id: check-github-workflows + +- repo: https://github.com/numpy/numpydoc + rev: v1.9.0 + hooks: + - id: numpydoc-validation + exclude: | + (?x)( + tests/ + ) + +- repo: https://github.com/pre-commit-ci/pre-commit-ci-config + rev: v1.6.1 + hooks: + - id: check-pre-commit-ci-config + +# NOTE: Ruff does not check for python code in some parts of the documentation, +# e.g. python code block, ..., see https://github.com/astral-sh/ruff/issues/8237 +- repo: https://github.com/adamchainz/blacken-docs + rev: 1.19.1 + hooks: + - id: blacken-docs + additional_dependencies: [black==25.1.0] + args: [--line-length=120] diff --git a/doc/changelog.d/62.maintenance.md b/doc/changelog.d/62.maintenance.md new file mode 100644 index 0000000..88b6f36 --- /dev/null +++ b/doc/changelog.d/62.maintenance.md @@ -0,0 +1 @@ +Update repository configuration files diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index d28a8b5..c386d5f 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -150,7 +150,7 @@ Fixed - fix: documentation link `#23 `_ -`2.0.dev0 `_ - 2025-01-22 +`2.2.0 `_ - 2025-01-22 =============================================================================================== Fixed diff --git a/doc/source/conf.py b/doc/source/conf.py index af182e4..67a2630 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -61,30 +61,6 @@ # "grpc": ("https://grpc.github.io/grpc/python/", None), } -# numpydoc configuration -numpydoc_show_class_members = False -numpydoc_xref_param_type = True - -# Consider enabling numpydoc validation. See: -# https://numpydoc.readthedocs.io/en/latest/validation.html# -numpydoc_validate = True -numpydoc_validation_checks = { - 'GL06', # Found unknown section - 'GL07', # Sections are in the wrong order. - # Disabled the docstring validation as most of the methods doesn't have the docstring - # TODO: Add docstring and enable GL08 validation - # "GL08", # The object does not have a docstring - 'GL09', # Deprecation warning should precede extended summary - 'GL10', # reST directives {directives} must be followed by two colons - 'SS01', # No summary found - 'SS02', # Summary does not start with a capital letter - 'SS03', # Summary does not end with a period - 'SS04', # Summary contains heading whitespaces - # "SS05", # Summary must start with infinitive verb, not third person - 'RT02', # The first line of the Returns section should contain only the - # type, unless multiple values are being returned" -} - # Favicon html_favicon = ansys_favicon @@ -100,13 +76,11 @@ # The master toctree document. master_doc = 'index' - -# TODO: remove ignore links after public release +linkcheck_exclude_documents = ['changelog'] linkcheck_ignore = [ - 'https://github.com/ansys/scade-python-wrapper', - 'https://github.com/ansys/scade-python-wrapper/actions/workflows/ci_cd.yml', - 'https://pypi.org/project/ansys-scade-python-wrapper', # The link below takes a long time to check + 'https://github.com/ansys/pre-commit-hooks/.*', + 'https://ansyshelp.ansys.com/*', 'https://www.ansys.com/products/embedded-software/ansys-scade-suite', 'https://www.ansys.com/*', ] diff --git a/doc/source/usage/proxy.rst b/doc/source/usage/proxy.rst index 6e51546..c0663d8 100644 --- a/doc/source/usage/proxy.rst +++ b/doc/source/usage/proxy.rst @@ -102,5 +102,6 @@ Access to values Limitations ----------- -The wrapper does not support for now the ``input_threshold`` and -``global_context`` KCG options. +* The wrapper does not support for now the ``input_threshold`` and + ``global_context`` KCG options. +* Imported types are not supported. diff --git a/pyproject.toml b/pyproject.toml index 870990c..ea8780c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,8 @@ [build-system] -requires = ["flit_core >=3.2,<4"] +requires = [ + "flit_core >=3.9,<3.12; python_version < '3.8'", + "flit_core >=3.12,<4; python_version >= '3.8'", +] build-backend = "flit_core.buildapi" [project] @@ -60,9 +63,6 @@ doc = [ "sphinxnotes-strike==1.2.1; python_version <= '3.10'", ] -[tool.flit.module] -name = "ansys.scade.python_wrapper" - [project.urls] Source = "https://github.com/ansys/scade-python-wrapper" Issues = "https://github.com/ansys/scade-python-wrapper/issues" @@ -82,20 +82,11 @@ ansys_scade_python_wrapper_swanpython = "ansys.scade.python_wrapper.swanpython:m register_ansys_scade_python_wrapper = "ansys.scade.python_wrapper.register:main" unregister_ansys_scade_python_wrapper = "ansys.scade.python_wrapper.unregister:main" +[tool.flit.module] +name = "ansys.scade.python_wrapper" + [tool.ruff] -line-length = 99 -lint.select = [ - "E", # pycodestyle, see https://beta.ruff.rs/docs/rules/#pycodestyle-e-w - "D", # pydocstyle, see https://beta.ruff.rs/docs/rules/#pydocstyle-d - "F", # pyflakes, see https://beta.ruff.rs/docs/rules/#pyflakes-f - "I", # isort, see https://beta.ruff.rs/docs/rules/#isort-i - "N", # pep8-naming, see https://beta.ruff.rs/docs/rules/#pep8-naming-n -] -lint.ignore = [ - "D203", # One blank line required before class docstring. - "D204", # One blank line required after class docstring. - "D206", # To ignore: conflict with the formatter -] +line-length = 100 exclude = [ # generated file "pydata.py", @@ -103,8 +94,23 @@ exclude = [ # TODO: "preserve" leads to an error, although it is documented # and used in other repositories +fix = true + [tool.ruff.format] quote-style = "single" +indent-style = "space" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle, see https://docs.astral.sh/ruff/rules/#pycodestyle-e-w + "D", # pydocstyle, see https://docs.astral.sh/ruff/rules/#pydocstyle-d + "F", # pyflakes, see https://docs.astral.sh/ruff/rules/#pyflakes-f + "I", # isort, see https://docs.astral.sh/ruff/rules/#isort-i + "N", # pep8-naming, see https://docs.astral.sh/ruff/rules/#pep8-naming-n + "PTH", # flake8-use-pathlib, https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + "TD", # flake8-todos, https://docs.astral.sh/ruff/rules/#flake8-todos-td +] +ignore = [] [tool.ruff.lint.isort] combine-as-imports = true @@ -117,15 +123,19 @@ convention = "numpy" [tool.ruff.lint.per-file-ignores] "tests/*.py" = ["D",] # generated file -"src/ansys/scade/python-wrapper/pydata.py" = ["D101", "D102", "D103"] +"src/ansys/scade/python-wrapper/pydata.py" = ["D101", "D102", "D103"] # sync: skip # naming rules not applicable -"tools/BuildData/*.py" = ["N",] +"tools/BuildData/*.py" = ["N",] # sync: skip [tool.coverage.run] source = ["ansys.scade.python_wrapper"] +patch = ["subprocess"] [tool.coverage.report] show_missing = true +exclude_also = [ + "@(abc\\.)?abstractmethod", +] [tool.pytest.ini_options] testpaths = "tests" @@ -185,3 +195,28 @@ directory = "changed" name = "Changed" showcontent = true +[tool.codespell] +ignore-words-list = ["synopsys"] + +[tool.numpydoc_validation] +checks = [ + 'GL06', # Found unknown section + 'GL07', # Sections are in the wrong order. + 'GL08', # The object does not have a docstring + 'GL09', # Deprecation warning should precede extended summary + 'GL10', # reST directives {directives} must be followed by two colons + 'SS01', # No summary found + 'SS02', # Summary does not start with a capital letter + 'SS03', # Summary does not end with a period + 'SS04', # Summary contains heading whitespaces + # "SS05", # Summary must start with infinitive verb, not third person + 'RT02', # The first line of the Returns section should contain only the + # type, unless multiple values are being returned" +] +exclude = [ + '\._', # ignore private definitions + 'conftest.', + 'test.', + # generated file + 'pydata\.', +] diff --git a/src/ansys/scade/python_wrapper/kcg_data_parser.py b/src/ansys/scade/python_wrapper/kcg_data_parser.py index 74f9dcc..1ba79d8 100644 --- a/src/ansys/scade/python_wrapper/kcg_data_parser.py +++ b/src/ansys/scade/python_wrapper/kcg_data_parser.py @@ -87,9 +87,7 @@ def _build_type(model: data.Model, c_type: c.Type) -> Tuple[List[int], data.Type ) if c_type.is_context(): # consider only the interface, if any - c_fields = [ - _ for _ in c_type.get_fields() if isinstance(_.get_model(), m.Variable) - ] + c_fields = [_ for _ in c_type.get_fields() if isinstance(_.get_model(), m.Variable)] else: c_fields = [_ for _ in c_type.get_fields()] for c_field in c_fields: @@ -136,7 +134,7 @@ def _build_operator(model: data.Model, m_op: m.Operator): if c_op.get_reset(): op.set_reset(data.Function(c_name=c_op.get_reset().get_name())) - # TODO? + # is that needed? # _add_c_type(model, c_op.get_state_vector()) _, type_ = _build_type(model, c_op.get_input_struct()) if type_: @@ -148,7 +146,7 @@ def _build_operator(model: data.Model, m_op: m.Operator): op.in_context.link_type(type_) op.in_context.c_type = c_op.get_input_struct().get_name() op.in_context.pointer = True - # TODO? + # is that needed? # _add_c_type(model, c_op.get_output_struct()) _, type_ = _build_type(model, c_op.get_context()) if type_: diff --git a/src/ansys/scade/python_wrapper/kcgpython.py b/src/ansys/scade/python_wrapper/kcgpython.py index fafcf37..3e7c159 100644 --- a/src/ansys/scade/python_wrapper/kcgpython.py +++ b/src/ansys/scade/python_wrapper/kcgpython.py @@ -375,7 +375,7 @@ def _generate_display( ['%sLayer' % layer.name for spec in specifications for layer in spec.layers] ) - with open(str(pathname), 'w') as f: + with pathname.open('w') as f: f.write('import os.path\n') f.write('import ctypes\n') f.write('from sdyproxy import SdyProxy, SdyLayer\n') diff --git a/src/ansys/scade/python_wrapper/rd/c_gen.py b/src/ansys/scade/python_wrapper/rd/c_gen.py index f74014d..992a5c8 100644 --- a/src/ansys/scade/python_wrapper/rd/c_gen.py +++ b/src/ansys/scade/python_wrapper/rd/c_gen.py @@ -63,9 +63,7 @@ def generate_c(model: data.Model, c_pathname: Path, banner: str = '') -> None: f.write(' return 0;\n') else: # regular context to allocate - f.write( - ' {0} *ctx = ({0}*)malloc(sizeof({0}));\n'.format(op.context.c_type) - ) + f.write(' {0} *ctx = ({0}*)malloc(sizeof({0}));\n'.format(op.context.c_type)) if op.init is not None: f.write(' %s(ctx);\n' % op.init.c_name) f.write(' return (void*)ctx;\n') diff --git a/src/ansys/scade/python_wrapper/rd/def_gen.py b/src/ansys/scade/python_wrapper/rd/def_gen.py index 66f5b6a..24c42eb 100644 --- a/src/ansys/scade/python_wrapper/rd/def_gen.py +++ b/src/ansys/scade/python_wrapper/rd/def_gen.py @@ -32,6 +32,7 @@ def generate_def(model: data.Model, def_pathname: Path, cosim: bool, banner: str """Generate the C definition file for the DLL.""" def add_export(function: Optional[data.Function]): + """Add an export declaration for a declaration.""" nonlocal f, i if function: f.write('\t%s @ %d;\n' % (function.c_name, i)) diff --git a/src/ansys/scade/python_wrapper/rd/python_gen.py b/src/ansys/scade/python_wrapper/rd/python_gen.py index 19785dd..38fd2d0 100644 --- a/src/ansys/scade/python_wrapper/rd/python_gen.py +++ b/src/ansys/scade/python_wrapper/rd/python_gen.py @@ -28,7 +28,7 @@ # * add type: ignore comments # * use setattr/getattr functions -# TODO: +# room for improvements: # * error (or warning multiple instances not supported) with global context # * rename when name of io is either a Python keyword or conflicts with one of: # * call_reset @@ -38,7 +38,7 @@ # * in_c # * declare a structure of classes corresponding to the package hierarchy # * refactor the design: -# * rely more on c_op.get_cycle().get_parameters() to simply the algorithms +# * rely more on c_op.get_cycle().get_parameters() to simplify the algorithms from collections import namedtuple from keyword import iskeyword @@ -113,7 +113,7 @@ def _get_python_type_name(type_: data.Type, native: bool, sizes=None) -> str: # make anonymous structures, that must be contexts, private if type_.scalar: # must be a predefined type - # TODO: what about imported scalar types? + # (imported scalar types are nbot supported) pi = _get_predef_info(type_.m_name, native) assert pi is not None # nosec B101 # addresses linter name = pi.type_name @@ -208,6 +208,7 @@ def generate_python( global _pep8 def write_accessors(typed: data.Feature): + """Generate accessors to the feature.""" # typed is either a sensor or an i/o type_ = typed.type assert type_ is not None # nosec B101 # addresses linter @@ -217,12 +218,12 @@ def write_accessors(typed: data.Feature): else: assert isinstance(typed, data.IO) # nosec B101 # addresses linter setter = typed.input - # TODO: no name for not scalar types if typed.scalar(): type_name = _get_python_type_name(type_, True) arg_type = ': %s' % type_name return_type = ' -> %s' % type_name else: + # no name for not scalar types arg_type = '' return_type = '' f.write(' @property\n') @@ -480,7 +481,6 @@ def write_accessors(typed: data.Feature): if op.context: f.write('\n') f.write(' def __del__(self):\n') - # TODO: separate_io f.write(' free_fct = _lib.py_free_%s\n' % op.c_name) f.write(' free_fct.argtypes = [ctypes.c_void_p]\n') f.write(' free_fct.restype = None\n') @@ -488,7 +488,6 @@ def write_accessors(typed: data.Feature): f.write('\n') f.write(' def call_reset(self) -> None:\n') if op.reset: - # TODO: reuse function.parameters instead of hard-coding op.context arg = ('ctypes.byref(%s)' % op.context.py_member) if op.reset.parameters else '' f.write(' self.reset_fct(%s)\n' % arg) else: @@ -540,7 +539,7 @@ def write_accessors(typed: data.Feature): f.write('\n') for c_type in sorted(c_interface_types): - # TODO: share code with _get_cvt_name + # code could be shared with _get_cvt_name f.write( '_%s_cvt = _cvt(_lib.%s_to_string)\n' % ((utils.lower_name(c_type) if _pep8 else c_type), c_type) diff --git a/tests/Debug/debug.py b/tests/Debug/debug.py index 29877f6..41ef3f8 100644 --- a/tests/Debug/debug.py +++ b/tests/Debug/debug.py @@ -37,9 +37,7 @@ from scade.code.suite.sctoc import raw_tcl, sc_to_c_core parser = argparse.ArgumentParser(description='Python way for scade -code') -parser.add_argument( - '-p', '--project', metavar='', help='SCADE Suite', required=True -) +parser.add_argument('-p', '--project', metavar='', help='SCADE Suite', required=True) parser.add_argument( '-c', '--configuration', metavar='', help='configuration', required=True )