Skip to content

Commit

Permalink
fix: ci (#52)
Browse files Browse the repository at this point in the history
* fix: tests

* fix: compat

* fix: tests

* fix: tests

* fix: tests
  • Loading branch information
joshua-auchincloss authored Apr 28, 2024
1 parent 5e89646 commit 17f8699
Show file tree
Hide file tree
Showing 17 changed files with 188 additions and 63 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,11 @@ jobs:
- name: Display downloaded files
run: ls -R ./dist
- name: Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
fail_on_unmatched_files: true
prerelease: ${{ contains(github.ref, 'rc') }}
files: |-
dist/*
- name: Publish package
Expand Down
20 changes: 10 additions & 10 deletions COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
| src/hatch_cython/config/\_\_init\_\_.py | 2 | 0 | 0 | 0 | 100% |
| src/hatch_cython/config/autoimport.py | 9 | 0 | 2 | 0 | 100% |
| src/hatch_cython/config/config.py | 143 | 9 | 60 | 6 | 92% |
| src/hatch_cython/config/defaults.py | 28 | 3 | 10 | 2 | 87% |
| src/hatch_cython/config/files.py | 35 | 1 | 16 | 2 | 94% |
| src/hatch_cython/config/flags.py | 70 | 1 | 24 | 0 | 99% |
| src/hatch_cython/config/includes.py | 15 | 1 | 8 | 1 | 91% |
| src/hatch_cython/config/defaults.py | 27 | 0 | 8 | 1 | 97% |
| src/hatch_cython/config/files.py | 35 | 0 | 16 | 1 | 98% |
| src/hatch_cython/config/flags.py | 69 | 1 | 24 | 0 | 99% |
| src/hatch_cython/config/includes.py | 15 | 0 | 8 | 0 | 100% |
| src/hatch_cython/config/macros.py | 12 | 0 | 7 | 0 | 100% |
| src/hatch_cython/config/platform.py | 75 | 2 | 30 | 4 | 94% |
| src/hatch_cython/config/templates.py | 62 | 6 | 32 | 4 | 89% |
| src/hatch_cython/constants.py | 11 | 0 | 0 | 0 | 100% |
| src/hatch_cython/config/platform.py | 75 | 0 | 30 | 3 | 97% |
| src/hatch_cython/config/templates.py | 62 | 5 | 32 | 4 | 90% |
| src/hatch_cython/constants.py | 15 | 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 | 244 | 12 | 156 | 10 | 94% |
| src/hatch_cython/plugin.py | 248 | 12 | 156 | 10 | 95% |
| src/hatch_cython/temp.py | 13 | 0 | 2 | 0 | 100% |
| src/hatch_cython/utils.py | 44 | 4 | 20 | 1 | 89% |
| **TOTAL** | **775** | **40** | **369** | **30** | **94%** |
| src/hatch_cython/utils.py | 39 | 0 | 16 | 0 | 100% |
| **TOTAL** | **776** | **28** | **363** | **25** | **95%** |
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ target-version = "py310"
ignore = [
"B027",
"FBT001",
"FBT002",
"FBT003",
"S105",
"S106",
Expand Down
6 changes: 2 additions & 4 deletions src/hatch_cython/config/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@

from hatch_cython.config.platform import PlatformArgs
from hatch_cython.constants import POSIX_CORE
from hatch_cython.utils import aarch, memo, plat
from hatch_cython.utils import aarch, plat

BREW = "brew"


# pragma: no cover
@memo
def brew_path():
if plat() == "darwin":
# no user input - S603 is false positive
try:
proc = check_output([BREW, "--prefix"]) # noqa: S603
except CalledProcessError:
except (CalledProcessError, FileNotFoundError):
proc = None
dec = proc.decode().replace("\n", "") if proc else None
if dec and dec != "":
Expand Down
5 changes: 2 additions & 3 deletions src/hatch_cython/config/flags.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from dataclasses import dataclass, field
from os import environ
from os import environ, pathsep
from typing import ClassVar

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


@dataclass
Expand All @@ -29,7 +28,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()),
EnvFlag(env="PATH", merges=True, sep=pathsep),
)


Expand Down
7 changes: 1 addition & 6 deletions src/hatch_cython/config/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,7 @@ def __init__(self, index: ListT[IndexItem] = None, **kwargs):
self.kwargs = kwargs

def __repr__(self) -> str:
return dedent(
f"""Templates(
index={self.index},
kwargs={self.kwargs}
)"""
)
return dedent(f"""Templates(index={self.index!r}, kwargs={self.kwargs!r})""")

def asdict(self):
return {
Expand Down
20 changes: 19 additions & 1 deletion src/hatch_cython/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from hatch_cython.types import CorePlatforms, ListT
from hatch_cython.types import CorePlatforms, ListT, Set

NORM_GLOB = r"([^\s]*)"
UAST = "${U_AST}"
Expand All @@ -13,3 +13,21 @@
LTPY311 = "python_version < '3.11'"
MUST_UNIQUE = ["-O", "-arch", "-march"]
POSIX_CORE: ListT[CorePlatforms] = ["darwin", "linux"]

precompiled_extensions: Set[str] = {
# py is left out as we have it optional / runtime value
".pyx",
".pxd",
}
intermediate_extensions: Set[str] = {
".c",
".cpp",
}
templated_extensions: Set[str] = {f"{f}.in" for f in {".py", ".pyi", *precompiled_extensions, *intermediate_extensions}}
compiled_extensions: Set[str] = {
".dll",
# unix
".so",
# windows
".pyd",
}
38 changes: 17 additions & 21 deletions src/hatch_cython/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,38 @@
from contextlib import contextmanager
from glob import glob
from tempfile import TemporaryDirectory
from typing import ClassVar

from Cython.Tempita import sub as render_template
from hatchling.builders.hooks.plugin.interface import BuildHookInterface

from hatch_cython.config import parse_from_dict
from hatch_cython.constants import (
compiled_extensions,
intermediate_extensions,
precompiled_extensions,
templated_extensions,
)
from hatch_cython.temp import ExtensionArg, setup_py
from hatch_cython.types import CallableT, DictT, ListStr, ListT, P
from hatch_cython.types import CallableT, DictT, ListStr, ListT, P, Set
from hatch_cython.utils import autogenerated, memo, parse_user_glob, plat


class CythonBuildHook(BuildHookInterface):
PLUGIN_NAME = "cython"

precompiled_extensions: ClassVar[set] = {
# py is left out as we have it optional / runtime value
".pyx",
".pxd",
}
intermediate_extensions: ClassVar[set] = {
".c",
".cpp",
}
templated_extensions: ClassVar[set] = {
f"{f}.in" for f in {".py", ".pyi", *precompiled_extensions, *intermediate_extensions}
}
compiled_extensions: ClassVar[set] = {
".dll",
# unix
".so",
# windows
".pyd",
}
precompiled_extensions: Set[str]
intermediate_extensions: Set[str]
templated_extensions: Set[str]
compiled_extensions: Set[str]

def __init__(self, *args: P.args, **kwargs: P.kwargs):
self.precompiled_extensions = precompiled_extensions.copy()
self.intermediate_extensions = intermediate_extensions.copy()
self.templated_extensions = templated_extensions.copy()
self.compiled_extensions = compiled_extensions.copy()

super().__init__(*args, **kwargs)

_ = self.options

@property
Expand Down
3 changes: 2 additions & 1 deletion src/hatch_cython/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
TupleT = tuple
DictT = dict
ListT = list
Set = set
else:
from typing import Callable, Dict, List, Tuple # noqa: UP035
from typing import Callable, Dict, List, Set, Tuple # noqa: UP035, F401

from typing_extensions import ParamSpec

Expand Down
11 changes: 2 additions & 9 deletions src/hatch_cython/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@


def stale(src: str, dest: str):
if not os.path.exists(src) and os.path.exists(dest):
if not os.path.exists(src) or not os.path.exists(dest):
return True
return os.path.getmtime(src) > os.path.getmtime(dest)
return (os.path.getmtime(src) >= os.path.getmtime(dest)) or (os.path.getctime(src) >= os.path.getctime(dest))


def memo(func: CallableT[P, T]) -> CallableT[P, T]:
Expand Down Expand Up @@ -66,13 +66,6 @@ def parse_user_glob(
return imd.replace(UAST, "*")


@memo
def path_delim() -> str:
if plat() == "windows":
return ";"
return ":"


def autogenerated(keywords: dict):
return dedent(
f"""# DO NOT EDIT.
Expand Down
10 changes: 10 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from textwrap import dedent
from types import SimpleNamespace
from unittest.mock import patch

from toml import loads

Expand All @@ -19,6 +20,15 @@ def test_brew_path():
assert brew_path() is None


def test_brew_fails_safely():
with patch("hatch_cython.config.defaults.BREW", "some-cmd-that-doesnt-exist"):
with patch("hatch_cython.utils.memo", lambda f: f):
with arch_platform("x86_64", "darwin", brew=False):
assert brew_path() == "/usr/local"
with arch_platform("arm64", "darwin", brew=False):
assert brew_path() == "/opt/homebrew"


def test_config_with_brew():
with pyversion("3", "9"), arch_platform("arm64", "darwin"), patch_path("arm64"), patch_brew("/opt/homebrew"):
ok = parse_from_dict(SimpleNamespace(config={"options": {"parallel": True}}))
Expand Down
14 changes: 14 additions & 0 deletions tests/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,17 @@ def test_fc_with_explicit_targets():
fa = FileArgs(**cfg)

assert fa.explicit_targets


def test_fc_aliasing():
cfg = {
"targets": [],
"exclude": [],
"aliases": {
"somelib.abc.next": "somelib.abc.not_first",
"somelib.abc.alias": "somelib.abc.compiled",
},
}

fa = FileArgs(**cfg)
assert fa.matches_alias("somelib.abc.alias") == "somelib.abc.compiled"
17 changes: 16 additions & 1 deletion tests/test_includes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_includes():
}
ok = parse_includes(
"include_abc",
parse,
parse.copy(),
)
assert ok == Autoimport(**parse)
ok = parse_includes(
Expand All @@ -24,6 +24,21 @@ def test_includes():
assert ok == Autoimport(pkg="abc", include="some_attr")


def test_with_resolved():
parse = {
"include": "gets_include",
"libraries": "gets_libraries",
"library_dirs": "gets_library_dirs",
"required_call": "some_setup_op",
}
ok = parse_includes(
"include_abc",
parse.copy(),
)

assert ok == Autoimport(pkg="abc", **parse)


def test_invalid():
with raises(ValueError, match="either provide a known package"):
parse_includes("list", [])
26 changes: 26 additions & 0 deletions tests/test_platform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from pytest import raises

from hatch_cython.config.platform import PlatformArgs, parse_platform_args, parse_to_plat


def test_platform():
out = [{"arg": "a"}, "b"]
parse_to_plat(PlatformArgs, {"arg": "a"}, out, 0, False)
assert out[0] == PlatformArgs(
platforms="*", arch="*", depends_path=False, marker=None, apply_to_marker=None, arg="a"
)
assert out[1] == "b"

parsed = parse_platform_args(
{
"some": [{"arg": "a"}, "b"],
},
"some",
lambda: [],
)
assert parsed == out


def test_invalid_platform():
with raises(ValueError, match="arg 0 is invalid"):
parse_to_plat(PlatformArgs, "a", ["a"], 0, True)
1 change: 1 addition & 0 deletions tests/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ def test(kwds: dict):
def test_defaults():
parsed = parse_template_kwds({})
assert parsed == Templates(index=[])
assert repr(parsed) == "Templates(index=[], kwargs={})"
53 changes: 52 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from src.hatch_cython.utils import memo
from time import sleep

import pytest

from src.hatch_cython.utils import memo, stale


def test_memo():
Expand Down Expand Up @@ -30,3 +34,50 @@ def ok_property(self):
assert tc2.do() == 44

assert tc.ok_property == "OK"


@pytest.fixture
def new_tmp_dir(tmp_path):
project_dir = tmp_path / "app"
project_dir.mkdir()
return project_dir


def test_stale(new_tmp_dir):
src = new_tmp_dir / "test.txt"
dest = new_tmp_dir / "dest.txt"

src.write_text("hello world")

# mtime may not be within resolution to run tests consistently, so we need to wait for sys
# to reflect the modification times
# https://stackoverflow.com/questions/19059877/python-os-path-getmtime-time-not-changing
sleep(5)

dest.write_text("hello world")

sleep(5)

assert not stale(
str(src),
str(dest),
)

with src.open("w") as f:
f.write("now stale")

sleep(5)

assert stale(
str(src),
str(dest),
)

dest.unlink()

sleep(5)

assert stale(
str(src),
str(dest),
)
Loading

0 comments on commit 17f8699

Please sign in to comment.