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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .envrc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ PROJECT_SCRATCH_IDENTITY="${PROJECT_SCRATCH_IDENTITY%/}"
PROJECT_SCRATCH="${HOME}/tmp/.flext-runtime${PROJECT_SCRATCH_IDENTITY}/scratch"
mkdir -p "${PROJECT_SCRATCH}"
if command -v chattr >/dev/null 2>&1; then
if [[ "$(stat -f -c %T "${PROJECT_SCRATCH}")" == "btrfs" ]]; then
project_scratch_fs="$(stat -f -c %T "${PROJECT_SCRATCH}" 2>/dev/null || true)"
if [[ "${project_scratch_fs}" == "btrfs" ]]; then
chattr +C "${PROJECT_SCRATCH}"
fi
fi
Expand Down Expand Up @@ -92,7 +93,7 @@ if [[ -v AGENTS_GAS_CITY_ROOT ]]; then
# Source: typed BeadsWorkspaceEnvironmentSpec shared by both direnv profiles.
# Environment sources are host-provided and optional: isolated CI must still
# activate while a city-connected host keeps their exports.
source_env_if_exists "$HOME/.config/environment.d/projects/agent-tools.envrc"
source_env_if_exists "${HOME}/.config/environment.d/projects/agent-tools.envrc"
# End SECTION: beads watched inputs

# === SECTION: canonical roots (managed) ===
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ jobs:
run: CI=Y make check

# Why (aihub-v01jg): CI=Y runs ONE HALF of the gate set
# (lint pyright silent-failure deferred-self-reference security markdown markdown-format markdown-code loc-cap boundary runtime-census namespace tier-whitelist index-declarations smells codemod layout canonical-alias direnv duplication); the complement
# (lint pyright silent-failure deferred-self-reference security markdown loc-cap boundary runtime-census namespace tier-whitelist index-declarations smells codemod layout canonical-alias direnv duplication); the complement
# (pyrefly mypy) is owned by
# CI=N and, without this step, ran on developer
# machines only. That split let real defects reach main twice: five
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ jobs:
pages: write
id-token: write
environment:
name: ${{ 'github-pages' }}
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,6 @@ CLAUDE.local.md
!.github/workflows/ci.yml
!.github/workflows/docs.yml
!.github/workflows/release.yml
!.github/workflows/_fragments/
!.github/workflows/_fragments/cross_repo_dependency_credential
!.github/workflows/_fragments/testmon_cache
!.qlty/
!.qlty/qlty.toml
!.vscode/settings.json
Expand Down
12 changes: 6 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1037,10 +1037,10 @@ _builtin-self-test: _builtin_require_environment

