Skip to content

Commit

Permalink
merge: pr #16 from joshua-auchincloss/v0.2.5
Browse files Browse the repository at this point in the history
feat: add macro support
  • Loading branch information
joshua-auchincloss authored Aug 30, 2023
2 parents 1fcf9aa + 7395d10 commit 54a3699
Show file tree
Hide file tree
Showing 22 changed files with 274 additions and 131 deletions.
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
rev: v4.4.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 23.7.0
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.285
rev: v0.0.286
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
15 changes: 8 additions & 7 deletions COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
| src/hatch\_cython/\_\_init\_\_.py | 2 | 0 | 0 | 0 | 100% |
| src/hatch\_cython/config/\_\_init\_\_.py | 2 | 0 | 0 | 0 | 100% |
| src/hatch\_cython/config/autoimport.py | 9 | 0 | 4 | 0 | 100% |
| src/hatch\_cython/config/config.py | 145 | 16 | 70 | 10 | 87% |
| src/hatch\_cython/config/config.py | 138 | 12 | 58 | 7 | 89% |
| src/hatch\_cython/config/defaults.py | 6 | 0 | 0 | 0 | 100% |
| src/hatch\_cython/config/files.py | 22 | 1 | 12 | 2 | 91% |
| src/hatch\_cython/config/flags.py | 53 | 1 | 16 | 0 | 99% |
| src/hatch\_cython/config/flags.py | 70 | 1 | 26 | 0 | 99% |
| src/hatch\_cython/config/includes.py | 15 | 1 | 8 | 1 | 91% |
| src/hatch\_cython/config/macros.py | 12 | 0 | 7 | 0 | 100% |
| src/hatch\_cython/config/platform.py | 71 | 2 | 28 | 2 | 96% |
| src/hatch\_cython/constants.py | 11 | 0 | 0 | 0 | 100% |
| src/hatch\_cython/devel.py | 5 | 0 | 0 | 0 | 100% |
| src/hatch\_cython/hooks.py | 5 | 1 | 2 | 0 | 86% |
| src/hatch\_cython/plugin.py | 189 | 9 | 142 | 8 | 95% |
| src/hatch\_cython/temp.py | 12 | 0 | 2 | 0 | 100% |
| src/hatch\_cython/types.py | 19 | 4 | 2 | 1 | 76% |
| src/hatch\_cython/utils.py | 22 | 0 | 8 | 0 | 100% |
| **TOTAL** | **573** | **34** | **286** | **23** | **93%** |
| src/hatch\_cython/plugin.py | 192 | 9 | 142 | 8 | 95% |
| src/hatch\_cython/temp.py | 11 | 0 | 0 | 0 | 100% |
| src/hatch\_cython/utils.py | 27 | 1 | 12 | 1 | 95% |
| **TOTAL** | **598** | **28** | **299** | **21** | **94%** |
3 changes: 3 additions & 0 deletions example/hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ env = [
]

cythonize_kwargs = { annotate = true, nthreads = 4 }
define_macros = [
["NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"],
]

