Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Commits on Source (2)
...@@ -23,12 +23,10 @@ typechecker: ...@@ -23,12 +23,10 @@ typechecker:
script: script:
- nox --session typecheck - nox --session typecheck
testsuite: .testsuite-base:
extends: .nox-base extends: .nox-base
stage: "Tests" stage: "Tests"
needs: [] needs: []
script:
- nox --session testsuite
coverage: '/TOTAL.*\s+(\d+%)$/' coverage: '/TOTAL.*\s+(\d+%)$/'
artifacts: artifacts:
when: always when: always
...@@ -40,6 +38,16 @@ testsuite: ...@@ -40,6 +38,16 @@ testsuite:
coverage_format: cobertura coverage_format: cobertura
path: coverage.xml path: coverage.xml
"testsuite-py3.10":
extends: .testsuite-base
script:
- nox --session testsuite-3.10
"testsuite-py3.13":
extends: .testsuite-base
script:
- nox --session testsuite-3.13
build-documentation: build-documentation:
extends: .nox-base extends: .nox-base
stage: "Documentation" stage: "Documentation"
......
...@@ -11,7 +11,14 @@ def add_pystencils_git(session: nox.Session): ...@@ -11,7 +11,14 @@ def add_pystencils_git(session: nox.Session):
cache_dir = session.cache_dir cache_dir = session.cache_dir
pystencils_dir = cache_dir / "pystencils" pystencils_dir = cache_dir / "pystencils"
if not pystencils_dir.exists(): if pystencils_dir.exists():
with session.chdir(pystencils_dir):
session.run_install(
"git",
"pull",
external=True
)
else:
session.run_install( session.run_install(
"git", "git",
"clone", "clone",
...@@ -50,7 +57,7 @@ def typecheck(session: nox.Session): ...@@ -50,7 +57,7 @@ def typecheck(session: nox.Session):
session.run("mypy", "src/pystencilssfg") session.run("mypy", "src/pystencilssfg")
@nox.session(python=["3.10"], tags=["tests"]) @nox.session(python=["3.10", "3.11", "3.12", "3.13"], tags=["tests"])
def testsuite(session: nox.Session): def testsuite(session: nox.Session):
"""Run the testsuite and measure coverage.""" """Run the testsuite and measure coverage."""
editable_install(session, ["testsuite"]) editable_install(session, ["testsuite"])
......
...@@ -3,101 +3,27 @@ from __future__ import annotations ...@@ -3,101 +3,27 @@ from __future__ import annotations
from argparse import ArgumentParser from argparse import ArgumentParser
from types import ModuleType from types import ModuleType
from typing import Generic, TypeVar, Callable, Any, Sequence from typing import Any, Sequence
from abc import ABC from dataclasses import dataclass
from dataclasses import dataclass, fields, field
from enum import Enum, auto from enum import Enum, auto
from os import path from os import path
from importlib import util as iutil from importlib import util as iutil
class SfgConfigException(Exception): ... # noqa: E701 from pystencils.codegen.config import ConfigBase, BasicOption, Category
Option_T = TypeVar("Option_T")
class Option(Generic[Option_T]):
"""Option descriptor.
This descriptor is used to model configuration options.
It maintains a default value for the option that is used when no value
was specified by the user.
In configuration options, the value `None` stands for `unset`.
It can therefore not be used to set an option to the meaning "not any", or "empty"
- for these, special values need to be used.
"""
def __init__(
self,
default: Option_T | None = None,
validator: Callable[[Any, Option_T | None], Option_T | None] | None = None,
) -> None:
self._default = default
self._validator = validator
self._name: str
self._lookup: str
def validate(self, validator: Callable[[Any, Any], Any] | None):
self._validator = validator
return validator
@property class SfgConfigException(Exception): ... # noqa: E701
def default(self) -> Option_T | None:
return self._default
def get(self, obj) -> Option_T | None:
val = getattr(obj, self._lookup, None)
if val is None:
return self._default
else:
return val
def __set_name__(self, owner, name: str):
self._name = name
self._lookup = f"_{name}"
def __get__(self, obj, objtype=None) -> Option_T | None:
if obj is None:
return None
return getattr(obj, self._lookup, None)
def __set__(self, obj, value: Option_T | None):
if self._validator is not None:
value = self._validator(obj, value)
setattr(obj, self._lookup, value)
def __delete__(self, obj):
delattr(obj, self._lookup)
class ConfigBase(ABC):
def get_option(self, name: str) -> Any:
"""Get the value set for the specified option, or the option's default value if none has been set."""
descr: Option = type(self).__dict__[name]
return descr.get(self)
def override(self, other: ConfigBase):
for f in fields(self): # type: ignore
fvalue = getattr(self, f.name)
if isinstance(fvalue, ConfigBase): # type: ignore
fvalue.override(getattr(other, f.name))
else:
new_val = getattr(other, f.name)
if new_val is not None:
setattr(self, f.name, new_val)
@dataclass @dataclass
class FileExtensions(ConfigBase): class FileExtensions(ConfigBase):
"""Option category containing output file extensions.""" """BasicOption category containing output file extensions."""
header: Option[str] = Option("hpp") header: BasicOption[str] = BasicOption("hpp")
"""File extension for generated header file.""" """File extension for generated header file."""
impl: Option[str] = Option() impl: BasicOption[str] = BasicOption()
"""File extension for generated implementation file.""" """File extension for generated implementation file."""
@header.validate @header.validate
...@@ -132,7 +58,7 @@ class OutputMode(Enum): ...@@ -132,7 +58,7 @@ class OutputMode(Enum):
class CodeStyle(ConfigBase): class CodeStyle(ConfigBase):
"""Options affecting the code style used by the source file generator.""" """Options affecting the code style used by the source file generator."""
indent_width: Option[int] = Option(2) indent_width: BasicOption[int] = BasicOption(2)
"""The number of spaces successively nested blocks should be indented with""" """The number of spaces successively nested blocks should be indented with"""
# TODO possible future options: # TODO possible future options:
...@@ -150,20 +76,20 @@ class CodeStyle(ConfigBase): ...@@ -150,20 +76,20 @@ class CodeStyle(ConfigBase):
class ClangFormatOptions(ConfigBase): class ClangFormatOptions(ConfigBase):
"""Options affecting the invocation of ``clang-format`` for automatic code formatting.""" """Options affecting the invocation of ``clang-format`` for automatic code formatting."""
code_style: Option[str] = Option("file") code_style: BasicOption[str] = BasicOption("file")
"""Code style to be used by clang-format. Passed verbatim to `--style` argument of the clang-format CLI. """Code style to be used by clang-format. Passed verbatim to `--style` argument of the clang-format CLI.
Similar to clang-format itself, the default value is `file`, such that a `.clang-format` file found in the build Similar to clang-format itself, the default value is `file`, such that a `.clang-format` file found in the build
tree will automatically be used. tree will automatically be used.
""" """
force: Option[bool] = Option(False) force: BasicOption[bool] = BasicOption(False)
"""If set to ``True``, abort code generation if ``clang-format`` binary cannot be found.""" """If set to ``True``, abort code generation if ``clang-format`` binary cannot be found."""
skip: Option[bool] = Option(False) skip: BasicOption[bool] = BasicOption(False)
"""If set to ``True``, skip formatting using ``clang-format``.""" """If set to ``True``, skip formatting using ``clang-format``."""
binary: Option[str] = Option("clang-format") binary: BasicOption[str] = BasicOption("clang-format")
"""Path to the clang-format executable""" """Path to the clang-format executable"""
@force.validate @force.validate
...@@ -194,7 +120,7 @@ GLOBAL_NAMESPACE = _GlobalNamespace() ...@@ -194,7 +120,7 @@ GLOBAL_NAMESPACE = _GlobalNamespace()
class SfgConfig(ConfigBase): class SfgConfig(ConfigBase):
"""Configuration options for the `SourceFileGenerator`.""" """Configuration options for the `SourceFileGenerator`."""
extensions: FileExtensions = field(default_factory=FileExtensions) extensions: Category[FileExtensions] = Category(FileExtensions())
"""File extensions of the generated files """File extensions of the generated files
Options in this category: Options in this category:
...@@ -203,7 +129,7 @@ class SfgConfig(ConfigBase): ...@@ -203,7 +129,7 @@ class SfgConfig(ConfigBase):
FileExtensions.impl FileExtensions.impl
""" """
output_mode: Option[OutputMode] = Option(OutputMode.STANDALONE) output_mode: BasicOption[OutputMode] = BasicOption(OutputMode.STANDALONE)
"""The generator's output mode; defines which files to generate, and the set of legal file extensions. """The generator's output mode; defines which files to generate, and the set of legal file extensions.
Possible parameters: Possible parameters:
...@@ -213,7 +139,7 @@ class SfgConfig(ConfigBase): ...@@ -213,7 +139,7 @@ class SfgConfig(ConfigBase):
OutputMode.HEADER_ONLY OutputMode.HEADER_ONLY
""" """
outer_namespace: Option[str | _GlobalNamespace] = Option(GLOBAL_NAMESPACE) outer_namespace: BasicOption[str | _GlobalNamespace] = BasicOption(GLOBAL_NAMESPACE)
"""The outermost namespace in the generated file. May be a valid C++ nested namespace qualifier """The outermost namespace in the generated file. May be a valid C++ nested namespace qualifier
(like ``a::b::c``) or `GLOBAL_NAMESPACE` if no outer namespace should be generated. (like ``a::b::c``) or `GLOBAL_NAMESPACE` if no outer namespace should be generated.
...@@ -221,7 +147,7 @@ class SfgConfig(ConfigBase): ...@@ -221,7 +147,7 @@ class SfgConfig(ConfigBase):
GLOBAL_NAMESPACE GLOBAL_NAMESPACE
""" """
codestyle: CodeStyle = field(default_factory=CodeStyle) codestyle: Category[CodeStyle] = Category(CodeStyle())
"""Options affecting the code style emitted by pystencils-sfg. """Options affecting the code style emitted by pystencils-sfg.
Options in this category: Options in this category:
...@@ -229,7 +155,7 @@ class SfgConfig(ConfigBase): ...@@ -229,7 +155,7 @@ class SfgConfig(ConfigBase):
CodeStyle.indent_width CodeStyle.indent_width
""" """
clang_format: ClangFormatOptions = field(default_factory=ClangFormatOptions) clang_format: Category[ClangFormatOptions] = Category(ClangFormatOptions())
"""Options governing the code style used by the code generator """Options governing the code style used by the code generator
Options in this category: Options in this category:
...@@ -240,7 +166,7 @@ class SfgConfig(ConfigBase): ...@@ -240,7 +166,7 @@ class SfgConfig(ConfigBase):
ClangFormatOptions.binary ClangFormatOptions.binary
""" """
output_directory: Option[str] = Option(".") output_directory: BasicOption[str] = BasicOption(".")
"""Directory to which the generated files should be written.""" """Directory to which the generated files should be written."""
......