_builtin-self-check: _builtin_require_environment
@set -eu; \
gates="lint,pyrefly,mypy,pyright,silent-failure,deferred-self-reference,security,markdown,markdown-format,markdown-code,loc-cap,boundary,runtime-census,namespace,tier-whitelist,index-declarations,smells,codemod,layout,canonical-alias,direnv,duplication"; \
gates="lint,pyrefly,mypy,pyright,silent-failure,deferred-self-reference,security,markdown,loc-cap,boundary,runtime-census,namespace,tier-whitelist,index-declarations,smells,codemod,layout,canonical-alias,direnv,duplication"; \
if [ "$(strip $(CI))" = "Y" ]; then \
gates="lint,pyright,silent-failure,deferred-self-reference,security,markdown,markdown-format,markdown-code,loc-cap,boundary,runtime-census,namespace,tier-whitelist,index-declarations,smells,codemod,layout,canonical-alias,direnv,duplication"; \
printf 'INFO: CI=Y runs check gates: lint pyright silent-failure deferred-self-reference security markdown markdown-format markdown-code loc-cap boundary runtime-census namespace tier-whitelist index-declarations smells codemod layout canonical-alias direnv duplication\n'; \
gates="lint,pyright,silent-failure,deferred-self-reference,security,markdown,loc-cap,boundary,runtime-census,namespace,tier-whitelist,index-declarations,smells,codemod,layout,canonical-alias,direnv,duplication"; \
printf 'INFO: CI=Y runs check gates: lint pyright silent-failure deferred-self-reference security markdown loc-cap boundary runtime-census namespace tier-whitelist index-declarations smells codemod layout canonical-alias direnv duplication\n'; \
fi; \
if [ -z "$$gates" ]; then \
printf 'ERROR: no check gates remain after CI=Y filtering\n' >&2; \
Expand Down Expand Up @@ -1075,10 +1075,10 @@ _builtin_build_artifacts:
# make.ci.local_check_gates.
_builtin_check_all: _builtin_require_environment
@set -eu; \
gates="lint,pyrefly,mypy,pyright,silent-failure,deferred-self-reference,security,markdown,markdown-format,markdown-code,loc-cap,boundary,runtime-census,namespace,tier-whitelist,index-declarations,smells,codemod,layout,canonical-alias,direnv,duplication"; \
gates="lint,pyrefly,mypy,pyright,silent-failure,deferred-self-reference,security,markdown,loc-cap,boundary,runtime-census,namespace,tier-whitelist,index-declarations,smells,codemod,layout,canonical-alias,direnv,duplication"; \
if [ "$(strip $(CI))" = "Y" ]; then \
gates="lint,pyright,silent-failure,deferred-self-reference,security,markdown,markdown-format,markdown-code,loc-cap,boundary,runtime-census,namespace,tier-whitelist,index-declarations,smells,codemod,layout,canonical-alias,direnv,duplication"; \
printf 'INFO: CI=Y runs check gates: lint pyright silent-failure deferred-self-reference security markdown markdown-format markdown-code loc-cap boundary runtime-census namespace tier-whitelist index-declarations smells codemod layout canonical-alias direnv duplication\n'; \
gates="lint,pyright,silent-failure,deferred-self-reference,security,markdown,loc-cap,boundary,runtime-census,namespace,tier-whitelist,index-declarations,smells,codemod,layout,canonical-alias,direnv,duplication"; \
printf 'INFO: CI=Y runs check gates: lint pyright silent-failure deferred-self-reference security markdown loc-cap boundary runtime-census namespace tier-whitelist index-declarations smells codemod layout canonical-alias direnv duplication\n'; \
fi; \
if [ -z "$$gates" ]; then \
printf 'ERROR: no check gates remain after CI=Y filtering\n' >&2; \
Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ FLEXT API - High-Performance REST API with FastAPI

## Module Map

::: flext_api options: members: false show_root_heading: false show_root_toc_entry:
false show_source: false
::: flext_api

options:
members: false
show_root_heading: false
show_root_toc_entry: false
show_source: false

## Collection Rules

Expand Down Expand Up @@ -57,9 +62,10 @@ verification).
## Quality Gates

