pyinternet 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyinternet-0.1.0/PKG-INFO +365 -0
- pyinternet-0.1.0/README.md +351 -0
- pyinternet-0.1.0/cache/__init__.py +173 -0
- pyinternet-0.1.0/cache/_et.py +143 -0
- pyinternet-0.1.0/cache/_logs.py +63 -0
- pyinternet-0.1.0/cache/_msg.py +9 -0
- pyinternet-0.1.0/cache/cache.py +101 -0
- pyinternet-0.1.0/cache/connect.py +62 -0
- pyinternet-0.1.0/cache/ctx.py +231 -0
- pyinternet-0.1.0/cache/handler.py +571 -0
- pyinternet-0.1.0/cache/pubsub.py +78 -0
- pyinternet-0.1.0/claim/__init__.py +95 -0
- pyinternet-0.1.0/claim/_et.py +10 -0
- pyinternet-0.1.0/claim/_mem.py +55 -0
- pyinternet-0.1.0/claim/_msg.py +10 -0
- pyinternet-0.1.0/claim/_timezone.py +39 -0
- pyinternet-0.1.0/claim/_utility.py +15 -0
- pyinternet-0.1.0/claim/claim.py +464 -0
- pyinternet-0.1.0/envar/__init__.py +65 -0
- pyinternet-0.1.0/envar/deprecate.py +80 -0
- pyinternet-0.1.0/envar/envar.py +215 -0
- pyinternet-0.1.0/event/__init__.py +117 -0
- pyinternet-0.1.0/event/_et.py +38 -0
- pyinternet-0.1.0/event/_logs.py +65 -0
- pyinternet-0.1.0/event/_loop.py +32 -0
- pyinternet-0.1.0/event/_msg.py +15 -0
- pyinternet-0.1.0/event/_timezone.py +28 -0
- pyinternet-0.1.0/event/_utility.py +25 -0
- pyinternet-0.1.0/event/connect.py +74 -0
- pyinternet-0.1.0/event/emiter.py +126 -0
- pyinternet-0.1.0/event/event.py +119 -0
- pyinternet-0.1.0/event/handler.py +266 -0
- pyinternet-0.1.0/event/message.py +112 -0
- pyinternet-0.1.0/event/workstate.py +52 -0
- pyinternet-0.1.0/middleware/__init__.py +122 -0
- pyinternet-0.1.0/middleware/_et.py +33 -0
- pyinternet-0.1.0/middleware/_stdrout.py +33 -0
- pyinternet-0.1.0/middleware/_timezone.py +26 -0
- pyinternet-0.1.0/middleware/_utility.py +32 -0
- pyinternet-0.1.0/middleware/autentication.py +104 -0
- pyinternet-0.1.0/middleware/autorization.py +147 -0
- pyinternet-0.1.0/middleware/cors.py +48 -0
- pyinternet-0.1.0/middleware/ephemeral.py +71 -0
- pyinternet-0.1.0/middleware/logger.py +190 -0
- pyinternet-0.1.0/middleware/recoverer.py +54 -0
- pyinternet-0.1.0/middleware/request_id.py +90 -0
- pyinternet-0.1.0/middleware/telemetry.py +526 -0
- pyinternet-0.1.0/middleware/wrap_write.py +31 -0
- pyinternet-0.1.0/nethttp/__init__.py +167 -0
- pyinternet-0.1.0/pyinternet.egg-info/PKG-INFO +365 -0
- pyinternet-0.1.0/pyinternet.egg-info/SOURCES.txt +57 -0
- pyinternet-0.1.0/pyinternet.egg-info/dependency_links.txt +1 -0
- pyinternet-0.1.0/pyinternet.egg-info/requires.txt +7 -0
- pyinternet-0.1.0/pyinternet.egg-info/top_level.txt +7 -0
- pyinternet-0.1.0/pyproject.toml +25 -0
- pyinternet-0.1.0/response/__init__.py +68 -0
- pyinternet-0.1.0/response/_et.py +65 -0
- pyinternet-0.1.0/response/response.py +285 -0
- pyinternet-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyinternet
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python port of github.com/celsiainternet/elvis-python packages (cache, envar, event, nethttp, response, claim, middleware)
|
|
5
|
+
Author-email: Cesar Galvis Leon <cgalvisleon@gmail.com>
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: redis>=5.0
|
|
9
|
+
Requires-Dist: python-dotenv>=1.0
|
|
10
|
+
Requires-Dist: nats-py>=2.6
|
|
11
|
+
Requires-Dist: PyJWT>=2.8
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
14
|
+
|
|
15
|
+
# elvis (Python)
|
|
16
|
+
|
|
17
|
+
Port a Python de paquetes Go de [`github.com/celsiainternet/elvis`](../elvis).
|
|
18
|
+
Los nombres de funciones y el orden/tipo de sus parámetros replican el
|
|
19
|
+
original 1:1, para que el código traducido desde Go se lea igual en Python.
|
|
20
|
+
|
|
21
|
+
Paquetes portados hasta ahora:
|
|
22
|
+
|
|
23
|
+
- **`cache`** ← [`elvis/cache`](../elvis/cache)
|
|
24
|
+
- **`envar`** ← [`elvis/envar`](../elvis/envar)
|
|
25
|
+
- **`event`** ← [`elvis/event`](../elvis/event)
|
|
26
|
+
- **`nethttp`** — no es un port de un paquete elvis; ver su sección abajo.
|
|
27
|
+
- **`response`** ← [`elvis/response`](../elvis/response)
|
|
28
|
+
- **`claim`** ← [`elvis/claim`](../elvis/claim)
|
|
29
|
+
- **`middleware`** ← [`elvis/middleware`](../elvis/middleware)
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import envar
|
|
33
|
+
import cache
|
|
34
|
+
import event
|
|
35
|
+
|
|
36
|
+
cache.Load() # usa REDIS_HOST / REDIS_PASSWORD / REDIS_DB
|
|
37
|
+
cache.Set("greeting", "hello", 60) # segundos, no time.Duration
|
|
38
|
+
print(cache.Get("greeting", "")) # "hello"
|
|
39
|
+
cache.Close()
|
|
40
|
+
|
|
41
|
+
print(envar.GetInt(10, "REDIS_POOL_SIZE"))
|
|
42
|
+
|
|
43
|
+
event.Load() # NATS_HOST / NATS_USER / NATS_PASSWORD
|
|
44
|
+
event.Subscribe("orders:created", lambda msg: print(msg.Data))
|
|
45
|
+
event.Publish("orders:created", {"order_id": "1"})
|
|
46
|
+
event.Close()
|
|
47
|
+
|
|
48
|
+
import claim
|
|
49
|
+
import nethttp
|
|
50
|
+
import response
|
|
51
|
+
import middleware
|
|
52
|
+
|
|
53
|
+
token = claim.NewToken("user-1", "myapp", "Jane", "jane", "web", 3600) # 1h, in seconds
|
|
54
|
+
|
|
55
|
+
def hello(w: nethttp.ResponseWriter, r: nethttp.Request) -> None:
|
|
56
|
+
response.JSON(w, r, 200, {"hello": claim.ClientId(r)})
|
|
57
|
+
|
|
58
|
+
chain = middleware.RequestID(middleware.Logger(middleware.Recoverer(middleware.Autentication(hello))))
|
|
59
|
+
|
|
60
|
+
r = nethttp.Request(method="GET", path="/hello")
|
|
61
|
+
r.header.Set("Authorization", f"Bearer {token}")
|
|
62
|
+
w = nethttp.ResponseWriter()
|
|
63
|
+
chain(w, r)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Instalación
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cd elvis
|
|
70
|
+
pip install -e .
|
|
71
|
+
# o, sin instalar el paquete:
|
|
72
|
+
pip install -r requirements.txt
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## `cache`
|
|
76
|
+
|
|
77
|
+
Port de [`elvis/cache`](../elvis/cache) (`cache.go`, `connect.go`, `ctx.go`,
|
|
78
|
+
`handler.go`, `pubsub.go`).
|
|
79
|
+
|
|
80
|
+
### Variables de entorno
|
|
81
|
+
|
|
82
|
+
| Variable | Uso | Default |
|
|
83
|
+
| ---------------------- | -------------------------------------------------------- | ------- |
|
|
84
|
+
| `REDIS_HOST` | host:puerto de Redis | — |
|
|
85
|
+
| `REDIS_PASSWORD` | password de Redis | — |
|
|
86
|
+
| `REDIS_DB` | número de base de datos | `0` |
|
|
87
|
+
| `REDIS_POOL_SIZE` | tamaño del pool de conexiones | `10` |
|
|
88
|
+
| `REDIS_MIN_IDLE_CONNS` | leída por paridad; redis-py no tiene equivalente directo | `2` |
|
|
89
|
+
|
|
90
|
+
### Mapeo Go -> Python
|
|
91
|
+
|
|
92
|
+
| Archivo Go | Funciones | Archivo Python |
|
|
93
|
+
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
|
|
94
|
+
| `cache.go` | `Load`, `Close`, `IsLoad`, `HealthCheck`, `FromId`, tipo `Conn` | `cache.py` |
|
|
95
|
+
| `connect.go` | `ConnectTo` | `connect.py` |
|
|
96
|
+
| `ctx.go` | `SetCtx`, `ExpireCtx`, `GetCtx`, `ExistsCtx`, `DeleteCtx`, `IncrCtx`, `DecrCtx`, `LPushCtx`, `LRemCtx`, `LRangeCtx`, `LTrimCtx`, `HSetCtx`, `HGetCtx`, `HDeleteCtx`, `IncrSetTTLScript`, `DecrKeepTTLScript` | `ctx.py` |
|
|
97
|
+
| `handler.go` | `GenId`, `GenKey`, `Set`, `Get`, `Exists`, `Delete`, `Expire`, `Incr`, `Decr`, `LPush`, `LRem`, `LRange`, `LTrim`, `SetH/D/W/M/Y`, `Empty`, `More`, `HSet`, `HGet`, `HSetAtrib`, `HGetAtrib`, `HDelete`, `SetVerify`, `GetVerify`, `DeleteVerify`, `AllCache`, `GetInt`, `GetInt64`, `GetFloat`, `GetBool`, `GetJson`, `GetItem`, `GetItems`, `IsNil`, `HandlerAll`, `HandlerGet`, `HandlerDelete` | `handler.py` |
|
|
98
|
+
| `pubsub.go` | tipo `Message`, métodos `Pub`, `Sub`, `Unsub` en `Conn` | `pubsub.py` |
|
|
99
|
+
| `et.Json/Item/Items/List` (dependencia) | tipos mínimos usados por `Set`/`GetJson`/`GetItem`/`GetItems`/`AllCache` | `_et.py` |
|
|
100
|
+
|
|
101
|
+
### Desviaciones deliberadas frente al original en Go
|
|
102
|
+
|
|
103
|
+
- **Manejo de errores**: Go devuelve `(valor, error)`; Python lanza excepciones
|
|
104
|
+
(`RuntimeError` cuando no hay conexión, igual que
|
|
105
|
+
`msg.ERR_NOT_CACHE_SERVICE`). El resto de la firma (nombre y parámetros) no cambia.
|
|
106
|
+
- **`second time.Duration` -> `second: float`**: en Python se pasan segundos
|
|
107
|
+
como `float`/`int` en vez de `time.Duration`.
|
|
108
|
+
- **`def` como nombre de parámetro**: es palabra reservada en Python; se usa
|
|
109
|
+
`def_` (documentado en cada docstring).
|
|
110
|
+
- **`context.Context`**: las funciones `*Ctx` conservan `ctx` como primer
|
|
111
|
+
parámetro por fidelidad de firma, pero no se usa — pásese `None`. El
|
|
112
|
+
cliente `redis-py` síncrono no necesita cancelación/deadlines explícitos.
|
|
113
|
+
- **`cache.conn`**: en Go es una variable de paquete no exportada
|
|
114
|
+
(minúscula); en Python tampoco se expone en `cache.__all__`. Use
|
|
115
|
+
`IsLoad()` / `HealthCheck()`.
|
|
116
|
+
- **`HandlerAll` / `HandlerGet` / `HandlerDelete`**: en Go son
|
|
117
|
+
`http.HandlerFunc` atados a chi/net-http. Python no tiene un framework web
|
|
118
|
+
dominante único, así que aquí reciben un `dict` de query params (el
|
|
119
|
+
equivalente de `response.GetQuery(r)`) y devuelven `(status_code, body)`
|
|
120
|
+
en vez de escribir en un `http.ResponseWriter`.
|
|
121
|
+
|
|
122
|
+
## `envar`
|
|
123
|
+
|
|
124
|
+
Port completo de [`elvis/envar`](../elvis/envar) (`envar.go` +
|
|
125
|
+
`deprecate.go`, incluidas las funciones marcadas como deprecadas en Go —
|
|
126
|
+
se portaron igual, por completitud).
|
|
127
|
+
|
|
128
|
+
### Mapeo Go -> Python
|
|
129
|
+
|
|
130
|
+
| Archivo Go | Funciones | Archivo Python |
|
|
131
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- |
|
|
132
|
+
| `envar.go` | interfaz `Store`, `Load`, `MetaSet`, `SetStr`, `SetInt`, `SetInt64`, `SetBool`, `UpSetStr`, `UpSetInt`, `UpSetFloat`, `UpSetBool`, `GetStr`, `GetInt`, `GetInt64`, `GetFloat64`, `GetBool` | `envar.py` |
|
|
133
|
+
| `deprecate.go` | `SetvarStr`, `SetvarInt`, `SetvarBool`, `EnvarStr`, `EnvarInt`, `EnvarInt64`, `EnvarNumber`, `EnvarBool` | `deprecate.py` |
|
|
134
|
+
|
|
135
|
+
`cache` consume este paquete directamente (`cache/connect.py` hace
|
|
136
|
+
`import envar`) en vez de duplicar su propia copia — igual que en Go, donde
|
|
137
|
+
`elvis/cache` importa `elvis/envar`.
|
|
138
|
+
|
|
139
|
+
### Desviaciones deliberadas frente al original en Go
|
|
140
|
+
|
|
141
|
+
- **Autoload de `.env`**: Go carga `.env` mediante el import en blanco
|
|
142
|
+
`_ "github.com/joho/godotenv/autoload"`; aquí se replica con
|
|
143
|
+
`python-dotenv`'s `load_dotenv()`, ejecutado una vez al importar `envar`.
|
|
144
|
+
- **`Store` interface**: en Go es una `interface`; en Python es un
|
|
145
|
+
`typing.Protocol` con el mismo método `Get(name, _default) -> str`. La
|
|
146
|
+
firma real de `GetStr`/`GetBool` invoca `store.Get(_default, _var)` — el
|
|
147
|
+
mismo orden (aparentemente invertido respecto a los nombres del
|
|
148
|
+
parámetro) que el código Go original, replicado tal cual.
|
|
149
|
+
- **`EnvarStr`/`EnvarInt`/... (deprecadas)** leen `os.environ` directo y
|
|
150
|
+
**no** consultan el `Store` fijado con `Load()`, igual que en Go — a
|
|
151
|
+
diferencia de `GetStr` y compañía, que sí lo consultan.
|
|
152
|
+
|
|
153
|
+
## `event`
|
|
154
|
+
|
|
155
|
+
Port de [`elvis/event`](../elvis/event) (`event.go`, `connect.go`,
|
|
156
|
+
`emiter.go`, `handler.go`, `message.go`, `msg.go`, `workState.go`). Tiene
|
|
157
|
+
dos modos, igual que en Go:
|
|
158
|
+
|
|
159
|
+
- **Distribuido, vía NATS**: `event.Load()` / `event.Publish(channel, data)`
|
|
160
|
+
/ `event.Subscribe(channel, f)` / `event.Stack(channel, f)`.
|
|
161
|
+
- **Local, en el mismo proceso**: `event.On(channel, handler)` /
|
|
162
|
+
`event.Emit(channel, data)`, vía `EventEmiter` — no toca NATS.
|
|
163
|
+
|
|
164
|
+
### Variables de entorno
|
|
165
|
+
|
|
166
|
+
| Variable | Uso | Default |
|
|
167
|
+
| --------------- | ------------------------------------------------------------ | ---------------- |
|
|
168
|
+
| `NATS_HOST` | host/URL del servidor NATS | — |
|
|
169
|
+
| `NATS_USER` | usuario NATS | — |
|
|
170
|
+
| `NATS_PASSWORD` | password NATS | — |
|
|
171
|
+
| `STAGE` | usada por `Publish` para el prefijo `pipe:<stage>:<channel>` | `local` |
|
|
172
|
+
| `TIMEZONE` | zona horaria para timestamps (`created_at`, etc.) | `America/Bogota` |
|
|
173
|
+
|
|
174
|
+
### Mapeo Go -> Python
|
|
175
|
+
|
|
176
|
+
| Archivo Go | Funciones | Archivo Python |
|
|
177
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------- |
|
|
178
|
+
| `event.go` | constantes `EVENT*`, `Load`, `Close`, `Id`, `HealthCheck`, tipo `Conn` | `event.py` |
|
|
179
|
+
| `connect.go` | `ConnectTo` | `connect.py` |
|
|
180
|
+
| `emiter.go` | `Handler`, `EventEmiter`, `NewEventEmiter`, `On`, `Emit` | `emiter.py` |
|
|
181
|
+
| `message.go` | interfaz `Message`, `EvenMessage` (`Encode`, `ToString`, `ToJson`), `NewEvenMessage`, `DecodeMessage` | `message.py` |
|
|
182
|
+
| `workState.go` | `WorkStatus` (`String`), `WorkStatusPending/Accepted/Processing/Completed/Failed`, `ToWorkStatus` | `workstate.py` |
|
|
183
|
+
| `handler.go` | `Publish`, `Subscribe`, `Queue`, `Stack`, `Unsubscribe`, `Work`, `WorkState`, `Source`, `Log`, `Overflow`, `HttpEventWork` | `handler.py` |
|
|
184
|
+
| `msg.go` | constantes de error (`ERR_NOT_CONNECT`, `ERR_CHANNEL_REQUIRED`, ...) | `_msg.py` |
|
|
185
|
+
|
|
186
|
+
### Desviaciones deliberadas frente al original en Go
|
|
187
|
+
|
|
188
|
+
- **Cliente NATS asíncrono**: `nats.go` (usado en Go) es síncrono; el
|
|
189
|
+
cliente oficial en Python (`nats-py`) es exclusivamente `asyncio`. Para
|
|
190
|
+
que `Load`, `Publish`, `Subscribe`, etc. sigan siendo llamadas bloqueantes
|
|
191
|
+
normales (igual que en Go), cada `Conn` levanta un event loop en un hilo
|
|
192
|
+
de fondo (`event/_loop.py`) y cada llamada a NATS se despacha ahí vía
|
|
193
|
+
`asyncio.run_coroutine_threadsafe(...).result()`. Detalle interno, no
|
|
194
|
+
cambia la firma pública.
|
|
195
|
+
- **Manejo de errores mixto, igual que en Go**: `Publish` (y todo lo que
|
|
196
|
+
se apoya en ella: `Work`, `WorkState`, `Source`, `Log`, `Overflow`) hace
|
|
197
|
+
_no-op_ silencioso si no hay conexión — así se comporta el Go original
|
|
198
|
+
(`if conn == nil { return nil }`). En cambio `Subscribe`, `Queue`,
|
|
199
|
+
`Stack` y `Unsubscribe` lanzan `RuntimeError(ERR_NOT_CONNECT)`, tal como
|
|
200
|
+
el Go original devuelve `error` explícito en esos casos.
|
|
201
|
+
- **`go Publish(...)` → `threading.Thread(..., daemon=True).start()`**:
|
|
202
|
+
`WorkState`, `Source`, `Log` y `Overflow` disparan la publicación en un
|
|
203
|
+
hilo de fondo, igual que Go las dispara en una goroutine. `Work` en
|
|
204
|
+
cambio publica de forma síncrona, igual que en Go (no usa `go`).
|
|
205
|
+
- **`HttpEventWork`**: recibe un `dict` (equivalente de
|
|
206
|
+
`response.GetBody(r)`) y devuelve `(status_code, body)` en vez de
|
|
207
|
+
`http.ResponseWriter`/`*http.Request` — misma convención que
|
|
208
|
+
`cache.HandlerAll/Get/Delete`. Replica también el orden de validación
|
|
209
|
+
del original: `Work(...)` se ejecuta _antes_ de comprobar que `event` y
|
|
210
|
+
`data` no estén vacíos, así que ya se publicó el evento aunque la
|
|
211
|
+
respuesta termine siendo 400.
|
|
212
|
+
- **`et.Object(m)` en `EvenMessage.ToJson`**: en Go usa reflexión genérica;
|
|
213
|
+
aquí se construye el `dict` directamente a partir de los campos
|
|
214
|
+
conocidos de `EvenMessage` (mismo resultado, sin necesitar reflexión).
|
|
215
|
+
|
|
216
|
+
## `nethttp`
|
|
217
|
+
|
|
218
|
+
**No es un port de un paquete de `elvis`.** `claim`, `response` y
|
|
219
|
+
`middleware` en Go se construyen directamente sobre la librería estándar
|
|
220
|
+
(`*http.Request`, `http.ResponseWriter`, `http.Header`) en vez de definir
|
|
221
|
+
sus propios tipos de request/response — no hay un paquete `elvis/nethttp`
|
|
222
|
+
que portar. Python no tiene un único framework HTTP dominante, así que
|
|
223
|
+
este módulo hace las veces de esa porción de `net/http` que los tres
|
|
224
|
+
paquetes comparten: `Header` (case-insensitive), `Request`, `ResponseWriter`
|
|
225
|
+
(ya con `Status()`/`BytesWritten()` incorporados) y los códigos de estado
|
|
226
|
+
usados (`StatusOK`, `StatusUnauthorized`, etc. + `StatusText`). Conéctelo a
|
|
227
|
+
Flask/FastAPI/Django/WSGI/ASGI según se necesite. También define
|
|
228
|
+
`Handler`/`Middleware` como los equivalentes de `http.Handler` y
|
|
229
|
+
`func(http.Handler) http.Handler`.
|
|
230
|
+
|
|
231
|
+
## `response`
|
|
232
|
+
|
|
233
|
+
Port de [`elvis/response`](../elvis/response) (`response.go`), construido
|
|
234
|
+
sobre `nethttp.Request`/`nethttp.ResponseWriter`.
|
|
235
|
+
|
|
236
|
+
### Mapeo Go -> Python
|
|
237
|
+
|
|
238
|
+
| Función Go | Función Python |
|
|
239
|
+
| ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
240
|
+
| `ScanBody`, `ScanStr`, `ScanJson` | igual (`ScanBody` toma `bytes` en vez de `io.Reader`) |
|
|
241
|
+
| `GetBody`, `GetArray`, `GetQuery`, `GetHeader`, `GetParam` | igual |
|
|
242
|
+
| `WriteResponse`, `RESULT`, `ANY`, `JSON`, `ITEM`, `ITEMS` | igual |
|
|
243
|
+
| `HTTPError`, `HTTPAlert`, `Unauthorized`, `Forbidden`, `InternalServerError`, `PreconditionRequired` | igual |
|
|
244
|
+
| `Stream` | igual; `DataFunction` es `Callable[[page, rows], (ok, items)]` en vez de devolver `et.Items` |
|
|
245
|
+
| `HTTPApp` | **no portado** — sirve archivos estáticos vía `chi.Router`/`http.FileSystem`; es plomería de router/filesystem, no lógica de formateo de respuesta, y no hay un equivalente framework-agnóstico razonable |
|
|
246
|
+
|
|
247
|
+
## `claim`
|
|
248
|
+
|
|
249
|
+
Port de [`elvis/claim`](../elvis/claim) (`claim.go`, `msg.go`): emisión,
|
|
250
|
+
parseo y validación de JWT, respaldado por `cache` (Redis) para poder
|
|
251
|
+
invalidar tokens.
|
|
252
|
+
|
|
253
|
+
### Variables de entorno
|
|
254
|
+
|
|
255
|
+
| Variable | Uso | Default |
|
|
256
|
+
| -------- | -------------------- | -------- |
|
|
257
|
+
| `SECRET` | clave de firma HS256 | `"1977"` |
|
|
258
|
+
|
|
259
|
+
### Desviaciones deliberadas frente al original en Go
|
|
260
|
+
|
|
261
|
+
- **`Duration` en segundos**, no el `time.Duration` (nanosegundos) crudo de
|
|
262
|
+
Go — consistente con la convención `second: float` usada en todo este
|
|
263
|
+
port (`cache`, `event`).
|
|
264
|
+
- **`context.Context` → `Request.context`**: los accesores
|
|
265
|
+
(`Token`, `ServiceId`, `ClientId`, `GetClient`, ...) leen de
|
|
266
|
+
`nethttp.Request.context` (un `dict` simple) en vez de un
|
|
267
|
+
`context.Context` tipado por separado.
|
|
268
|
+
- **`mem` no se portó completo**: `ValidToken` usa una caché local L1 en
|
|
269
|
+
memoria antes de golpear Redis; en vez de portar el paquete completo
|
|
270
|
+
`elvis/mem` (750+ líneas, no solicitado), `claim/_mem.py` implementa solo
|
|
271
|
+
el `Get`/`Set`/`Del` con TTL que `ValidToken` necesita.
|
|
272
|
+
- **`ValidToken` devuelve `None`, no lanza**, cuando el token no coincide
|
|
273
|
+
con el guardado en caché — replica exactamente que el Go original
|
|
274
|
+
retorna `(nil, nil)` en ese caso (el llamador debe comprobar
|
|
275
|
+
`if clm is None`, igual que Go comprueba `if clm == nil`).
|
|
276
|
+
|
|
277
|
+
## `middleware`
|
|
278
|
+
|
|
279
|
+
Port de [`elvis/middleware`](../elvis/middleware) (los 9 archivos:
|
|
280
|
+
`autentication.go`, `autorization.go`, `cors.go`, `ephemeral.go`,
|
|
281
|
+
`logger.go`, `recoverer.go`, `request_id.go`, `telemetry.go`,
|
|
282
|
+
`wrap_write.go`). Cada middleware tiene la forma `nethttp.Middleware`
|
|
283
|
+
(`Callable[[Handler], Handler]`), igual que Go's `func(next http.Handler) http.Handler`.
|
|
284
|
+
|
|
285
|
+
### Mapeo Go -> Python
|
|
286
|
+
|
|
287
|
+
| Archivo Go | Contenido | Archivo Python |
|
|
288
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
289
|
+
| `autentication.go` | `GetAuthorization`, `Autentication` (nombre con el typo del Go original, preservado) | `autentication.py` |
|
|
290
|
+
| `autorization.go` | `Store`, `SetAuthorizationStore`, `Authorization` | `autorization.py` |
|
|
291
|
+
| `cors.go` | `AllowAll` | `cors.py` |
|
|
292
|
+
| `ephemeral.go` | `Ephemeral` | `ephemeral.py` |
|
|
293
|
+
| `logger.go` | `Logger`, `RequestLogger`, `LogFormatter`, `LogEntry`, `GetLogEntry`, `WithLogEntry`, `DefaultLogFormatter` | `logger.py` |
|
|
294
|
+
| `recoverer.go` | `Recoverer`, `PrintPrettyStack` | `recoverer.py` |
|
|
295
|
+
| `request_id.go` | `RequestID`, `RequestIDHeader`, `GetReqID`, `NextRequestID` | `request_id.py` |
|
|
296
|
+
| `telemetry.go` | `Metrics`, `Telemetry`, `NewMetric`, `NewRpcMetric`, `SetServiceName`, `PushTelemetry*` | `telemetry.py` |
|
|
297
|
+
| `wrap_write.go` | `WrapResponseWriter`, `NewWrapResponseWriter` | `wrap_write.py` |
|
|
298
|
+
|
|
299
|
+
### Desviaciones deliberadas frente al original en Go
|
|
300
|
+
|
|
301
|
+
- **`cors.go`**: Go configura la librería externa `github.com/rs/cors`;
|
|
302
|
+
aquí `AllowAll` implementa directamente la misma política que esa
|
|
303
|
+
configuración expresa (orígenes permitidos o todos si la lista está
|
|
304
|
+
vacía; `HEAD/GET/POST/PUT/PATCH/DELETE`; todos los headers; credenciales
|
|
305
|
+
permitidas), como un `nethttp.Middleware` plano.
|
|
306
|
+
- **`wrap_write.go`**: `nethttp.ResponseWriter` ya trackea
|
|
307
|
+
`Status()`/`BytesWritten()` nativamente (no envuelve un socket real), así
|
|
308
|
+
que `NewWrapResponseWriter` es una función identidad; no se portan
|
|
309
|
+
`Tee`/`Unwrap` ni la detección de `Flusher`/`Hijacker`/`ReaderFrom`
|
|
310
|
+
(irrelevantes sin un socket que envolver).
|
|
311
|
+
- **`recoverer.go`**: `PrintPrettyStack` imprime el traceback nativo de
|
|
312
|
+
Python (`traceback` de la stdlib) en vez de parsear el formato de texto
|
|
313
|
+
específico de `debug.Stack()` de Go; `prettyStack.parse/decorateLine/...`
|
|
314
|
+
no se portan por la misma razón (formato de stack ajeno, no aplica).
|
|
315
|
+
- **`logger.go`**: sin `stdrout` (no solicitado/portado), el color ANSI
|
|
316
|
+
usa un helper mínimo propio (`middleware/_stdrout.py`, no un port). El
|
|
317
|
+
campo `Logger LoggerInterface` de `DefaultLogFormatter` se cae — este
|
|
318
|
+
port imprime directo a stdout. `DefaultLogger` (paquete-variable
|
|
319
|
+
reconfigurable en Go) no se reexporta como `middleware.DefaultLogger`
|
|
320
|
+
porque Python no tiene bindings "vivos" entre módulos: reasignar
|
|
321
|
+
`middleware.DefaultLogger = x` no afectaría lo que `Logger()` realmente
|
|
322
|
+
usa. Para personalizarlo, asigne `middleware.logger.DefaultLogger`
|
|
323
|
+
directamente.
|
|
324
|
+
- **`autorization.go`**: se preserva tal cual el bug del original — el
|
|
325
|
+
chequeo está invertido (`if store is not None: ...error...` en vez de
|
|
326
|
+
`is None`), así que `Authorization` nunca puede autorizar exitosamente
|
|
327
|
+
ninguna solicitud tal como está escrito en Go. Además, si `store` queda
|
|
328
|
+
en `None` y el token es válido, Go entra en pánico al invocar un método
|
|
329
|
+
sobre una interfaz `nil`; este port en cambio captura esa falla y
|
|
330
|
+
responde 500 en vez de propagar/crashear — ver nota en
|
|
331
|
+
`middleware/autorization.py`.
|
|
332
|
+
- **`ephemeral.go`**: usa `claim.ParceToken` (solo valida el JWT), no
|
|
333
|
+
`claim.ValidToken` — no consulta Redis, igual que en Go.
|
|
334
|
+
- **Manejo de errores**: en general, excepciones (`ValueError`,
|
|
335
|
+
`RuntimeError`) en vez de `(valor, error)`; los middlewares las capturan
|
|
336
|
+
con `try/except` en los mismos puntos donde Go comprueba `if err != nil`.
|
|
337
|
+
|
|
338
|
+
## Tests
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
pip install -e ".[dev]"
|
|
342
|
+
python -m pytest tests/ -v
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
- `tests/cache/` — tests de `cache`, con un cliente Redis falso en memoria
|
|
346
|
+
(`tests/cache/fake_redis.py`), sin requerir un servidor Redis real.
|
|
347
|
+
- `tests/envar/` — tests de `envar`, usando `monkeypatch` de pytest para
|
|
348
|
+
variables de entorno y `sys.argv`.
|
|
349
|
+
- `tests/event/` — tests de `event`, con un cliente NATS falso en memoria
|
|
350
|
+
(`tests/event/fake_nats.py`) para `Load`/`Publish`/`Subscribe`, más
|
|
351
|
+
cobertura directa de `EventEmiter`, `EvenMessage` y `WorkStatus`. No
|
|
352
|
+
requiere un servidor NATS real.
|
|
353
|
+
- `tests/nethttp/`, `tests/response/` — tests de los tipos HTTP base y del
|
|
354
|
+
formateo de respuestas.
|
|
355
|
+
- `tests/claim/`, `tests/middleware/` — tests de JWT y de la cadena de
|
|
356
|
+
middlewares completa, reutilizando el cliente Redis falso de
|
|
357
|
+
`tests/cache/fake_redis.py` vía fixtures (`cache_conn`) en sus
|
|
358
|
+
respectivos `conftest.py`.
|
|
359
|
+
|
|
360
|
+
cat > ~/.pypirc <<'EOF'
|
|
361
|
+
[pypi]
|
|
362
|
+
username = cgalvisleon
|
|
363
|
+
password = pypi-AgEIcHlwaS5vcmcCJGVlZGU4MjRkLTRkZjYtNDYwYS04OTQ2LTM5NmZjNTRmMDhjZQACKlszLCI1ODJlYTIwNC1mYWM2LTQ5MWQtYTM4NC0yODI1YjY4N2ZhYWIiXQAABiASzdbHGHhllL0mlOsMDVr22p-QJbgcN9HbGzF9YdB_dQ
|
|
364
|
+
EOF
|
|
365
|
+
chmod 600 ~/.pypirc
|