mirror of
https://github.com/qdrant/landing_page.git
synced 2026-10-03 01:48:32 +02:00
Snippet tools
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
.idea
|
.idea
|
||||||
|
__pycache__
|
||||||
qdrant-landing/.DS_Store
|
qdrant-landing/.DS_Store
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.gradle
|
||||||
|
/cache
|
||||||
|
/clients
|
||||||
|
/tmp
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# Snippet tools
|
||||||
|
|
||||||
|
This is a tool to work with code snippets.
|
||||||
|
|
||||||
|
1. Automated CI type checking of snippets.
|
||||||
|
2. Writing new snippets and checking/running/testing them locally.
|
||||||
|
This tool hides the complexity of setting up every build system/dev env for each client.
|
||||||
|
Also, it supports testing snippets against unreleased versions of clients (from git branches/PRs).
|
||||||
|
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
To convert runnable snippets into markdown ([`generate-md.py`](./generate-md.py)) you need only python with no extra dependencies.
|
||||||
|
|
||||||
|
To typecheck/build snippets, you need a bunch of tools installed.
|
||||||
|
The all-in-one [`Dockerfile`](./docker/Dockerfile) and [`shell.nix`](./shell.nix) are provided.
|
||||||
|
Run [`./docker.sh`](./docker.sh) to start a shell in the docker container.
|
||||||
|
|
||||||
|
On ARM-based macOS, ensure that the Docker Desktop setting "General" > "Virtual Machine Options" > "Use Rosetta for x86\_64/amd64 emulation on Apple Silicon" is selected.
|
||||||
|
Note: on macOS, if you see Java compile failures related to `protobuf`, re-running the test will often solve those.
|
||||||
|
|
||||||
|
|
||||||
|
## Workflow: typechecking and running
|
||||||
|
|
||||||
|
Clone and build clients (latest git versions).
|
||||||
|
```bash
|
||||||
|
./sync-clients.py fetch --csharp --java --typescript
|
||||||
|
```
|
||||||
|
|
||||||
|
(optional) You might specify particular tags/branches/commits instead.
|
||||||
|
```bash
|
||||||
|
./sync-clients.py fetch --csharp=1.16.0 --java=b290b14892534fc0d76893d41b8f656855cd589b --typescript=main
|
||||||
|
```
|
||||||
|
|
||||||
|
(optional) Update the lockfiles for templates (see ./sync-clients.py lock --help for syntax).
|
||||||
|
```bash
|
||||||
|
./sync-clients.py lock --go=v1.16.0 --rust=branch=master --python=tag=v1.16.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Typecheck/build all snippets or a particular language/snippet.
|
||||||
|
```bash
|
||||||
|
./check.py build
|
||||||
|
./check.py build -l csharp,java
|
||||||
|
./check.py build ./snippets/create-collection/simple
|
||||||
|
./check.py build ./snippets/create-collection/simple/go.go
|
||||||
|
```
|
||||||
|
|
||||||
|
Test all testable snippets against localhost Qdrant instance. (you start it yourself).
|
||||||
|
Testable snippets are the ones that have `test.py` file in the same directory.
|
||||||
|
```bash
|
||||||
|
./check.py test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run a particular snippet without running a test.
|
||||||
|
```bash
|
||||||
|
./check.py run ./snippets/create-collection/simple/go.go
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Workflow: writing new snippets/converting old ones
|
||||||
|
|
||||||
|
Convert existing old markdown snippets.
|
||||||
|
```bash
|
||||||
|
./migrate-snippet.py ./snippets/create-collection/simple/go.md
|
||||||
|
```
|
||||||
|
Or create new snippets from scratch.
|
||||||
|
|
||||||
|
To reduce noise, you can hide boilerplate code using special comment:
|
||||||
|
```go
|
||||||
|
if err != nil { panic(err) } // @hide
|
||||||
|
```
|
||||||
|
The usual wrapping boileplate like `public static void run() throws Exception {` is already hidden by default.
|
||||||
|
|
||||||
|
Typecheck/build the snippet.
|
||||||
|
```bash
|
||||||
|
./check.py build ./snippets/create-collection/simple
|
||||||
|
```
|
||||||
|
|
||||||
|
(Optional) Start Qdrant and run the snippet.
|
||||||
|
```bash
|
||||||
|
./check.py run ./snippets/create-collection/simple/go.go
|
||||||
|
```
|
||||||
|
|
||||||
|
(Optional) Write `test.py` and test it.
|
||||||
|
```bash
|
||||||
|
./check.py test ./snippets/create-collection/simple
|
||||||
|
```
|
||||||
|
|
||||||
|
Before committing, regenerate markdown files.
|
||||||
|
If migrated, also delete old markdown files.
|
||||||
|
```bash
|
||||||
|
./generate-md.py
|
||||||
|
git rm ./snippets/create-collection/simple/go.md # if migrated
|
||||||
|
git add ./snippets/create-collection/simple/generated/go.md # made by generate-md.py
|
||||||
|
git add ./snippets/create-collection/simple/go.go
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
For each language, the snippet tester collects all code snippets from the [`snippets/`](../../qdrant-landing/content/documentation/headless/snippets) directory and puts them in a single scratch project in a temporary directory.
|
||||||
|
Then those projects are compiled/typechecked.
|
||||||
|
Putting all snippets in a single project rather than compiling them one by one saves time: some build systems like `gradle` take a lot of time to spin up.
|
||||||
|
|
||||||
|
Each supported language has:
|
||||||
|
- A template project in [`templates/`](./templates) directory.
|
||||||
|
- A Python subclass of `Language` in [`lib/languages/`](./lib/languages). It is responsible for:
|
||||||
|
- Creating the scratch project from snippets and the template.
|
||||||
|
- Building/typechecking the project.
|
||||||
|
- Running a particular snippet for testing.
|
||||||
|
- A list of build dependencies in [`docker/Dockerfile`](./docker/Dockerfile) and [`shell.nix`](./shell.nix).
|
||||||
|
- A handler in [`sync-clients.py`](./sync-clients.py) to fetch the particular git revision of the client.
|
||||||
|
- For Java, C#, Typescript it fetches the client sources to the `clients/` directory, and builds them.
|
||||||
|
This directory is gitignored.
|
||||||
|
- For Go, Rust, Python it updates the lockfile in the [`templates/`](./templates) directory.
|
||||||
|
These lockfiles are checked in this repo.
|
||||||
Executable
+174
@@ -0,0 +1,174 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import types
|
||||||
|
import typing
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from lib import SNIPPETS_DIR, CollectedSnippetsType, collect_snippets, log
|
||||||
|
from lib.languages import ALL_LANGUAGES, CompileResult, Language, parse_languages
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
default_langs = ",".join([lang.NAME for lang in ALL_LANGUAGES])
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-l",
|
||||||
|
"--langs",
|
||||||
|
help=f"comma-separated list of languages, default is {default_langs}",
|
||||||
|
default=default_langs,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"command",
|
||||||
|
help="command to execute",
|
||||||
|
choices=["build", "run", "test"],
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"snippets",
|
||||||
|
nargs="*",
|
||||||
|
help=f"Snippet filenames/directories to process, default: {SNIPPETS_DIR}",
|
||||||
|
default=[SNIPPETS_DIR],
|
||||||
|
type=Path,
|
||||||
|
metavar="SNIPPET_PATH",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
languages = parse_languages(args.langs)
|
||||||
|
snippets = collect_snippets(args.snippets, languages)
|
||||||
|
assert args.command in ("build", "run", "test")
|
||||||
|
|
||||||
|
# Q: Why not `tempfile.TemporaryDirectory()`?
|
||||||
|
# A: Harder to debug/inspect generated files.
|
||||||
|
tmpdir = Path(__file__).parent / "tmp"
|
||||||
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
||||||
|
tmpdir.mkdir(parents=True)
|
||||||
|
build_and_run(tmpdir=tmpdir, snippets=snippets, mode=args.command)
|
||||||
|
|
||||||
|
|
||||||
|
def build_and_run(
|
||||||
|
tmpdir: Path,
|
||||||
|
snippets: CollectedSnippetsType,
|
||||||
|
mode: typing.Literal[
|
||||||
|
"build", # just build
|
||||||
|
"run", # build and run each snippet
|
||||||
|
"test", # build, then run/test all snippets that have a test.py file
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
|
snippets_by_lang: dict[type[Language], list[Path]] = {}
|
||||||
|
|
||||||
|
for snippets2 in snippets.values():
|
||||||
|
for lang, fname in snippets2.items():
|
||||||
|
snippets_by_lang.setdefault(lang, []).append(fname)
|
||||||
|
|
||||||
|
compile_results: dict[type[Language], CompileResult] = {}
|
||||||
|
errors: list[str] = []
|
||||||
|
|
||||||
|
# Load test modules before compilation.
|
||||||
|
# We want them to crash early.
|
||||||
|
test_modules: dict[Path, types.ModuleType] = {}
|
||||||
|
if mode == "test":
|
||||||
|
for snippet_dir in snippets:
|
||||||
|
test_file = snippet_dir / "test.py"
|
||||||
|
if not test_file.exists():
|
||||||
|
continue
|
||||||
|
spec = importlib.util.spec_from_file_location("test_module", test_file)
|
||||||
|
assert spec is not None
|
||||||
|
mod = importlib.util.module_from_spec(spec)
|
||||||
|
assert spec.loader is not None
|
||||||
|
spec.loader.exec_module(mod)
|
||||||
|
test_modules[snippet_dir] = mod
|
||||||
|
|
||||||
|
log("Compile stage")
|
||||||
|
for lang, fnames in snippets_by_lang.items():
|
||||||
|
log(f"· Compiling {lang.NAME} snippets")
|
||||||
|
try:
|
||||||
|
res = lang.compile(tmpdir / lang.NAME, fnames)
|
||||||
|
if res.has_issues:
|
||||||
|
log(f"· · Compilation had issues for {lang.NAME}")
|
||||||
|
errors.append(f"Compilation had issues for {lang.NAME}")
|
||||||
|
compile_results[lang] = res
|
||||||
|
except Exception as e:
|
||||||
|
log(f"· · Compilation failed for {lang.NAME}")
|
||||||
|
errors.append(f"Compilation failed for {lang.NAME}")
|
||||||
|
if not isinstance(e, subprocess.CalledProcessError):
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
if mode in ("run", "test"):
|
||||||
|
log("Run stage")
|
||||||
|
for snippet_dir, snippets2 in snippets.items():
|
||||||
|
if mode == "run":
|
||||||
|
for lang, snippet_fname in snippets2.items():
|
||||||
|
if (compile_result := compile_results.get(lang)) is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
log(f"· Running {snippet_fname}")
|
||||||
|
p = subprocess.run(
|
||||||
|
compile_result.run_args[snippet_fname],
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
if p.returncode != 0:
|
||||||
|
log(f"· · Exit code {p.returncode}")
|
||||||
|
|
||||||
|
if mode == "test" and snippet_dir in test_modules:
|
||||||
|
log(f"· Testing snippets in {snippet_dir}")
|
||||||
|
mod = test_modules[snippet_dir]
|
||||||
|
|
||||||
|
for lang, snippet_fname in snippets2.items():
|
||||||
|
compile_result = compile_results.get(lang)
|
||||||
|
if compile_result is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
log(f"· · Testing {snippet_fname}")
|
||||||
|
|
||||||
|
output = None
|
||||||
|
try:
|
||||||
|
if hasattr(mod, "prepare"):
|
||||||
|
mod.prepare()
|
||||||
|
|
||||||
|
p = subprocess.run(
|
||||||
|
compile_result.run_args[snippet_fname],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
output = p.stdout
|
||||||
|
if p.returncode != 0:
|
||||||
|
msg = f"Process exited with code {p.returncode}"
|
||||||
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
if hasattr(mod, "check"):
|
||||||
|
mod.check()
|
||||||
|
except Exception:
|
||||||
|
log(f"· · · Testing {snippet_fname} failed")
|
||||||
|
if output:
|
||||||
|
print(output.rstrip())
|
||||||
|
traceback.print_exc()
|
||||||
|
errors.append(f"Testing {snippet_fname} failed")
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
if hasattr(mod, "cleanup"):
|
||||||
|
mod.cleanup()
|
||||||
|
except Exception:
|
||||||
|
log(f"· · · Teardown for {snippet_fname} failed")
|
||||||
|
errors.append(f"Teardown for {snippet_fname} failed")
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
log("Errors encountered:")
|
||||||
|
for err in errors:
|
||||||
|
log(f"· {err}")
|
||||||
|
sys.exit(1)
|
||||||
|
elif mode == "test":
|
||||||
|
log("All tests passed successfully.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
IMAGE_NAME="snippet-checker"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
REPO_ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
# check if on macos
|
||||||
|
|
||||||
|
if ! docker image inspect "$IMAGE_NAME" > /dev/null 2>&1; then
|
||||||
|
# TODO: comment why
|
||||||
|
[ "$(uname)" != Darwin ] || export DOCKER_DEFAULT_PLATFORM=linux/amd64
|
||||||
|
docker build -t "$IMAGE_NAME" "$SCRIPT_DIR/docker"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ $# -ne 0 ] || set -- bash
|
||||||
|
|
||||||
|
[ -t 0 ] && TTY_FLAG=--tty || TTY_FLAG=
|
||||||
|
|
||||||
|
docker run \
|
||||||
|
--rm \
|
||||||
|
--interactive \
|
||||||
|
$TTY_FLAG \
|
||||||
|
--network host \
|
||||||
|
--user "$(id -u):$(id -g)" \
|
||||||
|
--volume "$REPO_ROOT_DIR:/workspace" \
|
||||||
|
"$IMAGE_NAME" \
|
||||||
|
uv run "$@"
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
software-properties-common \
|
||||||
|
# for rust client
|
||||||
|
gcc \
|
||||||
|
libc6-dev \
|
||||||
|
# for go client
|
||||||
|
golang \
|
||||||
|
# for java client
|
||||||
|
openjdk-21-jdk-headless \
|
||||||
|
# for python client
|
||||||
|
python3 \
|
||||||
|
# for typescript client
|
||||||
|
nodejs \
|
||||||
|
npm \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# for csharp client
|
||||||
|
RUN add-apt-repository ppa:dotnet/backports && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y dotnet-sdk-8.0 --no-install-recommends && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# for rust client
|
||||||
|
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
|
||||||
|
CARGO_HOME=/opt/rust RUSTUP_HOME=/opt/rust \
|
||||||
|
sh -s -- -y --default-toolchain 1.91.0
|
||||||
|
|
||||||
|
# for typescript client
|
||||||
|
RUN npm install --global --prefix /usr/local pnpm
|
||||||
|
# Without this, typescript snippets tries to connect to ::1 instead of 127.1
|
||||||
|
RUN echo "precedence ::ffff:0:0/96 100" >> /etc/gai.conf
|
||||||
|
|
||||||
|
# for python client
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||||
|
|
||||||
|
ENV RUSTUP_HOME=/opt/rust \
|
||||||
|
PATH="/opt/rust/bin:${PATH}"
|
||||||
|
|
||||||
|
# /workspace is the repo root
|
||||||
|
WORKDIR /workspace/automation/snippets
|
||||||
|
|
||||||
|
# Build tools put their caches in $HOME
|
||||||
|
ENV HOME=/workspace/automation/snippets/cache \
|
||||||
|
# gradle ignores $HOME
|
||||||
|
GRADLE_USER_HOME=/workspace/automation/snippets/cache/.gradle
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/bash", "-c", "mkdir -p $HOME && \"$@\"", "--"]
|
||||||
Executable
+84
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Converts all runnable snippets to markdown files.
|
||||||
|
|
||||||
|
E.g., converts
|
||||||
|
`snippets/create-collection/simple/python.py`
|
||||||
|
to
|
||||||
|
`snippets/create-collection/simple/generated/python.md`
|
||||||
|
|
||||||
|
For using on CI and running before committing changes.
|
||||||
|
The reverse is `./migrate-snippet.py`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import difflib
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from lib import SNIPPETS_DIR, collect_snippets
|
||||||
|
from lib.languages import ALL_LANGUAGES
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
snippets = collect_snippets(bases=[SNIPPETS_DIR], languages=ALL_LANGUAGES)
|
||||||
|
|
||||||
|
issues = 0
|
||||||
|
|
||||||
|
for snippet_dir, snippets2 in snippets.items():
|
||||||
|
generated_dir = snippet_dir / "generated"
|
||||||
|
|
||||||
|
shutil.rmtree(generated_dir, ignore_errors=True)
|
||||||
|
generated_dir.mkdir()
|
||||||
|
|
||||||
|
for lang, snippet_fname in snippets2.items():
|
||||||
|
try:
|
||||||
|
shortened = lang.shorten(snippet_fname.read_text())
|
||||||
|
except Exception as e:
|
||||||
|
issues += 1
|
||||||
|
print(f"Warning: failed to shorten snippet {snippet_fname}: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
continue
|
||||||
|
generated = f"```{lang.NAME}\n{shortened}```\n"
|
||||||
|
|
||||||
|
generated_fname = generated_dir / f"{lang.NAME}.md"
|
||||||
|
handwritten_fname = snippet_dir / f"{lang.NAME}.md"
|
||||||
|
generated_fname.write_text(generated)
|
||||||
|
|
||||||
|
if handwritten_fname.exists():
|
||||||
|
issues += 1
|
||||||
|
print(
|
||||||
|
"Warning: both snippet and generated file exist:",
|
||||||
|
snippet_dir / f"{lang.NAME}.md",
|
||||||
|
)
|
||||||
|
handwritten = (snippet_dir / f"{lang.NAME}.md").read_text()
|
||||||
|
if handwritten != generated:
|
||||||
|
print_and_colorize_diff(
|
||||||
|
difflib.unified_diff(
|
||||||
|
handwritten.splitlines(keepends=True),
|
||||||
|
generated.splitlines(keepends=True),
|
||||||
|
fromfile=str(handwritten_fname),
|
||||||
|
tofile=str(generated_fname),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
if issues:
|
||||||
|
print(f"Total issues found: {issues}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def print_and_colorize_diff(diff: typing.Iterable[str]) -> None:
|
||||||
|
for line in diff:
|
||||||
|
if line.startswith("+"):
|
||||||
|
print(end=f"\x1b[32m{line}\x1b[0m", file=sys.stderr)
|
||||||
|
elif line.startswith("-"):
|
||||||
|
print(end=f"\x1b[31m{line}\x1b[0m", file=sys.stderr)
|
||||||
|
elif line.startswith("@"):
|
||||||
|
print(end=f"\x1b[36m{line}\x1b[0m", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
print(end=line, file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import typing
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .languages import Language
|
||||||
|
|
||||||
|
SNIPPETS_DIR = Path(os.path.normpath(Path(__file__) / "../../snippets")).relative_to(
|
||||||
|
Path.cwd()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def log(msg: str) -> None:
|
||||||
|
"""Print a message.
|
||||||
|
|
||||||
|
Use this instead of `print()` to make the output more visible compared to
|
||||||
|
the output of builders/snippet runs.
|
||||||
|
"""
|
||||||
|
print(f"\x1b[30;43m + \x1b[m {msg}", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
type CollectedSnippetsType = dict[
|
||||||
|
Path, # directory path
|
||||||
|
dict[type[Language], Path], # language -> snippet path
|
||||||
|
]
|
||||||
|
"""Snippet filenames grouped by directory then by language.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
{
|
||||||
|
Path("snippets/example1"): {
|
||||||
|
LanguageGo: Path("snippets/example1/go.go"),
|
||||||
|
LanguagePython: Path("snippets/example1/python.py"),
|
||||||
|
},
|
||||||
|
Path("snippets/example2"): {
|
||||||
|
LanguageGo: Path("snippets/example2/go.go"),
|
||||||
|
LanguagePython: Path("snippets/example2/python.py"),
|
||||||
|
LanguageRust: Path("snippets/example2/rust.rs"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def collect_snippets(
|
||||||
|
bases: list[Path],
|
||||||
|
languages: typing.Sequence[type[Language]],
|
||||||
|
) -> CollectedSnippetsType:
|
||||||
|
"""Locate snippets in the given base paths."""
|
||||||
|
|
||||||
|
result: CollectedSnippetsType = {}
|
||||||
|
|
||||||
|
for base in bases:
|
||||||
|
found = False
|
||||||
|
if base.is_file():
|
||||||
|
for lang in languages:
|
||||||
|
if base.name == lang.SNIPPET_FILENAME:
|
||||||
|
result.setdefault(base.parent, {})[lang] = base
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
elif base.is_dir():
|
||||||
|
for root, dirs, files in os.walk(base):
|
||||||
|
dirs[:] = [d for d in dirs if d != "__pycache__"]
|
||||||
|
dirs.sort()
|
||||||
|
dir_path = Path(root)
|
||||||
|
|
||||||
|
for lang in languages:
|
||||||
|
if lang.SNIPPET_FILENAME in files:
|
||||||
|
found = True
|
||||||
|
result.setdefault(dir_path, {})[lang] = (
|
||||||
|
dir_path / lang.SNIPPET_FILENAME
|
||||||
|
)
|
||||||
|
if not found:
|
||||||
|
log(f"Warning: No snippets found in {base}")
|
||||||
|
|
||||||
|
return result
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
from .base import CompileResult, Language
|
||||||
|
from .bash import LanguageBash
|
||||||
|
from .csharp import LanguageCsharp
|
||||||
|
from .go import LanguageGo
|
||||||
|
from .java import LanguageJava
|
||||||
|
from .python import LanguagePython
|
||||||
|
from .rust import LanguageRust
|
||||||
|
from .typescript import LanguageTypescript
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ALL_LANGUAGES",
|
||||||
|
"CompileResult",
|
||||||
|
"Language",
|
||||||
|
"LanguageBash",
|
||||||
|
"LanguageCsharp",
|
||||||
|
"LanguageGo",
|
||||||
|
"LanguageJava",
|
||||||
|
"LanguagePython",
|
||||||
|
"LanguageRust",
|
||||||
|
"LanguageTypescript",
|
||||||
|
"parse_languages",
|
||||||
|
]
|
||||||
|
|
||||||
|
ALL_LANGUAGES: tuple[type[Language], ...] = (
|
||||||
|
LanguageBash,
|
||||||
|
LanguageCsharp,
|
||||||
|
LanguageGo,
|
||||||
|
LanguageJava,
|
||||||
|
LanguagePython,
|
||||||
|
LanguageRust,
|
||||||
|
LanguageTypescript,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_languages(langs: str) -> list[type[Language]]:
|
||||||
|
"""Parse comma-separated list of languages into a list of Language classes.
|
||||||
|
|
||||||
|
Accepts either language names or snippet file extensions.
|
||||||
|
Example: "python,ts,go" -> [LanguagePython, LanguageTypescript, LanguageGo]
|
||||||
|
"""
|
||||||
|
result: list[type[Language]] = []
|
||||||
|
for lang_name in langs.split(","):
|
||||||
|
lang_name = lang_name.strip()
|
||||||
|
for lang in ALL_LANGUAGES:
|
||||||
|
if lang in result:
|
||||||
|
continue
|
||||||
|
if lang_name in (lang.NAME, lang.SNIPPET_FILENAME.split(".")[1]):
|
||||||
|
result.append(lang)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
msg = f"Unknown language: {lang_name}"
|
||||||
|
raise ValueError(msg)
|
||||||
|
return result
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import dataclasses
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import typing
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
from _typeshed import StrOrBytesPath
|
||||||
|
|
||||||
|
|
||||||
|
class Language:
|
||||||
|
"""Base class for a programming language snippet tester."""
|
||||||
|
|
||||||
|
NAME: str
|
||||||
|
"""Name of the programming language, e.g., 'python'."""
|
||||||
|
|
||||||
|
SNIPPET_FILENAME: str
|
||||||
|
"""Filename of the snippet in this language, e.g., 'python.py'."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compile(cls, tmpdir: Path, fnames: list[Path]) -> "CompileResult":
|
||||||
|
"""Compile, typecheck, and prepare all snippets listed in fnames.
|
||||||
|
|
||||||
|
Might create scratch projects in tmpdir.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def shorten(cls, contents: str) -> str:
|
||||||
|
"""Shorten the snippet contents into a form suitable for inclusion in
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
Removes boilerplate code, e.g. class wrappers, main functions, etc.
|
||||||
|
"""
|
||||||
|
return generic_shorten(contents)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unshorten(cls, contents: str) -> str:
|
||||||
|
"""Convert the shortened snippet into runnable code.
|
||||||
|
|
||||||
|
Not reliable, might require manual adjustments.
|
||||||
|
"""
|
||||||
|
return contents
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def format(cls, fnames: list[str]) -> None:
|
||||||
|
"""Format the snippets specified by fnames in place."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class CompileResult:
|
||||||
|
run_args: dict[Path, typing.Sequence["StrOrBytesPath"]] = dataclasses.field(
|
||||||
|
default_factory=dict
|
||||||
|
)
|
||||||
|
"""Mapping from the snippet filename to the command that runs the
|
||||||
|
snippet."""
|
||||||
|
|
||||||
|
has_issues: bool = False
|
||||||
|
"""If true, indicates that there were issues during
|
||||||
|
compilation/typechecking, but the snippets can still be run.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def copy_template(src: Path, dst: Path) -> None:
|
||||||
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
dst.write_text(src.read_text().replace("{collection_name}", "collection_name"))
|
||||||
|
|
||||||
|
|
||||||
|
def template(
|
||||||
|
original_fname: Path,
|
||||||
|
target_fname: Path,
|
||||||
|
replacements: dict[str, str],
|
||||||
|
) -> None:
|
||||||
|
result = []
|
||||||
|
for line in original_fname.read_text().splitlines(keepends=True):
|
||||||
|
if (r := replacements.get(line.strip())) is not None:
|
||||||
|
result.append(r + "\n")
|
||||||
|
else:
|
||||||
|
result.append(line)
|
||||||
|
target_fname.write_text("".join(result))
|
||||||
|
|
||||||
|
|
||||||
|
_RE_COMMENT = re.compile(r".*\s(?://|#)\s*(@.*)$")
|
||||||
|
|
||||||
|
|
||||||
|
def generic_shorten(text: str) -> str:
|
||||||
|
"""Generic implementation of Language.shorten().
|
||||||
|
|
||||||
|
Removes comments with @hide annotation and trims excessive newlines.
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
for line in text.splitlines():
|
||||||
|
if (m := _RE_COMMENT.match(line)) is not None:
|
||||||
|
if m[1] == "@hide":
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
msg = f"Unknown annotation: {m[1]}"
|
||||||
|
raise ValueError(msg)
|
||||||
|
result.append(line + "\n")
|
||||||
|
text = "".join(result)
|
||||||
|
text = text.lstrip("\n").rstrip("\n")
|
||||||
|
text = re.sub(r"\n{3,}", "\n\n", text)
|
||||||
|
if text:
|
||||||
|
text += "\n"
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def trim_commonpath(fnames: list[Path]) -> dict[Path, Path]:
|
||||||
|
"""Trim the common path prefix from a list of file paths.
|
||||||
|
|
||||||
|
Returns a mapping from original paths to trimmed paths.
|
||||||
|
"""
|
||||||
|
common_prefix = os.path.commonpath(
|
||||||
|
[(Path.cwd() / fname.parent) for fname in fnames]
|
||||||
|
)
|
||||||
|
result = {}
|
||||||
|
for fname in fnames:
|
||||||
|
trimmed = Path(os.path.relpath(fname, common_prefix))
|
||||||
|
result[fname] = trimmed
|
||||||
|
return result
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .base import CompileResult, Language, copy_template
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageBash(Language):
|
||||||
|
NAME = "bash"
|
||||||
|
SNIPPET_FILENAME = "bash.sh"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
|
||||||
|
tmpdir.mkdir(parents=True)
|
||||||
|
result = CompileResult()
|
||||||
|
for no, fname in enumerate(fnames):
|
||||||
|
copy_template(fname, tmpdir / f"snippet{no}.sh")
|
||||||
|
result.run_args[fname] = ["bash", "-ex", f"{tmpdir}/snippet{no}.sh"]
|
||||||
|
return result
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import textwrap
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .base import CompileResult, Language, generic_shorten, template, trim_commonpath
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageCsharp(Language):
|
||||||
|
NAME = "csharp"
|
||||||
|
SNIPPET_FILENAME = "csharp.cs"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
|
||||||
|
shutil.copytree("templates/csharp", tmpdir)
|
||||||
|
|
||||||
|
csproj_path = tmpdir / "csharp.csproj"
|
||||||
|
csproj_path.write_text(
|
||||||
|
csproj_path.read_text().replace(
|
||||||
|
"../../clients/csharp/",
|
||||||
|
f"{Path('clients/csharp').absolute()}/",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
trimmed_fnames = trim_commonpath(fnames)
|
||||||
|
|
||||||
|
src_cases = []
|
||||||
|
|
||||||
|
result = CompileResult()
|
||||||
|
|
||||||
|
for no, (snippet_fname, trimmed_fname) in enumerate(trimmed_fnames.items()):
|
||||||
|
content = (
|
||||||
|
snippet_fname.read_text()
|
||||||
|
.replace("public class Snippet", f"public class Snippet{no}")
|
||||||
|
.replace("{collection_name}", "collection_name")
|
||||||
|
)
|
||||||
|
dest_path = tmpdir / "s" / trimmed_fname.parent / f"Snippet{no}.cs"
|
||||||
|
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
dest_path.write_text(content)
|
||||||
|
src_cases.append(
|
||||||
|
f'case "{snippet_fname}": await Snippet{no}.Run(); break;\n'
|
||||||
|
)
|
||||||
|
result.run_args[snippet_fname] = [
|
||||||
|
"dotnet",
|
||||||
|
f"{tmpdir}/bin/snippets-amalgamation.dll",
|
||||||
|
snippet_fname,
|
||||||
|
]
|
||||||
|
|
||||||
|
template(
|
||||||
|
original_fname=Path("templates/csharp/Program.cs"),
|
||||||
|
target_fname=tmpdir / "Program.cs",
|
||||||
|
replacements={"// %cases%": "".join(src_cases)},
|
||||||
|
)
|
||||||
|
|
||||||
|
subprocess.run(["dotnet", "build", "-o", "bin"], cwd=tmpdir, check=True)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
RE_CODE = re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<imports> .*? )
|
||||||
|
\s*public\ class\ Snippet\s*\{\s*
|
||||||
|
public\ static\ async\ Task\ Run\(\)\s*\{\n
|
||||||
|
(?P<body> .*? )
|
||||||
|
\s*\}\s*}\s*
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.DOTALL | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
EXAMPLE_CODE = textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
using Qdrant.Client;
|
||||||
|
using Qdrant.Client.Grpc;
|
||||||
|
|
||||||
|
public class Snippet
|
||||||
|
{
|
||||||
|
public static async Task Run()
|
||||||
|
{
|
||||||
|
// Your code here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
assert RE_CODE.match(EXAMPLE_CODE) is not None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def shorten(cls, contents: str) -> str:
|
||||||
|
if (m := LanguageCsharp.RE_CODE.match(contents)) is None:
|
||||||
|
msg = "Invalid snippet format"
|
||||||
|
raise ValueError(msg)
|
||||||
|
return generic_shorten(
|
||||||
|
m["imports"].strip() + "\n\n" + textwrap.dedent(m["body"]).strip()
|
||||||
|
)
|
||||||
|
|
||||||
|
RE_RENDERED = re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<imports> (?:using\s[^;]+;\n|\n)* )
|
||||||
|
(?P<body> .* )
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.DOTALL | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unshorten(cls, contents: str) -> str:
|
||||||
|
if m := LanguageCsharp.RE_RENDERED.match(contents):
|
||||||
|
return textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
{imports}
|
||||||
|
|
||||||
|
public class Snippet
|
||||||
|
{{
|
||||||
|
\tpublic static async Task Run()
|
||||||
|
\t{{
|
||||||
|
{body}
|
||||||
|
\t}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
).format(
|
||||||
|
imports=m["imports"].strip(),
|
||||||
|
body=textwrap.indent(m["body"].strip(), "\t" * 2),
|
||||||
|
)
|
||||||
|
return contents
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import textwrap
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .base import (
|
||||||
|
CompileResult,
|
||||||
|
Language,
|
||||||
|
copy_template,
|
||||||
|
generic_shorten,
|
||||||
|
template,
|
||||||
|
trim_commonpath,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageGo(Language):
|
||||||
|
NAME = "go"
|
||||||
|
SNIPPET_FILENAME = "go.go"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
|
||||||
|
shutil.copytree("templates/go", tmpdir)
|
||||||
|
|
||||||
|
trimmed_fnames = trim_commonpath(fnames)
|
||||||
|
|
||||||
|
src_imports = []
|
||||||
|
src_cases = []
|
||||||
|
|
||||||
|
result = CompileResult()
|
||||||
|
|
||||||
|
for no, (snippet_fname, trimmed_fname) in enumerate(trimmed_fnames.items()):
|
||||||
|
dest_path = tmpdir / "s" / trimmed_fname
|
||||||
|
copy_template(snippet_fname, dest_path)
|
||||||
|
src_imports.append(
|
||||||
|
f's{no} "example.com/snippets-amalgamation/{("s" / trimmed_fname).parent}"'
|
||||||
|
)
|
||||||
|
src_cases.append(f'case "{snippet_fname}": s{no}.Main()\n')
|
||||||
|
result.run_args[snippet_fname] = [tmpdir / "tester", snippet_fname]
|
||||||
|
|
||||||
|
template(
|
||||||
|
original_fname=Path("templates/go/main.go"),
|
||||||
|
target_fname=tmpdir / "main.go",
|
||||||
|
replacements={
|
||||||
|
"// %imports%": "\n".join(src_imports),
|
||||||
|
"// %cases%": "\n".join(src_cases),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
subprocess.run(["go", "build", "-o", "tester", "."], cwd=tmpdir, check=True)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
RE_CODE = re.compile(
|
||||||
|
r"""
|
||||||
|
package\ [a-zA-Z0-9_]+\n
|
||||||
|
(?P<imports> .*? )
|
||||||
|
func\ Main\(\)\ \{\n
|
||||||
|
(?P<body> .*? )
|
||||||
|
\}\s*$
|
||||||
|
""",
|
||||||
|
re.DOTALL | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
EXAMPLE_CODE = textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/qdrant/go-client/qdrant"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Main() {
|
||||||
|
// Your code here
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
assert RE_CODE.match(EXAMPLE_CODE) is not None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def shorten(cls, contents: str) -> str:
|
||||||
|
if (m := LanguageGo.RE_CODE.match(contents)) is None:
|
||||||
|
msg = "Invalid snippet format"
|
||||||
|
raise ValueError(msg)
|
||||||
|
return generic_shorten(
|
||||||
|
m["imports"].strip() + "\n\n" + textwrap.dedent(m["body"]).strip()
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def format(cls, fnames: list[str]) -> None:
|
||||||
|
subprocess.run(["gofmt", "-w", *fnames], check=True)
|
||||||
|
|
||||||
|
RE_RENDERED = re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<imports> (?:import\s*\([^)]+\)\n|\n)* )
|
||||||
|
(?P<body> .* )
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.DOTALL | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unshorten(cls, contents: str) -> str:
|
||||||
|
if m := LanguageGo.RE_RENDERED.match(contents):
|
||||||
|
return textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
package snippet
|
||||||
|
|
||||||
|
{imports}
|
||||||
|
|
||||||
|
func Main() {{
|
||||||
|
{body}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
).format(
|
||||||
|
imports=m["imports"].strip(),
|
||||||
|
body=textwrap.indent(m["body"].strip(), "\t"),
|
||||||
|
)
|
||||||
|
return contents
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import textwrap
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .base import CompileResult, Language, generic_shorten, template, trim_commonpath
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageJava(Language):
|
||||||
|
NAME = "java"
|
||||||
|
SNIPPET_FILENAME = "java.java"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
|
||||||
|
(tmpdir / "src/main/java/s").mkdir(parents=True)
|
||||||
|
shutil.copyfile("templates/java/build.gradle", f"{tmpdir}/build.gradle")
|
||||||
|
shutil.copyfile("templates/java/settings.gradle", f"{tmpdir}/settings.gradle")
|
||||||
|
|
||||||
|
trimmed_fnames = trim_commonpath(fnames)
|
||||||
|
|
||||||
|
result = CompileResult()
|
||||||
|
src_cases = []
|
||||||
|
|
||||||
|
for no, (snippet_fname, trimmed_fname) in enumerate(trimmed_fnames.items()):
|
||||||
|
content = (
|
||||||
|
snippet_fname.read_text()
|
||||||
|
.replace("public class Snippet", f"public class Snippet{no}")
|
||||||
|
.replace("{collection_name}", "collection_name")
|
||||||
|
)
|
||||||
|
dest_path = (
|
||||||
|
tmpdir / "src/main/java/s" / trimmed_fname.parent / f"Snippet{no}.java"
|
||||||
|
)
|
||||||
|
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
dest_path.write_text(content)
|
||||||
|
src_cases.append(f'case "{snippet_fname}": Snippet{no}.run(); break;\n')
|
||||||
|
result.run_args[snippet_fname] = [
|
||||||
|
"java",
|
||||||
|
"-jar",
|
||||||
|
f"{tmpdir}/build/libs/app-1.0.0-all.jar",
|
||||||
|
snippet_fname,
|
||||||
|
]
|
||||||
|
|
||||||
|
template(
|
||||||
|
original_fname=Path("templates/java/src/main/java/Main.java"),
|
||||||
|
target_fname=tmpdir / "src/main/java/Main.java",
|
||||||
|
replacements={
|
||||||
|
"// %cases%": "\n".join(src_cases),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
gradlew = os.path.abspath("templates/java/gradlew")
|
||||||
|
client = os.path.abspath("clients/java")
|
||||||
|
subprocess.run(
|
||||||
|
[gradlew, "--no-daemon", f"-PclientPath={client}", "shadowJar"],
|
||||||
|
cwd=tmpdir,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
RE_CODE = re.compile(
|
||||||
|
r"""
|
||||||
|
package\s[a-zA-Z0-9_.]+;\n
|
||||||
|
(?P<imports> .*? )
|
||||||
|
\s*public\ class\ Snippet\ \{\n
|
||||||
|
\s*public\ static\ void\ run\(\)\ throws\ Exception\ \{\n
|
||||||
|
(?P<body> .*? )
|
||||||
|
\s*\}\s*}\s*
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.DOTALL | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
EXAMPLE_CODE = textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
package com.example.snippets_amalgamation;
|
||||||
|
|
||||||
|
import io.qdrant.client.QdrantClient;
|
||||||
|
|
||||||
|
public class Snippet {
|
||||||
|
public static void run() throws Exception {
|
||||||
|
// Your code here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
assert RE_CODE.match(EXAMPLE_CODE) is not None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def shorten(cls, contents: str) -> str:
|
||||||
|
if (m := LanguageJava.RE_CODE.match(contents)) is None:
|
||||||
|
msg = "Invalid snippet format"
|
||||||
|
raise ValueError(msg)
|
||||||
|
return generic_shorten(
|
||||||
|
m["imports"].strip() + "\n\n" + textwrap.dedent(m["body"]).strip()
|
||||||
|
)
|
||||||
|
|
||||||
|
RE_RENDERED = re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<imports> (?: import\s[^;]+;\n|\n)* )
|
||||||
|
(?P<body> .* )
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.DOTALL | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unshorten(cls, contents: str) -> str:
|
||||||
|
if m := LanguageJava.RE_RENDERED.match(contents):
|
||||||
|
return textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
package com.example.snippets_amalgamation;
|
||||||
|
|
||||||
|
{imports}
|
||||||
|
|
||||||
|
public class Snippet {{
|
||||||
|
public static void run() throws Exception {{
|
||||||
|
{body}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
).format(
|
||||||
|
imports=m["imports"].strip(),
|
||||||
|
body=textwrap.indent(m["body"].strip(), " " * 16),
|
||||||
|
)
|
||||||
|
return contents
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .base import CompileResult, Language, copy_template, trim_commonpath
|
||||||
|
|
||||||
|
|
||||||
|
class LanguagePython(Language):
|
||||||
|
NAME = "python"
|
||||||
|
SNIPPET_FILENAME = "python.py"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
|
||||||
|
trimmed_fnames = trim_commonpath(fnames)
|
||||||
|
result = CompileResult()
|
||||||
|
|
||||||
|
for snippet_fname, trimmed_fname in trimmed_fnames.items():
|
||||||
|
target_path = tmpdir / str(trimmed_fname).replace("-", "_")
|
||||||
|
target_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
dir = tmpdir
|
||||||
|
for component in Path(str(trimmed_fname).replace("-", "_")).parts:
|
||||||
|
(dir / "__init__.py").touch()
|
||||||
|
dir /= component
|
||||||
|
copy_template(snippet_fname, target_path)
|
||||||
|
result.run_args[snippet_fname] = [
|
||||||
|
"uv",
|
||||||
|
"--project=templates/python",
|
||||||
|
"run",
|
||||||
|
target_path,
|
||||||
|
]
|
||||||
|
|
||||||
|
p = subprocess.run(["uv", "--project=templates/python", "run", "mypy", tmpdir])
|
||||||
|
result.has_issues = p.returncode != 0
|
||||||
|
|
||||||
|
return result
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import textwrap
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .base import (
|
||||||
|
CompileResult,
|
||||||
|
Language,
|
||||||
|
copy_template,
|
||||||
|
generic_shorten,
|
||||||
|
template,
|
||||||
|
trim_commonpath,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageRust(Language):
|
||||||
|
NAME = "rust"
|
||||||
|
SNIPPET_FILENAME = "rust.rs"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
|
||||||
|
tmpdir.mkdir()
|
||||||
|
(tmpdir / "src").mkdir()
|
||||||
|
shutil.copyfile("templates/rust/Cargo.toml", f"{tmpdir}/Cargo.toml")
|
||||||
|
shutil.copyfile("templates/rust/Cargo.lock", f"{tmpdir}/Cargo.lock")
|
||||||
|
|
||||||
|
src_mods = []
|
||||||
|
src_cases = []
|
||||||
|
|
||||||
|
trimmed_fnames = trim_commonpath(fnames)
|
||||||
|
|
||||||
|
result = CompileResult()
|
||||||
|
binary_path = Path.cwd() / "templates/rust/target/debug/snippets-amalgamation"
|
||||||
|
|
||||||
|
for no, (snippet_fname, trimmed_fname) in enumerate(trimmed_fnames.items()):
|
||||||
|
copy_template(snippet_fname, tmpdir / "src" / "s" / trimmed_fname)
|
||||||
|
src_mods.append(f'#[path = "s/{trimmed_fname}"] mod s{no};')
|
||||||
|
src_cases.append(f'"{snippet_fname}" => s{no}::main().await.unwrap(),\n')
|
||||||
|
result.run_args[snippet_fname] = [binary_path, snippet_fname]
|
||||||
|
|
||||||
|
template(
|
||||||
|
original_fname=Path("templates/rust/src/main.rs"),
|
||||||
|
target_fname=tmpdir / "src/main.rs",
|
||||||
|
replacements={
|
||||||
|
"// %mods%": "\n".join(src_mods),
|
||||||
|
"// %cases%": "\n".join(src_cases),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
subprocess.run(
|
||||||
|
["cargo", "build"],
|
||||||
|
cwd=tmpdir,
|
||||||
|
env={
|
||||||
|
**os.environ,
|
||||||
|
"CARGO_TARGET_DIR": str(Path.cwd() / "templates/rust/target"),
|
||||||
|
},
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def format(cls, fnames: list[str]) -> None:
|
||||||
|
subprocess.run(["rustfmt", "--edition=2024", *fnames], check=True)
|
||||||
|
|
||||||
|
RE_CODE = re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<uses> (?:use\s[^;]+;\n|\n)* )
|
||||||
|
pub\s+async\s+fn\s+main\(\)\s*->\s*anyhow::Result<\(\)>\s*\{\n
|
||||||
|
(?P<body> .*? )
|
||||||
|
\s*Ok\(\(\)\)\n
|
||||||
|
\}\n?
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.DOTALL | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
EXAMPLE_CODE = textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
use qdrant_client::Qdrant;
|
||||||
|
|
||||||
|
pub async fn main() -> anyhow::Result<()> {
|
||||||
|
// Your code here
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
assert RE_CODE.match(EXAMPLE_CODE) is not None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def shorten(cls, contents: str) -> str:
|
||||||
|
if (m := LanguageRust.RE_CODE.match(contents)) is None:
|
||||||
|
msg = "Invalid snippet format"
|
||||||
|
raise ValueError(msg)
|
||||||
|
return generic_shorten(
|
||||||
|
m["uses"].strip() + "\n\n" + textwrap.dedent(m["body"]).strip()
|
||||||
|
)
|
||||||
|
|
||||||
|
RE_RENDERED = re.compile(
|
||||||
|
r"""
|
||||||
|
(?P<uses> (?:use\s[^;]+;\n|\n)* )
|
||||||
|
(?P<body> .* )
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.DOTALL | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unshorten(cls, contents: str) -> str:
|
||||||
|
if m := LanguageRust.RE_RENDERED.match(contents):
|
||||||
|
return textwrap.dedent(
|
||||||
|
"""\
|
||||||
|
{uses}
|
||||||
|
|
||||||
|
pub async fn main() -> anyhow::Result<()> {{
|
||||||
|
{body}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
).format(
|
||||||
|
uses=m["uses"].strip(),
|
||||||
|
body=textwrap.indent(m["body"].strip(), " " * 4),
|
||||||
|
)
|
||||||
|
return contents
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .base import CompileResult, Language, copy_template, trim_commonpath
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageTypescript(Language):
|
||||||
|
NAME = "typescript"
|
||||||
|
SNIPPET_FILENAME = "typescript.ts"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
|
||||||
|
tmpdir.mkdir()
|
||||||
|
|
||||||
|
package_data = json.loads(Path("templates/typescript/package.json").read_text())
|
||||||
|
|
||||||
|
package_data["dependencies"]["@qdrant/js-client-rest"] = "file:" + str(
|
||||||
|
Path("clients/typescript/packages/js-client-rest").resolve().absolute()
|
||||||
|
)
|
||||||
|
(tmpdir / "package.json").write_text(json.dumps(package_data, indent=2))
|
||||||
|
|
||||||
|
shutil.copyfile(
|
||||||
|
"templates/typescript/package-lock.json", f"{tmpdir}/package-lock.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
trimmed_fnames = trim_commonpath(fnames)
|
||||||
|
result = CompileResult()
|
||||||
|
|
||||||
|
for snippet_fname, trimmed_fname in trimmed_fnames.items():
|
||||||
|
dest_path = tmpdir / "s" / trimmed_fname
|
||||||
|
copy_template(snippet_fname, dest_path)
|
||||||
|
result.run_args[snippet_fname] = ["node", dest_path.with_suffix(".js")]
|
||||||
|
|
||||||
|
p = subprocess.run(["npm", "install"], cwd=tmpdir)
|
||||||
|
if p.returncode != 0:
|
||||||
|
result.has_issues = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
p = subprocess.run(
|
||||||
|
["node_modules/.bin/tsc"]
|
||||||
|
+ ["s" / trimmed_fname for trimmed_fname in trimmed_fnames.values()],
|
||||||
|
cwd=tmpdir,
|
||||||
|
)
|
||||||
|
result.has_issues = p.returncode != 0
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def format(cls, fnames: list[str]) -> None:
|
||||||
|
# subprocess.run(["npx", "prettier", "--write", *fnames], check=True)
|
||||||
|
pass
|
||||||
Executable
+48
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Migrate the specified `.md` snippet to the runnable snippet file.
|
||||||
|
|
||||||
|
E.g., converts
|
||||||
|
`snippets/create-collection/simple/python.md`
|
||||||
|
to
|
||||||
|
`snippets/create-collection/simple/python.py`
|
||||||
|
|
||||||
|
Not reliable, might require manual adjustments.
|
||||||
|
Intended to be manually used to migrate non-runnable snippets into runnable
|
||||||
|
ones.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from lib import log
|
||||||
|
from lib.languages import ALL_LANGUAGES
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("MD_FILE", nargs="+", type=Path)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
for md_file in args.MD_FILE:
|
||||||
|
if md_file.suffix != ".md":
|
||||||
|
log(f"Skipping non-markdown file: {md_file}")
|
||||||
|
continue
|
||||||
|
for lang in ALL_LANGUAGES:
|
||||||
|
if md_file.name == f"{lang.NAME}.md":
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
log(f"Skipping unknown language file: {md_file}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
lines = md_file.read_text().rstrip().splitlines()
|
||||||
|
if not lines[0].startswith("```") or lines[-1] != "```":
|
||||||
|
continue
|
||||||
|
content = "\n".join(lines[1:-1]) + "\n"
|
||||||
|
content = lang.unshorten(content)
|
||||||
|
|
||||||
|
log(f"Converting {md_file} to {lang.SNIPPET_FILENAME}")
|
||||||
|
(md_file.parent / lang.SNIPPET_FILENAME).write_text(content)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[project]
|
||||||
|
name = "snippet-checker"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"requests>=2.32.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"mypy>=1.18.2",
|
||||||
|
]
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
with import <nixpkgs> { };
|
||||||
|
mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
# for ./sync-clients.py
|
||||||
|
git
|
||||||
|
|
||||||
|
# for csharp client
|
||||||
|
dotnetCorePackages.sdk_8_0-bin
|
||||||
|
|
||||||
|
# for go client
|
||||||
|
go
|
||||||
|
|
||||||
|
# for java client
|
||||||
|
openjdk
|
||||||
|
|
||||||
|
# for python client
|
||||||
|
uv
|
||||||
|
|
||||||
|
# for rust client
|
||||||
|
cargo
|
||||||
|
rustfmt
|
||||||
|
|
||||||
|
# for typescript client
|
||||||
|
pnpm
|
||||||
|
];
|
||||||
|
}
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../qdrant-landing/content/documentation/headless/snippets
|
||||||
Executable
+202
@@ -0,0 +1,202 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
# Change these to use different revisions by default/on CI
|
||||||
|
CSHARP_CLIENT_REVISION = "main"
|
||||||
|
JAVA_CLIENT_REVISION = "master"
|
||||||
|
TYPESCRIPT_CLIENT_REVISION = "master"
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Fetch and prepare Qdrant client libraries."
|
||||||
|
)
|
||||||
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||||
|
|
||||||
|
fetch_parser = subparsers.add_parser(
|
||||||
|
"fetch",
|
||||||
|
help="""Fetch client libraries and put them in the clients/ directory.
|
||||||
|
This is for java and C# clients.""",
|
||||||
|
)
|
||||||
|
fetch_parser.add_argument(
|
||||||
|
"--csharp",
|
||||||
|
help="Fetch and build the C# client.",
|
||||||
|
nargs="?",
|
||||||
|
const=CSHARP_CLIENT_REVISION,
|
||||||
|
metavar="GIT_REV",
|
||||||
|
)
|
||||||
|
fetch_parser.add_argument(
|
||||||
|
"--java",
|
||||||
|
help="Fetch the Java client.",
|
||||||
|
nargs="?",
|
||||||
|
const=JAVA_CLIENT_REVISION,
|
||||||
|
metavar="GIT_REV",
|
||||||
|
)
|
||||||
|
fetch_parser.add_argument(
|
||||||
|
"--typescript",
|
||||||
|
help="Fetch the TypeScript client.",
|
||||||
|
nargs="?",
|
||||||
|
const=TYPESCRIPT_CLIENT_REVISION,
|
||||||
|
metavar="GIT_REV",
|
||||||
|
)
|
||||||
|
|
||||||
|
lock_parser = subparsers.add_parser(
|
||||||
|
"lock",
|
||||||
|
help="Update lockfiles for templates/ directories.",
|
||||||
|
)
|
||||||
|
lock_parser.add_argument(
|
||||||
|
"--go",
|
||||||
|
help="Set Go client version",
|
||||||
|
nargs="?",
|
||||||
|
const="master",
|
||||||
|
metavar="GIT_REV",
|
||||||
|
)
|
||||||
|
lock_parser.add_argument(
|
||||||
|
"--rust",
|
||||||
|
help="""Set Rust client version.
|
||||||
|
Format: branch=BRANCH_NAME or tag=TAG_NAME or rev=COMMIT_HASH""",
|
||||||
|
nargs="?",
|
||||||
|
const="branch=master",
|
||||||
|
metavar="KIND=REV",
|
||||||
|
)
|
||||||
|
lock_parser.add_argument(
|
||||||
|
"--python",
|
||||||
|
help="""Set Python client version.
|
||||||
|
Format: branch=BRANCH_NAME or tag=TAG_NAME or rev=COMMIT_HASH""",
|
||||||
|
nargs="?",
|
||||||
|
const="branch=master",
|
||||||
|
metavar="KIND=REV",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
ok = False
|
||||||
|
if args.command == "fetch":
|
||||||
|
if args.csharp is not None:
|
||||||
|
url = "https://github.com/qdrant/qdrant-dotnet"
|
||||||
|
dir = BASE_DIR / "clients" / "csharp"
|
||||||
|
checkout_repo(url, dir, args.csharp)
|
||||||
|
prepare_csharp(dir)
|
||||||
|
ok = True
|
||||||
|
if args.java is not None:
|
||||||
|
url = "https://github.com/qdrant/java-client"
|
||||||
|
dir = BASE_DIR / "clients" / "java"
|
||||||
|
checkout_repo(url, dir, "master")
|
||||||
|
ok = True
|
||||||
|
if args.typescript is not None:
|
||||||
|
url = "https://github.com/qdrant/qdrant-js"
|
||||||
|
dir = BASE_DIR / "clients" / "typescript"
|
||||||
|
checkout_repo(url, dir, args.typescript)
|
||||||
|
prepare_typescript(dir)
|
||||||
|
ok = True
|
||||||
|
if not ok:
|
||||||
|
print("Specify at least one of --csharp, --java, --typescript to fetch.")
|
||||||
|
elif args.command == "lock":
|
||||||
|
if args.go is not None:
|
||||||
|
lock_go(args.go)
|
||||||
|
ok = True
|
||||||
|
if args.rust is not None:
|
||||||
|
lock_rust(args.rust)
|
||||||
|
ok = True
|
||||||
|
if args.python is not None:
|
||||||
|
lock_python(args.python)
|
||||||
|
ok = True
|
||||||
|
if not ok:
|
||||||
|
print("Specify at least one of --go, --rust to lock.")
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_csharp(dir: Path) -> None:
|
||||||
|
subprocess.run(["git", "-C", dir, "clean", "-fxd"], check=True)
|
||||||
|
subprocess.run(
|
||||||
|
["dotnet", "run", "--project", "build", "--", "download-protos", "restore"],
|
||||||
|
check=True,
|
||||||
|
cwd=dir,
|
||||||
|
)
|
||||||
|
subprocess.run(
|
||||||
|
["dotnet", "build", "-c", "Release", "--nologo"],
|
||||||
|
check=True,
|
||||||
|
cwd=dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_typescript(dir: Path) -> None:
|
||||||
|
subprocess.run(["pnpm", "install"], cwd=dir, check=True)
|
||||||
|
subprocess.run(["pnpm", "-r", "build"], cwd=dir, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def lock_go(rev: str) -> None:
|
||||||
|
dir = BASE_DIR / "templates" / "go"
|
||||||
|
subprocess.run(
|
||||||
|
["go", "get", f"github.com/qdrant/go-client@{rev}"], cwd=dir, check=True
|
||||||
|
)
|
||||||
|
subprocess.run(["go", "mod", "tidy"], cwd=dir, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def lock_rust(spec: str) -> None:
|
||||||
|
dir = BASE_DIR / "templates" / "rust"
|
||||||
|
update_toml_spec(dir / "Cargo.toml", spec)
|
||||||
|
subprocess.run(["cargo", "update", "-p", "qdrant-client"], cwd=dir, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def lock_python(spec: str) -> None:
|
||||||
|
dir = BASE_DIR / "templates" / "python"
|
||||||
|
update_toml_spec(dir / "pyproject.toml", spec)
|
||||||
|
subprocess.run(["uv", "lock"], cwd=dir, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def update_toml_spec(path: Path, spec: str) -> None:
|
||||||
|
try:
|
||||||
|
kind, rev = spec.split("=", 1)
|
||||||
|
if kind not in ("branch", "tag", "rev"):
|
||||||
|
raise ValueError
|
||||||
|
except ValueError:
|
||||||
|
print("Invalid format for --rust/--python.")
|
||||||
|
print("Use branch=BRANCH_NAME or tag=TAG_NAME or rev=COMMIT_HASH.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
toml_content = path.read_text().splitlines()
|
||||||
|
re_line = re.compile(r'^(qdrant-client = \{ git = "[^"]+").*$')
|
||||||
|
idx, m = next(
|
||||||
|
(i, m)
|
||||||
|
for i, line in enumerate(toml_content)
|
||||||
|
if (m := re_line.match(line)) is not None
|
||||||
|
)
|
||||||
|
|
||||||
|
toml_content[idx] = f'{m[1]}, {kind} = "{rev}" }}'
|
||||||
|
path.write_text("\n".join(toml_content) + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def checkout_repo(
|
||||||
|
repo_url: str,
|
||||||
|
dest_dir: Path,
|
||||||
|
rev: str,
|
||||||
|
) -> None:
|
||||||
|
if not dest_dir.exists():
|
||||||
|
print(f"Cloning repository from {repo_url} to {dest_dir}...")
|
||||||
|
subprocess.run(["git", "clone", repo_url, dest_dir], check=True)
|
||||||
|
else:
|
||||||
|
print(f"Repository already exists at {dest_dir}. Fetching latest changes...")
|
||||||
|
subprocess.run(["git", "-C", dest_dir, "fetch", "-p", "origin"], check=True)
|
||||||
|
|
||||||
|
# Try remote branch first (ignore local branches)
|
||||||
|
# If this check fails, it's either a commit hash, local branch, or tag
|
||||||
|
p = subprocess.run(
|
||||||
|
["git", "-C", dest_dir, "rev-parse", "--verify", f"refs/remotes/origin/{rev}"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
if p.returncode == 0:
|
||||||
|
rev = p.stdout.strip()
|
||||||
|
|
||||||
|
subprocess.run(["git", "-C", dest_dir, "checkout", "--detach", rev], check=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/bin
|
||||||
|
/obj
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var arg in args)
|
||||||
|
{
|
||||||
|
switch (arg)
|
||||||
|
{
|
||||||
|
// %cases%
|
||||||
|
default:
|
||||||
|
Console.WriteLine($"Unknown argument: {arg}");
|
||||||
|
Environment.Exit(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Error occurred: {e.Message}");
|
||||||
|
Console.Error.WriteLine(e.StackTrace);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AssemblyName>snippets-amalgamation</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../clients/csharp/src/Qdrant.Client/Qdrant.Client.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
module example.com/snippets-amalgamation
|
||||||
|
|
||||||
|
go 1.25.2
|
||||||
|
|
||||||
|
require github.com/qdrant/go-client v1.16.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/net v0.47.0 // indirect
|
||||||
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
|
golang.org/x/text v0.31.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
|
||||||
|
google.golang.org/grpc v1.76.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/qdrant/go-client v1.16.0 h1:DTkC3eppWXKhjQs+IgA9LFKOkSjJ4sTHs2jJPCni7dY=
|
||||||
|
github.com/qdrant/go-client v1.16.0/go.mod h1:I+EL3h4HRoRTeHtbfOd/4kDXwCukZfkd41j/9wryGkw=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
|
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||||
|
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||||
|
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||||
|
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||||
|
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||||
|
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||||
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||||
|
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||||
|
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||||
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
_ "github.com/qdrant/go-client/qdrant"
|
||||||
|
"os"
|
||||||
|
// %imports%
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
for _, arg := range os.Args[1:] {
|
||||||
|
switch arg {
|
||||||
|
// %cases%
|
||||||
|
default:
|
||||||
|
fmt.Printf("Unknown argument: %s\n", arg)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'application'
|
||||||
|
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.example.snippets_amalgamation'
|
||||||
|
version = '1.0.0'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'io.qdrant:client'
|
||||||
|
|
||||||
|
// gRPC dependencies - use the same version as Qdrant client
|
||||||
|
implementation 'io.grpc:grpc-netty-shaded:1.65.1'
|
||||||
|
implementation 'io.grpc:grpc-protobuf:1.65.1'
|
||||||
|
implementation 'io.grpc:grpc-stub:1.65.1'
|
||||||
|
implementation 'io.grpc:grpc-services:1.65.1'
|
||||||
|
|
||||||
|
// Protobuf runtime
|
||||||
|
implementation 'com.google.protobuf:protobuf-java:3.25.4'
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
implementation 'org.slf4j:slf4j-simple:2.0.14'
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass = 'com.example.snippets_amalgamation.Main'
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a proper fat jar and merge service descriptor files (required by gRPC)
|
||||||
|
shadowJar {
|
||||||
|
mergeServiceFiles()
|
||||||
|
archiveBaseName.set('app')
|
||||||
|
archiveClassifier.set('all')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the plain jar minimal (no dependency unpacking)
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes 'Main-Class': 'com.example.snippets_amalgamation.Main'
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
+251
@@ -0,0 +1,251 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH="\\\"\\\""
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
includeBuild(clientPath)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.example.snippets_amalgamation;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
for (String arg : args) {
|
||||||
|
switch (arg) {
|
||||||
|
// %cases%
|
||||||
|
default:
|
||||||
|
System.out.println("Unknown argument: " + arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error occurred: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from qdrant_client import QdrantClient
|
||||||
|
|
||||||
|
print(f"Hello, {QdrantClient}!")
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[project]
|
||||||
|
name = "snippets-amalgamation"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"qdrant-client",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
qdrant-client = { git = "https://github.com/qdrant/qdrant-client", tag = "v1.16.0" }
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"mypy>=1.18.2",
|
||||||
|
]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "snippets-amalgamation"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.100"
|
||||||
|
qdrant-client = { git = "https://github.com/qdrant/rust-client", branch = "master" }
|
||||||
|
tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros"] }
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// %mods%
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
for arg in std::env::args().skip(1) {
|
||||||
|
match arg.as_str() {
|
||||||
|
// %cases%
|
||||||
|
_ => {
|
||||||
|
eprintln!("Unknown argument: {}", arg);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/node_modules
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "snippets-amalgamation",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "1.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@qdrant/js-client-rest": "file:../../clients/typescript/packages/js-client-rest"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user