Canonical `make` verbs (`gen`, `check`, `test`, `fmt`, `docs`) execute their declared
operations directly — see
[`/flext/AGENTS.md`](https://github.com/flext-sh/flext/blob/0.12.0-dev/AGENTS.md)
`Build & Test` and `Required Python quality gates`.
operations directly.

See [`/flext/AGENTS.md`](https://github.com/flext-sh/flext/blob/0.12.0-dev/AGENTS.md)
for the build, test, and Python quality gates.

## Governance Pointer

Expand Down
10 changes: 5 additions & 5 deletions docs/api-reference/generated/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
- Version: `0.12.0`
- Description: FLEXT API - High-Performance REST API with FastAPI
- Doc summary: Flext Api package.
- Classifiers: `Development Status :: 3 - Alpha`, `Framework :: FastAPI`,
`Intended Audience :: Developers`, `Operating System :: OS Independent`,
`Programming Language :: Python :: 3 :: Only`,
`Programming Language :: Python :: 3.13` (+3 more)
- Classifiers: Development Status :: 3 - Alpha, Framework :: FastAPI, Intended Audience
:: Developers, Operating System :: OS Independent, Programming Language :: Python :: 3
:: Only, Programming Language :: Python :: 3.13, Topic :: Internet :: WWW/HTTP :: HTTP
Servers, Topic :: Software Development :: Libraries :: Python Modules, Typing :: Typed
- Project class: `domain`
- Keywords: `enterprise`, `fastapi`, `flext`, `http`, `rest`, `typed`
- Main facades: `FlextApi`, `FlextApiAsyncClient`, `FlextApiCli`, `FlextApiClient`,
Expand All @@ -24,7 +24,7 @@
- Alias exports: `c`, `d`, `e`, `h`, `m`, `p`, `r`, `s`, `t`, `u`, `x`
- Public symbol exports: `FlextApi`, `FlextApiAsyncClient`, `FlextApiCli`,
`FlextApiClient`, `FlextApiClientBase`, `FlextApiConfig`, `FlextApiConstants`,
`FlextApiModels`, `FlextApiProtocols`, `FlextApiServiceBase` (+5 more)
`FlextApiModels`, `FlextApiProtocols`, `FlextApiServiceBase` (+12 more)
- Exported module shortcuts: `api`, `services`
- Generated module pages: `12`

Expand Down
8 changes: 6 additions & 2 deletions docs/api-reference/generated/public-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@

<!-- AUTO-GENERATED — DO NOT EDIT MANUALLY -->

::: flext_api options: show_root_heading: true show_root_full_path: false show_source:
false
::: flext_api

options:
show_root_heading: true
show_root_full_path: false
show_source: false
20 changes: 11 additions & 9 deletions docs/api/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ executes validated `m.Api.HttpRequest` instances through `request(...)`. It does
expose `get/post/put/delete/patch` directly; those methods live on the `FlextApi`
facade.

````python notest
```python notest
from __future__ import annotations

from flext_api import FlextApiClient, FlextApiSettings, c, m, p
Expand Down Expand Up @@ -62,7 +62,8 @@ if result.success:
print(response.body)
else:
print(f"Transport error: {result.error}")
```
```

**Key Features:**

- Type-safe HTTP operations via Pydantic models
Expand All @@ -80,8 +81,8 @@ else:

### FlextApi - Unified Facade

`FlextApi` is the public entry point. It creates and owns a `FlextApiClient`
lazily and exposes convenience methods for each HTTP verb.
`FlextApi` is the public entry point. It creates and owns a `FlextApiClient` lazily and
exposes convenience methods for each HTTP verb.

```python
from __future__ import annotations
Expand All @@ -102,7 +103,8 @@ if result.success:
print(f"Body: {response.body}")
else:
print(f"Error: {result.error}")
```
```

### HTTP Methods

All methods return `p.Result[m.Api.HttpResponse]`.
Expand All @@ -121,7 +123,7 @@ result: p.Result[m.Api.HttpResponse] = api.get("/users")
result = api.get("/users", request_kwargs={"params": {"limit": 10, "offset": 0}})

result = api.get("/users", headers={"Accept": "application/json"})
````
```

**POST/PUT/PATCH/DELETE Requests:**

Expand Down Expand Up @@ -268,7 +270,7 @@ print(timeout_result.unwrap())

### Complete HTTP Client Example

````python notest
```python notest
from __future__ import annotations

from flext_api import FlextApi, FlextApiSettings, m, p, r
Expand Down Expand Up @@ -367,8 +369,8 @@ if update_result.success:
delete_result = client.delete_user(1)
if delete_result.success:
print(f"Deleted user, status: {delete_result.unwrap().status_code}")
```
```

This core API provides the public HTTP surface for `flext-api`: typed settings, a
validated request model, a monadic response model, and the `FlextApi` facade for
convenient HTTP verbs.
````
21 changes: 10 additions & 11 deletions docs/architecture/decisions/002-railway-pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Every public method returns `p.Result[T]`. Operations are composed using `flat_m

### Option 1: Traditional Exceptions

````python notest
```python notest
from __future__ import annotations

import httpx
Expand All @@ -75,7 +75,8 @@ def get_user(user_id: int) -> dict:
response = httpx.get(f"https://api.example.com/users/{user_id}")
response.raise_for_status()
return response.json()
```
```

### Option 2: Result Pattern (Custom Implementation)

```python
Expand All @@ -89,7 +90,8 @@ class Result:
self.success = success
self.value = value
self.error = error
```
```

### Option 3: Hybrid Approach

- **Description**: Use railway pattern internally but expose traditional APIs
Expand Down Expand Up @@ -145,11 +147,11 @@ api = FakeUserApi(runtime_settings=FlextApiSettings(base_url="https://example.co
result = api.fetch_user(123)
assert result.success
assert result.unwrap().body["name"] == "Alice"
````
```

### Usage in Application Code

````python notest
```python notest
from __future__ import annotations

from flext_api import FlextApi, FlextApiSettings, m, p, r
Expand All @@ -176,7 +178,8 @@ if result.success:
print(f"Found profile: {profile['bio']}")
else:
print(f"Error: {result.error}")
```
```

### Testing Railway Code

```python
Expand Down Expand Up @@ -216,7 +219,7 @@ def test_get_user_not_found():

test_get_user_success()
test_get_user_not_found()
````
```

## Migration Strategy

Expand Down Expand Up @@ -249,7 +252,3 @@ r[str].fail("JSON parsing failed: invalid response format")

- [Railway-Oriented Programming](https://fsharpforfunandprofit.com/rop/)
- GitHub Issue: #156 - Railway Pattern Implementation

```

```
16 changes: 11 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ docstrings.

## Public Surface Summary

::: flext_api options: members: false show_root_heading: false show_root_toc_entry:
false show_source: false
::: flext_api

options:
members: false
show_root_heading: false
show_root_toc_entry: false
show_source: false

## Collection Rules

Expand All @@ -42,9 +47,10 @@ verification).
## Quality Gates

Canonical `make` verbs (`gen`, `check`, `test`, `fmt`, `docs`) execute their declared
operations directly — see
[`/flext/AGENTS.md`](https://github.com/flext-sh/flext/blob/0.12.0-dev/AGENTS.md)
`Build & Test` and `Required Python quality gates`.
operations directly.

See [`/flext/AGENTS.md`](https://github.com/flext-sh/flext/blob/0.12.0-dev/AGENTS.md)
for the build, test, and Python quality gates.

## Governance Pointer

Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ theme:
icon: material/weather-sunny
name: Switch to light mode


plugins:
- search
- autorefs
Expand Down
31 changes: 29 additions & 2 deletions src/flext_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,17 @@
from .cli import FlextApiCli
from .constants import FlextApiConstants, FlextApiConstants as c
from .models import FlextApiModels, FlextApiModels as m
from .protocols import FlextApiProtocols, FlextApiProtocols as p
from .protocols import (
FlextApiProtocols,
FlextApiProtocols as p,
HttpxAsyncClient,
HttpxClient,
HttpxHTTPError,
HttpxHTTPStatusError,
HttpxRequestError,
HttpxResponse,
HttpxTimeoutException,
)
from .services.async_client import FlextApiAsyncClient
from .services.base_client import FlextApiClientBase
from .services.client import FlextApiClient
Expand All @@ -50,6 +60,13 @@
"FlextApiSettings",
"FlextApiTypes",
"FlextApiUtilities",
"HttpxAsyncClient",
"HttpxClient",
"HttpxHTTPError",
"HttpxHTTPStatusError",
"HttpxRequestError",
"HttpxResponse",
"HttpxTimeoutException",
"__author__",
"__author_email__",
"__description__",
Expand Down Expand Up @@ -85,7 +102,17 @@
".cli": ("FlextApiCli",),
".constants": ("FlextApiConstants", "c"),
".models": ("FlextApiModels", "m"),
".protocols": ("FlextApiProtocols", "p"),
".protocols": (
"FlextApiProtocols",
"HttpxAsyncClient",
"HttpxClient",
"HttpxHTTPError",
"HttpxHTTPStatusError",
"HttpxRequestError",
"HttpxResponse",
"HttpxTimeoutException",
"p",
),
".services": ("services",),
".services.async_client": ("FlextApiAsyncClient",),
".services.base_client": ("FlextApiClientBase",),
Expand Down
14 changes: 13 additions & 1 deletion src/flext_api/_protocols/transports.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from __future__ import annotations

from typing import TYPE_CHECKING, override
from typing import TYPE_CHECKING, ClassVar, Protocol, override, runtime_checkable

import httpx

Expand All @@ -29,6 +29,18 @@
class FlextApiProtocolsTransports:
"""FLEXT API transport implementations."""

@runtime_checkable
class Httpx(Protocol):
"""Protocol namespace for owner-derived HTTP status contracts.

Runtime classes and exceptions are published as module-level
``Httpx*`` class-object re-exports. Keeping class identity out of this
protocol namespace preserves both construction/isinstance semantics and
the Protocol/namespace census contract.
"""

CONFLICT: ClassVar[int] = int(httpx.codes.CONFLICT)

# Why: no member here carries @abstractmethod (TransportPlugin's Protocol
# bodies are structural, not abstract), so an explicit ABC base added
# nothing but tripped pyrefly's direct-abstract-base-instantiation check
Expand Down
Loading
Loading