[build.targets.wheel.hooks.custom.options.files]
exclude = [
Expand Down
2 changes: 0 additions & 2 deletions example/src/example_lib/test.pyx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# distutils: language=c++

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

cimport numpy as cnp

from cython.parallel import parallel, prange
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ branch = true
parallel = true
omit = [
"src/hatch_cython/__about__.py",
"src/hatch_cython/types.py",
]

[tool.coverage.paths]
Expand Down
2 changes: 1 addition & 1 deletion src/hatch_cython/__about__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-present joshua-auchincloss <[email protected]>
#
# SPDX-License-Identifier: MIT
__version__ = "0.2.4"
__version__ = "0.2.5"
90 changes: 39 additions & 51 deletions src/hatch_cython/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
from hatch.utils.ci import running_in_ci
from hatchling.builders.hooks.plugin.interface import BuildHookInterface

from hatch_cython.config.autoimport import Autoimport, __packages__
from hatch_cython.config.autoimport import Autoimport
from hatch_cython.config.defaults import get_default_compile, get_default_link
from hatch_cython.config.files import FileArgs
from hatch_cython.config.flags import EnvFlag, EnvFlags, parse_env_args
from hatch_cython.config.flags import EnvFlags, parse_env_args
from hatch_cython.config.includes import parse_includes
from hatch_cython.config.macros import DefineMacros, parse_macros
from hatch_cython.config.platform import ListedArgs, PlatformArgs, parse_platform_args
from hatch_cython.constants import DIRECTIVES, EXIST_TRIM, INCLUDE, LTPY311, MUST_UNIQUE
from hatch_cython.types import CallableT, ListStr
Expand All @@ -24,6 +26,7 @@
"includes",
"libraries",
"library_dirs",
"define_macros",
"directives",
"compile_args",
"cythonize_kwargs",
Expand All @@ -38,70 +41,52 @@ def parse_from_dict(cls: BuildHookInterface):
kwargs = {}
for kw, val in given.items():
if kw in __known__:
parsed: any
if kw == "files":
val = FileArgs(**val) # noqa: PLW2901
kwargs[kw] = val
val: dict
parsed: FileArgs = FileArgs(**val)
elif kw == "define_macros":
val: list
parsed: DefineMacros = parse_macros(val)
else:
val: any
parsed: any = val
kwargs[kw] = parsed
passed.pop(kw)
continue

compile_args = parse_platform_args(kwargs, "compile_args", get_default_compile)
link_args = parse_platform_args(kwargs, "extra_link_args", get_default_link)
env = parse_env_args(kwargs)

kw = {"custom": {}}
for arg in env:
arg: EnvFlag
if arg.applies():
if arg.env in EnvFlags.__known__:
kw[arg.env] = arg
else:
kw["custom"][arg.env] = arg
envflags = EnvFlags(**kw)
envflags = parse_env_args(kwargs)
cfg = Config(**kwargs, compile_args=compile_args, extra_link_args=link_args, envflags=envflags)

for kw, val in passed.copy().items():
is_include = kw.startswith(INCLUDE)
if is_include and val:
import_p = __packages__.get(kw.replace(INCLUDE, ""))
if import_p is None:
if isinstance(val, str):
import_p = Autoimport(pkg=kw, include=val)
elif isinstance(val, dict):
if "pkg" not in val:
val["pkg"] = kw
import_p = Autoimport(**val)
else:
msg = " ".join(
(
"%s (%s) is invalid, either provide a known package or",
"a path in the format of module.get_xxx where get_xxx is",
"the directory to be included",
)
).format(val, type(val))
raise ValueError(msg)

cfg.resolve_pkg(
cls,
import_p,
parse_includes(kw, val),
)
passed.pop(kw)
continue
elif is_include:
passed.pop(kw)

if "parallel" in passed and passed.get("parallel"):
passed.pop("parallel")
comp = [
PlatformArgs(arg="/openmp", platforms="windows"),
PlatformArgs(arg="-fopenmp", platforms=["linux"]),
]
link = [
PlatformArgs(arg="/openmp", platforms="windows"),
PlatformArgs(arg="-fopenmp", platforms="linux"),
PlatformArgs(arg="-lomp", platforms="darwin", marker=LTPY311, apply_to_marker=running_in_ci),
]
cma = ({*cfg.compile_args}).union({*comp})
cfg.compile_args = list(cma)
seb = ({*cfg.extra_link_args}).union({*link})
cfg.extra_link_args = list(seb)
continue
elif kw == "parallel" and passed.get(kw):
comp = [
PlatformArgs(arg="/openmp", platforms="windows"),
PlatformArgs(arg="-fopenmp", platforms=["linux"]),
]
link = [
PlatformArgs(arg="/openmp", platforms="windows"),
PlatformArgs(arg="-fopenmp", platforms="linux"),
PlatformArgs(arg="-lomp", platforms="darwin", marker=LTPY311, apply_to_marker=running_in_ci),
]
cma = ({*cfg.compile_args}).union({*comp})
cfg.compile_args = list(cma)
seb = ({*cfg.extra_link_args}).union({*link})
cfg.extra_link_args = list(seb)
passed.pop(kw)

cfg.compile_kwargs = passed
return cfg
Expand All @@ -112,6 +97,7 @@ class Config:
src: Optional[str] = field(default=None) # noqa: UP007
files: FileArgs = field(default_factory=FileArgs)
includes: ListStr = field(default_factory=list)
define_macros: DefineMacros = field(default_factory=list)
libraries: ListStr = field(default_factory=list)
library_dirs: ListStr = field(default_factory=list)
directives: dict = field(default_factory=lambda: DIRECTIVES)
Expand Down Expand Up @@ -231,7 +217,9 @@ def flush(it):
return flat

def asdict(self):
return asdict(self)
d = asdict(self)
d["envflags"]["env"] = self.envflags.masked_environ()
return d

def validate_include_opts(self):
for opt in self.includes:
Expand Down
4 changes: 2 additions & 2 deletions src/hatch_cython/config/files.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import re
from dataclasses import dataclass, field

from hatch_cython.types import ListStr, UnionT, dict_t
from hatch_cython.types import DictT, ListStr, UnionT
from hatch_cython.utils import parse_user_glob


@dataclass
class FileArgs:
exclude: ListStr = field(default_factory=list)
aliases: dict_t[str, str] = field(default_factory=dict)
aliases: DictT[str, str] = field(default_factory=dict)

def __post_init__(self):
rep = {}
Expand Down
35 changes: 29 additions & 6 deletions src/hatch_cython/config/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
from typing import ClassVar

from hatch_cython.config.platform import PlatformArgs, parse_to_plat
from hatch_cython.types import CallableT, dict_t
from hatch_cython.types import CallableT, DictT
from hatch_cython.utils import path_delim


@dataclass
class EnvFlag(PlatformArgs):
env: str = field(default="")
merges: bool = field(default=False)
sep: str = field(default=" ")

def __hash__(self) -> int:
return hash(self.field)
Expand All @@ -27,6 +29,7 @@ def __hash__(self) -> int:
EnvFlag(env="SHLIB_SUFFIX", merges=False),
EnvFlag(env="AR", merges=False),
EnvFlag(env="ARFLAGS", merges=True),
EnvFlag(env="PATH", merges=True, sep=path_delim()),
)


