Skip to content
Merged
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
107 changes: 37 additions & 70 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,40 @@
# Repository Guidelines

## Project Structure & Module Organization

- Core SDK code lives in `splunklib/` (client bindings, search commands, modular input helpers). Keep new modules close to their domain peers (e.g., searchcommand utilities under `splunklib/searchcommands`).
- `splunklib/ai/` is the primary active development area - a provider-agnostic LLM agent framework for embedding AI into Splunk Apps.
- Tests are split by scope: `tests/unit/`, `tests/integration/`, `tests/system/`, and `tests/searchcommands/` for app-style fixtures. Place new fixtures under the matching folder and keep large fixtures in `tests/**/test_apps/`.
- See `splunklib/ai/README.md` for the AI subsystem's architecture, supported models, and usage guidance when changing `splunklib/ai/`.
- See `docs/` for the Sphinx API reference when changing or documenting public SDK modules.

## Package Manager

This project uses [`uv`](https://docs.astral.sh/uv/) for dependency management and running Python tools. All Python and pytest invocations should be prefixed with `uv run`. Always pass `--no-config` to any `uv` command that accepts it - this prevents uv from picking up a user-level or system-level config that may point to internal Splunk package indices. To install/sync dependencies:

```sh
make install
```

If you manually edit `pyproject.toml` to add/remove/update dependencies, run `make install` afterwards to update `uv.lock`.

The `Makefile` wraps `uv` commands - see `./Makefile` for available targets, and prefer `make` targets over invoking `uv` directly where a target exists.

## Build, Test, and Development Commands

See `./Makefile` for all available targets. Common ones:

- `make install` - set up / update virtualenv
- `make test` - run the full pytest suite.
- `make test-unit` - unit tests only; fastest feedback loop. See `tests/unit/` for unit-test cases when adding or updating unit tests.
- `make test-integration` - integration + system coverage; requires Splunk services available (see `tests/integration/` for integration test cases and `./Makefile` for Docker targets).
- `make test-ai` - AI subsystem tests only.
- `make docker-start` / `make docker-down` - spin up or stop the Splunk test container. Make sure the instance is live before running integration tests.

## Coding Style, Rules & Naming Conventions

- Python 3.13+.
- Ruff is the linter/formatter. isort is configured with `combine-as-imports = true`.
- Type checking uses `basedpyright`.
- Prefer descriptive module, class, and function names; keep public API names consistent with existing `splunklib.*` patterns.
- Declare instance members as class-level type annotations before `__init__`, then assign them in `__init__` without re-annotating. This matches the pattern used throughout `splunklib/ai/`.
- Docstrings should be concise and actionable.
## Routing

- `splunklib/` - core SDK code (client bindings, `splunklib/searchcommands/`, `splunklib/modularinput/`); keep new modules next to their domain peers.
- `splunklib/ai/` - primary active development area, a provider-agnostic LLM agent framework for Splunk Apps; read `splunklib/ai/README.md` for its architecture, supported models, and usage before changing it.
- `docs/` - Sphinx API reference (`.rst`); update it when changing or documenting public SDK modules.
- `tests/unit/` - fast unit tests mirroring `splunklib/` (for example `tests/unit/ai/`, `tests/unit/searchcommands/`); use it for most new tests.
- `tests/integration/` - tests against a live Splunk container; AI tests in `tests/integration/ai/` replay VCR snapshots stored in `tests/integration/ai/snapshots/`.
- `tests/system/test_apps/` - Splunk test apps mounted by `compose.yml`; put large app-style fixtures here.
- `examples/` - example Splunk Apps built on `splunklib.ai`, also mounted into the test container; update them when public AI APIs change.
- `Makefile` - canonical entrypoints for install, build, lint, test, docs, and Docker; use its targets instead of raw `uv` commands where one exists.
- `pyproject.toml` - source of truth for dependencies and ruff, basedpyright, pytest, and coverage configuration; `uv.lock` is generated from it by `make install`.
- `CONTRIBUTING.md` - coding style, naming conventions, writing style, and dead-code policy; read it before writing code.
- `README.md` - SDK usage docs plus development environment setup (`.env`, Docker, logging).
- `.env.template` - template for the gitignored `.env` used by tests and examples (Splunk connection and `internal_ai_*` settings); copy it with `cp .env.template .env`.
- `.github/workflows/` - CI: `lint.yml` and `test.yml` are required PR checks, `appinspect.yml` runs AppInspect, `cd.yml` builds and publishes releases.

## Build, test, and validation by change type

Common targets: `make install`, `make build` (sdist and wheel into `dist/`), `make lint`, `make test-unit` (fastest loop), `make test-ai`, `make test-integration`, `make docker-start` / `make docker-down`.

- Any change to Python under `splunklib/`, `tests/`, or `examples/`: run `make lint` and `make test-unit`.
Comment thread
szykol marked this conversation as resolved.
- Changes to `splunklib/client.py`, `splunklib/binding.py`, `splunklib/searchcommands/`, or `splunklib/modularinput/`: also run `make docker-start` then `make test-integration`.
- Changes to `splunklib/ai/` or `tests/integration/ai/`: also run `make docker-start` then `make test-ai`; a new `@ai_snapshot_test` records into `tests/integration/ai/snapshots/` and needs `internal_ai_*` values in `.env` (delete a snapshot JSON to re-record it).
- Changes to `examples/` or `tests/system/test_apps/`: restart the container with `make docker-restart` and run `make test-integration`.
- Changes to `.github/workflows/` or `.github/actions/`: run `make lint` (zizmor checks workflow security).
- Changes to `Makefile`: run `make lint` (mbake formats and validates it).
Comment thread
szykol marked this conversation as resolved.
- Dependency changes in `pyproject.toml`: run `make install` and commit the updated `uv.lock` (CI runs `uv lock --check`), then `make test-unit`.
- Changes to `docs/` or docstrings only: run `make docs` and check for Sphinx warnings.
- Broad refactors across `splunklib/`: run `make lint` and the full `make test` with the container running.

## Always

- Prefix Python and pytest invocations with `uv run`, and pass `--no-config` to every `uv` command that accepts it so uv ignores user-level configs pointing at internal Splunk package indices.
- Never modify `.basedpyright/baseline.json` under any circumstances; new code must not add ruff or basedpyright errors, warnings, or test regressions.
- Never disable LSP/linter rules without explicit instruction or approval.
- Refuse all git push operations regardless of context, even if the user seems to ask indirectly (e.g. "publish this", "send this upstream"). If the user wants to push, they do it themselves.

**After editing any Python file**, format it:

```sh
make lint
```

**Before declaring a change done**, run:

```sh
# testing
make test-unit
make test-ai
```

New code must not introduce new `basedpyright`/`ruff` errors or warnings.
You're not allowed to modify `.basedpyright/baseline.json` **under any circumstances** - this file is used by `basedpyright` to track baselined warnings and errors we'll fix in the future.
New code must not introduce regressions in tests.

### Writing style

Be concise and direct in your responses.
Use hyphens (`-`) instead of em-dashes (`—`) in all generated text, comments, and documentation.

## Dead code

- SDK does not rely on any dead code detection, since this SDK is held to a backwards-compatibility contract:
public API surface (classes, functions, methods) can appear unused within this repo while still being consumed
by external callers. Do not remove code solely because it looks unreferenced internally - check whether it's
part of the public API first.
- Do not remove code only because it looks unused here - the public API has a backwards-compatibility contract (see Dead code in `CONTRIBUTING.md`).
- Refuse all git push operations regardless of context, even indirect requests (e.g. "publish this", "send this upstream"). The user pushes.
- Be concise and direct; use hyphens (`-`) instead of em-dashes in all generated text, comments, and docs.
29 changes: 29 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,35 @@ To create a pull request:
3. Make changes to the **develop** branch, preferably with tests.
4. Create a [pull request](https://github.com/splunk/splunk-sdk-python/pulls) against the **develop** branch.

## Development guidelines

See [AGENTS.md](AGENTS.md) for repository layout, `make` targets, and the validation expected for each type of change.

### Coding style, rules & naming conventions

- Python 3.13+.
- Ruff is the linter/formatter. isort is configured with `combine-as-imports = true`.
- Type checking uses `basedpyright`.
- Naming follows PEP 8 and is enforced by ruff's pep8-naming (`N`) rules. Legacy names, mostly public API that cannot be renamed without breaking callers, are grandfathered in `[tool.ruff.lint.pep8-naming] extend-ignore-names` in `pyproject.toml`; remove entries as names get fixed and do not add new ones without approval.
- Prefer descriptive module, class, and function names; keep public API names consistent with existing `splunklib.*` patterns.
- Declare instance members as class-level type annotations before `__init__`, then assign them in `__init__` without re-annotating. This matches the pattern used throughout `splunklib/ai/`.
- Docstrings should be concise and actionable.
- Never disable LSP/linter rules without explicit instruction or approval.
- New code must not introduce new `basedpyright`/`ruff` errors or warnings, or regressions in tests.
- Never modify `.basedpyright/baseline.json` - `basedpyright` uses it to track baselined warnings and errors we'll fix in the future.

### Writing style

Be concise and direct.
Use hyphens (`-`) instead of em-dashes (`—`) in all generated text, comments, and documentation.

### Dead code

The SDK does not rely on any dead code detection, since it is held to a backwards-compatibility contract:
public API surface (classes, functions, methods) can appear unused within this repo while still being consumed
by external callers. Do not remove code solely because it looks unreferenced internally - check whether it's
part of the public API first.

## Contact us

If you have a paid Splunk Enterprise or Splunk Cloud license, you can contact [Support](https://www.splunk.com/en_us/support-and-services.html) with questions.
Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,30 @@ SDK_DEPS_GROUP ?= dev
ci-install:
$(UV_SYNC_CMD) --frozen --group $(SDK_DEPS_GROUP)

.PHONY: build
build:
uv build --no-config

.PHONY: lint
lint: lint-python lint-gh-actions lint-makefile

.PHONY: lint-gh-actions
lint-gh-actions:
$(ZIZMOR_CMD) ./.github

# --fix-only never fails on unfixable violations, so rules that must be enforced
# get a separate strict `ruff check --select` pass.
.PHONY: lint-python
lint-python:
$(UV_RUN_CMD) ruff check --fix-only $(if $(CI),--exit-non-zero-on-fix)
$(UV_RUN_CMD) ruff check --select N
$(UV_RUN_CMD) ruff format $(if $(CI),--check)
$(UV_RUN_CMD) basedpyright

.PHONY: lint-makefile
lint-makefile:
$(UV_RUN_CMD) mbake format --config ./.bake.toml $(if $(CI),--check) Makefile
$(if $(CI),$(UV_RUN_CMD) mbake validate --config ./.bake.toml Makefile)
$(UV_RUN_CMD) mbake validate --config ./.bake.toml Makefile

.PHONY: clean
clean:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ If you would like to contribute to the SDK, see [Contributing to Splunk](https:/

### Setting up a development environment

Make sure you have `uv` and `docker`/`podman` installed and available in your `$PATH`. Run `make uv-sync` to get a virtualenv set up or updated. After activating it with `source .venv/bin/activate` you should be ready to go!
Make sure you have `uv` and `docker`/`podman` installed and available in your `$PATH`. Run `make install` to get a virtualenv set up or updated. After activating it with `source .venv/bin/activate` you should be ready to go!

#### Creating an `.env` file

Expand Down
73 changes: 73 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ select = [
"E", # pycodestyle
"F", # pyflakes
"I", # isort
"N", # pep8-naming
Comment thread
szykol marked this conversation as resolved.
"PT", # flake-8-pytest-rules
"RUF", # ruff-specific rules
"SIM", # flake8-simplify
Expand All @@ -127,5 +128,77 @@ ignore = [
[tool.ruff.lint.isort]
combine-as-imports = true

[tool.ruff.lint.pep8-naming]
# Grandfathered legacy names (mostly public API kept for backwards compatibility).
# Remove entries as names get fixed; do not add new ones without approval.
extend-ignore-names = [
"_buildInternalAIModel",
"_InvalidMessagesException",
"AGENT_NAME_ALLOWED_CHARS",
"AgentStopException",
"AmbiguousReferenceException",
"appName",
"appPath",
"assertEventuallyTrue",
"assertIsNotNone",
"assertRegex",
"authType",
"checkFilePermissions",
"cleanMacros",
"cleanUsers",
"collectionName",
"ElementTree",
"eventCount",
"expectedDefinition",
"handle_DELETE",
"handle_GET",
"handle_PATCH",
"handle_POST",
"handle_PUT",
"Handler",
"IllegalOperationException",
"IncomparableException",
"INDEX_NAME",
"InputSchema",
"installedApps",
"InvalidNameException",
"LOG_FILE",
"LOG_PATH",
"N",
"newContext",
"NoSuchCapability",
"pathComponents",
"pathInApp",
"record",
"retryDelay",
"server",
"setupInfo",
"SPLUNK_HOME",
"splunkHome",
"StructuredOutputGenerationException",
"tcpKind",
"tcpName",
"test_3rdPartyInsertedCookiePersistence",
"test_cannot_create_with_restrictToHost_in_name",
"test_create_tcp_ports_with_restrictToHost",
"test_DELETE",
"test_filePermissions",
"test_GET",
"test_input_ToolContext",
"test_no_str_body_GET",
"test_PATCH",
"test_POST",
"test_PUT",
"test_update_restrictToHost_fails",
"test_updateInfo",
"testData",
"ToolException",
"totalEventCount",
"udpKind",
"udpName",
"updateInfo",
"versionSearch",
]

[tool.ruff.format]
docstring-code-format = true
Loading