diff --git a/seed-gen/Makefile b/seed-gen/Makefile new file mode 100644 index 00000000..6e2744c0 --- /dev/null +++ b/seed-gen/Makefile @@ -0,0 +1,86 @@ +SHELL := /bin/bash + +PY_IMPORT = seed_gen + +ALL_PY_SRCS := $(shell find src -name '*.py') \ + $(shell find test -name '*.py') + +# Optionally overriden by the user, if they're using a virtual environment manager. +VENV ?= env + +# On Windows, venv scripts/shims are under `Scripts` instead of `bin`. +VENV_BIN := $(VENV)/bin +ifeq ($(OS),Windows_NT) + VENV_BIN := $(VENV)/Scripts +endif + +# Optionally overridden by the user in the `release` target. +BUMP_ARGS := + +# Optionally overridden by the user in the `test` target. +TESTS := + +# Optionally overridden by the user/CI, to limit the installation to a specific +# subset of development dependencies. +INSTALL_EXTRA := dev + +# If the user selects a specific test pattern to run, set `pytest` to fail fast +# and only run tests that match the pattern. +# Otherwise, run all tests and enable coverage assertions, since we expect +# complete test coverage. +ifneq ($(TESTS),) + TEST_ARGS := -x -k $(TESTS) + COV_ARGS := +else + TEST_ARGS := + COV_ARGS := --fail-under 100 +endif + +.PHONY: all +all: + @echo "Run my targets individually!" + +.PHONY: dev +dev: $(VENV)/pyvenv.cfg +.PHONY: run +run: $(VENV)/pyvenv.cfg + # Once we can specify the default VENV name in uv, use `uv run` here + # https://github.com/astral-sh/uv/issues/1422 + @. $(VENV_BIN)/activate && seed-gen $(ARGS) + +$(VENV)/pyvenv.cfg: pyproject.toml + uv venv $(VENV) + @. $(VENV_BIN)/activate && uv pip install -e '.[$(INSTALL_EXTRA)]' + +.PHONY: lint +lint: $(VENV)/pyvenv.cfg + . $(VENV_BIN)/activate && \ + ruff format --check && \ + ruff check && \ + mypy + . $(VENV_BIN)/activate && \ + interrogate -c pyproject.toml . + +.PHONY: reformat +reformat: + . $(VENV_BIN)/activate && \ + ruff format && \ + ruff check --fix + +.PHONY: test tests +test tests: $(VENV)/pyvenv.cfg + . $(VENV_BIN)/activate && \ + pytest --cov=$(PY_IMPORT) $(T) $(TEST_ARGS) && \ + python -m coverage report -m $(COV_ARGS) + +.PHONY: doc +doc: + @echo "No documentation set up" + +.PHONY: package +package: $(VENV)/pyvenv.cfg + uv build + +.PHONY: edit +edit: + $(EDITOR) $(ALL_PY_SRCS) diff --git a/seed-gen/README.md b/seed-gen/README.md new file mode 100644 index 00000000..89d878cc --- /dev/null +++ b/seed-gen/README.md @@ -0,0 +1 @@ +# seed-gen \ No newline at end of file diff --git a/seed-gen/pyproject.toml b/seed-gen/pyproject.toml new file mode 100644 index 00000000..21f73383 --- /dev/null +++ b/seed-gen/pyproject.toml @@ -0,0 +1,80 @@ +[project] +name = "seed-gen" +dynamic = ["version"] +description = "LLM seed generation and vulnerability discovery" +readme = "README.md" +authors = [ + { name = "Trail of Bits", email = "opensource@trailofbits.com" }, +] +dependencies = [] +requires-python = ">=3.9" + +[tool.setuptools.dynamic] +version = { attr = "seed_gen.__version__" } + +[project.optional-dependencies] +doc = [] +test = ["pytest", "pytest-cov", "pretend", "coverage[toml]"] +lint = [ + # NOTE: ruff is under active development, so we pin conservatively here + # and let Dependabot periodically perform this update. + "ruff ~= 0.6.2", + "mypy >= 1.0", + "types-html5lib", + "types-requests", + "types-toml", + "interrogate", +] +dev = ["seed-gen[doc,test,lint]", "twine", "build"] + +[project.scripts] +"seed-gen" = "seed_gen._cli:main" + +[tool.coverage.run] +# don't attempt code coverage for the CLI entrypoints +omit = ["src/seed_gen/_cli.py"] + +[tool.mypy] +mypy_path = "src" +packages = "seed_gen" +allow_redefinition = true +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +ignore_missing_imports = true +no_implicit_optional = true +show_error_codes = true +sqlite_cache = true +strict_equality = true +warn_no_return = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true +warn_unused_ignores = true + +[tool.ruff] +line-length = 100 +include = ["src/**/*.py", "test/**/*.py"] + +[tool.ruff.lint] +select = ["ALL"] +# D203 and D213 are incompatible with D211 and D212 respectively. +# COM812 and ISC001 can cause conflicts when using ruff as a formatter. +# See https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules. +ignore = ["D203", "D213", "COM812", "ISC001"] + +[tool.ruff.lint.per-file-ignores] +"src/seed_gen/_cli.py" = [ + "T201", # allow `print` in cli module +] +"test/**/*.py" = [ + "D", # no docstrings in tests + "S101", # asserts are expected in tests +] +[tool.interrogate] +# don't enforce documentation coverage for packaging, testing, the virtual +# environment, or the CLI (which is documented separately). +exclude = ["env", "test", "src/seed_gen/_cli.py"] +ignore-semiprivate = true +fail-under = 100 diff --git a/seed-gen/src/seed_gen/__init__.py b/seed-gen/src/seed_gen/__init__.py new file mode 100644 index 00000000..aac338b0 --- /dev/null +++ b/seed-gen/src/seed_gen/__init__.py @@ -0,0 +1,3 @@ +"""The `seed-gen` APIs.""" + +__version__ = "0.0.1" diff --git a/seed-gen/src/seed_gen/__main__.py b/seed-gen/src/seed_gen/__main__.py new file mode 100644 index 00000000..1677871b --- /dev/null +++ b/seed-gen/src/seed_gen/__main__.py @@ -0,0 +1,6 @@ +"""The `python -m seed-gen` entrypoint.""" + +if __name__ == "__main__": # pragma: no cover + from seed_gen._cli import main + + main() diff --git a/seed-gen/src/seed_gen/_cli.py b/seed-gen/src/seed_gen/_cli.py new file mode 100644 index 00000000..8e0f81d6 --- /dev/null +++ b/seed-gen/src/seed_gen/_cli.py @@ -0,0 +1,5 @@ +"""The `seed-gen` entrypoint.""" + + +def main() -> None: + print("Hello, world!") diff --git a/seed-gen/src/seed_gen/py.typed b/seed-gen/src/seed_gen/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/seed-gen/test/__init__.py b/seed-gen/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/seed-gen/test/test_init.py b/seed-gen/test/test_init.py new file mode 100644 index 00000000..3c5a8ddc --- /dev/null +++ b/seed-gen/test/test_init.py @@ -0,0 +1,9 @@ +"""Initial testing module.""" + +import seed_gen + + +def test_version() -> None: + version = getattr(seed_gen, "__version__", None) + assert version is not None + assert isinstance(version, str)