Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,9 @@ jobs:
uv sync

- name: Run tests
env:
# WATSONX_APIKEY: ${{ secrets.WATSONX_APIKEY }}
# WATSONX_PROJECTID: ${{ secrets.WATSONX_PROJECTID }}
# WATSONX_URL: ${{ secrets.WATSONX_URL }}
run: |
uv run pytest
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ dev = [
"invoke>=2.2.0",
"papermill>=2.6.0",
"pytest-xdist>=3.8.0",
"nbconvert>=7.16.6",
"ipykernel>=6.30.1",
"pre-commit-uv>=4.1.5",
]
docling = [
"langchain-docling >=0.2.0,<0.3.0"
Expand Down
187 changes: 187 additions & 0 deletions tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "invoke",
# "pdbpp",
# "hunter",
# "rich",
# ]
# ///

"""
You can run this tasks with *any* of these options:

1. uv run inv -l | --help | [command]
2. uv run tasks.py -l | --help | [command]

"""
# pylint: disable=dangerous-default-value

import contextlib
import tempfile
from io import StringIO
from pathlib import Path
from textwrap import dedent
from typing import Annotated, Any, Generator, List

import rich
from invoke import Task, task
from invoke.collection import Collection
from invoke.context import Context
from rich import pretty
from rich.console import Console

pretty.install()

console = Console(stderr=True)


TOP_LEVEL: Annotated[Path, "The root directory of the repo"] = Path(__file__).parent


@task(default=True, autoprint=True)
def version(
ctx: Context,
):
"""Shows package version (git based)"""
with ctx.cd(TOP_LEVEL):
return ctx.run(
"uvx --with uv-dynamic-versioning hatchling version",
hide=not ctx.config.run.echo,
).stdout.strip()


@task()
def cfg(ctx: Context):
"""Task configuration file."""
for key in ctx.config:
rich.print(f"[bold]{key}[/bold]")
rich.print(dict(ctx.config[key]))


@task(
help={
"target_": "Target format",
"output": "Output directory, by default is ./dist/",
},
autoprint=True,
)
def build(ctx: Context, target_=[], output="./dist/"):
"""Builds distributable package"""
args = ""
if target_:
target = " ".join(f"-t {t}" for t in target_)
args = f"{args} {target}"
if output:
args = f"{args} -d {output}"

return ctx.run(
f"uvx --with uv-dynamic-versioning hatchling build {args}",
hide=not ctx.config.run.echo,
).stderr.strip()


@task()
def clean(ctx: Context):
"""Cleans dist"""
ctx.run(r"rm -rf ./dist/*.{tar.gz,whl}")


@contextlib.contextmanager
def temp_dir(ctx: Context, post_clean: bool = True) -> Generator["Path", Any, Any]:
"""Creates a temporary directory and changes the context to that directory"""
tmp_dir = Path(tempfile.mkdtemp())
with ctx.cd(tmp_dir):
yield tmp_dir
if post_clean:
with console.status(
"Cleaning temporary directory (keep it with --no-post-clean)"
):
ctx.run(f"rm -rf {tmp_dir}")


NO_VENV = {"VIRTUAL_ENV": ""}


@task(
aliases=[
"tpkg",
]
)
def test_package_isolated(
ctx: Context,
post_clean: bool = True,
command_to_run: str = "ipython",
python: str = "3.12",
):
"""
Builds the package in a temporary directory, creates a new virtualenv and installs it
there. Then runs the command, by default IPython
"""

with temp_dir(ctx, post_clean == post_clean) as tmpd:
with console.status("Building wheel"):
with ctx.cd(TOP_LEVEL):
wheel_location = build(ctx, target_=("wheel",), output=tmpd)

with console.status("Creating virtualenv..."):
ctx.run(f"uv venv --python {python}", env=NO_VENV)
with console.status("Installing freshly backed wheel file"):
ctx.run(f"uv pip install {wheel_location}", env=NO_VENV)
ctx.run(
"uv run python",
in_stream=StringIO(
dedent(
r"""
from importlib.metadata import version
ver = version('agentics-py')
print(f"\n\nagentics-py version: {ver}\n\n", )
"""
)
),
pty=False,
env=NO_VENV,
)
ctx.run(f"uv run {command_to_run}", pty=True, env=NO_VENV)


@task(aliases=["tpypi"])
def test_package_pypi(
ctx: Context,
package_: List[str] = [],
python: str = "3.12",
command_to_run: str = "ipython",
post_clean: bool = False,
) -> None:
"""
Creates a new virtualenv and installs the package PyPI version in it.
Then runs the command, by default IPython
"""
if not package_:
package_ = ["agentics-py"]
with temp_dir(ctx, post_clean=post_clean) as tmpd:
with console.status("Creating virtualenv..."):
ctx.run(f"uv venv --python {python}", env=NO_VENV)
packages = " ".join(package_)
with console.status(f"Installing {packages}"):
ctx.run(f"uv pip install {packages}", env=NO_VENV)
for pkg in package_:
console.print(f"[bold]{pkg}[/bold]")
ctx.run(f"uv pip show {pkg}", env=NO_VENV)

ctx.run(f"uv run {command_to_run}", pty=True, env=NO_VENV)


# This is only required for configuration
ns: Collection = Collection()
local_tasks: List[Task] = [
obj for name, obj in list(locals().items()) if isinstance(obj, Task)
]
for tsk in local_tasks:
ns.add_task(tsk)

if __name__ == "__main__":
from invoke.program import Program

p = Program(namespace=ns)
p.run()
11 changes: 10 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ def wheel(
) -> Annotated[Path, "The wheel file to install"]:
with ctx.cd(git_root):
output = tmp_path_factory.mktemp("dist")
ctx.run(f"uv build -o {output}", in_stream=False)
# uv build is not compatible with uv-dynamic-versioning
ctx.run(
f"uvx --with uv-dynamic-versioning hatchling build -d {output} -t wheel",
in_stream=False,
)
wheel_file, *_ = output.glob("*.whl")
return wheel_file

Expand All @@ -59,3 +63,8 @@ def llm_provider():
return get_llm_provider()
except ValueError:
raise pytest.skip(reason="No available LLM")


@pytest.fixture()
def jupyter_kernel(ctx):
ctx.run()
Loading