Skip to content

Commit

Permalink
✨ Add cython extensions with ozi-fix
Browse files Browse the repository at this point in the history
Also add ``--[no-]enable-cython`` to ``ozi-new``.
:arrow_up: blastpipe 2024.5

Signed-off-by: rjdbcm <[email protected]>
  • Loading branch information
rjdbcm committed May 9, 2024
1 parent 1892e22 commit 407db9b
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 53 deletions.
75 changes: 41 additions & 34 deletions ozi/fix/rewrite_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from warnings import warn

from ozi.render import build_child
from ozi.render import build_file
from ozi.render import find_user_template

if TYPE_CHECKING:
Expand Down Expand Up @@ -114,24 +115,41 @@ def __post_init__(self: Rewriter) -> None:

def _add_dunder_init(self: Self, filename: str) -> None:
"""Render a :file:`{filename}/__init__.py`."""
with open(
(
self.path_map.get(self.fix, partial(Path))(
*filename.rstrip('/').split('/'),
)
/ '__init__.py'
build_file(
self.env,
self.fix,
self.path_map.get(self.fix, partial(Path))(
*filename.rstrip('/').split('/'),
)
/ '__init__.py',
find_user_template(
self.target,
filename,
self.fix,
),
'x',
) as f:
f.write(
self.env.get_template('project.name/__init__.py.j2').render(
user_template=find_user_template(
self.target,
filename,
self.fix,
),
),
)

def _add_files(
self: Self,
child: Path,
filename: str,
cmd_files: RewriteCommand,
) -> RewriteCommand:
"""add files to a project if they do not exist."""
if child.exists(): # pragma: no cover
pass
else:
build_file(
self.env,
self.fix,
child,
user_template=find_user_template(self.target, filename, self.fix),
)
if filename.endswith('.pyx'): # pragma: no cover
cmd_files.add('ext', 'files', str(Path(filename)))
else:
cmd_files.add(self.fix, 'files', str(Path(filename)))
return cmd_files

def _add(
self: Rewriter,
Expand All @@ -144,24 +162,15 @@ def _add(
if self.fix not in ['source', 'test', 'root']:
warn('Invalid fix mode nothing will be added.', RuntimeWarning, stacklevel=0)
elif filename.endswith('/'):
build_child(self.env, filename, child)
if self.fix == 'source':
self._add_dunder_init(filename)
if child.exists(): # pragma: no cover
pass
else:
build_child(self.env, filename, child)
if self.fix == 'source':
self._add_dunder_init(filename)
cmd_children.add(self.fix, 'children', filename.rstrip('/'))
elif filename.endswith('.py'):
child.write_text(
self.base_templates.get(
self.fix,
self.base_templates.setdefault(
self.fix,
self.env.get_template('project.name/new_module.py.j2'),
),
).render(user_template=find_user_template(self.target, filename, self.fix)),
)
cmd_files.add(self.fix, 'files', str(Path(filename)))
else:
child.touch()
cmd_files.add(self.fix, 'files', str(Path(filename)))
cmd_files = self._add_files(child, filename, cmd_files)
return cmd_files, cmd_children

def __iadd__(self: Self, other: list[str]) -> Self:
Expand Down Expand Up @@ -190,8 +199,6 @@ def _sub(
cmd_files, cmd_children = cmd_files_children
if filename.endswith('/'):
cmd_children.rem(self.fix, 'children', str(child / 'meson.build'))
elif filename.endswith('.py'):
cmd_files.rem(self.fix, 'files', str(Path(filename)))
else:
cmd_files.rem(self.fix, 'files', str(Path(filename)))
return cmd_files, cmd_children
Expand Down
13 changes: 12 additions & 1 deletion ozi/new/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
# Part of the OZI Project, under the Apache License v2.0 with LLVM Exceptions.
# See LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""``ozi-new`` argparse argument parser."""
"""``ozi-new`` argparse argument parser.
.. versionchanged:: 1.5
Added `--[no-]enable-cython` argument. Default: `--no-enable-cython`
"""
from __future__ import annotations

import argparse
Expand Down Expand Up @@ -224,6 +229,12 @@
action=argparse.BooleanOptionalAction,
help='verify email domain deliverability(default: --no-verify-email)',
)
ozi_defaults.add_argument(
'--enable-cython',
default=False,
action=argparse.BooleanOptionalAction,
help='build extension module with Cython(default: --no-enable-cython)',
)
ozi_defaults.add_argument(
'--strict',
default=False,
Expand Down
81 changes: 75 additions & 6 deletions ozi/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

from pathlib import Path
from typing import TYPE_CHECKING
from typing import AnyStr
from typing import Literal
from warnings import warn

from blastpipe.ozi_templates.filter import underscorify # pyright: ignore
Expand Down Expand Up @@ -43,6 +45,67 @@ def find_user_template(target: str, file: str, fix: str) -> str | None:
return user_template


def map_to_template(
fix: Literal['source', 'root', 'test'] | AnyStr,
filename: str,
) -> str:
"""Map an appropriate template for an ozi-fix mode and filename.
.. versionadded:: 1.5
:param fix: ozi-fix mode setting
:type fix: Literal[&#39;source&#39;, &#39;root&#39;, &#39;test&#39;] | AnyStr
:param filename: name with file extension
:type filename: str
:return: template path
:rtype: str
"""
match fix, filename:
case ['test' | 'root', f] if f.endswith('.py'):
x = 'tests/new_test.py.j2'
case ['source', f] if f.endswith('.py'):
x = 'project.name/new_module.py.j2'
case ['source', f] if f.endswith('.pyx'): # pragma: no cover
x = 'project.name/new_ext.pyx.j2'
case ['root', f]:
x = f'{f}.j2'
case ['source', f]:
x = f'project.name/{f}.j2'
case ['test', f]:
x = f'tests/{f}.j2'
case [_, _]: # pragma: no cover
x = ''
return x


def build_file(
env: Environment,
fix: Literal['source', 'root', 'test'] | AnyStr,
path: Path,
user_template: str | None,
) -> None:
"""Render project file based on OZI templates.
.. versionadded:: 1.5
:param env: rendering environment
:type env: Environment
:param fix: ozi-fix setting
:type fix: Literal[&#39;source&#39;, &#39;root&#39;, &#39;test&#39;] | AnyStr
:param path: full path of file to be rendered
:type path: Path
:param user_template: path to a user template to extend
:type user_template: str | None
"""
try:
template = env.get_template(map_to_template(fix, path.name)).render(
user_template=user_template,
)
path.write_text(template)
except LookupError as e:
warn(str(e), RuntimeWarning)


def build_child(env: Environment, parent: str, child: Path) -> None:
"""Add a child directory to a parent in an existing OZI-style project.
Expand Down Expand Up @@ -119,15 +182,21 @@ def render_project_files(env: Environment, target: Path, name: str) -> None:
f.write(content)

for filename in templates.source:
template = env.get_template(f'{filename}.j2')
filename = filename.replace('project.name', underscorify(name).lower())
with open(target / filename, 'w') as f:
f.write(template.render())
build_file(
env,
'source',
target / filename,
find_user_template(str(target), filename, 'source'),
)

for filename in templates.test:
template = env.get_template(f'{filename}.j2')
with open(target / filename, 'w') as f:
f.write(template.render())
build_file(
env,
'test',
target / filename,
find_user_template(str(target), filename, 'test'),
)

template = env.get_template('project.ozi.wrap.j2')
with open(target / 'subprojects' / 'ozi.wrap', 'w') as f:
Expand Down
19 changes: 10 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Version: @SCM_VERSION@
Summary: Packager for Python projects using Meson.
Download-URL: https://github.com/rjdbcm/OZI/archive/refs/tags/@[email protected]
Home-page: https://oziproject.dev/
Author: Eden Rose Duff MSc
Author: Eden Ross Duff MSc
Author-email: [email protected]
License: @LICENSE@
Keywords: meson,packaging,wheel
Expand Down Expand Up @@ -153,33 +153,34 @@ extend-exclude = ["meson-private", "scripts", "vendor"]
[tool.ruff.lint]
ignore = [
"A003",
"ARG",
"ANN401",
"TRY003",
"ARG",
"B028",
"B905",
"D1",
"D2",
"D101",
"D2",
"D4",
"FLY",
"EM",
"FBT",
"FLY",
"PERF203",
"PGH003",
"PLR",
"PT001",
"RET",
"EM",
"PLW",
"PT001",
"PTH",
"RET",
"RUF009",
"RUF012",
"RUF015",
"RUF200",
"SIM",
"T201",
"TCH002",
"TCH004",
"TRY003",
"UP",
"PERF203",
]
select = ["ALL"]

Expand Down
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
blastpipe==2024.4.3
blastpipe==2024.5.0
GitPython>=3
dnspython
idna>=2
Expand Down
5 changes: 3 additions & 2 deletions tests/test_ozi_fix.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ def test_Rewriter_bad_project__iadd__file( # noqa: N802, DC102, RUF100

@pytest.mark.parametrize('fix', ['test', 'root', 'source'])
def test_Rewriter_bad_project__iadd__file_from_template( # noqa: N802, DC102, RUF100
bad_project: pytest.FixtureRequest,
bad_project: pytest.FixtureRequest | pathlib.Path,
fix: str,
) -> None:
rewriter = ozi.fix.rewrite_command.Rewriter(
Expand Down Expand Up @@ -348,7 +348,8 @@ def test_Rewriter_bad_project__iadd__non_python_file( # noqa: N802, DC102, RUF1
fix=fix,
env=env,
)
rewriter += ['foo']
with pytest.warns(RuntimeWarning):
rewriter += ['foo']
assert len(rewriter.commands) == 1


Expand Down

0 comments on commit 407db9b

Please sign in to comment.