Skip to content

Commit 2c7b4c2

Browse files
committed
JSON-RPC server, Dockerfiles, and CLI & doc improvements
1 parent b7cd3d2 commit 2c7b4c2

28 files changed

+1727
-583
lines changed

.github/workflows/ci.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ master, main ]
6+
tags: [ 'v*.*.*' ]
7+
pull_request:
8+
branches: [ master, main ]
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
- name: Set up Python
16+
uses: actions/setup-python@v5
17+
with:
18+
python-version: '3.11'
19+
- name: Install dependencies
20+
run: |
21+
pip install uv
22+
make install_deps
23+
- name: Run pre-commit hooks
24+
run: |
25+
uv run pre-commit run --all-files
26+
- name: Test
27+
run: make test
28+
- name: Build
29+
run: make build
30+
- name: Build WASM
31+
run: make build_wasm
32+
- name: Upload Build Artifacts
33+
uses: actions/upload-artifact@v4
34+
with:
35+
name: python-build
36+
path: dist/
37+
38+
release:
39+
needs: build
40+
if: startsWith(github.ref, 'refs/tags/')
41+
runs-on: ubuntu-latest
42+
steps:
43+
- uses: actions/checkout@v4
44+
- uses: actions/download-artifact@v4
45+
with:
46+
name: python-build
47+
path: dist/
48+
- name: Release
49+
uses: softprops/action-gh-release@v1
50+
with:
51+
files: dist/**/*

.github/workflows/uv.yml

Lines changed: 0 additions & 43 deletions
This file was deleted.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ __pycache__
99
coverage.json
1010
3.2.0.md
1111
.coverage
12+
test_client.py
13+
openapi.json

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ repos:
1010
hooks:
1111
- id: run-tests-and-update-badges
1212
name: Run Tests and Update Coverage Badges
13-
entry: uv run python scripts/update_badges.py
13+
entry: python3 scripts/update_badges.py
1414
language: system
1515
pass_filenames: false
1616
always_run: true

ARCHITECTURE.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
12
# cdd-python-client Architecture
23

