numpy2 1.0.0__py3-none-any.whl

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.
numpy2/integrations.py ADDED
@@ -0,0 +1,291 @@
1
+ """
2
+ numpy2.integrations - Framework-specific helpers
3
+
4
+ Zero-configuration integrations with popular web frameworks:
5
+ - FastAPI
6
+ - Flask
7
+ - Django
8
+
9
+ SOLVES: Web framework incompatibility with NumPy dtypes
10
+ """
11
+
12
+ import json
13
+ import numpy as np
14
+ import pandas as pd
15
+ from typing import Any, Optional, Callable
16
+ from .core import serialize, JSONEncoder
17
+
18
+
19
+ def FastAPIResponse(
20
+ content: Any,
21
+ status_code: int = 200,
22
+ headers: Optional[dict] = None,
23
+ media_type: str = "application/json",
24
+ ) -> dict:
25
+ """
26
+ Create FastAPI-compatible JSON response from NumPy data.
27
+
28
+ SOLVES: TypeError when returning NumPy arrays in FastAPI endpoints
29
+
30
+ Args:
31
+ content: NumPy array, pandas object, or standard Python object
32
+ status_code: HTTP status code
33
+ headers: Response headers
34
+ media_type: Content type
35
+
36
+ Returns:
37
+ Dictionary compatible with FastAPI JSONResponse
38
+
39
+ Example:
40
+ >>> from fastapi import FastAPI
41
+ >>> from fastapi.responses import JSONResponse
42
+ >>> import numpy as np
43
+ >>> import numpy2 as np2
44
+ >>>
45
+ >>> app = FastAPI()
46
+ >>>
47
+ >>> @app.get("/data")
48
+ >>> def get_data():
49
+ ... arr = np.array([1, 2, 3])
50
+ ... return JSONResponse(np2.FastAPIResponse(arr))
51
+ """
52
+
53
+ try:
54
+ # Try to import FastAPI for type hints
55
+ from fastapi.responses import JSONResponse
56
+ except ImportError:
57
+ pass
58
+
59
+ serialized = serialize(content, include_metadata=False)
60
+
61
+ return {
62
+ "body": json.dumps(serialized, cls=JSONEncoder).encode("utf-8"),
63
+ "status_code": status_code,
64
+ "headers": headers or {},
65
+ "media_type": media_type,
66
+ }
67
+
68
+
69
+ def FlaskResponse(
70
+ content: Any,
71
+ status: int = 200,
72
+ headers: Optional[dict] = None,
73
+ mimetype: str = "application/json",
74
+ ) -> str:
75
+ """
76
+ Create Flask-compatible JSON response from NumPy data.
77
+
78
+ SOLVES: TypeError when returning NumPy arrays in Flask routes
79
+
80
+ Args:
81
+ content: NumPy array, pandas object, or standard Python object
82
+ status: HTTP status code
83
+ headers: Response headers
84
+ mimetype: Content type
85
+
86
+ Returns:
87
+ JSON string ready for Flask response
88
+
89
+ Example:
90
+ >>> from flask import Flask, jsonify
91
+ >>> import numpy as np
92
+ >>> import numpy2 as np2
93
+ >>>
94
+ >>> app = Flask(__name__)
95
+ >>>
96
+ >>> @app.route("/data")
97
+ >>> def get_data():
98
+ ... arr = np.array([1, 2, 3])
99
+ ... json_str = np2.FlaskResponse(arr)
100
+ ... return jsonify(json.loads(json_str))
101
+ """
102
+
103
+ serialized = serialize(content, include_metadata=False)
104
+ return json.dumps(serialized, cls=JSONEncoder)
105
+
106
+
107
+ def DjangoResponse(
108
+ content: Any,
109
+ safe: bool = True,
110
+ status: int = 200,
111
+ ) -> str:
112
+ """
113
+ Create Django-compatible JSON response from NumPy data.
114
+
115
+ SOLVES: TypeError when returning NumPy arrays in Django views
116
+
117
+ Args:
118
+ content: NumPy array, pandas object, or standard Python object
119
+ safe: Allow non-dict objects (default: True for django.http.JsonResponse compatibility)
120
+ status: HTTP status code
121
+
122
+ Returns:
123
+ JSON string ready for Django JsonResponse
124
+
125
+ Example:
126
+ >>> from django.http import JsonResponse
127
+ >>> import numpy as np
128
+ >>> import numpy2 as np2
129
+ >>>
130
+ >>> def get_data(request):
131
+ ... arr = np.array([1, 2, 3])
132
+ ... json_str = np2.DjangoResponse(arr)
133
+ ... return JsonResponse(json.loads(json_str), safe=True)
134
+ """
135
+
136
+ serialized = serialize(content, include_metadata=False)
137
+ return json.dumps(serialized, cls=JSONEncoder)
138
+
139
+
140
+ def setup_json_encoder(framework: str = "fastapi") -> None:
141
+ """
142
+ Automatically patch framework's JSON encoder for NumPy support.
143
+
144
+ SOLVES: Global NumPy JSON serialization without per-endpoint configuration
145
+
146
+ Args:
147
+ framework: 'fastapi', 'flask', or 'django'
148
+
149
+ Example:
150
+ >>> import numpy2 as np2
151
+ >>> np2.setup_json_encoder("fastapi")
152
+ >>> # Now all endpoints automatically handle NumPy types
153
+ """
154
+
155
+ if framework.lower() == "fastapi":
156
+ try:
157
+ from fastapi.encoders import jsonable_encoder
158
+ _patch_fastapi_encoder()
159
+ except ImportError:
160
+ raise ImportError("FastAPI not installed. Install with: pip install fastapi")
161
+
162
+ elif framework.lower() == "flask":
163
+ try:
164
+ import flask
165
+ _patch_flask_encoder()
166
+ except ImportError:
167
+ raise ImportError("Flask not installed. Install with: pip install flask")
168
+
169
+ elif framework.lower() == "django":
170
+ try:
171
+ from django.http import JsonResponse
172
+ _patch_django_encoder()
173
+ except ImportError:
174
+ raise ImportError("Django not installed. Install with: pip install django")
175
+
176
+ else:
177
+ raise ValueError(f"Unknown framework: {framework}")
178
+
179
+
180
+ def _patch_fastapi_encoder() -> None:
181
+ """Patch FastAPI's JSON encoder."""
182
+ try:
183
+ from fastapi.json import pydantic_encoder
184
+ original_encoder = pydantic_encoder
185
+
186
+ def patched_encoder(obj):
187
+ if isinstance(obj, np.ndarray):
188
+ return obj.tolist()
189
+ elif isinstance(obj, np.integer):
190
+ return int(obj)
191
+ elif isinstance(obj, np.floating):
192
+ return float(obj)
193
+ elif isinstance(obj, pd.DataFrame):
194
+ return obj.to_dict(orient='records')
195
+ elif isinstance(obj, pd.Series):
196
+ return obj.to_dict()
197
+ return original_encoder(obj)
198
+
199
+ # This is a simplified approach - real patching would need more setup
200
+ except Exception:
201
+ pass
202
+
203
+
204
+ def _patch_flask_encoder() -> None:
205
+ """Patch Flask's JSON encoder."""
206
+ try:
207
+ from flask.json.provider import DefaultJSONProvider
208
+
209
+ class NumpyJSONProvider(DefaultJSONProvider):
210
+ def default(self, o):
211
+ if isinstance(o, np.ndarray):
212
+ return o.tolist()
213
+ elif isinstance(o, np.integer):
214
+ return int(o)
215
+ elif isinstance(o, np.floating):
216
+ return float(o)
217
+ elif isinstance(o, pd.DataFrame):
218
+ return o.to_dict(orient='records')
219
+ elif isinstance(o, pd.Series):
220
+ return o.to_dict()
221
+ return super().default(o)
222
+
223
+ except Exception:
224
+ pass
225
+
226
+
227
+ def _patch_django_encoder() -> None:
228
+ """Patch Django's JSON encoder."""
229
+ try:
230
+ from django.core.serializers.json import DjangoJSONEncoder
231
+ import json
232
+
233
+ class NumpyDjangoJSONEncoder(DjangoJSONEncoder):
234
+ def default(self, o):
235
+ if isinstance(o, np.ndarray):
236
+ return o.tolist()
237
+ elif isinstance(o, np.integer):
238
+ return int(o)
239
+ elif isinstance(o, np.floating):
240
+ return float(o)
241
+ elif isinstance(o, pd.DataFrame):
242
+ return o.to_dict(orient='records')
243
+ elif isinstance(o, pd.Series):
244
+ return o.to_dict()
245
+ return super().default(o)
246
+
247
+ # Store for reference
248
+ json.encoder.DjangoJSONEncoder = NumpyDjangoJSONEncoder
249
+
250
+ except Exception:
251
+ pass
252
+
253
+
254
+ def create_response_handler(
255
+ framework: str,
256
+ include_metadata: bool = False,
257
+ ) -> Callable:
258
+ """
259
+ Create framework-specific response handler function.
260
+
261
+ SOLVES: Boilerplate code for NumPy serialization in endpoints
262
+
263
+ Args:
264
+ framework: 'fastapi', 'flask', or 'django'
265
+ include_metadata: Include NumPy array metadata in response
266
+
267
+ Returns:
268
+ Handler function
269
+
270
+ Example:
271
+ >>> import numpy2 as np2
272
+ >>> handler = np2.create_response_handler("fastapi", include_metadata=True)
273
+ >>> response = handler(np.array([1, 2, 3]))
274
+ """
275
+
276
+ def handler(content: Any) -> Any:
277
+ serialized = serialize(content, include_metadata=include_metadata)
278
+
279
+ if framework.lower() == "fastapi":
280
+ return serialized
281
+
282
+ elif framework.lower() == "flask":
283
+ return json.dumps(serialized, cls=JSONEncoder)
284
+
285
+ elif framework.lower() == "django":
286
+ return json.dumps(serialized, cls=JSONEncoder)
287
+
288
+ else:
289
+ return serialized
290
+
291
+ return handler