createsonline 0.1.26__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.
- createsonline/__init__.py +46 -0
- createsonline/admin/__init__.py +7 -0
- createsonline/admin/content.py +526 -0
- createsonline/admin/crud.py +805 -0
- createsonline/admin/field_builder.py +559 -0
- createsonline/admin/integration.py +482 -0
- createsonline/admin/interface.py +2562 -0
- createsonline/admin/model_creator.py +513 -0
- createsonline/admin/model_manager.py +388 -0
- createsonline/admin/modern_dashboard.py +498 -0
- createsonline/admin/permissions.py +264 -0
- createsonline/admin/user_forms.py +594 -0
- createsonline/ai/__init__.py +202 -0
- createsonline/ai/fields.py +1226 -0
- createsonline/ai/orm.py +325 -0
- createsonline/ai/services.py +1244 -0
- createsonline/app.py +506 -0
- createsonline/auth/__init__.py +8 -0
- createsonline/auth/management.py +228 -0
- createsonline/auth/models.py +552 -0
- createsonline/cli/__init__.py +5 -0
- createsonline/cli/commands/__init__.py +122 -0
- createsonline/cli/commands/database.py +416 -0
- createsonline/cli/commands/info.py +173 -0
- createsonline/cli/commands/initdb.py +218 -0
- createsonline/cli/commands/project.py +545 -0
- createsonline/cli/commands/serve.py +173 -0
- createsonline/cli/commands/shell.py +93 -0
- createsonline/cli/commands/users.py +148 -0
- createsonline/cli/main.py +2041 -0
- createsonline/cli/manage.py +274 -0
- createsonline/config/__init__.py +9 -0
- createsonline/config/app.py +2577 -0
- createsonline/config/database.py +179 -0
- createsonline/config/docs.py +384 -0
- createsonline/config/errors.py +160 -0
- createsonline/config/orm.py +43 -0
- createsonline/config/request.py +93 -0
- createsonline/config/settings.py +176 -0
- createsonline/data/__init__.py +23 -0
- createsonline/data/dataframe.py +925 -0
- createsonline/data/io.py +453 -0
- createsonline/data/series.py +557 -0
- createsonline/database/__init__.py +60 -0
- createsonline/database/abstraction.py +440 -0
- createsonline/database/assistant.py +585 -0
- createsonline/database/fields.py +442 -0
- createsonline/database/migrations.py +132 -0
- createsonline/database/models.py +604 -0
- createsonline/database.py +438 -0
- createsonline/http/__init__.py +28 -0
- createsonline/http/client.py +535 -0
- createsonline/ml/__init__.py +55 -0
- createsonline/ml/classification.py +552 -0
- createsonline/ml/clustering.py +680 -0
- createsonline/ml/metrics.py +542 -0
- createsonline/ml/neural.py +560 -0
- createsonline/ml/preprocessing.py +784 -0
- createsonline/ml/regression.py +501 -0
- createsonline/performance/__init__.py +19 -0
- createsonline/performance/cache.py +444 -0
- createsonline/performance/compression.py +335 -0
- createsonline/performance/core.py +419 -0
- createsonline/project_init.py +789 -0
- createsonline/routing.py +528 -0
- createsonline/security/__init__.py +34 -0
- createsonline/security/core.py +811 -0
- createsonline/security/encryption.py +349 -0
- createsonline/server.py +295 -0
- createsonline/static/css/admin.css +263 -0
- createsonline/static/css/common.css +358 -0
- createsonline/static/css/dashboard.css +89 -0
- createsonline/static/favicon.ico +0 -0
- createsonline/static/icons/icon-128x128.png +0 -0
- createsonline/static/icons/icon-128x128.webp +0 -0
- createsonline/static/icons/icon-16x16.png +0 -0
- createsonline/static/icons/icon-16x16.webp +0 -0
- createsonline/static/icons/icon-180x180.png +0 -0
- createsonline/static/icons/icon-180x180.webp +0 -0
- createsonline/static/icons/icon-192x192.png +0 -0
- createsonline/static/icons/icon-192x192.webp +0 -0
- createsonline/static/icons/icon-256x256.png +0 -0
- createsonline/static/icons/icon-256x256.webp +0 -0
- createsonline/static/icons/icon-32x32.png +0 -0
- createsonline/static/icons/icon-32x32.webp +0 -0
- createsonline/static/icons/icon-384x384.png +0 -0
- createsonline/static/icons/icon-384x384.webp +0 -0
- createsonline/static/icons/icon-48x48.png +0 -0
- createsonline/static/icons/icon-48x48.webp +0 -0
- createsonline/static/icons/icon-512x512.png +0 -0
- createsonline/static/icons/icon-512x512.webp +0 -0
- createsonline/static/icons/icon-64x64.png +0 -0
- createsonline/static/icons/icon-64x64.webp +0 -0
- createsonline/static/image/android-chrome-192x192.png +0 -0
- createsonline/static/image/android-chrome-512x512.png +0 -0
- createsonline/static/image/apple-touch-icon.png +0 -0
- createsonline/static/image/favicon-16x16.png +0 -0
- createsonline/static/image/favicon-32x32.png +0 -0
- createsonline/static/image/favicon.ico +0 -0
- createsonline/static/image/favicon.svg +17 -0
- createsonline/static/image/icon-128x128.png +0 -0
- createsonline/static/image/icon-128x128.webp +0 -0
- createsonline/static/image/icon-16x16.png +0 -0
- createsonline/static/image/icon-16x16.webp +0 -0
- createsonline/static/image/icon-180x180.png +0 -0
- createsonline/static/image/icon-180x180.webp +0 -0
- createsonline/static/image/icon-192x192.png +0 -0
- createsonline/static/image/icon-192x192.webp +0 -0
- createsonline/static/image/icon-256x256.png +0 -0
- createsonline/static/image/icon-256x256.webp +0 -0
- createsonline/static/image/icon-32x32.png +0 -0
- createsonline/static/image/icon-32x32.webp +0 -0
- createsonline/static/image/icon-384x384.png +0 -0
- createsonline/static/image/icon-384x384.webp +0 -0
- createsonline/static/image/icon-48x48.png +0 -0
- createsonline/static/image/icon-48x48.webp +0 -0
- createsonline/static/image/icon-512x512.png +0 -0
- createsonline/static/image/icon-512x512.webp +0 -0
- createsonline/static/image/icon-64x64.png +0 -0
- createsonline/static/image/icon-64x64.webp +0 -0
- createsonline/static/image/logo-header-h100.png +0 -0
- createsonline/static/image/logo-header-h100.webp +0 -0
- createsonline/static/image/logo-header-h200@2x.png +0 -0
- createsonline/static/image/logo-header-h200@2x.webp +0 -0
- createsonline/static/image/logo.png +0 -0
- createsonline/static/js/admin.js +274 -0
- createsonline/static/site.webmanifest +35 -0
- createsonline/static/templates/admin/base.html +87 -0
- createsonline/static/templates/admin/dashboard.html +217 -0
- createsonline/static/templates/admin/model_form.html +270 -0
- createsonline/static/templates/admin/model_list.html +202 -0
- createsonline/static/test_script.js +15 -0
- createsonline/static/test_styles.css +59 -0
- createsonline/static_files.py +365 -0
- createsonline/templates/404.html +100 -0
- createsonline/templates/admin_login.html +169 -0
- createsonline/templates/base.html +102 -0
- createsonline/templates/index.html +151 -0
- createsonline/templates.py +205 -0
- createsonline/testing.py +322 -0
- createsonline/utils.py +448 -0
- createsonline/validation/__init__.py +49 -0
- createsonline/validation/fields.py +598 -0
- createsonline/validation/models.py +504 -0
- createsonline/validation/validators.py +561 -0
- createsonline/views.py +184 -0
- createsonline-0.1.26.dist-info/METADATA +46 -0
- createsonline-0.1.26.dist-info/RECORD +152 -0
- createsonline-0.1.26.dist-info/WHEEL +5 -0
- createsonline-0.1.26.dist-info/entry_points.txt +2 -0
- createsonline-0.1.26.dist-info/licenses/LICENSE +21 -0
- createsonline-0.1.26.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CREATESONLINE Internal HTTP Client Implementation
|
|
3
|
+
|
|
4
|
+
Pure Python HTTP client with zero external dependencies.
|
|
5
|
+
Supports sync/async operations, authentication, file uploads, and more.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import ssl
|
|
11
|
+
import socket
|
|
12
|
+
import urllib.parse
|
|
13
|
+
import urllib.request
|
|
14
|
+
import urllib.error
|
|
15
|
+
from typing import Dict, Any, Optional, Union
|
|
16
|
+
import time
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Custom exception classes
|
|
20
|
+
class HTTPError(Exception):
|
|
21
|
+
"""Base HTTP error"""
|
|
22
|
+
def __init__(self, message: str, status_code: Optional[int] = None, response: Optional['HTTPResponse'] = None):
|
|
23
|
+
super().__init__(message)
|
|
24
|
+
self.status_code = status_code
|
|
25
|
+
self.response = response
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ConnectionError(HTTPError):
|
|
29
|
+
"""Connection failed"""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TimeoutError(HTTPError):
|
|
34
|
+
"""Request timeout"""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class RequestError(HTTPError):
|
|
39
|
+
"""Invalid request"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class HTTPResponse:
|
|
44
|
+
"""HTTP Response object"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
status_code: int,
|
|
49
|
+
headers: Dict[str, str],
|
|
50
|
+
content: bytes,
|
|
51
|
+
url: str,
|
|
52
|
+
encoding: str = 'utf-8'
|
|
53
|
+
):
|
|
54
|
+
self.status_code = status_code
|
|
55
|
+
self.headers = headers
|
|
56
|
+
self.content = content
|
|
57
|
+
self.url = url
|
|
58
|
+
self.encoding = encoding
|
|
59
|
+
self._json_cache = None
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def text(self) -> str:
|
|
63
|
+
"""Get response as text"""
|
|
64
|
+
return self.content.decode(self.encoding, errors='replace')
|
|
65
|
+
|
|
66
|
+
def json(self) -> Dict[str, Any]:
|
|
67
|
+
"""Parse response as JSON"""
|
|
68
|
+
if self._json_cache is None:
|
|
69
|
+
try:
|
|
70
|
+
self._json_cache = json.loads(self.text)
|
|
71
|
+
except json.JSONDecodeError as e:
|
|
72
|
+
raise HTTPError(f"Failed to parse JSON: {e}")
|
|
73
|
+
return self._json_cache
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def ok(self) -> bool:
|
|
77
|
+
"""Check if response is successful (2xx status code)"""
|
|
78
|
+
return 200 <= self.status_code < 300
|
|
79
|
+
|
|
80
|
+
def raise_for_status(self):
|
|
81
|
+
"""Raise HTTPError for bad responses"""
|
|
82
|
+
if not self.ok:
|
|
83
|
+
raise HTTPError(
|
|
84
|
+
f"HTTP {self.status_code} Error",
|
|
85
|
+
status_code=self.status_code,
|
|
86
|
+
response=self
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class HTTPRequest:
|
|
91
|
+
"""HTTP Request object"""
|
|
92
|
+
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
method: str,
|
|
96
|
+
url: str,
|
|
97
|
+
headers: Optional[Dict[str, str]] = None,
|
|
98
|
+
data: Optional[Union[str, bytes, Dict[str, Any]]] = None,
|
|
99
|
+
json_data: Optional[Dict[str, Any]] = None,
|
|
100
|
+
files: Optional[Dict[str, Any]] = None,
|
|
101
|
+
params: Optional[Dict[str, Any]] = None,
|
|
102
|
+
auth: Optional[tuple] = None,
|
|
103
|
+
timeout: Optional[float] = None
|
|
104
|
+
):
|
|
105
|
+
self.method = method.upper()
|
|
106
|
+
self.url = url
|
|
107
|
+
self.headers = headers or {}
|
|
108
|
+
self.data = data
|
|
109
|
+
self.json_data = json_data
|
|
110
|
+
self.files = files or {}
|
|
111
|
+
self.params = params or {}
|
|
112
|
+
self.auth = auth
|
|
113
|
+
self.timeout = timeout or 30.0
|
|
114
|
+
|
|
115
|
+
# Process URL with parameters
|
|
116
|
+
if self.params:
|
|
117
|
+
parsed = urllib.parse.urlparse(self.url)
|
|
118
|
+
query_params = urllib.parse.parse_qs(parsed.query)
|
|
119
|
+
query_params.update(self.params)
|
|
120
|
+
query_string = urllib.parse.urlencode(query_params, doseq=True)
|
|
121
|
+
self.url = urllib.parse.urlunparse((
|
|
122
|
+
parsed.scheme, parsed.netloc, parsed.path,
|
|
123
|
+
parsed.params, query_string, parsed.fragment
|
|
124
|
+
))
|
|
125
|
+
|
|
126
|
+
# Set Content-Type for JSON
|
|
127
|
+
if self.json_data:
|
|
128
|
+
self.headers['Content-Type'] = 'application/json'
|
|
129
|
+
self.data = json.dumps(self.json_data).encode('utf-8')
|
|
130
|
+
|
|
131
|
+
# Handle authentication
|
|
132
|
+
if self.auth:
|
|
133
|
+
import base64
|
|
134
|
+
username, password = self.auth
|
|
135
|
+
credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
|
|
136
|
+
self.headers['Authorization'] = f'Basic {credentials}'
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class HTTPClient:
|
|
140
|
+
"""Synchronous HTTP Client"""
|
|
141
|
+
|
|
142
|
+
def __init__(
|
|
143
|
+
self,
|
|
144
|
+
base_url: Optional[str] = None,
|
|
145
|
+
headers: Optional[Dict[str, str]] = None,
|
|
146
|
+
timeout: Optional[float] = 30.0,
|
|
147
|
+
verify_ssl: bool = True,
|
|
148
|
+
max_retries: int = 3,
|
|
149
|
+
retry_delay: float = 1.0
|
|
150
|
+
):
|
|
151
|
+
self.base_url = base_url
|
|
152
|
+
self.default_headers = headers or {}
|
|
153
|
+
self.timeout = timeout
|
|
154
|
+
self.verify_ssl = verify_ssl
|
|
155
|
+
self.max_retries = max_retries
|
|
156
|
+
self.retry_delay = retry_delay
|
|
157
|
+
|
|
158
|
+
# Add User-Agent
|
|
159
|
+
self.default_headers.setdefault(
|
|
160
|
+
'User-Agent',
|
|
161
|
+
'CREATESONLINE-HTTP-Client/1.0'
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def _build_url(self, url: str) -> str:
|
|
165
|
+
"""Build full URL with base URL"""
|
|
166
|
+
if self.base_url and not url.startswith(('http://', 'https://')):
|
|
167
|
+
return urllib.parse.urljoin(self.base_url, url)
|
|
168
|
+
return url
|
|
169
|
+
|
|
170
|
+
def _prepare_headers(self, headers: Optional[Dict[str, str]]) -> Dict[str, str]:
|
|
171
|
+
"""Merge default and request headers"""
|
|
172
|
+
final_headers = self.default_headers.copy()
|
|
173
|
+
if headers:
|
|
174
|
+
final_headers.update(headers)
|
|
175
|
+
return final_headers
|
|
176
|
+
|
|
177
|
+
def _make_request(self, request: HTTPRequest) -> HTTPResponse:
|
|
178
|
+
"""Make the actual HTTP request"""
|
|
179
|
+
url = self._build_url(request.url)
|
|
180
|
+
headers = self._prepare_headers(request.headers)
|
|
181
|
+
|
|
182
|
+
# Create urllib request
|
|
183
|
+
req = urllib.request.Request(
|
|
184
|
+
url=url,
|
|
185
|
+
data=request.data if isinstance(request.data, bytes) else
|
|
186
|
+
request.data.encode('utf-8') if request.data else None,
|
|
187
|
+
headers=headers,
|
|
188
|
+
method=request.method
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Create SSL context if needed
|
|
192
|
+
ssl_context = None
|
|
193
|
+
if not self.verify_ssl:
|
|
194
|
+
ssl_context = ssl.create_default_context()
|
|
195
|
+
ssl_context.check_hostname = False
|
|
196
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
197
|
+
|
|
198
|
+
# Make request with retry logic
|
|
199
|
+
last_error = None
|
|
200
|
+
for attempt in range(self.max_retries + 1):
|
|
201
|
+
try:
|
|
202
|
+
with urllib.request.urlopen(
|
|
203
|
+
req,
|
|
204
|
+
timeout=request.timeout,
|
|
205
|
+
context=ssl_context
|
|
206
|
+
) as response:
|
|
207
|
+
content = response.read()
|
|
208
|
+
status_code = response.getcode()
|
|
209
|
+
headers_dict = dict(response.headers)
|
|
210
|
+
|
|
211
|
+
return HTTPResponse(
|
|
212
|
+
status_code=status_code,
|
|
213
|
+
headers=headers_dict,
|
|
214
|
+
content=content,
|
|
215
|
+
url=url
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
except urllib.error.HTTPError as e:
|
|
219
|
+
# HTTP errors (4xx, 5xx) are not retried
|
|
220
|
+
content = e.read() if hasattr(e, 'read') else b''
|
|
221
|
+
return HTTPResponse(
|
|
222
|
+
status_code=e.code,
|
|
223
|
+
headers=dict(e.headers) if hasattr(e, 'headers') else {},
|
|
224
|
+
content=content,
|
|
225
|
+
url=url
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
except (urllib.error.URLError, socket.timeout, OSError) as e:
|
|
229
|
+
last_error = e
|
|
230
|
+
if attempt < self.max_retries:
|
|
231
|
+
time.sleep(self.retry_delay * (2 ** attempt)) # Exponential backoff
|
|
232
|
+
continue
|
|
233
|
+
|
|
234
|
+
# Convert to our exception types
|
|
235
|
+
if isinstance(e, socket.timeout):
|
|
236
|
+
raise TimeoutError(f"Request timeout after {request.timeout}s")
|
|
237
|
+
else:
|
|
238
|
+
raise ConnectionError(f"Connection failed: {e}")
|
|
239
|
+
|
|
240
|
+
# If we get here, all retries failed
|
|
241
|
+
raise ConnectionError(f"Max retries exceeded: {last_error}")
|
|
242
|
+
|
|
243
|
+
def request(
|
|
244
|
+
self,
|
|
245
|
+
method: str,
|
|
246
|
+
url: str,
|
|
247
|
+
**kwargs
|
|
248
|
+
) -> HTTPResponse:
|
|
249
|
+
"""Make HTTP request"""
|
|
250
|
+
request = HTTPRequest(method, url, **kwargs)
|
|
251
|
+
return self._make_request(request)
|
|
252
|
+
|
|
253
|
+
def get(self, url: str, **kwargs) -> HTTPResponse:
|
|
254
|
+
"""HTTP GET request"""
|
|
255
|
+
return self.request('GET', url, **kwargs)
|
|
256
|
+
|
|
257
|
+
def post(self, url: str, **kwargs) -> HTTPResponse:
|
|
258
|
+
"""HTTP POST request"""
|
|
259
|
+
return self.request('POST', url, **kwargs)
|
|
260
|
+
|
|
261
|
+
def put(self, url: str, **kwargs) -> HTTPResponse:
|
|
262
|
+
"""HTTP PUT request"""
|
|
263
|
+
return self.request('PUT', url, **kwargs)
|
|
264
|
+
|
|
265
|
+
def delete(self, url: str, **kwargs) -> HTTPResponse:
|
|
266
|
+
"""HTTP DELETE request"""
|
|
267
|
+
return self.request('DELETE', url, **kwargs)
|
|
268
|
+
|
|
269
|
+
def patch(self, url: str, **kwargs) -> HTTPResponse:
|
|
270
|
+
"""HTTP PATCH request"""
|
|
271
|
+
return self.request('PATCH', url, **kwargs)
|
|
272
|
+
|
|
273
|
+
def head(self, url: str, **kwargs) -> HTTPResponse:
|
|
274
|
+
"""HTTP HEAD request"""
|
|
275
|
+
return self.request('HEAD', url, **kwargs)
|
|
276
|
+
|
|
277
|
+
def options(self, url: str, **kwargs) -> HTTPResponse:
|
|
278
|
+
"""HTTP OPTIONS request"""
|
|
279
|
+
return self.request('OPTIONS', url, **kwargs)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class AsyncHTTPClient:
|
|
283
|
+
"""Asynchronous HTTP Client"""
|
|
284
|
+
|
|
285
|
+
def __init__(
|
|
286
|
+
self,
|
|
287
|
+
base_url: Optional[str] = None,
|
|
288
|
+
headers: Optional[Dict[str, str]] = None,
|
|
289
|
+
timeout: Optional[float] = 30.0,
|
|
290
|
+
verify_ssl: bool = True,
|
|
291
|
+
max_retries: int = 3,
|
|
292
|
+
retry_delay: float = 1.0,
|
|
293
|
+
max_connections: int = 100
|
|
294
|
+
):
|
|
295
|
+
self.base_url = base_url
|
|
296
|
+
self.default_headers = headers or {}
|
|
297
|
+
self.timeout = timeout
|
|
298
|
+
self.verify_ssl = verify_ssl
|
|
299
|
+
self.max_retries = max_retries
|
|
300
|
+
self.retry_delay = retry_delay
|
|
301
|
+
self.max_connections = max_connections
|
|
302
|
+
|
|
303
|
+
# Add User-Agent
|
|
304
|
+
self.default_headers.setdefault(
|
|
305
|
+
'User-Agent',
|
|
306
|
+
'CREATESONLINE-Async-HTTP-Client/1.0'
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Connection semaphore for limiting concurrent connections
|
|
310
|
+
self._connection_semaphore = asyncio.Semaphore(max_connections)
|
|
311
|
+
|
|
312
|
+
def _build_url(self, url: str) -> str:
|
|
313
|
+
"""Build full URL with base URL"""
|
|
314
|
+
if self.base_url and not url.startswith(('http://', 'https://')):
|
|
315
|
+
return urllib.parse.urljoin(self.base_url, url)
|
|
316
|
+
return url
|
|
317
|
+
|
|
318
|
+
def _prepare_headers(self, headers: Optional[Dict[str, str]]) -> Dict[str, str]:
|
|
319
|
+
"""Merge default and request headers"""
|
|
320
|
+
final_headers = self.default_headers.copy()
|
|
321
|
+
if headers:
|
|
322
|
+
final_headers.update(headers)
|
|
323
|
+
return final_headers
|
|
324
|
+
|
|
325
|
+
async def _make_request(self, request: HTTPRequest) -> HTTPResponse:
|
|
326
|
+
"""Make the actual async HTTP request"""
|
|
327
|
+
async with self._connection_semaphore:
|
|
328
|
+
url = self._build_url(request.url)
|
|
329
|
+
headers = self._prepare_headers(request.headers)
|
|
330
|
+
|
|
331
|
+
# Parse URL components
|
|
332
|
+
parsed = urllib.parse.urlparse(url)
|
|
333
|
+
host = parsed.hostname
|
|
334
|
+
port = parsed.port or (443 if parsed.scheme == 'https' else 80)
|
|
335
|
+
path = parsed.path or '/'
|
|
336
|
+
if parsed.query:
|
|
337
|
+
path += f'?{parsed.query}'
|
|
338
|
+
|
|
339
|
+
# Create SSL context if needed
|
|
340
|
+
ssl_context = None
|
|
341
|
+
if parsed.scheme == 'https':
|
|
342
|
+
ssl_context = ssl.create_default_context()
|
|
343
|
+
if not self.verify_ssl:
|
|
344
|
+
ssl_context.check_hostname = False
|
|
345
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
346
|
+
|
|
347
|
+
# Retry logic
|
|
348
|
+
last_error = None
|
|
349
|
+
for attempt in range(self.max_retries + 1):
|
|
350
|
+
try:
|
|
351
|
+
# Open connection
|
|
352
|
+
if parsed.scheme == 'https':
|
|
353
|
+
reader, writer = await asyncio.wait_for(
|
|
354
|
+
asyncio.open_connection(host, port, ssl=ssl_context),
|
|
355
|
+
timeout=request.timeout
|
|
356
|
+
)
|
|
357
|
+
else:
|
|
358
|
+
reader, writer = await asyncio.wait_for(
|
|
359
|
+
asyncio.open_connection(host, port),
|
|
360
|
+
timeout=request.timeout
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
try:
|
|
364
|
+
# Build HTTP request
|
|
365
|
+
http_request = f"{request.method} {path} HTTP/1.1\r\n"
|
|
366
|
+
http_request += f"Host: {host}\r\n"
|
|
367
|
+
|
|
368
|
+
for key, value in headers.items():
|
|
369
|
+
http_request += f"{key}: {value}\r\n"
|
|
370
|
+
|
|
371
|
+
if request.data:
|
|
372
|
+
content_length = len(request.data)
|
|
373
|
+
http_request += f"Content-Length: {content_length}\r\n"
|
|
374
|
+
|
|
375
|
+
http_request += "Connection: close\r\n"
|
|
376
|
+
http_request += "\r\n"
|
|
377
|
+
|
|
378
|
+
# Send request
|
|
379
|
+
writer.write(http_request.encode('utf-8'))
|
|
380
|
+
if request.data:
|
|
381
|
+
writer.write(request.data)
|
|
382
|
+
await writer.drain()
|
|
383
|
+
|
|
384
|
+
# Read response
|
|
385
|
+
response_data = await asyncio.wait_for(
|
|
386
|
+
reader.read(),
|
|
387
|
+
timeout=request.timeout
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Parse HTTP response
|
|
391
|
+
response_lines = response_data.decode('utf-8', errors='replace').split('\r\n')
|
|
392
|
+
status_line = response_lines[0]
|
|
393
|
+
status_code = int(status_line.split()[1])
|
|
394
|
+
|
|
395
|
+
# Parse headers
|
|
396
|
+
response_headers = {}
|
|
397
|
+
body_start = 0
|
|
398
|
+
for i, line in enumerate(response_lines[1:], 1):
|
|
399
|
+
if line == '':
|
|
400
|
+
body_start = i + 1
|
|
401
|
+
break
|
|
402
|
+
if ':' in line:
|
|
403
|
+
key, value = line.split(':', 1)
|
|
404
|
+
response_headers[key.strip()] = value.strip()
|
|
405
|
+
|
|
406
|
+
# Get body
|
|
407
|
+
body_text = '\r\n'.join(response_lines[body_start:])
|
|
408
|
+
content = body_text.encode('utf-8')
|
|
409
|
+
|
|
410
|
+
return HTTPResponse(
|
|
411
|
+
status_code=status_code,
|
|
412
|
+
headers=response_headers,
|
|
413
|
+
content=content,
|
|
414
|
+
url=url
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
finally:
|
|
418
|
+
writer.close()
|
|
419
|
+
await writer.wait_closed()
|
|
420
|
+
|
|
421
|
+
except (asyncio.TimeoutError, OSError, ConnectionResetError) as e:
|
|
422
|
+
last_error = e
|
|
423
|
+
if attempt < self.max_retries:
|
|
424
|
+
await asyncio.sleep(self.retry_delay * (2 ** attempt))
|
|
425
|
+
continue
|
|
426
|
+
|
|
427
|
+
# Convert to our exception types
|
|
428
|
+
if isinstance(e, asyncio.TimeoutError):
|
|
429
|
+
raise TimeoutError(f"Request timeout after {request.timeout}s")
|
|
430
|
+
else:
|
|
431
|
+
raise ConnectionError(f"Connection failed: {e}")
|
|
432
|
+
|
|
433
|
+
# If we get here, all retries failed
|
|
434
|
+
raise ConnectionError(f"Max retries exceeded: {last_error}")
|
|
435
|
+
|
|
436
|
+
async def request(
|
|
437
|
+
self,
|
|
438
|
+
method: str,
|
|
439
|
+
url: str,
|
|
440
|
+
**kwargs
|
|
441
|
+
) -> HTTPResponse:
|
|
442
|
+
"""Make async HTTP request"""
|
|
443
|
+
request = HTTPRequest(method, url, **kwargs)
|
|
444
|
+
return await self._make_request(request)
|
|
445
|
+
|
|
446
|
+
async def get(self, url: str, **kwargs) -> HTTPResponse:
|
|
447
|
+
"""Async HTTP GET request"""
|
|
448
|
+
return await self.request('GET', url, **kwargs)
|
|
449
|
+
|
|
450
|
+
async def post(self, url: str, **kwargs) -> HTTPResponse:
|
|
451
|
+
"""Async HTTP POST request"""
|
|
452
|
+
return await self.request('POST', url, **kwargs)
|
|
453
|
+
|
|
454
|
+
async def put(self, url: str, **kwargs) -> HTTPResponse:
|
|
455
|
+
"""Async HTTP PUT request"""
|
|
456
|
+
return await self.request('PUT', url, **kwargs)
|
|
457
|
+
|
|
458
|
+
async def delete(self, url: str, **kwargs) -> HTTPResponse:
|
|
459
|
+
"""Async HTTP DELETE request"""
|
|
460
|
+
return await self.request('DELETE', url, **kwargs)
|
|
461
|
+
|
|
462
|
+
async def patch(self, url: str, **kwargs) -> HTTPResponse:
|
|
463
|
+
"""Async HTTP PATCH request"""
|
|
464
|
+
return await self.request('PATCH', url, **kwargs)
|
|
465
|
+
|
|
466
|
+
async def head(self, url: str, **kwargs) -> HTTPResponse:
|
|
467
|
+
"""Async HTTP HEAD request"""
|
|
468
|
+
return await self.request('HEAD', url, **kwargs)
|
|
469
|
+
|
|
470
|
+
async def options(self, url: str, **kwargs) -> HTTPResponse:
|
|
471
|
+
"""Async HTTP OPTIONS request"""
|
|
472
|
+
return await self.request('OPTIONS', url, **kwargs)
|
|
473
|
+
|
|
474
|
+
async def close(self):
|
|
475
|
+
"""Close the client and cleanup resources"""
|
|
476
|
+
# In a more sophisticated implementation, we'd track open connections
|
|
477
|
+
# and close them here. For now, connections are closed after each request.
|
|
478
|
+
pass
|
|
479
|
+
|
|
480
|
+
async def __aenter__(self):
|
|
481
|
+
"""Async context manager entry"""
|
|
482
|
+
return self
|
|
483
|
+
|
|
484
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
485
|
+
"""Async context manager exit"""
|
|
486
|
+
await self.close()
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
# Convenience functions for quick HTTP requests
|
|
490
|
+
def get(url: str, **kwargs) -> HTTPResponse:
|
|
491
|
+
"""Quick HTTP GET request"""
|
|
492
|
+
client = HTTPClient()
|
|
493
|
+
return client.get(url, **kwargs)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def post(url: str, **kwargs) -> HTTPResponse:
|
|
497
|
+
"""Quick HTTP POST request"""
|
|
498
|
+
client = HTTPClient()
|
|
499
|
+
return client.post(url, **kwargs)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def put(url: str, **kwargs) -> HTTPResponse:
|
|
503
|
+
"""Quick HTTP PUT request"""
|
|
504
|
+
client = HTTPClient()
|
|
505
|
+
return client.put(url, **kwargs)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def delete(url: str, **kwargs) -> HTTPResponse:
|
|
509
|
+
"""Quick HTTP DELETE request"""
|
|
510
|
+
client = HTTPClient()
|
|
511
|
+
return client.delete(url, **kwargs)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
async def async_get(url: str, **kwargs) -> HTTPResponse:
|
|
515
|
+
"""Quick async HTTP GET request"""
|
|
516
|
+
async with AsyncHTTPClient() as client:
|
|
517
|
+
return await client.get(url, **kwargs)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
async def async_post(url: str, **kwargs) -> HTTPResponse:
|
|
521
|
+
"""Quick async HTTP POST request"""
|
|
522
|
+
async with AsyncHTTPClient() as client:
|
|
523
|
+
return await client.post(url, **kwargs)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
async def async_put(url: str, **kwargs) -> HTTPResponse:
|
|
527
|
+
"""Quick async HTTP PUT request"""
|
|
528
|
+
async with AsyncHTTPClient() as client:
|
|
529
|
+
return await client.put(url, **kwargs)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
async def async_delete(url: str, **kwargs) -> HTTPResponse:
|
|
533
|
+
"""Quick async HTTP DELETE request"""
|
|
534
|
+
async with AsyncHTTPClient() as client:
|
|
535
|
+
return await client.delete(url, **kwargs)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CREATESONLINE Internal Machine Learning Module
|
|
3
|
+
|
|
4
|
+
Pure Python ML algorithms with zero external dependencies (except numpy).
|
|
5
|
+
Lightweight replacement for scikit-learn with AI-native features.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .regression import LinearRegression, LogisticRegression
|
|
9
|
+
from .classification import DecisionTreeClassifier, KNearestNeighbors
|
|
10
|
+
from .clustering import KMeans, DBScan
|
|
11
|
+
from .metrics import (
|
|
12
|
+
accuracy_score, precision_score, recall_score, f1_score,
|
|
13
|
+
mean_squared_error, mean_absolute_error, r2_score,
|
|
14
|
+
confusion_matrix, classification_report
|
|
15
|
+
)
|
|
16
|
+
from .preprocessing import (
|
|
17
|
+
StandardScaler, MinMaxScaler, LabelEncoder,
|
|
18
|
+
train_test_split, cross_validate
|
|
19
|
+
)
|
|
20
|
+
from .neural import NeuralNetwork
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
# Regression
|
|
24
|
+
'LinearRegression',
|
|
25
|
+
'LogisticRegression',
|
|
26
|
+
|
|
27
|
+
# Classification
|
|
28
|
+
'DecisionTreeClassifier',
|
|
29
|
+
'KNearestNeighbors',
|
|
30
|
+
|
|
31
|
+
# Clustering
|
|
32
|
+
'KMeans',
|
|
33
|
+
'DBScan',
|
|
34
|
+
|
|
35
|
+
# Metrics
|
|
36
|
+
'accuracy_score',
|
|
37
|
+
'precision_score',
|
|
38
|
+
'recall_score',
|
|
39
|
+
'f1_score',
|
|
40
|
+
'mean_squared_error',
|
|
41
|
+
'mean_absolute_error',
|
|
42
|
+
'r2_score',
|
|
43
|
+
'confusion_matrix',
|
|
44
|
+
'classification_report',
|
|
45
|
+
|
|
46
|
+
# Preprocessing
|
|
47
|
+
'StandardScaler',
|
|
48
|
+
'MinMaxScaler',
|
|
49
|
+
'LabelEncoder',
|
|
50
|
+
'train_test_split',
|
|
51
|
+
'cross_validate',
|
|
52
|
+
|
|
53
|
+
# Neural Networks
|
|
54
|
+
'NeuralNetwork'
|
|
55
|
+
]
|