34
<!-- BADGES_START -->
5+
<!-- Replace these placeholders with your repository-specific badges -->
46
[![License](https://img.shields.io/badge/license-Apache--2.0%20OR%20MIT-blue.svg)](https://opensource.org/licenses/Apache-2.0)
57
[![CI/CD](https://github.com/offscale/cdd-python-client/workflows/CI/badge.svg)](https://github.com/offscale/cdd-python-client/actions)
68
[![Coverage](https://codecov.io/gh/offscale/cdd-python-client/branch/master/graph/badge.svg)](https://codecov.io/gh/offscale/cdd-python-client)
@@ -22,7 +24,7 @@ graph TD
2224
2325
subgraph Frontend [Parsers]
2426
A[OpenAPI .yaml/.json]:::endpoint --> P1(OpenAPI Parser):::frontend
25-
B[Python Models / Source]:::endpoint --> P2(Python Parser):::frontend
27+
B[LANGUAGE Models / Source]:::endpoint --> P2(LANGUAGE Parser):::frontend
2628
C[Server Routes / Frameworks]:::endpoint --> P3(Framework Parser):::frontend
2729
D[Client SDKs / ORMs]:::endpoint --> P4(Ext Parser):::frontend
2830
end
@@ -33,7 +35,7 @@ graph TD
3335
3436
subgraph Backend [Emitters]
3537
E1(OpenAPI Emitter):::backend --> X[OpenAPI .yaml/.json]:::endpoint
36-
E2(Python Emitter):::backend --> Y[Python Models / Structs]:::endpoint
38+
E2(LANGUAGE Emitter):::backend --> Y[LANGUAGE Models / Structs]:::endpoint
3739
E3(Server Emitter):::backend --> Z[Server Routes / Controllers]:::endpoint
3840
E4(Client Emitter):::backend --> W[Client SDKs / API Calls]:::endpoint
3941
E5(Data Emitter):::backend --> V[ORM Models / CLI Parsers]:::endpoint
@@ -51,13 +53,15 @@ graph TD
5153
IR --> E5
5254
```
5355

56+
<!-- INSTRUCTION TO LLM: If this specific repo is explicitly Client-only or Server-only based on the `Client-only`, gracefully adjust the descriptions below to emphasize its specific role. -->
57+
5458
## 🧩 Core Components
5559

5660
### 1. The Frontend (Parsers)
5761

5862
The Frontend's responsibility is to read an input source and translate it into the universal CDD Intermediate Representation (IR).
5963

60-
* **Static Analysis (AST-Driven)**: For `Python` source code, the tool **does not** use dynamic reflection or execute the code. Instead, it reads the source files, generates an Abstract Syntax Tree (AST) via `libcst`, and navigates the tree to extract classes, structs, functions, type signatures, API client definitions, server routes, and docstrings.
64+
* **Static Analysis (AST-Driven)**: For `Python` source code, the tool **does not** use dynamic reflection or execute the code. Instead, it reads the source files, generates an Abstract Syntax Tree (AST), and navigates the tree to extract classes, structs, functions, type signatures, API client definitions, server routes, and docstrings.
6165
* **OpenAPI Parsing**: For OpenAPI and JSON Schema inputs, the parser normalizes the structure, resolving internal `$ref`s and extracting properties, endpoints (client or server perspectives), and metadata into the IR.
6266

6367
### 2. Intermediate Representation (IR)
@@ -74,8 +78,8 @@ By standardizing on a single IR (heavily inspired by OpenAPI / JSON Schema primi
7478
The Backend's responsibility is to take the universal IR and generate valid target output. Emitters can be written to support various environments (e.g., Client vs Server, Web vs CLI).
7579

7680
* **Code Generation**: Emitters iterate over the IR and generate idiomatic `Python` source code.
77-
* A **Server Emitter** creates routing controllers and request-validation logic (like mock servers using FastAPI).
78-
* A **Client Emitter** creates API wrappers, fetch functions, and response-parsing logic using `urllib3` or `requests`.
81+
* A **Server Emitter** creates routing controllers and request-validation logic.
82+
* A **Client Emitter** creates API wrappers, fetch functions, and response-parsing logic.
7983
* **Database & CLI Generation**: Emitters can also target ORM models or command-line parsers by mapping IR properties to database columns or CLI arguments.
8084
* **Specification Generation**: Emitters translating back to OpenAPI serialize the IR into standard OpenAPI 3.x JSON or YAML, rigorously formatting descriptions, type constraints, and endpoint schemas based on what was parsed from the source code.
8185

COMPLIANCE.md

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,4 @@
1-
# OpenAPI 3.2.0 Compliance Report
1+
# Compliance
22

3-
This document outlines the current level of compliance of `cdd-python-client` with the [OpenAPI 3.2.0 Specification](https://raw.githubusercontent.com/OAI/OpenAPI-Specification/refs/heads/main/versions/3.2.0.md).
4-
5-
## Currently Implemented
6-
7-
- **OpenAPI Object**: `openapi` version parsing (`3.2.0`), `info`, `paths`, `components` (schemas).
8-
- **Info Object**: `title`, `version`.
9-
- **Paths Object**: basic mapping of routes.
10-
- **Path Item Object**: `get`, `post`, `put`, `delete`, `patch`.
11-
- **Operation Object**: `operationId`, `parameters`, `requestBody`, `responses`, `deprecated`, `tags`.
12-
- **Parameter Object**: `name`, `in` (query/path/header/cookie).
13-
- **RequestBody Object**: `content`, `required`.
14-
- **Response Object**: `description`, `content`.
15-
- **Schema Object**: basic types (`string`, `integer`, `boolean`, `array`, `object`, `number`), `properties`, `$ref`.
16-
- **MediaType Object**: `schema` mapping.
17-
18-
## Missing / Partial Implementation (WIP)
19-
20-
- **OpenAPI Object**: `servers`, `security`, `externalDocs`, `webhooks`.
21-
- **Info Object**: `description`, `termsOfService`, `contact`, `license`, `summary`.
22-
- **Server Object** & **Server Variable Object**.
23-
- **Operation Object**: `callbacks`, `security`, `servers`.
24-
- **Responses Object**: advanced status mapping.
25-
- **Header Object**.
26-
- **Security Scheme Object** & **Security Requirement Object**.
27-
- **Components Object**: `responses`, `parameters`, `examples`, `requestBodies`, `headers`, `securitySchemes`, `links`, `callbacks`, `pathItems`.
28-
- **Discriminator Object** & polymorphism (`allOf`, `anyOf`, `oneOf`).
29-
- **XML Object**.
30-
31-
## Goal
32-
33-
Achieve 100% compliance with OpenAPI 3.2.0 by progressively adding AST mapping for missing components and expanding the Intermediate Representation (IR) to natively support all 3.2.0 features.
3+
This project aims to be 100% compliant with the OpenAPI 3.2.0 specification.
4+
Current status: Partial compliance.

DEVELOPING.md

Lines changed: 7 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,11 @@
1-
# Developing `cdd-python-client`
1+
# Developing
22

3-
This project is a compiler converting between OpenAPI 3.2.0 and native Python code using static analysis (`libcst`).
4-
5-
## Getting Started
6-
7-
1. **Install Base Requirements**:
8-
```sh
9-
make install_base
10-
```
11-
12-
2. **Install Local Dependencies**:
13-
```sh
14-
make install_deps
15-
```
16-
17-
3. **Running Tests**:
18-
Ensure 100% test coverage!
19-
```sh
20-
make test
21-
```
22-
23-
4. **Building Documentation**:
24-
Ensure 100% doc coverage!
25-
```sh
26-
make build_docs
27-
```
28-
29-
## Directory Structure
30-
31-
We use a modular architecture organized by parsing and emitting logic for specific features:
3+
To develop `cdd-python-client`, you will need Python 3.9+.
324

5+
```bash
6+
make install_deps
7+
make test
8+
make build
339
```
34-
src/openapi_client/
35-
├── classes/ # Parsing & emitting class definitions
36-
├── docstrings/ # Parsing & emitting Python docstrings
37-
├── functions/ # Parsing & emitting function signatures
38-
├── mocks/ # Generating mock servers
39-
├── openapi/ # Parsing & emitting OpenAPI JSON
40-
├── routes/ # Parsing & emitting routing configurations
41-
└── tests/ # Generating unit tests
42-
```
43-
44-
Each subdirectory contains:
45-
- `parse.py`: Extracts information from Python AST or OpenAPI dicts to populate the unified Intermediate Representation (IR).
46-
- `emit.py`: Uses the unified IR to generate target code (Python AST) or OpenAPI JSON.
47-
- `__init__.py`
48-
49-
## Updating the AST
50-
51-
Whenever a mock is edited, the changes should mirror into `routes` and `openapi`, ensuring the generated specs are completely synchronized.
5210

53-
## Pre-commit Hooks
54-
55-
We use `pre-commit` to ensure tests and linting run before commits. Install via:
56-
57-
```sh
58-
pre-commit install
59-
```
11+
We aim for 100% doc coverage and 100% test coverage on all PRs.

Makefile

Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,92 @@
1-
.PHONY: install_base install_deps build_docs build test run help all build_wasm
1+
.PHONY: install_base install_deps build_docs build test run help all build_wasm build_docker run_docker
22

3-
# Default target
43
.DEFAULT_GOAL := help
54

5+
# Extract arguments for build, build_docs, run
6+
ifeq ($(firstword $(MAKECMDGOALS)),build)
7+
BIN_DIR := $(word 2,$(MAKECMDGOALS))
8+
ifeq ($(BIN_DIR),)
9+
BIN_DIR := dist
10+
else
11+
$(eval $(BIN_DIR):;@:)
12+
endif
13+
endif
14+
15+
ifeq ($(firstword $(MAKECMDGOALS)),build_docs)
16+
DOCS_DIR := $(word 2,$(MAKECMDGOALS))
17+
ifeq ($(DOCS_DIR),)
18+
DOCS_DIR := docs
19+
else
20+
$(eval $(DOCS_DIR):;@:)
21+
endif
22+
endif
23+
24+
ifeq ($(firstword $(MAKECMDGOALS)),run)
25+
RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
26+
$(eval $(RUN_ARGS):;@:)
27+
endif
28+
629
install_base:
7-
@echo "Installing base dependencies..."
8-
uv python install
9-
uv pip install --upgrade pip build hatchling
10-
uv pip install -e ".[dev]"
30+
@echo "Installing base tools (Python 3)"
31+
@if [ -x "$$(command -v apt-get)" ]; then sudo apt-get update && sudo apt-get install -y python3 python3-pip python3-venv; fi
32+
@if [ -x "$$(command -v brew)" ]; then brew install python; fi
33+
@if [ -x "$$(command -v dnf)" ]; then sudo dnf install -y python3 python3-pip; fi
34+
1135
install_deps:
12-
@echo "Installing local dependencies..."
13-
uv pip install -e ".[dev]"
36+
pip install uv || pip3 install uv
37+
uv venv || python3 -m venv .venv
38+
uv pip install -e ".[dev]" || pip install -e ".[dev]"
1439

1540
build_docs:
16-
@echo "Building docs..."
17-
@mkdir -p $(if $(word 2, $(MAKECMDGOALS)),$(word 2, $(MAKECMDGOALS)),docs)
18-
uv run python -m pydoc -w src/openapi_client/cli.py
19-
@mv cli.html $(if $(word 2, $(MAKECMDGOALS)),$(word 2, $(MAKECMDGOALS)),docs)/
41+
@echo "Building docs in $(DOCS_DIR)"
42+
@mkdir -p $(DOCS_DIR)
43+
.venv/bin/python3 -m pdoc src/openapi_client -o $(DOCS_DIR) || pdoc src/openapi_client -o $(DOCS_DIR)
2044

2145
build:
22-
@echo "Building CLI binary (wheel)..."
23-
uv run python -m build --wheel --outdir $(if $(word 2, $(MAKECMDGOALS)),$(word 2, $(MAKECMDGOALS)),dist)
24-
@echo "Build complete."
46+
@echo "Building binary/package in $(BIN_DIR)"
47+
@mkdir -p $(BIN_DIR)
48+
.venv/bin/python3 -m build --wheel --outdir $(BIN_DIR) || python3 -m build --wheel --outdir $(BIN_DIR)
2549

2650
build_wasm:
27-
@echo "Building WASM..."
28-
@echo "WASM build using Emscripten. See WASM.md."
51+
@echo "Building WASM to dist/wasm"
52+
@mkdir -p dist/wasm
53+
@echo "A full CPython WASM standalone build requires Pyodide. Stubbed for now." > dist/wasm/README.txt
2954

3055
test:
31-
@echo "Running tests..."
32-
uv run pytest tests/ --cov=src/openapi_client --cov-report=term-missing
56+
.venv/bin/python3 -m pytest tests/ || pytest tests/
3357

3458
run: build
35-
@echo "Running CLI..."
36-
uv run cdd-python $(filter-out $@,$(MAKECMDGOALS))
59+
.venv/bin/python3 -m openapi_client.cli $(RUN_ARGS) || cdd-python $(RUN_ARGS)
3760

3861
help:
39-
@echo "Available commands:"
40-
@echo " install_base : install language runtime and anything else relevant"
62+
@echo "Available tasks:"
63+
@echo " install_base : install language runtime (Python 3)"
4164
@echo " install_deps : install local dependencies"
42-
@echo " build_docs : build the API docs and put them in the 'docs' directory"
43-
@echo " build : build the CLI binary"
44-
@echo " build_wasm : build the WASM binary"
65+
@echo " build_docs : build the API docs [dir]"
66+
@echo " build : build the CLI [dir]"
4567
@echo " test : run tests locally"
46-
@echo " run : run the CLI"
47-
@echo " help : show what options are available"
48-
@echo " all : show help text"
68+
@echo " run : run the CLI [args...]"
69+
@echo " build_wasm : build WASM output"
70+
@echo " build_docker : build Docker images"
71+
@echo " run_docker : run and test Docker containers"
72+
@echo " help : show this help text"
73+
@echo " all : show this help text"
4974

5075
all: help
5176

52-
%:
53-
@:
77+
build_docker:
78+
@echo "Building Docker images"
79+
docker build -t cdd-python-client-alpine -f alpine.Dockerfile .
80+
docker build -t cdd-python-client-debian -f debian.Dockerfile .
81+
82+
run_docker: build_docker
83+
docker run -d -p 8080:8080 --name cdd_alpine cdd-python-client-alpine
84+
sleep 3
85+
curl -X POST http://localhost:8080 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "missing", "id": 1}' || echo "container failed to respond"
86+
docker stop cdd_alpine
87+
docker rm cdd_alpine
88+
docker run -d -p 8080:8080 --name cdd_debian cdd-python-client-debian
89+
sleep 3
90+
curl -X POST http://localhost:8080 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "missing", "id": 1}' || echo "container failed to respond"
91+
docker stop cdd_debian
92+
docker rm cdd_debian

0 commit comments

Comments
 (0)