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
Select Git revision

Target

Select target project
  • ob28imeq/pystencils-sfg
  • brendan-waters/pystencils-sfg
  • pycodegen/pystencils-sfg
3 results
Select Git revision
Show changes
Showing
with 1373 additions and 1603 deletions
from pystencils import Target, CreateKernelConfig, no_jit
from lbmpy import create_lb_update_rule, LBMOptimisation
from pystencilssfg import SourceFileGenerator, SfgConfig
sfg_config = SfgConfig()
sfg_config.extensions.impl = "cu"
sfg_config.output_directory = "out/test_cuda"
sfg_config.outer_namespace = "gen_code"
with SourceFileGenerator(sfg_config) as sfg:
gen_config = CreateKernelConfig(target=Target.CUDA, jit=no_jit)
opt = LBMOptimisation(field_layout="fzyx")
update = create_lb_update_rule()
kernel = sfg.kernels.create(update, "lbm_update", gen_config)
sfg.function("lb_update")(sfg.call(kernel))
from pystencils import Target, CreateKernelConfig, create_kernel, no_jit
from lbmpy import create_lb_update_rule, LBMOptimisation
from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgOutputMode
from pystencilssfg.lang.cpp import mdspan_ref
sfg_config = SfgConfiguration(
output_directory="out/test_sycl",
outer_namespace="gen_code",
impl_extension="ipp",
output_mode=SfgOutputMode.INLINE
)
with SourceFileGenerator(sfg_config) as sfg:
gen_config = CreateKernelConfig(target=Target.SYCL, jit=no_jit)
opt = LBMOptimisation(field_layout="fzyx")
update = create_lb_update_rule()
kernel = sfg.kernels.create(update, "lbm_update", gen_config)
sfg.function("lb_update")(
sfg.call(kernel)
)
from pystencils import Target, CreateKernelConfig, no_jit
from lbmpy import create_lb_update_rule, LBMOptimisation
from pystencilssfg import SourceFileGenerator, SfgConfig
from pystencilssfg.lang.cpp.sycl_accessor import SyclAccessor
import pystencilssfg.extensions.sycl as sycl
from itertools import chain
sfg_config = SfgConfig(
output_directory="out/test_sycl_buffer",
outer_namespace="gen_code",
header_only=True
)
with SourceFileGenerator(sfg_config) as sfg:
sfg = sycl.SyclComposer(sfg)
gen_config = CreateKernelConfig(target=Target.SYCL, jit=no_jit)
opt = LBMOptimisation(field_layout="fzyx")
update = create_lb_update_rule(lbm_optimisation=opt)
kernel = sfg.kernels.create(update, "lbm_update", gen_config)
cgh = sfg.sycl_handler("handler")
rang = sfg.sycl_range(update.method.dim, "range")
mappings = [
sfg.map_field(field, SyclAccessor.from_field(field))
for field in chain(update.free_fields, update.bound_fields)
]
sfg.function("lb_update")(
cgh.parallel_for(rang)(
*mappings,
sfg.call(kernel),
),
)
site_name: pystencils Source File Generator Documentation
site_author: Frederik Hennig
copyright: © 2023 Frederik Hennig
repo_name: GitLab
repo_url: https://i10git.cs.fau.de/pycodegen/pystencils-sfg
theme:
name: material
features:
- navigation.tabs
- content.code.copy
palette:
scheme: slate
primary: deep purple
extra_css:
- css/mkdocstrings.css
plugins:
- search
- autorefs
- mkdocstrings:
default_handler: python
handlers:
python:
paths: [src]
options:
heading_level: 2
members_order: source
group_by_category: False
show_root_heading: True
show_root_full_path: True
show_symbol_type_heading: True
show_symbol_type_toc: True
show_source: False
show_signature_annotations: True
signature_crossrefs: True
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.superfences
nav:
- Home: index.md
- 'User Guides':
- 'Overview': usage/index.md
- 'Writing Generator Scripts': usage/generator_scripts.md
- 'In-Depth: Building Source Files': usage/building.md
- 'CLI and Build System Integration': usage/cli_and_build_system.md
- 'Tips and Tricks': usage/tips_n_tricks.md
- 'API Documentation':
- 'Overview': api/index.md
- 'Front End':
- 'Source File Generator': api/generator.md
- 'Code Generation Context': api/context.md
- 'Composer': api/composer.md
- 'Source File Modelling':
- 'Source File Components': api/source_components.md
- 'Kernel Call Tree': api/tree.md
- 'High-Level Language Concepts':
- 'Base Classes': 'api/source_objects.md'
- 'C++ Standard Library': 'api/cpp_std.md'
- 'Code Generation':
- 'Emission and Printing': api/emission.md
......@@ -3,3 +3,6 @@ python_version=3.10
[mypy-pystencils.*]
ignore_missing_imports=true
[mypy-sympy.*]
ignore_missing_imports=true
from __future__ import annotations
from typing import Sequence
import nox
nox.options.sessions = ["lint", "typecheck", "testsuite"]
def add_pystencils_git(session: nox.Session):
"""Clone the pystencils 2.0 development branch and install it in the current session"""
cache_dir = session.cache_dir
pystencils_dir = cache_dir / "pystencils"
if pystencils_dir.exists():
with session.chdir(pystencils_dir):
session.run_install("git", "pull", external=True)
else:
session.run_install(
"git",
"clone",
"--branch",
"v2.0-dev",
"--single-branch",
"https://i10git.cs.fau.de/pycodegen/pystencils.git",
pystencils_dir,
external=True,
)
session.install("-e", str(pystencils_dir))
def editable_install(session: nox.Session, opts: Sequence[str] = ()):
add_pystencils_git(session)
if opts:
opts_str = "[" + ",".join(opts) + "]"
else:
opts_str = ""
session.install("-e", f".{opts_str}")
@nox.session(python="3.10", tags=["qa", "code-quality"])
def lint(session: nox.Session):
"""Lint code using flake8"""
session.install("flake8")
session.run("flake8", "src/pystencilssfg")
@nox.session(python="3.10", tags=["qa", "code-quality"])
def typecheck(session: nox.Session):
"""Run MyPy for static type checking"""
editable_install(session)
session.install("mypy")
session.run("mypy", "src/pystencilssfg")
@nox.session(python=["3.10", "3.11", "3.12", "3.13"], tags=["tests"])
def testsuite(session: nox.Session):
"""Run the testsuite and measure coverage."""
editable_install(session, ["testsuite"])
session.run(
"pytest",
"-v",
"--cov=src/pystencilssfg",
"--cov-report=term",
"--cov-config=pyproject.toml",
)
session.run("coverage", "html")
session.run("coverage", "xml")
@nox.session(python=["3.10"], tags=["docs"])
def docs(session: nox.Session):
"""Build the documentation pages"""
editable_install(session, ["docs"])
env = {}
session_args = session.posargs
if "--fail-on-warnings" in session_args:
env["SPHINXOPTS"] = "-W --keep-going"
session.chdir("docs")
if "--clean" in session_args:
session.run("make", "clean", external=True)
session.run("make", "html", external=True)
@nox.session()
def dev_env(session: nox.Session):
"""Set up the development environment at .venv"""
session.install("virtualenv")
session.run("virtualenv", ".venv", "--prompt", "pystencils-sfg")
session.run(
".venv/bin/pip",
"install",
"git+https://i10git.cs.fau.de/pycodegen/pystencils.git@v2.0-dev",
external=True,
)
session.run(".venv/bin/pip", "install", "-e", ".[dev]", external=True)
# This file is @generated by PDM.
# It is not intended for manual editing.
[metadata]
groups = ["default", "code_quality", "docs", "interactive"]
strategy = ["cross_platform", "inherit_metadata"]
lock_version = "4.4.1"
content_hash = "sha256:2c854f8da4b29c3080cd89c774409f95c47d3532c953cf10ecaa67d0b77ff9cf"
[[package]]
name = "appdirs"
version = "1.4.4"
summary = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
groups = ["default"]
files = [
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
]
[[package]]
name = "asttokens"
version = "2.4.1"
summary = "Annotate AST trees with source code positions"
groups = ["interactive"]
dependencies = [
"six>=1.12.0",
]
files = [
{file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"},
{file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"},
]
[[package]]
name = "babel"
version = "2.14.0"
requires_python = ">=3.7"
summary = "Internationalization utilities"
groups = ["docs"]
files = [
{file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"},
{file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"},
]
[[package]]
name = "certifi"
version = "2023.11.17"
requires_python = ">=3.6"
summary = "Python package for providing Mozilla's CA Bundle."
groups = ["docs"]
files = [
{file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
requires_python = ">=3.7.0"
summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
groups = ["docs"]
files = [
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
{file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
{file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
{file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
]
[[package]]
name = "click"
version = "8.1.7"
requires_python = ">=3.7"
summary = "Composable command line interface toolkit"
groups = ["docs"]
dependencies = [
"colorama; platform_system == \"Windows\"",
]
files = [
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
]
[[package]]
name = "colorama"
version = "0.4.6"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
summary = "Cross-platform colored terminal text."
groups = ["docs", "interactive"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "decorator"
version = "5.1.1"
requires_python = ">=3.5"
summary = "Decorators for Humans"
groups = ["interactive"]
files = [
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
]
[[package]]
name = "exceptiongroup"
version = "1.2.0"
requires_python = ">=3.7"
summary = "Backport of PEP 654 (exception groups)"
groups = ["interactive"]
marker = "python_version < \"3.11\""
files = [
{file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
]
[[package]]
name = "executing"
version = "2.0.1"
requires_python = ">=3.5"
summary = "Get the currently executing AST node of a frame, and other information"
groups = ["interactive"]
files = [
{file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"},
{file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"},
]
[[package]]
name = "flake8"
version = "7.0.0"
requires_python = ">=3.8.1"
summary = "the modular source code checker: pep8 pyflakes and co"
groups = ["code_quality"]
dependencies = [
"mccabe<0.8.0,>=0.7.0",
"pycodestyle<2.12.0,>=2.11.0",
"pyflakes<3.3.0,>=3.2.0",
]
files = [
{file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"},
{file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"},
]
[[package]]
name = "ghp-import"
version = "2.1.0"
summary = "Copy your docs directly to the gh-pages branch."
groups = ["docs"]
dependencies = [
"python-dateutil>=2.8.1",
]
files = [
{file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
{file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
]
[[package]]
name = "griffe"
version = "0.38.1"
requires_python = ">=3.8"
summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
groups = ["docs"]
dependencies = [
"colorama>=0.4",
]
files = [
{file = "griffe-0.38.1-py3-none-any.whl", hash = "sha256:334c79d3b5964ade65c05dfcaf53518c576dedd387aaba5c9fd71212f34f1483"},
{file = "griffe-0.38.1.tar.gz", hash = "sha256:bd68d7da7f3d87bc57eb9962b250db123efd9bbcc06c11c1a91b6e583b2a9361"},
]
[[package]]
name = "idna"
version = "3.6"
requires_python = ">=3.5"
summary = "Internationalized Domain Names in Applications (IDNA)"
groups = ["docs"]
files = [
{file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
{file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
]
[[package]]
name = "ipython"
version = "8.19.0"
requires_python = ">=3.10"
summary = "IPython: Productive Interactive Computing"
groups = ["interactive"]
dependencies = [
"colorama; sys_platform == \"win32\"",
"decorator",
"exceptiongroup; python_version < \"3.11\"",
"jedi>=0.16",
"matplotlib-inline",
"pexpect>4.3; sys_platform != \"win32\"",
"prompt-toolkit<3.1.0,>=3.0.41",
"pygments>=2.4.0",
"stack-data",
"traitlets>=5",
]
files = [
{file = "ipython-8.19.0-py3-none-any.whl", hash = "sha256:2f55d59370f59d0d2b2212109fe0e6035cfea436b1c0e6150ad2244746272ec5"},
{file = "ipython-8.19.0.tar.gz", hash = "sha256:ac4da4ecf0042fb4e0ce57c60430c2db3c719fa8bdf92f8631d6bd8a5785d1f0"},
]
[[package]]
name = "jedi"
version = "0.19.1"
requires_python = ">=3.6"
summary = "An autocompletion tool for Python that can be used for text editors."
groups = ["interactive"]
dependencies = [
"parso<0.9.0,>=0.8.3",
]
files = [
{file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"},
{file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"},
]
[[package]]
name = "jinja2"
version = "3.1.2"
requires_python = ">=3.7"
summary = "A very fast and expressive template engine."
groups = ["docs"]
dependencies = [
"MarkupSafe>=2.0",
]
files = [
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
]
[[package]]
name = "joblib"
version = "1.3.2"
requires_python = ">=3.7"
summary = "Lightweight pipelining with Python functions"
groups = ["default"]
files = [
{file = "joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9"},
{file = "joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1"},
]
[[package]]
name = "markdown"
version = "3.5.1"
requires_python = ">=3.8"
summary = "Python implementation of John Gruber's Markdown."
groups = ["docs"]
files = [
{file = "Markdown-3.5.1-py3-none-any.whl", hash = "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc"},
{file = "Markdown-3.5.1.tar.gz", hash = "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd"},
]
[[package]]
name = "markupsafe"
version = "2.1.3"
requires_python = ">=3.7"
summary = "Safely add untrusted strings to HTML/XML markup."
groups = ["docs"]
files = [
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"},
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"},
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"},
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"},
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"},
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"},
{file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"},
{file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"},
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
{file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
]
[[package]]
name = "matplotlib-inline"
version = "0.1.6"
requires_python = ">=3.5"
summary = "Inline Matplotlib backend for Jupyter"
groups = ["interactive"]
dependencies = [
"traitlets",
]
files = [
{file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
{file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
]
[[package]]
name = "mccabe"
version = "0.7.0"
requires_python = ">=3.6"
summary = "McCabe checker, plugin for flake8"
groups = ["code_quality"]
files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
[[package]]
name = "mergedeep"
version = "1.3.4"
requires_python = ">=3.6"
summary = "A deep merge function for 🐍."
groups = ["docs"]
files = [
{file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"},
{file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"},
]
[[package]]
name = "mkdocs"
version = "1.5.3"
requires_python = ">=3.7"
summary = "Project documentation with Markdown."
groups = ["docs"]
dependencies = [
"click>=7.0",
"colorama>=0.4; platform_system == \"Windows\"",
"ghp-import>=1.0",
"jinja2>=2.11.1",
"markdown>=3.2.1",
"markupsafe>=2.0.1",
"mergedeep>=1.3.4",
"packaging>=20.5",
"pathspec>=0.11.1",
"platformdirs>=2.2.0",
"pyyaml-env-tag>=0.1",
"pyyaml>=5.1",
"watchdog>=2.0",
]
files = [
{file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"},
{file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"},
]
[[package]]
name = "mkdocs-autorefs"
version = "0.5.0"
requires_python = ">=3.8"
summary = "Automatically link across pages in MkDocs."
groups = ["docs"]
dependencies = [
"Markdown>=3.3",
"mkdocs>=1.1",
]
files = [
{file = "mkdocs_autorefs-0.5.0-py3-none-any.whl", hash = "sha256:7930fcb8ac1249f10e683967aeaddc0af49d90702af111a5e390e8b20b3d97ff"},
{file = "mkdocs_autorefs-0.5.0.tar.gz", hash = "sha256:9a5054a94c08d28855cfab967ada10ed5be76e2bfad642302a610b252c3274c0"},
]
[[package]]
name = "mkdocs-material"
version = "9.5.3"
requires_python = ">=3.8"
summary = "Documentation that simply works"
groups = ["docs"]
dependencies = [
"babel~=2.10",
"colorama~=0.4",
"jinja2~=3.0",
"markdown~=3.2",
"mkdocs-material-extensions~=1.3",
"mkdocs~=1.5.3",
"paginate~=0.5",
"pygments~=2.16",
"pymdown-extensions~=10.2",
"regex>=2022.4",
"requests~=2.26",
]
files = [
{file = "mkdocs_material-9.5.3-py3-none-any.whl", hash = "sha256:76c93a8525cceb0b395b9cedab3428bf518cf6439adef2b940f1c1574b775d89"},
{file = "mkdocs_material-9.5.3.tar.gz", hash = "sha256:5899219f422f0a6de784232d9d40374416302ffae3c160cacc72969fcc1ee372"},
]
[[package]]
name = "mkdocs-material-extensions"
version = "1.3.1"
requires_python = ">=3.8"
summary = "Extension pack for Python Markdown and MkDocs Material."
groups = ["docs"]
files = [
{file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"},
{file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"},
]
[[package]]
name = "mkdocstrings"
version = "0.24.0"
requires_python = ">=3.8"
summary = "Automatic documentation from sources, for MkDocs."
groups = ["docs"]
dependencies = [
"Jinja2>=2.11.1",
"Markdown>=3.3",
"MarkupSafe>=1.1",
"click>=7.0",
"mkdocs-autorefs>=0.3.1",
"mkdocs>=1.4",
"platformdirs>=2.2.0",
"pymdown-extensions>=6.3",
]
files = [
{file = "mkdocstrings-0.24.0-py3-none-any.whl", hash = "sha256:f4908560c10f587326d8f5165d1908817b2e280bbf707607f601c996366a2264"},
{file = "mkdocstrings-0.24.0.tar.gz", hash = "sha256:222b1165be41257b494a9d29b14135d2b7ca43f38161d5b10caae03b87bd4f7e"},
]
[[package]]
name = "mkdocstrings-python"
version = "1.7.5"
requires_python = ">=3.8"
summary = "A Python handler for mkdocstrings."
groups = ["docs"]
dependencies = [
"griffe>=0.37",
"mkdocstrings>=0.20",
]
files = [
{file = "mkdocstrings_python-1.7.5-py3-none-any.whl", hash = "sha256:5f6246026353f0c0785135db70c3fe9a5d9318990fc7ceb11d62097b8ffdd704"},
{file = "mkdocstrings_python-1.7.5.tar.gz", hash = "sha256:c7d143728257dbf1aa550446555a554b760dcd40a763f077189d298502b800be"},
]
[[package]]
name = "mkdocstrings"
version = "0.24.0"
extras = ["python"]
requires_python = ">=3.8"
summary = "Automatic documentation from sources, for MkDocs."
groups = ["docs"]
dependencies = [
"mkdocstrings-python>=0.5.2",
"mkdocstrings==0.24.0",
]
files = [
{file = "mkdocstrings-0.24.0-py3-none-any.whl", hash = "sha256:f4908560c10f587326d8f5165d1908817b2e280bbf707607f601c996366a2264"},
{file = "mkdocstrings-0.24.0.tar.gz", hash = "sha256:222b1165be41257b494a9d29b14135d2b7ca43f38161d5b10caae03b87bd4f7e"},
]
[[package]]
name = "mpmath"
version = "1.3.0"
summary = "Python library for arbitrary-precision floating-point arithmetic"
groups = ["default"]
files = [
{file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"},
{file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"},
]
[[package]]
name = "mypy"
version = "1.8.0"
requires_python = ">=3.8"
summary = "Optional static typing for Python"
groups = ["code_quality"]
dependencies = [
"mypy-extensions>=1.0.0",
"tomli>=1.1.0; python_version < \"3.11\"",
"typing-extensions>=4.1.0",
]
files = [
{file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"},
{file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"},
{file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"},
{file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"},
{file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"},
{file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"},
{file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"},
{file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"},
{file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"},
{file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"},
{file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"},
{file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"},
{file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"},
{file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"},
{file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"},
{file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"},
{file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"},
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
requires_python = ">=3.5"
summary = "Type system extensions for programs checked with the mypy type checker."
groups = ["code_quality"]
files = [
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
]
[[package]]
name = "numpy"
version = "1.26.3"
requires_python = ">=3.9"
summary = "Fundamental package for array computing in Python"
groups = ["default"]
files = [
{file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"},
{file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"},
{file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"},
{file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"},
{file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"},
{file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"},
{file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"},
{file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"},
{file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"},
{file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"},
{file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"},
{file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"},
{file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"},
{file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"},
{file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"},
{file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"},
{file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"},
{file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"},
{file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"},
{file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"},
{file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"},
{file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"},
{file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"},
{file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"},
{file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"},
{file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"},
{file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"},
{file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"},
]
[[package]]
name = "packaging"
version = "23.2"
requires_python = ">=3.7"
summary = "Core utilities for Python packages"
groups = ["docs"]
files = [
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
]
[[package]]
name = "paginate"
version = "0.5.6"
summary = "Divides large result sets into pages for easier browsing"
groups = ["docs"]
files = [
{file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"},
]
[[package]]
name = "parso"
version = "0.8.3"
requires_python = ">=3.6"
summary = "A Python Parser"
groups = ["interactive"]
files = [
{file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
{file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
]
[[package]]
name = "pathspec"
version = "0.12.1"
requires_python = ">=3.8"
summary = "Utility library for gitignore style pattern matching of file paths."
groups = ["docs"]
files = [
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
]
[[package]]
name = "pexpect"
version = "4.9.0"
summary = "Pexpect allows easy control of interactive console applications."
groups = ["interactive"]
marker = "sys_platform != \"win32\""
dependencies = [
"ptyprocess>=0.5",
]
files = [
{file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
]
[[package]]
name = "platformdirs"
version = "4.1.0"
requires_python = ">=3.8"
summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
groups = ["docs"]
files = [
{file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"},
{file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"},
]
[[package]]
name = "prompt-toolkit"
version = "3.0.43"
requires_python = ">=3.7.0"
summary = "Library for building powerful interactive command lines in Python"
groups = ["interactive"]
dependencies = [
"wcwidth",
]
files = [
{file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"},
{file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"},
]
[[package]]
name = "ptyprocess"
version = "0.7.0"
summary = "Run a subprocess in a pseudo terminal"
groups = ["interactive"]
marker = "sys_platform != \"win32\""
files = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
]
[[package]]
name = "pure-eval"
version = "0.2.2"
summary = "Safely evaluate AST nodes without side effects"
groups = ["interactive"]
files = [
{file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"},
{file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"},
]
[[package]]
name = "pycodestyle"
version = "2.11.1"
requires_python = ">=3.8"
summary = "Python style guide checker"
groups = ["code_quality"]
files = [
{file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"},
{file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"},
]
[[package]]
name = "pyflakes"
version = "3.2.0"
requires_python = ">=3.8"
summary = "passive checker of Python programs"
groups = ["code_quality"]
files = [
{file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"},
{file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"},
]
[[package]]
name = "pygments"
version = "2.17.2"
requires_python = ">=3.7"
summary = "Pygments is a syntax highlighting package written in Python."
groups = ["docs", "interactive"]
files = [
{file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
{file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
]
[[package]]
name = "pymdown-extensions"
version = "10.7"
requires_python = ">=3.8"
summary = "Extension pack for Python Markdown."
groups = ["docs"]
dependencies = [
"markdown>=3.5",
"pyyaml",
]
files = [
{file = "pymdown_extensions-10.7-py3-none-any.whl", hash = "sha256:6ca215bc57bc12bf32b414887a68b810637d039124ed9b2e5bd3325cbb2c050c"},
{file = "pymdown_extensions-10.7.tar.gz", hash = "sha256:c0d64d5cf62566f59e6b2b690a4095c931107c250a8c8e1351c1de5f6b036deb"},
]
[[package]]
name = "pystencils"
version = "1.3.3"
requires_python = ">=3.8"
summary = "Speeding up stencil computations on CPUs and GPUs"
groups = ["default"]
dependencies = [
"appdirs",
"joblib",
"numpy>=1.8.0",
"sympy<=1.11.1,>=1.6",
]
files = [
{file = "pystencils-1.3.3.tar.gz", hash = "sha256:d066d8dd6eee355671dd6e93454f9a1bf143ab2528f76076ce15f0c15b5d29c7"},
]
[[package]]
name = "python-dateutil"
version = "2.8.2"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
summary = "Extensions to the standard Python datetime module"
groups = ["docs"]
dependencies = [
"six>=1.5",
]
files = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]
[[package]]
name = "pyyaml"
version = "6.0.1"
requires_python = ">=3.6"
summary = "YAML parser and emitter for Python"
groups = ["docs"]
files = [
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
]
[[package]]
name = "pyyaml-env-tag"
version = "0.1"
requires_python = ">=3.6"
summary = "A custom YAML tag for referencing environment variables in YAML files. "
groups = ["docs"]
dependencies = [
"pyyaml",
]
files = [
{file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
{file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
]
[[package]]
name = "regex"
version = "2023.12.25"
requires_python = ">=3.7"
summary = "Alternative regular expression module, to replace re."
groups = ["docs"]
files = [
{file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"},
{file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"},
{file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"},
{file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"},
{file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"},
{file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"},
{file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"},
{file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"},
{file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"},
{file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"},
{file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"},
{file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"},
{file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"},
{file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"},
{file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"},
{file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"},
{file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"},
{file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"},
{file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"},
{file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"},
{file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"},
{file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"},
{file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"},
{file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"},
{file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"},
{file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"},
{file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"},
{file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"},
{file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"},
{file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"},
{file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"},
{file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"},
{file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"},
{file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"},
{file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"},
{file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"},
{file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"},
{file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"},
{file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"},
{file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"},
{file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"},
{file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"},
{file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"},
{file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"},
{file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"},
{file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"},
{file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"},
]
[[package]]
name = "requests"
version = "2.31.0"
requires_python = ">=3.7"
summary = "Python HTTP for Humans."
groups = ["docs"]
dependencies = [
"certifi>=2017.4.17",
"charset-normalizer<4,>=2",
"idna<4,>=2.5",
"urllib3<3,>=1.21.1",
]
files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
]
[[package]]
name = "six"
version = "1.16.0"
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
summary = "Python 2 and 3 compatibility utilities"
groups = ["docs", "interactive"]
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[[package]]
name = "stack-data"
version = "0.6.3"
summary = "Extract data from python stack frames and tracebacks for informative displays"
groups = ["interactive"]
dependencies = [
"asttokens>=2.1.0",
"executing>=1.2.0",
"pure-eval",
]
files = [
{file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
]
[[package]]
name = "sympy"
version = "1.11.1"
requires_python = ">=3.8"
summary = "Computer algebra system (CAS) in Python"
groups = ["default"]
dependencies = [
"mpmath>=0.19",
]
files = [
{file = "sympy-1.11.1-py3-none-any.whl", hash = "sha256:938f984ee2b1e8eae8a07b884c8b7a1146010040fccddc6539c54f401c8f6fcf"},
{file = "sympy-1.11.1.tar.gz", hash = "sha256:e32380dce63cb7c0108ed525570092fd45168bdae2faa17e528221ef72e88658"},
]
[[package]]
name = "tomli"
version = "2.0.1"
requires_python = ">=3.7"
summary = "A lil' TOML parser"
groups = ["code_quality"]
marker = "python_version < \"3.11\""
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
[[package]]
name = "traitlets"
version = "5.14.1"
requires_python = ">=3.8"
summary = "Traitlets Python configuration system"
groups = ["interactive"]
files = [
{file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"},
{file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"},
]
[[package]]
name = "typing-extensions"
version = "4.9.0"
requires_python = ">=3.8"
summary = "Backported and Experimental Type Hints for Python 3.8+"
groups = ["code_quality"]
files = [
{file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
{file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
]
[[package]]
name = "urllib3"
version = "2.1.0"
requires_python = ">=3.8"
summary = "HTTP library with thread-safe connection pooling, file post, and more."
groups = ["docs"]
files = [
{file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"},
{file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"},
]
[[package]]
name = "watchdog"
version = "3.0.0"
requires_python = ">=3.7"
summary = "Filesystem events monitoring"
groups = ["docs"]
files = [
{file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"},
{file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"},
{file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"},
{file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"},
{file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"},
{file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"},
{file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"},
{file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"},
{file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"},
{file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"},
{file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"},
{file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"},
{file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"},
{file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"},
{file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"},
{file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"},
{file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"},
{file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"},
{file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"},
{file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"},
]
[[package]]
name = "wcwidth"
version = "0.2.13"
summary = "Measures the displayed width of unicode strings in a terminal"
groups = ["interactive"]
files = [
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
]
......@@ -5,11 +5,11 @@ authors = [
{name = "Frederik Hennig", email = "frederik.hennig@fau.de"},
]
dependencies = [
"pystencils>=1.3.2",
"pystencils>=2.0.dev0",
]
requires-python = ">=3.10"
readme = "README.md"
license = {text = "noneyet"}
license = { file = "LICENSE" }
dynamic = ["version"]
[project.scripts]
......@@ -18,23 +18,34 @@ sfg-cli = "pystencilssfg.cli:cli_main"
[build-system]
requires = [
"setuptools>=69",
"versioneer>=0.29",
"tomli; python_version < '3.11'"
"versioneer[toml]>=0.29",
]
build-backend = "setuptools.build_meta"
[tool.pdm.dev-dependencies]
interactive = [
"ipython>=8.17.2",
[project.optional-dependencies]
dev = [
"flake8",
"mypy",
"black",
"clang-format",
]
code_quality = [
"flake8>=6.1.0",
"mypy>=1.7.0",
testsuite = [
"pytest",
"pytest-cov",
"pyyaml",
"requests",
"fasteners",
]
docs = [
"mkdocs>=1.5.3",
"mkdocs-material>=9.4.8",
"mkdocstrings[python]>=0.24.0",
"sphinx",
"pydata-sphinx-theme==0.15.4",
"sphinx-book-theme==1.1.3", # workaround for https://github.com/executablebooks/sphinx-book-theme/issues/865
"myst-nb",
"sphinx_design",
"sphinx_autodoc_typehints",
"sphinx-copybutton",
"packaging",
"clang-format"
]
[tool.versioneer]
......@@ -42,5 +53,21 @@ VCS = "git"
style = "pep440"
versionfile_source = "src/pystencilssfg/_version.py"
versionfile_build = "pystencilssfg/_version.py"
tag_prefix = ""
tag_prefix = "v"
parentdir_prefix = "pystencilssfg-"
[tool.coverage.run]
omit = [
"setup.py",
"noxfile.py",
"src/pystencilssfg/_version.py",
"integration/*"
]
[tool.coverage.report]
exclude_also = [
"\\.\\.\\.\n",
"if TYPE_CHECKING:",
"@(abc\\.)?abstractmethod",
"assert False"
]
[pytest]
testpaths = src/pystencilssfg tests/
python_files = "test_*.py"
# Need to ignore the generator scripts, otherwise they would be executed
# during test collection
addopts =
--doctest-modules
--ignore=tests/generator_scripts/source
--ignore=tests/generator_scripts/deps
--ignore=tests/generator_scripts/expected
--ignore=tests/data
--ignore=tests/integration/cmake_project
doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
from .configuration import SfgConfiguration
from .config import SfgConfig, GLOBAL_NAMESPACE
from .generator import SourceFileGenerator
from .composer import SfgComposer
from .context import SfgContext
from .lang import SfgVar, AugExpr
from .exceptions import SfgException
__all__ = [
"SourceFileGenerator", "SfgComposer", "SfgConfiguration", "SfgContext"
"SfgConfig",
"GLOBAL_NAMESPACE",
"SourceFileGenerator",
"SfgComposer",
"SfgContext",
"SfgVar",
"AugExpr",
"SfgException",
]
from . import _version
__version__ = _version.get_versions()['version']
__version__ = _version.get_versions()["version"]
if __name__ == "__main__":
from .cli import cli_main
cli_main("python -m pystencilssfg")
# This file helps to compute a version number in source trees obtained from
# git-archive tarball (such as those provided by githubs download-from-tag
# feature). Distribution tarballs (built by setup.py sdist) and build
......@@ -51,7 +50,7 @@ def get_config() -> VersioneerConfig:
cfg = VersioneerConfig()
cfg.VCS = "git"
cfg.style = "pep440"
cfg.tag_prefix = ""
cfg.tag_prefix = "v"
cfg.parentdir_prefix = "pystencilssfg-"
cfg.versionfile_source = "src/pystencilssfg/_version.py"
cfg.verbose = False
......@@ -68,12 +67,14 @@ HANDLERS: Dict[str, Dict[str, Callable]] = {}
def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator
"""Create decorator to mark a method as the handler of a VCS."""
def decorate(f: Callable) -> Callable:
"""Store f in HANDLERS[vcs][method]."""
if vcs not in HANDLERS:
HANDLERS[vcs] = {}
HANDLERS[vcs][method] = f
return f
return decorate
......@@ -100,10 +101,14 @@ def run_command(
try:
dispcmd = str([command] + args)
# remember shell=False, so use git.cmd on windows, not just git
process = subprocess.Popen([command] + args, cwd=cwd, env=env,
stdout=subprocess.PIPE,
stderr=(subprocess.PIPE if hide_stderr
else None), **popen_kwargs)
process = subprocess.Popen(
[command] + args,
cwd=cwd,
env=env,
stdout=subprocess.PIPE,
stderr=(subprocess.PIPE if hide_stderr else None),
**popen_kwargs,
)
break
except OSError as e:
if e.errno == errno.ENOENT:
......@@ -141,15 +146,21 @@ def versions_from_parentdir(
for _ in range(3):
dirname = os.path.basename(root)
if dirname.startswith(parentdir_prefix):
return {"version": dirname[len(parentdir_prefix):],
"full-revisionid": None,
"dirty": False, "error": None, "date": None}
return {
"version": dirname[len(parentdir_prefix) :],
"full-revisionid": None,
"dirty": False,
"error": None,
"date": None,
}
rootdirs.append(root)
root = os.path.dirname(root) # up a level
if verbose:
print("Tried directories %s but none started with prefix %s" %
(str(rootdirs), parentdir_prefix))
print(
"Tried directories %s but none started with prefix %s"
% (str(rootdirs), parentdir_prefix)
)
raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
......@@ -212,7 +223,7 @@ def git_versions_from_keywords(
# starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
# just "foo-1.0". If we see a "tag: " prefix, prefer those.
TAG = "tag: "
tags = {r[len(TAG):] for r in refs if r.startswith(TAG)}
tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)}
if not tags:
# Either we're using git < 1.8.3, or there really are no tags. We use
# a heuristic: assume all version tags have a digit. The old git %d
......@@ -221,7 +232,7 @@ def git_versions_from_keywords(
# between branches and tags. By ignoring refnames without digits, we
# filter out many common branch names like "release" and
# "stabilization", as well as "HEAD" and "master".
tags = {r for r in refs if re.search(r'\d', r)}
tags = {r for r in refs if re.search(r"\d", r)}
if verbose:
print("discarding '%s', no digits" % ",".join(refs - tags))
if verbose:
......@@ -229,32 +240,36 @@ def git_versions_from_keywords(
for ref in sorted(tags):
# sorting will prefer e.g. "2.0" over "2.0rc1"
if ref.startswith(tag_prefix):
r = ref[len(tag_prefix):]
r = ref[len(tag_prefix) :]
# Filter out refs that exactly match prefix or that don't start
# with a number once the prefix is stripped (mostly a concern
# when prefix is '')
if not re.match(r'\d', r):
if not re.match(r"\d", r):
continue
if verbose:
print("picking %s" % r)
return {"version": r,
"full-revisionid": keywords["full"].strip(),
"dirty": False, "error": None,
"date": date}
return {
"version": r,
"full-revisionid": keywords["full"].strip(),
"dirty": False,
"error": None,
"date": date,
}
# no suitable tags, so version is "0+unknown", but full hex is still there
if verbose:
print("no suitable tags, using unknown + full revision id")
return {"version": "0+unknown",
"full-revisionid": keywords["full"].strip(),
"dirty": False, "error": "no suitable tags", "date": None}
return {
"version": "0+unknown",
"full-revisionid": keywords["full"].strip(),
"dirty": False,
"error": "no suitable tags",
"date": None,
}
@register_vcs_handler("git", "pieces_from_vcs")
def git_pieces_from_vcs(
tag_prefix: str,
root: str,
verbose: bool,
runner: Callable = run_command
tag_prefix: str, root: str, verbose: bool, runner: Callable = run_command
) -> Dict[str, Any]:
"""Get version from 'git describe' in the root of the source tree.
......@@ -273,8 +288,7 @@ def git_pieces_from_vcs(
env.pop("GIT_DIR", None)
runner = functools.partial(runner, env=env)
_, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
hide_stderr=not verbose)
_, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=not verbose)
if rc != 0:
if verbose:
print("Directory %s not under git control" % root)
......@@ -282,10 +296,19 @@ def git_pieces_from_vcs(
# if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
# if there isn't one, this yields HEX[-dirty] (no NUM)
describe_out, rc = runner(GITS, [
"describe", "--tags", "--dirty", "--always", "--long",
"--match", f"{tag_prefix}[[:digit:]]*"
], cwd=root)
describe_out, rc = runner(
GITS,
[
"describe",
"--tags",
"--dirty",
"--always",
"--long",
"--match",
f"{tag_prefix}[[:digit:]]*",
],
cwd=root,
)
# --long was added in git-1.5.5
if describe_out is None:
raise NotThisMethod("'git describe' failed")
......@@ -300,8 +323,7 @@ def git_pieces_from_vcs(
pieces["short"] = full_out[:7] # maybe improved later
pieces["error"] = None
branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"],
cwd=root)
branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root)
# --abbrev-ref was added in git-1.6.3
if rc != 0 or branch_name is None:
raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
......@@ -341,17 +363,16 @@ def git_pieces_from_vcs(
dirty = git_describe.endswith("-dirty")
pieces["dirty"] = dirty
if dirty:
git_describe = git_describe[:git_describe.rindex("-dirty")]
git_describe = git_describe[: git_describe.rindex("-dirty")]
# now we have TAG-NUM-gHEX or HEX
if "-" in git_describe:
# TAG-NUM-gHEX
mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe)
if not mo:
# unparsable. Maybe git-describe is misbehaving?
pieces["error"] = ("unable to parse git-describe output: '%s'"
% describe_out)
pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out
return pieces
# tag
......@@ -360,10 +381,12 @@ def git_pieces_from_vcs(
if verbose:
fmt = "tag '%s' doesn't start with prefix '%s'"
print(fmt % (full_tag, tag_prefix))
pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
% (full_tag, tag_prefix))
pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (
full_tag,
tag_prefix,
)
return pieces
pieces["closest-tag"] = full_tag[len(tag_prefix):]
pieces["closest-tag"] = full_tag[len(tag_prefix) :]
# distance: number of commits since tag
pieces["distance"] = int(mo.group(2))
......@@ -412,8 +435,7 @@ def render_pep440(pieces: Dict[str, Any]) -> str:
rendered += ".dirty"
else:
# exception #1
rendered = "0+untagged.%d.g%s" % (pieces["distance"],
pieces["short"])
rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
if pieces["dirty"]:
rendered += ".dirty"
return rendered
......@@ -442,8 +464,7 @@ def render_pep440_branch(pieces: Dict[str, Any]) -> str:
rendered = "0"
if pieces["branch"] != "master":
rendered += ".dev0"
rendered += "+untagged.%d.g%s" % (pieces["distance"],
pieces["short"])
rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
if pieces["dirty"]:
rendered += ".dirty"
return rendered
......@@ -604,11 +625,13 @@ def render_git_describe_long(pieces: Dict[str, Any]) -> str:
def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]:
"""Render the given version pieces into the requested style."""
if pieces["error"]:
return {"version": "unknown",
"full-revisionid": pieces.get("long"),
"dirty": None,
"error": pieces["error"],
"date": None}
return {
"version": "unknown",
"full-revisionid": pieces.get("long"),
"dirty": None,
"error": pieces["error"],
"date": None,
}
if not style or style == "default":
style = "pep440" # the default
......@@ -632,9 +655,13 @@ def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]:
else:
raise ValueError("unknown style '%s'" % style)
return {"version": rendered, "full-revisionid": pieces["long"],
"dirty": pieces["dirty"], "error": None,
"date": pieces.get("date")}
return {
"version": rendered,
"full-revisionid": pieces["long"],
"dirty": pieces["dirty"],
"error": None,
"date": pieces.get("date"),
}
def get_versions() -> Dict[str, Any]:
......@@ -648,8 +675,7 @@ def get_versions() -> Dict[str, Any]:
verbose = cfg.verbose
try:
return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
verbose)
return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose)
except NotThisMethod:
pass
......@@ -658,13 +684,16 @@ def get_versions() -> Dict[str, Any]:
# versionfile_source is the relative path from the top of the source
# tree (where the .git directory might live) to this file. Invert
# this to find the root from __file__.
for _ in cfg.versionfile_source.split('/'):
for _ in cfg.versionfile_source.split("/"):
root = os.path.dirname(root)
except NameError:
return {"version": "0+unknown", "full-revisionid": None,
"dirty": None,
"error": "unable to find root of source tree",
"date": None}
return {
"version": "0+unknown",
"full-revisionid": None,
"dirty": None,
"error": "unable to find root of source tree",
"date": None,
}
try:
pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
......@@ -678,6 +707,10 @@ def get_versions() -> Dict[str, Any]:
except NotThisMethod:
pass
return {"version": "0+unknown", "full-revisionid": None,
"dirty": None,
"error": "unable to compute version", "date": None}
return {
"version": "0+unknown",
"full-revisionid": None,
"dirty": None,
"error": "unable to compute version",
"date": None,
}
import sys
import os
from os import path
from typing import NoReturn
from argparse import ArgumentParser, BooleanOptionalAction
from .configuration import (
SfgConfigException, SfgConfigSource,
add_config_args_to_parser, config_from_parser_args, merge_configurations
)
from .config import CommandLineParameters, SfgConfigException
def add_newline_arg(parser):
parser.add_argument("--newline", action=BooleanOptionalAction, default=True,
help="Whether to add a terminating newline to the output.")
parser.add_argument(
"--newline",
action=BooleanOptionalAction,
default=True,
help="Whether to add a terminating newline to the output.",
)
def cli_main(program='sfg-cli'):
parser = ArgumentParser(program,
description="pystencilssfg command-line utility for build system integration")
def cli_main(program="sfg-cli") -> NoReturn:
parser = ArgumentParser(
program,
description="pystencilssfg command-line utility for build system integration",
)
subparsers = parser.add_subparsers(required=True, title="Subcommands")
......@@ -26,25 +30,33 @@ def cli_main(program='sfg-cli'):
version_parser.set_defaults(func=version)
outfiles_parser = subparsers.add_parser(
"list-files", help="List files produced by given codegen script.")
"list-files", help="List files produced by given codegen script."
)
outfiles_parser.set_defaults(func=list_files)
add_config_args_to_parser(outfiles_parser)
CommandLineParameters.add_args_to_parser(outfiles_parser)
add_newline_arg(outfiles_parser)
outfiles_parser.add_argument("--sep", type=str, default=" ", dest="sep", help="Separator for list items")
outfiles_parser.add_argument(
"--sep", type=str, default=" ", dest="sep", help="Separator for list items"
)
outfiles_parser.add_argument("codegen_script", type=str)
cmake_parser = subparsers.add_parser("cmake", help="Operations for CMake integation")
cmake_parser = subparsers.add_parser(
"cmake", help="Operations for CMake integation"
)
cmake_subparsers = cmake_parser.add_subparsers(required=True)
modpath = cmake_subparsers.add_parser(
"modulepath", help="Print the include path for the pystencils-sfg cmake module")
"modulepath", help="Print the include path for the pystencils-sfg cmake module"
)
add_newline_arg(modpath)
modpath.set_defaults(func=print_cmake_modulepath)
findmod = cmake_subparsers.add_parser("make-find-module",
help="Creates the pystencils-sfg CMake find module as" +
"'FindPystencilsSfg.cmake' in the current directory.")
findmod = cmake_subparsers.add_parser(
"make-find-module",
help="Creates the pystencils-sfg CMake find module as"
+ "'FindPystencilsSfg.cmake' in the current directory.",
)
findmod.set_defaults(func=make_cmake_find_module)
args = parser.parse_args()
......@@ -53,57 +65,45 @@ def cli_main(program='sfg-cli'):
exit(-1) # should never happen
def version(args):
def version(args) -> NoReturn:
from . import __version__
print(__version__, end=os.linesep if args.newline else '')
print(__version__, end=os.linesep if args.newline else "")
exit(0)
def list_files(args):
try:
project_config, cmdline_config = config_from_parser_args(args)
except SfgConfigException as exc:
abort_with_config_exception(exc)
config = merge_configurations(project_config, cmdline_config, None)
def list_files(args) -> NoReturn:
cli_params = CommandLineParameters(args)
config = cli_params.get_config()
_, scriptname = path.split(args.codegen_script)
basename = path.splitext(scriptname)[0]
from .emission import HeaderImplPairEmitter
emitter = HeaderImplPairEmitter(config.get_output_spec(basename))
output_files = config._get_output_files(basename)
print(args.sep.join(emitter.output_files), end=os.linesep if args.newline else '')
print(
args.sep.join(str(of) for of in output_files),
end=os.linesep if args.newline else "",
)
exit(0)
def print_cmake_modulepath(args):
def print_cmake_modulepath(args) -> NoReturn:
from .cmake import get_sfg_cmake_modulepath
print(get_sfg_cmake_modulepath(), end=os.linesep if args.newline else '')
print(get_sfg_cmake_modulepath(), end=os.linesep if args.newline else "")
exit(0)
def make_cmake_find_module(args):
def make_cmake_find_module(args) -> NoReturn:
from .cmake import make_find_module
make_find_module()
exit(0)
def abort_with_config_exception(exception: SfgConfigException):
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
match exception.config_source:
case SfgConfigSource.PROJECT:
eprint(
f"Invalid project configuration: {exception.message}\nCheck your configurator script.")
case SfgConfigSource.COMMANDLINE:
eprint(
f"Invalid configuration on command line: {exception.message}")
case _: assert False, "(Theoretically) unreachable code. Contact the developers."
def abort_with_config_exception(exception: SfgConfigException, source: str) -> NoReturn:
print(f"Invalid {source} configuration: {exception.args[0]}.", file=sys.stderr)
exit(1)
set( PystencilsSfg_FOUND OFF CACHE BOOL "pystencils source file generator found" )
#[[
Find-Module for pystencils-sfg.
mark_as_advanced( PystencilsSfg_FOUND )
# Setting the Python interpreter
find_package( Python COMPONENTS Interpreter REQUIRED )
If the cache entry PystencilsSfg_PYTHON_INTERPRETER is set, e.g. via the commandline
(`-DPystencilsSfg_PYTHON_INTERPRETER=<...>`), its value be taken as the Python interpreter
used to find and run pystencils-sfg.
If the cache entry is unset, but the hint PystencilsSfg_PYTHON_PATH is set, its value will
be used as the Python interpreter.
If none of these is set, a Python interpreter will be selected using the `FindPython` module.
#]]
if(NOT DEFINED CACHE{PystencilsSfg_PYTHON_INTERPRETER})
# The Python interpreter cache variable is not set externally, so...
if(DEFINED PystencilsSfg_PYTHON_PATH)
# ... either initialize it from the hint variable ...
set( _sfg_cache_python_init ${PystencilsSfg_PYTHON_PATH} )
else()
# ... or, if that is also unset, use the system Python
find_package( Python COMPONENTS Interpreter REQUIRED )
set( _sfg_cache_python_init ${Python_EXECUTABLE} )
endif()
endif()
set(PystencilsSfg_PYTHON_INTERPRETER ${_sfg_cache_python_init} CACHE PATH "Path to the Python executable used to run pystencils-sfg")
# Try to find pystencils-sfg in the python environment
execute_process(COMMAND ${Python_EXECUTABLE} -m pystencilssfg version --no-newline
execute_process(COMMAND ${PystencilsSfg_PYTHON_INTERPRETER} -m pystencilssfg version --no-newline
RESULT_VARIABLE _PystencilsSfgFindResult OUTPUT_VARIABLE PystencilsSfg_VERSION )
if(${_PystencilsSfgFindResult} EQUAL 0)
set( PystencilsSfg_FOUND ON )
else()
set( PystencilsSfg_FOUND OFF )
endif()
if(DEFINED PystencilsSfg_FIND_REQUIRED)
......@@ -21,8 +47,9 @@ endif()
if(${PystencilsSfg_FOUND})
message( STATUS "Found pystencils Source File Generator (Version ${PystencilsSfg_VERSION})")
execute_process(COMMAND ${Python_EXECUTABLE} -m pystencilssfg cmake modulepath --no-newline
message( STATUS "Using Python interpreter ${PystencilsSfg_PYTHON_INTERPRETER} for SFG generator scripts.")
execute_process(COMMAND ${PystencilsSfg_PYTHON_INTERPRETER} -m pystencilssfg cmake modulepath --no-newline
OUTPUT_VARIABLE _PystencilsSfg_CMAKE_MODULE_PATH)
set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${_PystencilsSfg_CMAKE_MODULE_PATH})
......
#[[
pystencils-sfg CMake module.
set(PystencilsSfg_GENERATED_SOURCES_DIR "${CMAKE_BINARY_DIR}/sfg_sources" CACHE PATH "Output directory for genenerated sources" )
Do not include this module directly; instead use the CMake find-module of pystencils-sfg
to dynamically locate it.
#]]
mark_as_advanced(PystencilsSfg_GENERATED_SOURCES_DIR)
file(MAKE_DIRECTORY "${PystencilsSfg_GENERATED_SOURCES_DIR}")
include_directories(${PystencilsSfg_GENERATED_SOURCES_DIR})
# This cache variable definition is a duplicate of the one in FindPystencilsSfg.cmake
if(NOT DEFINED CACHE{PystencilsSfg_PYTHON_INTERPRETER})
set(PystencilsSfg_PYTHON_INTERPRETER ${Python_EXECUTABLE} CACHE PATH "Path to the Python executable used to run pystencils-sfg")
endif()
if(NOT DEFINED CACHE{_Pystencils_Include_Dir})
execute_process(
COMMAND ${PystencilsSfg_PYTHON_INTERPRETER} -c "from pystencils.include import get_pystencils_include_path; print(get_pystencils_include_path(), end='')"
OUTPUT_VARIABLE _pystencils_includepath_result
)
set(_Pystencils_Include_Dir ${_pystencils_includepath_result} CACHE PATH "")
endif()
function(_pssfg_add_gen_source target script)
function(_pssfg_add_gen_source target script outputDirectory)
set(options)
set(oneValueArgs)
set(multiValueArgs GENERATOR_ARGS DEPENDS)
set(multiValueArgs GENERATOR_ARGS USER_ARGS DEPENDS)
cmake_parse_arguments(_pssfg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(generatedSourcesDir ${PystencilsSfg_GENERATED_SOURCES_DIR}/gen/${target})
get_filename_component(basename ${script} NAME_WLE)
cmake_path(ABSOLUTE_PATH script OUTPUT_VARIABLE scriptAbsolute)
execute_process(COMMAND ${Python_EXECUTABLE} -m pystencilssfg list-files "--sep=;" --no-newline ${_pssfg_GENERATOR_ARGS} ${script}
execute_process(COMMAND ${PystencilsSfg_PYTHON_INTERPRETER} -m pystencilssfg list-files "--sep=;" --no-newline ${_pssfg_GENERATOR_ARGS} ${script}
OUTPUT_VARIABLE generatedSources RESULT_VARIABLE _pssfg_result
ERROR_VARIABLE _pssfg_stderr)
......@@ -28,15 +39,15 @@ function(_pssfg_add_gen_source target script)
set(generatedSourcesAbsolute)
foreach (filename ${generatedSources})
list(APPEND generatedSourcesAbsolute "${generatedSourcesDir}/${filename}")
list(APPEND generatedSourcesAbsolute "${outputDirectory}/${filename}")
endforeach ()
file(MAKE_DIRECTORY "${generatedSourcesDir}")
file(MAKE_DIRECTORY ${outputDirectory})
add_custom_command(OUTPUT ${generatedSourcesAbsolute}
DEPENDS ${scriptAbsolute} ${_pssfg_DEPENDS}
COMMAND ${Python_EXECUTABLE} ${scriptAbsolute} ${_pssfg_GENERATOR_ARGS}
WORKING_DIRECTORY "${generatedSourcesDir}")
COMMAND ${PystencilsSfg_PYTHON_INTERPRETER} ${scriptAbsolute} ${_pssfg_GENERATOR_ARGS} ${_pssfg_USER_ARGS}
WORKING_DIRECTORY "${outputDirectory}")
target_sources(${target} PRIVATE ${generatedSourcesAbsolute})
endfunction()
......@@ -44,19 +55,52 @@ endfunction()
function(pystencilssfg_generate_target_sources TARGET)
set(options HEADER_ONLY)
set(multiValueArgs SCRIPTS DEPENDS FILE_EXTENSIONS)
set(oneValueArgs CONFIG_MODULE OUTPUT_DIRECTORY)
set(multiValueArgs SCRIPTS DEPENDS FILE_EXTENSIONS SCRIPT_ARGS)
cmake_parse_arguments(_pssfg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(generatorArgs)
if(_pssfg_HEADER_ONLY)
list(APPEND generatorArgs "--sfg-header-only")
else()
list(APPEND generatorArgs "--no-sfg-header-only")
endif()
if(DEFINED PystencilsSfg_CONFIGURATOR_SCRIPT)
cmake_path(ABSOLUTE_PATH PystencilsSfg_CONFIGURATOR_SCRIPT OUTPUT_VARIABLE configscript)
list(APPEND generatorArgs "--sfg-config-module=${configscript}")
list(APPEND _pssfg_DEPENDS ${configscript})
if(DEFINED _pssfg_CONFIG_MODULE)
cmake_path(ABSOLUTE_PATH _pssfg_CONFIG_MODULE OUTPUT_VARIABLE config_module)
list(APPEND generatorArgs "--sfg-config-module=${config_module}")
list(APPEND _pssfg_DEPENDS ${config_module})
else()
if(DEFINED PystencilsSfg_CONFIGURATOR_SCRIPT)
message(AUTHOR_WARNING "The variable PystencilsSfg_CONFIGURATOR_SCRIPT is deprecated. Set PystencilsSfg_CONFIG_MODULE instead.")
cmake_path(ABSOLUTE_PATH PystencilsSfg_CONFIGURATOR_SCRIPT OUTPUT_VARIABLE configscript)
list(APPEND generatorArgs "--sfg-config-module=${configscript}")
list(APPEND _pssfg_DEPENDS ${configscript})
endif()
if(DEFINED PystencilsSfg_CONFIG_MODULE)
if(DEFINED PystencilsSfg_CONFIGURATOR_SCRIPT)
message(FATAL_ERROR "At most one of PystencilsSfg_CONFIGURATOR_SCRIPT and PystencilsSfg_CONFIG_MODULE may be set.")
endif()
cmake_path(ABSOLUTE_PATH PystencilsSfg_CONFIG_MODULE OUTPUT_VARIABLE config_module)
list(APPEND generatorArgs "--sfg-config-module=${config_module}")
list(APPEND _pssfg_DEPENDS ${config_module})
endif()
endif()
if(DEFINED _pssfg_OUTPUT_DIRECTORY)
cmake_path(IS_RELATIVE _pssfg_OUTPUT_DIRECTORY _pssfg_output_dir_is_relative)
if(_pssfg_output_dir_is_relative)
set(outputDirectory ${CMAKE_CURRENT_BINARY_DIR}/${_pssfg_OUTPUT_DIRECTORY})
else()
set(outputDirectory ${_pssfg_OUTPUT_DIRECTORY})
endif()
else()
set(generatedSourcesIncludeDir ${CMAKE_CURRENT_BINARY_DIR}/_gen/${TARGET})
set(outputDirectory ${generatedSourcesIncludeDir}/gen)
target_include_directories(${TARGET} PRIVATE ${generatedSourcesIncludeDir})
endif()
if(DEFINED _pssfg_FILE_EXTENSIONS)
......@@ -65,8 +109,20 @@ function(pystencilssfg_generate_target_sources TARGET)
list(APPEND generatorArgs "--sfg-file-extensions=${extensionsString}")
endif()
if(DEFINED _pssfg_SCRIPT_ARGS)
# User has provided custom command line arguments
set(userArgs ${_pssfg_SCRIPT_ARGS})
endif()
foreach(codegenScript ${_pssfg_SCRIPTS})
_pssfg_add_gen_source(${TARGET} ${codegenScript} GENERATOR_ARGS ${generatorArgs} DEPENDS ${_pssfg_DEPENDS})
_pssfg_add_gen_source(
${TARGET} ${codegenScript} ${outputDirectory}
GENERATOR_ARGS ${generatorArgs}
USER_ARGS ${userArgs}
DEPENDS ${_pssfg_DEPENDS}
)
endforeach()
target_include_directories(${TARGET} PRIVATE ${_Pystencils_Include_Dir})
endfunction()
from .composer import SfgComposer
from .basic_composer import SfgBasicComposer, make_sequence
from .basic_composer import (
SfgIComposer,
SfgBasicComposer,
make_sequence,
make_statements,
SequencerArg,
ExprLike,
)
from .mixin import SfgComposerMixIn
from .class_composer import SfgClassComposer
from .gpu_composer import SfgGpuComposer
__all__ = [
'SfgComposer',
"SfgIComposer",
"SfgComposer",
"SfgComposerMixIn",
"make_sequence",
"make_statements",
"SequencerArg",
"ExprLike",
"SfgBasicComposer",
"SfgClassComposer"
"SfgClassComposer",
"SfgGpuComposer",
]
from __future__ import annotations
from typing import TYPE_CHECKING, Sequence
from abc import ABC, abstractmethod
import numpy as np
from pystencils import Field, TypedSymbol
from pystencils.astnodes import KernelFunction
from typing import Sequence, TypeAlias
from abc import ABC, abstractmethod
import sympy as sp
from functools import reduce
from warnings import warn
from pystencils import (
Field,
CreateKernelConfig,
create_kernel,
Assignment,
AssignmentCollection,
)
from pystencils.codegen import Kernel, Lambda
from pystencils.types import create_type, UserTypeSpec, PsType
from ..context import SfgContext, SfgCursor
from .custom import CustomGenerator
from ..tree import (
from ..ir import (
SfgCallTreeNode,
SfgKernelCallNode,
SfgStatements,
......@@ -15,155 +26,372 @@ from ..tree import (
SfgRequireIncludes,
SfgSequence,
SfgBlock,
SfgBranch,
SfgSwitch,
)
from ..ir.postprocessing import (
SfgDeferredParamSetter,
SfgDeferredFieldMapping,
SfgDeferredVectorMapping,
)
from ..tree.deferred_nodes import SfgDeferredFieldMapping
from ..tree.conditional import SfgCondition, SfgCustomCondition, SfgBranch, SfgSwitch
from ..source_components import (
from ..ir import (
SfgFunction,
SfgHeaderInclude,
SfgKernelNamespace,
SfgKernelHandle,
SfgClass,
SfgConstructor,
SfgMemberVariable,
SfgClassKeyword,
SfgEntityDecl,
SfgEntityDef,
SfgNamespaceBlock,
)
from ..lang import (
VarLike,
ExprLike,
_VarLike,
_ExprLike,
asvar,
depends,
HeaderFile,
includes,
SfgVar,
SfgKernelParamVar,
AugExpr,
SupportsFieldExtraction,
SupportsVectorExtraction,
void,
)
from ..source_concepts import SrcObject, SrcField, TypedSymbolOrObject, SrcVector
from ..types import cpp_typename, SrcType
from ..exceptions import SfgException
if TYPE_CHECKING:
from ..context import SfgContext
class SfgBasicComposer:
"""Composer for basic source components."""
class SfgIComposer(ABC):
def __init__(self, ctx: SfgContext):
self._ctx: SfgContext = ctx
self._ctx = ctx
self._cursor = ctx.cursor
@property
def context(self):
return self._ctx
def prelude(self, content: str):
class SfgNodeBuilder(ABC):
"""Base class for node builders used by the composer"""
@abstractmethod
def resolve(self) -> SfgCallTreeNode:
pass
_SequencerArg = (tuple, ExprLike, SfgCallTreeNode, SfgNodeBuilder)
SequencerArg: TypeAlias = tuple | ExprLike | SfgCallTreeNode | SfgNodeBuilder
"""Valid arguments to `make_sequence` and any sequencer that uses it."""
class KernelsAdder:
"""Handle on a kernel namespace that permits registering kernels."""
def __init__(self, cursor: SfgCursor, knamespace: SfgKernelNamespace):
self._cursor = cursor
self._kernel_namespace = knamespace
self._inline: bool = False
self._loc: SfgNamespaceBlock | None = None
def inline(self) -> KernelsAdder:
"""Generate kernel definitions ``inline`` in the header file."""
self._inline = True
return self
def add(self, kernel: Kernel, name: str | None = None):
"""Adds an existing pystencils AST to this namespace.
If a name is specified, the AST's function name is changed."""
if name is None:
kernel_name = kernel.name
else:
kernel_name = name
if self._kernel_namespace.find_kernel(kernel_name) is not None:
raise ValueError(
f"Duplicate kernels: A kernel called {kernel_name} already exists "
f"in namespace {self._kernel_namespace.fqname}"
)
if name is not None:
kernel.name = kernel_name
khandle = SfgKernelHandle(
kernel_name, self._kernel_namespace, kernel, inline=self._inline
)
self._kernel_namespace.add_kernel(khandle)
loc = self._get_loc()
loc.elements.append(SfgEntityDef(khandle))
for header in kernel.required_headers:
hfile = HeaderFile.parse(header)
if self._inline:
self._cursor.context.header_file.includes.append(hfile)
else:
impl_file = self._cursor.context.impl_file
assert impl_file is not None
impl_file.includes.append(hfile)
return khandle
def create(
self,
assignments: Assignment | Sequence[Assignment] | AssignmentCollection,
name: str | None = None,
config: CreateKernelConfig | None = None,
):
"""Creates a new pystencils kernel from a list of assignments and a configuration.
This is a wrapper around `create_kernel <pystencils.codegen.create_kernel>`
with a subsequent call to `add`.
"""
if config is None:
config = CreateKernelConfig()
if name is not None:
if self._kernel_namespace.find_kernel(name) is not None:
raise ValueError(
f"Duplicate kernels: A kernel called {name} already exists "
f"in namespace {self._kernel_namespace.fqname}"
)
config.function_name = name
kernel = create_kernel(assignments, config=config)
return self.add(kernel)
def _get_loc(self) -> SfgNamespaceBlock:
if self._loc is None:
kns_block = SfgNamespaceBlock(self._kernel_namespace)
if self._inline:
self._cursor.write_header(kns_block)
else:
self._cursor.write_impl(kns_block)
self._loc = kns_block
return self._loc
class SfgBasicComposer(SfgIComposer):
"""Composer for basic source components, and base class for all composer mix-ins."""
def __init__(self, sfg: SfgContext | SfgIComposer):
ctx: SfgContext = sfg if isinstance(sfg, SfgContext) else sfg.context
super().__init__(ctx)
def prelude(self, content: str, end: str = "\n"):
"""Append a string to the prelude comment, to be printed at the top of both generated files.
The string should not contain C/C++ comment delimiters, since these will be added automatically
during code generation.
:Example:
>>> sfg.prelude("This file was generated using pystencils-sfg; do not modify it directly!")
will appear in the generated files as
.. code-block:: C++
/*
* This file was generated using pystencils-sfg; do not modify it directly!
*/
"""
self._ctx.append_to_prelude(content)
for f in self._ctx.files:
if f.prelude is None:
f.prelude = content + end
else:
f.prelude += content + end
def code(self, *code: str, impl: bool = False):
"""Add arbitrary lines of code to the generated header file.
:Example:
>>> sfg.code(
... "#define PI 3.14 // more than enough for engineers",
... "using namespace std;"
... )
will appear as
.. code-block:: C++
#define PI 3.14 // more than enough for engineers
using namespace std;
Args:
code: Sequence of code strings to be written to the output file
impl: If `True`, write the code to the implementation file; otherwise, to the header file.
"""
for c in code:
if impl:
self._cursor.write_impl(c)
else:
self._cursor.write_header(c)
def define(self, *definitions: str):
"""Add custom definitions to the generated header file."""
for d in definitions:
self._ctx.add_definition(d)
from warnings import warn
def define_once(self, *definitions: str):
"""Same as `define`, but only adds definitions only if the same code string was not already added."""
for definition in definitions:
if all(d != definition for d in self._ctx.definitions()):
self._ctx.add_definition(definition)
warn(
"The `define` method of `SfgBasicComposer` is deprecated and will be removed in a future version."
"Use `sfg.code()` instead.",
FutureWarning,
)
self.code(*definitions)
def namespace(self, namespace: str):
"""Set the inner code namespace. Throws an exception if a namespace was already set."""
self._ctx.set_namespace(namespace)
"""Enter a new namespace block.
Calling `namespace` as a regular function will open a new namespace as a child of the
currently active namespace; this new namespace will then become active instead.
Using `namespace` as a context manager will instead activate the given namespace
only for the length of the ``with`` block.
Args:
namespace: Qualified name of the namespace
:Example:
The following calls will set the current namespace to ``outer::inner``
for the remaining code generation run:
.. code-block::
sfg.namespace("outer")
sfg.namespace("inner")
Subsequent calls to `namespace` can only create further nested namespaces.
To step back out of a namespace, `namespace` can also be used as a context manager:
.. code-block::
with sfg.namespace("detail"):
...
This way, code generated inside the ``with`` region is placed in the ``detail`` namespace,
and code after this block will again live in the enclosing namespace.
"""
return self._cursor.enter_namespace(namespace)
def generate(self, generator: CustomGenerator):
"""Invoke a custom code generator with the underlying context."""
generator.generate(self._ctx)
from .composer import SfgComposer
generator.generate(SfgComposer(self))
@property
def kernels(self) -> SfgKernelNamespace:
"""The default kernel namespace. Add kernels like:
```Python
sfg.kernels.add(ast, "kernel_name")
sfg.kernels.create(assignments, "kernel_name", config)
```"""
return self._ctx._default_kernel_namespace
def kernel_namespace(self, name: str) -> SfgKernelNamespace:
"""Return the kernel namespace of the given name, creating it if it does not exist yet."""
kns = self._ctx.get_kernel_namespace(name)
def kernels(self) -> KernelsAdder:
"""The default kernel namespace.
Add kernels like::
sfg.kernels.add(ast, "kernel_name")
sfg.kernels.create(assignments, "kernel_name", config)
"""
return self.kernel_namespace("kernels")
def kernel_namespace(self, name: str) -> KernelsAdder:
"""Return a view on a kernel namespace in order to add kernels to it."""
kns = self._cursor.get_entity(name)
if kns is None:
kns = SfgKernelNamespace(self._ctx, name)
self._ctx.add_kernel_namespace(kns)
kns = SfgKernelNamespace(name, self._cursor.current_namespace)
self._cursor.add_entity(kns)
elif not isinstance(kns, SfgKernelNamespace):
raise ValueError(
f"The existing entity {kns.fqname} is not a kernel namespace"
)
return kns
kadder = KernelsAdder(self._cursor, kns)
if self._ctx.impl_file is None:
kadder.inline()
return kadder
def include(self, header_file: str, private: bool = False):
def include(self, header: str | HeaderFile, private: bool = False):
"""Include a header file.
Args:
header_file: Path to the header file. Enclose in `<>` for a system header.
private: If `True`, in header-implementation code generation, the header file is
header_file: Path to the header file. Enclose in ``<>`` for a system header.
private: If ``True``, in header-implementation code generation, the header file is
only included in the implementation file.
"""
self._ctx.add_include(parse_include(header_file, private))
def numpy_struct(
self, name: str, dtype: np.dtype, add_constructor: bool = True
) -> SfgClass:
"""Add a numpy structured data type as a C++ struct
:Example:
Returns:
The created class object
"""
if self._ctx.get_class(name) is not None:
raise SfgException(f"Class with name {name} already exists.")
>>> sfg.include("<vector>")
>>> sfg.include("custom.h")
cls = struct_from_numpy_dtype(name, dtype, add_constructor=add_constructor)
self._ctx.add_class(cls)
return cls
will be printed as
def kernel_function(
self, name: str, ast_or_kernel_handle: KernelFunction | SfgKernelHandle
):
.. code-block:: C++
#include <vector>
#include "custom.h"
"""
header_file = HeaderFile.parse(header)
if private:
if self._ctx.impl_file is None:
raise ValueError(
"Cannot emit a private include since no implementation file is being generated"
)
self._ctx.impl_file.includes.append(header_file)
else:
self._ctx.header_file.includes.append(header_file)
def kernel_function(self, name: str, kernel: Kernel | SfgKernelHandle):
"""Create a function comprising just a single kernel call.
Args:
ast_or_kernel_handle: Either a pystencils AST, or a kernel handle for an already registered AST.
"""
if self._ctx.get_function(name) is not None:
raise ValueError(f"Function {name} already exists.")
if isinstance(ast_or_kernel_handle, KernelFunction):
khandle = self._ctx.default_kernel_namespace.add(ast_or_kernel_handle)
tree = SfgKernelCallNode(khandle)
elif isinstance(ast_or_kernel_handle, SfgKernelHandle):
tree = SfgKernelCallNode(ast_or_kernel_handle)
if isinstance(kernel, Kernel):
khandle = self.kernels.add(kernel, name)
else:
raise TypeError("Invalid type of argument `ast_or_kernel_handle`!")
khandle = kernel
func = SfgFunction(name, tree)
self._ctx.add_function(func)
self.function(name)(self.call(khandle))
def function(self, name: str):
def function(
self,
name: str,
return_type: UserTypeSpec | None = None,
) -> SfgFunctionSequencer:
"""Add a function.
The syntax of this function adder uses a chain of two calls to mimic C++ syntax:
```Python
sfg.function("FunctionName")(
# Function Body
)
```
.. code-block:: Python
The function body is constructed via sequencing;
refer to [make_sequence][pystencilssfg.composer.make_sequence].
sfg.function("FunctionName")(
# Function Body
)
The function body is constructed via sequencing (see `make_sequence`).
"""
if self._ctx.get_function(name) is not None:
raise ValueError(f"Function {name} already exists.")
seq = SfgFunctionSequencer(self._cursor, name)
def sequencer(*args: str | tuple | SfgCallTreeNode | SfgNodeBuilder):
tree = make_sequence(*args)
func = SfgFunction(name, tree)
self._ctx.add_function(func)
if return_type is not None:
warn(
"The parameter `return_type` to `function()` is deprecated and will be removed by version 0.1. "
"Use `.returns()` instead.",
FutureWarning,
)
seq.returns(return_type)
return sequencer
if self._ctx.impl_file is None:
seq.inline()
return seq
def call(self, kernel_handle: SfgKernelHandle) -> SfgKernelCallNode:
"""Use inside a function body to generate a kernel call.
def call(self, kernel_handle: SfgKernelHandle) -> SfgCallTreeNode:
"""Use inside a function body to directly call a kernel.
When using `call`, the given kernel will simply be called as a function.
To invoke a GPU kernel on a specified launch grid,
use `gpu_invoke <SfgGpuComposer.gpu_invoke>` instead.
Args:
kernel_handle: Handle to a kernel previously added to some kernel namespace.
......@@ -171,125 +399,223 @@ class SfgBasicComposer:
return SfgKernelCallNode(kernel_handle)
def seq(self, *args: tuple | str | SfgCallTreeNode | SfgNodeBuilder) -> SfgSequence:
"""Syntax sequencing. For details, refer to [make_sequence][pystencilssfg.composer.make_sequence]"""
"""Syntax sequencing. For details, see `make_sequence`"""
return make_sequence(*args)
def params(self, *args: TypedSymbolOrObject) -> SfgFunctionParams:
def params(self, *args: AugExpr) -> SfgFunctionParams:
"""Use inside a function body to add parameters to the function."""
return SfgFunctionParams(args)
return SfgFunctionParams([x.as_variable() for x in args])
def require(self, *incls: str | HeaderFile) -> SfgRequireIncludes:
"""Use inside a function body to require the inclusion of headers."""
return SfgRequireIncludes((HeaderFile.parse(incl) for incl in incls))
def var(self, name: str, dtype: UserTypeSpec) -> AugExpr:
"""Create a variable with given name and data type."""
return AugExpr(create_type(dtype)).var(name)
def require(self, *includes: str | SfgHeaderInclude) -> SfgRequireIncludes:
return SfgRequireIncludes(list(parse_include(incl) for incl in includes))
def vars(self, names: str, dtype: UserTypeSpec) -> tuple[AugExpr, ...]:
"""Create multiple variables with given names and the same data type.
Example:
>>> sfg.vars("x, y, z", "float32")
(x, y, z)
"""
varnames = names.split(",")
return tuple(self.var(n.strip(), dtype) for n in varnames)
def init(self, lhs: VarLike):
"""Create a C++ in-place initialization.
Usage:
.. code-block:: Python
obj = sfg.var("obj", "SomeClass")
sfg.init(obj)(arg1, arg2, arg3)
becomes
.. code-block:: C++
SomeClass obj { arg1, arg2, arg3 };
"""
lhs_var = asvar(lhs)
def parse_args(*args: ExprLike):
args_str = ", ".join(str(arg) for arg in args)
deps: set[SfgVar] = reduce(set.union, (depends(arg) for arg in args), set())
incls: set[HeaderFile] = reduce(set.union, (includes(arg) for arg in args))
return SfgStatements(
f"{lhs_var.dtype.c_string()} {lhs_var.name} {{ {args_str} }};",
(lhs_var,),
deps,
incls,
)
return parse_args
def expr(self, fmt: str, *deps, **kwdeps) -> AugExpr:
"""Create an expression while keeping track of variables it depends on.
This method is meant to be used similarly to `str.format`; in fact,
it calls `str.format` internally and therefore supports all of its
formatting features.
In addition, however, the format arguments are scanned for *variables*
(e.g. created using `var`), which are attached to the expression.
This way, *pystencils-sfg* keeps track of any variables an expression depends on.
:Example:
>>> x, y, z, w = sfg.vars("x, y, z, w", "float32")
>>> expr = sfg.expr("{} + {} * {}", x, y, z)
>>> expr
x + y * z
You can look at the expression's dependencies:
>>> sorted(expr.depends, key=lambda v: v.name)
[x: float32, y: float32, z: float32]
If you use an existing expression to create a larger one, the new expression
inherits all variables from its parts:
>>> expr2 = sfg.expr("{} + {}", expr, w)
>>> expr2
x + y * z + w
>>> sorted(expr2.depends, key=lambda v: v.name)
[w: float32, x: float32, y: float32, z: float32]
"""
return AugExpr.format(fmt, *deps, **kwdeps)
def expr_from_lambda(self, lamb: Lambda) -> AugExpr:
depends = set(SfgKernelParamVar(p) for p in lamb.parameters)
code = lamb.c_code()
return AugExpr.make(code, depends, dtype=lamb.return_type)
@property
def branch(self) -> SfgBranchBuilder:
"""Use inside a function body to create an if/else conditonal branch.
The syntax is:
```Python
sfg.branch("condition")(
# then-body
)(
# else-body (may be omitted)
)
```
.. code-block:: Python
sfg.branch("condition")(
# then-body
)(
# else-body (may be omitted)
)
"""
return SfgBranchBuilder()
def switch(self, switch_arg: str | TypedSymbolOrObject) -> SfgSwitchBuilder:
return SfgSwitchBuilder(switch_arg)
def switch(self, switch_arg: ExprLike, autobreak: bool = True) -> SfgSwitchBuilder:
"""Use inside a function to construct a switch-case statement.
Args:
switch_arg: Argument to the `switch()` statement
autobreak: Whether to automatically print a ``break;`` at the end of each case block
"""
return SfgSwitchBuilder(switch_arg, autobreak=autobreak)
def map_field(self, field: Field, src_object: SrcField) -> SfgDeferredFieldMapping:
def map_field(
self,
field: Field,
index_provider: SupportsFieldExtraction,
cast_indexing_symbols: bool = True,
) -> SfgDeferredFieldMapping:
"""Map a pystencils field to a field data structure, from which pointers, sizes
and strides should be extracted.
Args:
field: The pystencils field to be mapped
src_object: A [SrcField][pystencilssfg.source_concepts.SrcField] object representing a field data structure.
index_provider: An object that provides the field indexing information
cast_indexing_symbols: Whether to always introduce explicit casts for indexing symbols
"""
return SfgDeferredFieldMapping(field, src_object)
return SfgDeferredFieldMapping(
field, index_provider, cast_indexing_symbols=cast_indexing_symbols
)
def set_param(self, param: VarLike | sp.Symbol, expr: ExprLike):
"""Set a kernel parameter to an expression.
def map_param(
Code setting the parameter will only be generated if the parameter
is actually alive (i.e. required by some kernel, and not yet set) at
the point this method is called.
"""
var: SfgVar | sp.Symbol = asvar(param) if isinstance(param, _VarLike) else param
return SfgDeferredParamSetter(var, expr)
def map_vector(
self,
lhs: TypedSymbolOrObject,
rhs: TypedSymbolOrObject | Sequence[TypedSymbolOrObject],
mapping: str,
lhs_components: Sequence[VarLike | sp.Symbol],
rhs: SupportsVectorExtraction,
):
"""Arbitrary parameter mapping: Add a single line of code to define a left-hand
side object from one or multiple right-hand side dependencies."""
if isinstance(rhs, (TypedSymbol, SrcObject)):
return SfgStatements(mapping, (lhs,), (rhs,))
else:
return SfgStatements(mapping, (lhs,), rhs)
def map_vector(self, lhs_components: Sequence[TypedSymbolOrObject], rhs: SrcVector):
"""Extracts scalar numerical values from a vector data type.
Args:
lhs_components: Vector components as a list of symbols.
rhs: A [SrcVector][pystencilssfg.source_concepts.SrcVector] object representing a vector data structure.
rhs: An object providing access to vector components
"""
return make_sequence(
*(
rhs.extract_component(dest, coord)
for coord, dest in enumerate(lhs_components)
)
)
components: list[SfgVar | sp.Symbol] = [
(asvar(c) if isinstance(c, _VarLike) else c) for c in lhs_components
]
return SfgDeferredVectorMapping(components, rhs)
class SfgNodeBuilder(ABC):
@abstractmethod
def resolve(self) -> SfgCallTreeNode:
pass
def make_statements(arg: ExprLike) -> SfgStatements:
return SfgStatements(str(arg), (), depends(arg), includes(arg))
def make_sequence(*args: tuple | str | SfgCallTreeNode | SfgNodeBuilder) -> SfgSequence:
def make_sequence(*args: SequencerArg) -> SfgSequence:
"""Construct a sequence of C++ code from various kinds of arguments.
`make_sequence` is ubiquitous throughout the function building front-end;
among others, it powers the syntax of
[SfgComposer.function][pystencilssfg.SfgComposer.function] and
[SfgComposer.branch][pystencilssfg.SfgComposer.branch].
among others, it powers the syntax of `SfgBasicComposer.function`
and `SfgBasicComposer.branch`.
`make_sequence` constructs an abstract syntax tree for code within a function body, accepting various
types of arguments which then get turned into C++ code. These are:
- Strings (`str`) are printed as-is
- Tuples (`tuple`) signify *blocks*, i.e. C++ code regions enclosed in `{ }`
- Sub-ASTs and AST builders, which are often produced by the syntactic sugar and
factory methods of [SfgComposer][pystencilssfg.SfgComposer].
Its usage is best shown by example:
```Python
tree = make_sequence(
"int a = 0;",
"int b = 1;",
(
"int tmp = b;",
"b = a;",
"a = tmp;"
),
SfgKernelCall(kernel_handle)
)
sfg.context.add_function("myFunction", tree)
```
will translate to
```C++
void myFunction() {
int a = 0;
int b = 0;
{
int tmp = b;
b = a;
a = tmp;
}
kernels::kernel( ... );
}
```
types of arguments which then get turned into C++ code. These are
- Strings (`str`) are printed as-is
- Tuples (`tuple`) signify *blocks*, i.e. C++ code regions enclosed in ``{ }``
- Sub-ASTs and AST builders, which are often produced by the syntactic sugar and
factory methods of `SfgComposer`.
:Example:
.. code-block:: Python
tree = make_sequence(
"int a = 0;",
"int b = 1;",
(
"int tmp = b;",
"b = a;",
"a = tmp;"
),
SfgKernelCall(kernel_handle)
)
sfg.context.add_function("myFunction", tree)
will translate to
.. code-block:: C++
void myFunction() {
int a = 0;
int b = 0;
{
int tmp = b;
b = a;
a = tmp;
}
kernels::kernel( ... );
}
"""
children = []
for i, arg in enumerate(args):
......@@ -297,8 +623,8 @@ def make_sequence(*args: tuple | str | SfgCallTreeNode | SfgNodeBuilder) -> SfgS
children.append(arg.resolve())
elif isinstance(arg, SfgCallTreeNode):
children.append(arg)
elif isinstance(arg, str):
children.append(SfgStatements(arg, (), ()))
elif isinstance(arg, _ExprLike):
children.append(make_statements(arg))
elif isinstance(arg, tuple):
# Tuples are treated as blocks
subseq = make_sequence(*arg)
......@@ -309,13 +635,103 @@ def make_sequence(*args: tuple | str | SfgCallTreeNode | SfgNodeBuilder) -> SfgS
return SfgSequence(children)
class SfgFunctionSequencerBase:
"""Common base class for function and method sequencers.
This builder uses call sequencing to specify the function or method's properties.
Example:
>>> sfg.function(
... "myFunction"
... ).returns(
... "float32"
... ).attr(
... "nodiscard", "maybe_unused"
... ).inline().constexpr()(
... "return 31.2;"
... )
"""
def __init__(self, cursor: SfgCursor, name: str) -> None:
self._cursor = cursor
self._name = name
self._return_type: PsType = void
self._params: list[SfgVar] | None = None
# Qualifiers
self._inline: bool = False
self._constexpr: bool = False
# Attributes
self._attributes: list[str] = []
def returns(self, rtype: UserTypeSpec):
"""Set the return type of the function"""
self._return_type = create_type(rtype)
return self
def params(self, *args: VarLike):
"""Specify the parameters for this function.
Use this to manually specify the function's parameter list.
If any free variables collected from the function body are not contained
in the parameter list, an error will be raised.
"""
self._params = [asvar(v) for v in args]
return self
def inline(self):
"""Mark this function as ``inline``."""
self._inline = True
return self
def constexpr(self):
"""Mark this function as ``constexpr``."""
self._constexpr = True
return self
def attr(self, *attrs: str):
"""Add attributes to this function"""
self._attributes += attrs
return self
class SfgFunctionSequencer(SfgFunctionSequencerBase):
"""Sequencer for constructing functions."""
def __call__(self, *args: SequencerArg) -> None:
"""Populate the function body"""
tree = make_sequence(*args)
func = SfgFunction(
self._name,
self._cursor.current_namespace,
tree,
return_type=self._return_type,
inline=self._inline,
constexpr=self._constexpr,
attributes=self._attributes,
required_params=self._params,
)
self._cursor.add_entity(func)
if self._inline:
self._cursor.write_header(SfgEntityDef(func))
else:
self._cursor.write_header(SfgEntityDecl(func))
self._cursor.write_impl(SfgEntityDef(func))
class SfgBranchBuilder(SfgNodeBuilder):
def __init__(self):
"""Multi-call builder for C++ ``if/else`` statements."""
def __init__(self) -> None:
self._phase = 0
self._cond = None
self._cond: ExprLike | None = None
self._branch_true = SfgSequence(())
self._branch_false = None
self._branch_false: SfgSequence | None = None
def __call__(self, *args) -> SfgBranchBuilder:
match self._phase:
......@@ -325,16 +741,7 @@ class SfgBranchBuilder(SfgNodeBuilder):
"Must specify exactly one argument as branch condition!"
)
cond = args[0]
if isinstance(cond, str):
cond = SfgCustomCondition(cond)
elif not isinstance(cond, SfgCondition):
raise ValueError(
"Invalid type for branch condition. Must be either `str` or a subclass of `SfgCondition`."
)
self._cond = cond
self._cond = args[0]
case 1: # Then-branch
self._branch_true = make_sequence(*args)
......@@ -349,26 +756,38 @@ class SfgBranchBuilder(SfgNodeBuilder):
def resolve(self) -> SfgCallTreeNode:
assert self._cond is not None
return SfgBranch(self._cond, self._branch_true, self._branch_false)
return SfgBranch(
make_statements(self._cond), self._branch_true, self._branch_false
)
class SfgSwitchBuilder(SfgNodeBuilder):
def __init__(self, switch_arg: str | TypedSymbolOrObject):
"""Builder for C++ switches."""
def __init__(self, switch_arg: ExprLike, autobreak: bool = True):
self._switch_arg = switch_arg
self._cases: dict[str, SfgCallTreeNode] = dict()
self._default: SfgCallTreeNode | None = None
self._cases: dict[str, SfgSequence] = dict()
self._default: SfgSequence | None = None
self._autobreak = autobreak
def case(self, label: str):
if label in self._cases:
raise SfgException(f"Duplicate case: {label}")
def sequencer(*args):
def sequencer(*args: SequencerArg):
if self._autobreak:
args += ("break;",)
tree = make_sequence(*args)
self._cases[label] = tree
return self
return sequencer
def cases(self, cases_dict: dict[str, SequencerArg]):
for key, value in cases_dict.items():
self.case(key)(value)
return self
def default(self, *args):
if self._default is not None:
raise SfgException("Duplicate default case")
......@@ -379,46 +798,4 @@ class SfgSwitchBuilder(SfgNodeBuilder):
return self
def resolve(self) -> SfgCallTreeNode:
return SfgSwitch(self._switch_arg, self._cases, self._default)
def parse_include(incl: str | SfgHeaderInclude, private: bool = False):
if isinstance(incl, SfgHeaderInclude):
return incl
system_header = False
if incl.startswith("<") and incl.endswith(">"):
incl = incl[1:-1]
system_header = True
return SfgHeaderInclude(incl, system_header=system_header, private=private)
def struct_from_numpy_dtype(
struct_name: str, dtype: np.dtype, add_constructor: bool = True
):
cls = SfgClass(struct_name, class_keyword=SfgClassKeyword.STRUCT)
fields = dtype.fields
if fields is None:
raise SfgException(f"Numpy dtype {dtype} is not a structured type.")
constr_params = []
constr_inits = []
for member_name, type_info in fields.items():
member_type = SrcType(cpp_typename(type_info[0]))
member = SfgMemberVariable(member_name, member_type)
arg = SrcObject(f"{member_name}_", member_type)
cls.default.append_member(member)
constr_params.append(arg)
constr_inits.append(f"{member}({arg})")
if add_constructor:
cls.default.append_member(SfgConstructor(constr_params, constr_inits))
return cls
return SfgSwitch(make_statements(self._switch_arg), self._cases, self._default)
from __future__ import annotations
from typing import TYPE_CHECKING, Sequence
from typing import Sequence
from itertools import takewhile, dropwhile
import numpy as np
from pystencils.types import create_type
from ..context import SfgContext, SfgCursor
from ..lang import (
VarLike,
ExprLike,
asvar,
SfgVar,
)
from ..tree import SfgCallTreeNode
from ..source_components import (
from ..ir import (
SfgCallTreeNode,
SfgClass,
SfgClassMember,
SfgInClassDefinition,
SfgConstructor,
SfgMethod,
SfgMemberVariable,
SfgClassKeyword,
SfgVisibility,
SfgVisibilityBlock,
SfgEntityDecl,
SfgEntityDef,
SfgClassBody,
)
from ..source_concepts import SrcObject
from ..types import SrcType
from ..exceptions import SfgException
from .basic_composer import SfgBasicComposer, SfgNodeBuilder, make_sequence
from .mixin import SfgComposerMixIn
from .basic_composer import (
make_sequence,
SequencerArg,
SfgFunctionSequencerBase,
)
if TYPE_CHECKING:
from ..context import SfgContext
class SfgMethodSequencer(SfgFunctionSequencerBase):
def __init__(self, cursor: SfgCursor, name: str) -> None:
super().__init__(cursor, name)
self._const: bool = False
self._static: bool = False
self._virtual: bool = False
self._override: bool = False
self._tree: SfgCallTreeNode
def const(self):
"""Mark this method as ``const``."""
self._const = True
return self
def static(self):
"""Mark this method as ``static``."""
self._static = True
return self
def virtual(self):
"""Mark this method as ``virtual``."""
self._virtual = True
return self
def override(self):
"""Mark this method as ``override``."""
self._override = True
return self
def __call__(self, *args: SequencerArg):
self._tree = make_sequence(*args)
return self
def _resolve(self, ctx: SfgContext, cls: SfgClass, vis_block: SfgVisibilityBlock):
method = SfgMethod(
self._name,
cls,
self._tree,
return_type=self._return_type,
inline=self._inline,
const=self._const,
static=self._static,
constexpr=self._constexpr,
virtual=self._virtual,
override=self._override,
attributes=self._attributes,
required_params=self._params,
)
cls.add_member(method, vis_block.visibility)
if self._inline:
vis_block.elements.append(SfgEntityDef(method))
else:
vis_block.elements.append(SfgEntityDecl(method))
ctx._cursor.write_impl(SfgEntityDef(method))
class SfgClassComposer:
class SfgClassComposer(SfgComposerMixIn):
"""Composer for classes and structs.
This class cannot be instantiated on its own but must be mixed in with
[SfgBasicComposer][pystencilssfg.composer.SfgBasicComposer].
Use through [SfgComposer][pystencilssfg.SfgComposer].
:class:`SfgBasicComposer`.
Its interface is exposed by :class:`SfgComposer`.
"""
def __init__(self, ctx: SfgContext):
if not isinstance(self, SfgBasicComposer):
raise Exception("SfgClassComposer must be mixed-in with SfgBasicComposer.")
self._ctx: SfgContext = ctx
class VisibilityContext:
class VisibilityBlockSequencer:
"""Represent a visibility block in the composer syntax.
Returned by
[private][pystencilssfg.composer.SfgClassComposer.private],
[public][pystencilssfg.composer.SfgClassComposer.public] and
[protected][pystencilssfg.composer.SfgClassComposer.protected].
Returned by `private`, `public`, and `protected`.
"""
def __init__(self, visibility: SfgVisibility):
self._vis_block = SfgVisibilityBlock(visibility)
def members(self):
yield from self._vis_block.members()
self._visibility = visibility
self._args: tuple[
SfgMethodSequencer
| SfgClassComposer.ConstructorBuilder
| VarLike
| str,
...,
]
def __call__(
self,
*args: (
SfgClassMember | SfgClassComposer.ConstructorBuilder | SrcObject | str
SfgMethodSequencer | SfgClassComposer.ConstructorBuilder | VarLike | str
),
):
for arg in args:
self._vis_block.append_member(SfgClassComposer._resolve_member(arg))
self._args = args
return self
def resolve(self, cls: SfgClass) -> None:
cls.append_visibility_block(self._vis_block)
def _resolve(self, ctx: SfgContext, cls: SfgClass) -> SfgVisibilityBlock:
vis_block = SfgVisibilityBlock(self._visibility)
for arg in self._args:
match arg:
case SfgMethodSequencer() | SfgClassComposer.ConstructorBuilder():
arg._resolve(ctx, cls, vis_block)
case str():
vis_block.elements.append(arg)
case _:
var = asvar(arg)
member_var = SfgMemberVariable(var.name, var.dtype, cls)
cls.add_member(member_var, vis_block.visibility)
vis_block.elements.append(SfgEntityDef(member_var))
return vis_block
class ConstructorBuilder:
"""Composer syntax for constructor building.
Returned by [constructor][pystencilssfg.composer.SfgClassComposer.constructor].
Returned by `constructor`.
"""
def __init__(self, *params: SrcObject):
self._params = params
self._initializers: list[str] = []
def __init__(self, *params: VarLike):
self._params = list(asvar(p) for p in params)
self._initializers: list[tuple[SfgVar | str, tuple[ExprLike, ...]]] = []
self._body: str | None = None
def init(self, initializer: str) -> SfgClassComposer.ConstructorBuilder:
def add_param(self, param: VarLike, at: int | None = None):
if at is None:
self._params.append(asvar(param))
else:
self._params.insert(at, asvar(param))
@property
def parameters(self) -> list[SfgVar]:
return self._params
def init(self, var: VarLike | str):
"""Add an initialization expression to the constructor's initializer list."""
self._initializers.append(initializer)
return self
member = var if isinstance(var, str) else asvar(var)
def init_sequencer(*args: ExprLike):
self._initializers.append((member, args))
return self
return init_sequencer
def body(self, body: str):
"""Define the constructor body"""
......@@ -89,13 +183,19 @@ class SfgClassComposer:
self._body = body
return self
def resolve(self) -> SfgConstructor:
return SfgConstructor(
def _resolve(
self, ctx: SfgContext, cls: SfgClass, vis_block: SfgVisibilityBlock
):
ctor = SfgConstructor(
cls,
parameters=self._params,
initializers=self._initializers,
body=self._body if self._body is not None else "",
)
cls.add_member(ctor, vis_block.visibility)
vis_block.elements.append(SfgEntityDef(ctor))
def klass(self, class_name: str, bases: Sequence[str] = ()):
"""Create a class and add it to the underlying context.
......@@ -114,31 +214,30 @@ class SfgClassComposer:
"""
return self._class(class_name, SfgClassKeyword.STRUCT, bases)
def numpy_struct(self, name: str, dtype: np.dtype, add_constructor: bool = True):
"""Add a numpy structured data type as a C++ struct
Returns:
The created class object
"""
return self._struct_from_numpy_dtype(name, dtype, add_constructor)
@property
def public(self) -> SfgClassComposer.VisibilityContext:
def public(self) -> SfgClassComposer.VisibilityBlockSequencer:
"""Create a `public` visibility block in a class body"""
return SfgClassComposer.VisibilityContext(SfgVisibility.PUBLIC)
return SfgClassComposer.VisibilityBlockSequencer(SfgVisibility.PUBLIC)
@property
def protected(self) -> SfgClassComposer.VisibilityContext:
def protected(self) -> SfgClassComposer.VisibilityBlockSequencer:
"""Create a `protected` visibility block in a class or struct body"""
return SfgClassComposer.VisibilityContext(SfgVisibility.PROTECTED)
return SfgClassComposer.VisibilityBlockSequencer(SfgVisibility.PROTECTED)
@property
def private(self) -> SfgClassComposer.VisibilityContext:
def private(self) -> SfgClassComposer.VisibilityBlockSequencer:
"""Create a `private` visibility block in a class or struct body"""
return SfgClassComposer.VisibilityContext(SfgVisibility.PRIVATE)
return SfgClassComposer.VisibilityBlockSequencer(SfgVisibility.PRIVATE)
def var(self, name: str, dtype: SrcType):
"""In a class or struct body or visibility block, and a member variable.
Args:
name: The variable's name
dtype: The variable's data type
"""
return SfgMemberVariable(name, dtype)
def constructor(self, *params: SrcObject):
def constructor(self, *params: VarLike):
"""In a class or struct body or visibility block, add a constructor.
Args:
......@@ -146,85 +245,96 @@ class SfgClassComposer:
"""
return SfgClassComposer.ConstructorBuilder(*params)
def method(
self,
name: str,
returns: SrcType = SrcType("void"),
inline: bool = False,
const: bool = False,
):
def method(self, name: str):
"""In a class or struct body or visibility block, add a method.
The usage is similar to [SfgBasicComposer.function][pystencilssfg.composer.SfgBasicComposer.function].
The usage is similar to :any:`SfgBasicComposer.function`.
Args:
name: The method name
returns: The method's return type
inline: Whether or not the method should be defined in-line.
const: Whether or not the method is const-qualified.
"""
def sequencer(*args: str | tuple | SfgCallTreeNode | SfgNodeBuilder):
tree = make_sequence(*args)
return SfgMethod(
name, tree, return_type=returns, inline=inline, const=const
)
return sequencer
seq = SfgMethodSequencer(self._cursor, name)
if self._ctx.impl_file is None:
seq.inline()
return seq
# INTERNALS
def _class(self, class_name: str, keyword: SfgClassKeyword, bases: Sequence[str]):
if self._ctx.get_class(class_name) is not None:
raise ValueError(f"Class or struct {class_name} already exists.")
# TODO: Return a `CppClass` instance representing the generated class
if self._cursor.get_entity(class_name) is not None:
raise ValueError(
f"Another entity with name {class_name} already exists in the current namespace."
)
cls = SfgClass(class_name, class_keyword=keyword, bases=bases)
self._ctx.add_class(cls)
cls = SfgClass(
class_name,
self._cursor.current_namespace,
class_keyword=keyword,
bases=bases,
)
self._cursor.add_entity(cls)
def sequencer(
*args: (
SfgClassComposer.VisibilityContext
| SfgClassMember
SfgClassComposer.VisibilityBlockSequencer
| SfgMethodSequencer
| SfgClassComposer.ConstructorBuilder
| SrcObject
| VarLike
| str
),
):
default_ended = False
for arg in args:
if isinstance(arg, SfgClassComposer.VisibilityContext):
default_ended = True
arg.resolve(cls)
elif isinstance(
arg,
(
SfgClassMember,
SfgClassComposer.ConstructorBuilder,
SrcObject,
str,
),
):
if default_ended:
raise SfgException(
"Composer Syntax Error: "
"Cannot add members with default visibility after a visibility block."
)
else:
cls.default.append_member(self._resolve_member(arg))
default_vis_sequencer = SfgClassComposer.VisibilityBlockSequencer(
SfgVisibility.DEFAULT
)
def argfilter(arg):
return not isinstance(arg, SfgClassComposer.VisibilityBlockSequencer)
default_vis_args = takewhile(
argfilter,
args,
)
default_block = default_vis_sequencer(*default_vis_args)._resolve(self._ctx, cls) # type: ignore
vis_blocks: list[SfgVisibilityBlock] = []
for arg in dropwhile(argfilter, args):
if isinstance(arg, SfgClassComposer.VisibilityBlockSequencer):
vis_blocks.append(arg._resolve(self._ctx, cls))
else:
raise SfgException(f"{arg} is not a valid class member.")
raise SfgException(
"Composer Syntax Error: "
"Cannot add members with default visibility after a visibility block."
)
self._cursor.write_header(SfgClassBody(cls, default_block, vis_blocks))
return sequencer
@staticmethod
def _resolve_member(
arg: (SfgClassMember | SfgClassComposer.ConstructorBuilder | SrcObject | str),
def _struct_from_numpy_dtype(
self, struct_name: str, dtype: np.dtype, add_constructor: bool = True
):
if isinstance(arg, SrcObject):
return SfgMemberVariable(arg.name, arg.dtype)
elif isinstance(arg, str):
return SfgInClassDefinition(arg)
elif isinstance(arg, SfgClassComposer.ConstructorBuilder):
return arg.resolve()
else:
return arg
fields = dtype.fields
if fields is None:
raise SfgException(f"Numpy dtype {dtype} is not a structured type.")
members: list[SfgClassComposer.ConstructorBuilder | SfgVar] = []
if add_constructor:
ctor = self.constructor()
members.append(ctor)
for member_name, type_info in fields.items():
member_type = create_type(type_info[0])
member = SfgVar(member_name, member_type)
members.append(member)
if add_constructor:
arg = SfgVar(f"{member_name}_", member_type)
ctor.add_param(arg)
ctor.init(member)(arg)
return self.struct(
struct_name,
)(*members)
......@@ -3,20 +3,21 @@ from typing import TYPE_CHECKING
from .basic_composer import SfgBasicComposer
from .class_composer import SfgClassComposer
from .gpu_composer import SfgGpuComposer
if TYPE_CHECKING:
from ..context import SfgContext
class SfgComposer(SfgBasicComposer, SfgClassComposer):
class SfgComposer(SfgBasicComposer, SfgClassComposer, SfgGpuComposer):
"""Primary interface for constructing source files in pystencils-sfg.
The SfgComposer combines the [SfgBasicComposer][pystencilssfg.composer.SfgBasicComposer]
The SfgComposer combines the `SfgBasicComposer`
for the basic components (kernel namespaces, includes, definitions, and functions)
and the [SfgClassComposer][pystencilssfg.composer.SfgClassComposer] for constructing
`struct`s and `class`es.
and the `SfgClassComposer` for constructing ``struct`` s and ``class`` es.
"""
def __init__(self, ctx: SfgContext):
SfgBasicComposer.__init__(self, ctx)
SfgClassComposer.__init__(self, ctx)
def __init__(self, sfg: SfgContext | SfgBasicComposer):
SfgBasicComposer.__init__(self, sfg)
SfgClassComposer.__init__(self)
SfgGpuComposer.__init__(self)
from __future__ import annotations
from abc import ABC, abstractmethod
from ..context import SfgContext
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .composer import SfgComposer
class CustomGenerator(ABC):
"""Abstract base class for custom code generators that may be passed to
[SfgComposer.generate][pystencilssfg.SfgComposer.generate]."""
`SfgBasicComposer.generate`."""
@abstractmethod
def generate(self, ctx: SfgContext) -> None:
...
def generate(self, sfg: SfgComposer) -> None: ...