Expand All @@ -49,10 +52,12 @@ class EnvFlags:
AR: PlatformArgs = None
ARFLAGS: PlatformArgs = None

custom: dict_t[str, PlatformArgs] = field(default_factory=dict)
PATH: PlatformArgs = None

custom: DictT[str, PlatformArgs] = field(default_factory=dict)
env: dict = field(default_factory=environ.copy)

__known__: ClassVar[dict_t[str, EnvFlag]] = {e.env: e for e in __flags__}
__known__: ClassVar[DictT[str, EnvFlag]] = {e.env: e for e in __flags__}

def __post_init__(self):
for flag in __flags__:
Expand All @@ -64,7 +69,7 @@ def merge_to_env(self, flag: EnvFlag, get: CallableT[[str], EnvFlag]):
var = environ.get(flag.env)
override: EnvFlag = get(flag.env)
if override and flag.merges:
add = var + " " if var else ""
add = var + flag.sep if var else ""
self.env[flag.env] = add + override.arg
elif override:
self.env[flag.env] = override.arg
Expand All @@ -75,14 +80,32 @@ def get_from_self(self, attr):
def get_from_custom(self, attr):
return self.custom.get(attr)

def masked_environ(self) -> dict:
out = {}
for k, v in self.env.items():
if k not in self.__known__:
out[k] = "*" * len(v)
else:
out[k] = v
return out


def parse_env_args(
kwargs: dict,
):
) -> EnvFlags:
try:
args: list = kwargs.pop("env")
for i, arg in enumerate(args):
parse_to_plat(EnvFlag, arg, args, i, require_argform=True)
except KeyError:
args = []
return args
kw = {"custom": {}}
for arg in args:
arg: EnvFlag
if arg.applies():
if arg.env in EnvFlags.__known__:
kw[arg.env] = arg
else:
kw["custom"][arg.env] = arg
envflags = EnvFlags(**kw)
return envflags
24 changes: 24 additions & 0 deletions src/hatch_cython/config/includes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from hatch_cython.config.autoimport import Autoimport, __packages__
from hatch_cython.constants import INCLUDE


def parse_includes(kw: str, val: str):
alias = kw.replace(INCLUDE, "")
import_p = __packages__.get(alias)
if import_p is None:
if isinstance(val, str):
import_p = Autoimport(pkg=alias, include=val)
elif isinstance(val, dict):
if "pkg" not in val:
val["pkg"] = alias
import_p = Autoimport(**val)
else:
msg = " ".join(
(
"%s (%s) is invalid, either provide a known package or",
"a path in the format of module.get_xxx where get_xxx is",
"the directory to be included",
)
).format(val, type(val))
raise ValueError(msg)
return import_p
31 changes: 31 additions & 0 deletions src/hatch_cython/config/macros.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from hatch_cython.types import ListT, TupleT, UnionT

DefineMacros = ListT[TupleT[str, UnionT[str, None]]]


def parse_macros(define: ListT[ListT[str]]) -> DefineMacros:
"""Parses define_macros from list[list[str, ...]] -> list[tuple[str, str|None]]
Args:
define (ListT[ListT[str]]): list of listed strings of len 1 or 2. raises error if len > 2
Raises:
ValueError: length > 2 or types are not valid
Returns:
DefineMacros: list[tuple[str,str|None]]
"""
for i, inst in enumerate(define):
size = len(inst)
if not (isinstance(inst, list) and size in (1, 2) and all(isinstance(v, str) or v is None for v in inst)):
msg = "".join(
f"define_macros[{i}]: macros must be defined as [name, <value>], "
"where None value denotes #define FOO"
)
raise ValueError(msg, inst)
inst: list
if size == 1:
define[i] = (inst[0], None)
else:
define[i] = (inst[0], inst[1])
return define
Loading

0 comments on commit 54a3699

Please sign in to comment.