@sdk-it/python 0.39.0 → 0.41.0
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.
package/dist/index.js
CHANGED
|
@@ -1325,8 +1325,9 @@ var PythonEmitter = class {
|
|
|
1325
1325
|
}
|
|
1326
1326
|
#ref(ref) {
|
|
1327
1327
|
const cacheKey = ref.$ref;
|
|
1328
|
-
|
|
1329
|
-
|
|
1328
|
+
const cached = this.#typeCache.get(cacheKey);
|
|
1329
|
+
if (cached) {
|
|
1330
|
+
return cached;
|
|
1330
1331
|
}
|
|
1331
1332
|
const refInfo = parseRef(ref.$ref);
|
|
1332
1333
|
const refName = refInfo.model;
|
|
@@ -1382,7 +1383,7 @@ var PythonEmitter = class {
|
|
|
1382
1383
|
}
|
|
1383
1384
|
for (const [propName, propSchema] of Object.entries(properties)) {
|
|
1384
1385
|
if (isRef(propSchema)) {
|
|
1385
|
-
|
|
1386
|
+
this.#ref(propSchema);
|
|
1386
1387
|
const refInfo = parseRef(propSchema.$ref);
|
|
1387
1388
|
const refName = refInfo.model;
|
|
1388
1389
|
const pythonType = pascalcase(refName);
|
|
@@ -1458,7 +1459,7 @@ ${docstring}${fields.length > 0 ? fields.join("\n") : " pass"}${requestConfig
|
|
|
1458
1459
|
simple: false
|
|
1459
1460
|
};
|
|
1460
1461
|
}
|
|
1461
|
-
#primitive(schema
|
|
1462
|
+
#primitive(schema) {
|
|
1462
1463
|
const { type, format } = schema;
|
|
1463
1464
|
const nullable = schema.nullable;
|
|
1464
1465
|
let pythonType = "Any";
|
|
@@ -1525,22 +1526,22 @@ ${docstring}${fields.length > 0 ? fields.join("\n") : " pass"}${requestConfig
|
|
|
1525
1526
|
simple: true
|
|
1526
1527
|
};
|
|
1527
1528
|
}
|
|
1528
|
-
#enum(schema,
|
|
1529
|
+
#enum(schema, _context) {
|
|
1529
1530
|
const { enum: enumValues } = schema;
|
|
1530
1531
|
if (!enumValues || enumValues.length === 0) {
|
|
1531
|
-
return this.#primitive(schema
|
|
1532
|
+
return this.#primitive(schema);
|
|
1532
1533
|
}
|
|
1533
|
-
if (!
|
|
1534
|
+
if (!_context.name || typeof _context.name !== "string") {
|
|
1534
1535
|
throw new Error("Enum schemas must have a name in context");
|
|
1535
1536
|
}
|
|
1536
|
-
const className = pascalcase(
|
|
1537
|
+
const className = pascalcase(_context.name);
|
|
1537
1538
|
const enumItems = enumValues.map((value, index) => {
|
|
1538
1539
|
const name = typeof value === "string" ? value.toUpperCase().replace(/[^A-Z0-9]/g, "_") : `VALUE_${index}`;
|
|
1539
1540
|
const pythonValue = typeof value === "string" ? `'${value}'` : String(value);
|
|
1540
1541
|
return ` ${name} = ${pythonValue}`;
|
|
1541
1542
|
});
|
|
1542
1543
|
const content = `class ${className}(Enum):
|
|
1543
|
-
"""Enumeration for ${
|
|
1544
|
+
"""Enumeration for ${_context.name}."""
|
|
1544
1545
|
${enumItems.join("\n")}
|
|
1545
1546
|
`;
|
|
1546
1547
|
this.#emit(className, content, schema);
|
|
@@ -1552,7 +1553,7 @@ ${enumItems.join("\n")}
|
|
|
1552
1553
|
simple: false
|
|
1553
1554
|
};
|
|
1554
1555
|
}
|
|
1555
|
-
#const(schema
|
|
1556
|
+
#const(schema) {
|
|
1556
1557
|
const { const: constValue } = schema;
|
|
1557
1558
|
if (typeof constValue === "string") {
|
|
1558
1559
|
return {
|
|
@@ -1578,7 +1579,7 @@ ${enumItems.join("\n")}
|
|
|
1578
1579
|
return this.#ref(schema);
|
|
1579
1580
|
}
|
|
1580
1581
|
if ("const" in schema && schema.const !== void 0) {
|
|
1581
|
-
return this.#const(schema
|
|
1582
|
+
return this.#const(schema);
|
|
1582
1583
|
}
|
|
1583
1584
|
if (schema.enum) {
|
|
1584
1585
|
return this.#enum(schema, context);
|
|
@@ -1590,14 +1591,14 @@ ${enumItems.join("\n")}
|
|
|
1590
1591
|
return this.#oneOf(schema.oneOf || schema.anyOf || [], context);
|
|
1591
1592
|
}
|
|
1592
1593
|
if (schema.type === "object" || schema.properties || schema.allOf || schema.oneOf || schema.anyOf) {
|
|
1593
|
-
if (!context.name) {
|
|
1594
|
+
if (!context.name || typeof context.name !== "string") {
|
|
1594
1595
|
throw new Error("Object schemas must have a name in context");
|
|
1595
1596
|
}
|
|
1596
1597
|
const className = pascalcase(context.name);
|
|
1597
1598
|
return this.#object(className, schema, context);
|
|
1598
1599
|
}
|
|
1599
1600
|
if (isPrimitiveSchema(schema)) {
|
|
1600
|
-
return this.#primitive(schema
|
|
1601
|
+
return this.#primitive(schema);
|
|
1601
1602
|
}
|
|
1602
1603
|
return {
|
|
1603
1604
|
type: "Any",
|
|
@@ -1686,10 +1687,10 @@ ${methods.join("\n")}
|
|
|
1686
1687
|
);
|
|
1687
1688
|
const apiImports = Object.keys(groups).map(
|
|
1688
1689
|
(name) => `from .api.${snakecase2(name)}_api import ${pascalcase2(name)}Api`
|
|
1689
|
-
).join("
|
|
1690
|
+
).join("\n");
|
|
1690
1691
|
const apiProperties = Object.keys(groups).map(
|
|
1691
1692
|
(name) => ` self.${snakecase2(name)} = ${pascalcase2(name)}Api(dispatcher, receiver)`
|
|
1692
|
-
).join("
|
|
1693
|
+
).join("\n");
|
|
1693
1694
|
const clientCode = `"""Main API client."""
|
|
1694
1695
|
|
|
1695
1696
|
from typing import Optional, List
|
|
@@ -1869,7 +1870,7 @@ async function generateModuleInit(folder, readFolder) {
|
|
|
1869
1870
|
${imports}
|
|
1870
1871
|
`;
|
|
1871
1872
|
} catch {
|
|
1872
|
-
return '"""Package module."""
|
|
1873
|
+
return '"""Package module."""\n';
|
|
1873
1874
|
}
|
|
1874
1875
|
}
|
|
1875
1876
|
function toInputs(spec, { entry, operation }) {
|
|
@@ -1903,7 +1904,7 @@ function toOutput(spec, operation) {
|
|
|
1903
1904
|
if (!successResponse) {
|
|
1904
1905
|
return null;
|
|
1905
1906
|
}
|
|
1906
|
-
const [
|
|
1907
|
+
const [, response] = successResponse;
|
|
1907
1908
|
if (isRef2(response)) {
|
|
1908
1909
|
return null;
|
|
1909
1910
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/lib/generate.ts", "../src/lib/http/dispatcher.txt", "../src/lib/http/interceptors.txt", "../src/lib/http/responses.txt", "../src/lib/python-emitter.ts"],
|
|
4
|
-
"sourcesContent": ["import { readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { OpenAPIObject, OperationObject } from 'openapi3-ts/oas31';\nimport { snakecase } from 'stringcase';\n\nimport { isEmpty, isRef, pascalcase } from '@sdk-it/core';\nimport {\n type ReadFolderFn,\n type Writer,\n createWriterProxy,\n writeFiles,\n} from '@sdk-it/core/file-system.js';\nimport {\n type IR,\n cleanFiles,\n forEachOperation,\n isSuccessStatusCode,\n parseJsonContentType,\n readWriteMetadata,\n toIR,\n} from '@sdk-it/spec';\n\nimport dispatcherTxt from './http/dispatcher.txt';\nimport interceptorsTxt from './http/interceptors.txt';\nimport responsesTxt from './http/responses.txt';\nimport { PythonEmitter } from './python-emitter.ts';\n\nexport async function generate(\n openapi: OpenAPIObject,\n settings: {\n output: string;\n cleanup?: boolean;\n name?: string;\n writer?: Writer;\n readFolder?: ReadFolderFn;\n /**\n * full: generate a full project including requirements.txt\n * minimal: generate only the client sdk\n */\n mode?: 'full' | 'minimal';\n formatCode?: (options: { output: string }) => void | Promise<void>;\n },\n) {\n const spec = toIR({ spec: openapi }, true);\n\n const clientName = settings.name || 'Client';\n const output = settings.output;\n const { writer, files: writtenFiles } = createWriterProxy(\n settings.writer ?? writeFiles,\n settings.output,\n );\n settings.writer = writer;\n settings.readFolder ??= async (folder: string) => {\n const files = await readdir(folder, { withFileTypes: true });\n return files.map((file) => ({\n fileName: file.name,\n filePath: join(file.parentPath, file.name),\n isFolder: file.isDirectory(),\n }));\n };\n\n const groups: Record<\n string,\n {\n className: string;\n methods: string[];\n }\n > = {};\n\n // Process each operation and group by tags\n forEachOperation(spec, (entry, operation) => {\n console.log(`Processing ${entry.method} ${entry.path}`);\n const group = (groups[entry.tag] ??= {\n className: `${pascalcase(entry.tag)}Api`,\n methods: [],\n });\n\n const input = toInputs(spec, { entry, operation });\n const response = toOutput(spec, operation);\n\n // Generate method for this operation\n const methodName = snakecase(\n operation.operationId ||\n `${entry.method}_${entry.path.replace(/[^a-zA-Z0-9]/g, '_')}`,\n );\n const returnType = response ? response.returnType : 'httpx.Response';\n\n const docstring =\n operation.summary || operation.description\n ? ` \\\"\\\"\\\"${operation.summary || operation.description}\\\"\\\"\\\"`\n : '';\n\n group.methods.push(`\n async def ${methodName}(self${input.haveInput ? `, input_data: ${input.inputName}` : ''}) -> ${returnType}:\n${docstring}\n config = RequestConfig(\n method='${entry.method.toUpperCase()}',\n url='${entry.path}',\n )\n\n ${input.haveInput ? 'config = input_data.to_request_config(config)' : ''}\n\n response = await self.dispatcher.${input.contentType}(config)\n ${response ? `return await self.receiver.json(response, ${response.successModel || 'None'}, ${response.errorModel || 'None'})` : 'return response'}\n `);\n });\n\n // Generate models using the Python emitter\n const emitter = new PythonEmitter(spec);\n const models = await serializeModels(spec, emitter);\n\n // Generate API group classes\n const apiClasses = Object.entries(groups).reduce<Record<string, string>>(\n (acc, [name, { className, methods }]) => {\n const fileName = `api/${snakecase(name)}_api.py`;\n const imports = [\n 'from typing import Optional',\n 'import httpx',\n '',\n 'from ..http.dispatcher import Dispatcher, RequestConfig',\n 'from ..http.responses import Receiver',\n 'from ..inputs import *',\n 'from ..outputs import *',\n 'from ..models import *',\n '',\n ].join('\\n');\n\n acc[fileName] = `${imports}\nclass ${className}:\n \\\"\\\"\\\"API client for ${name} operations.\\\"\\\"\\\"\n\n def __init__(self, dispatcher: Dispatcher, receiver: Receiver):\n self.dispatcher = dispatcher\n self.receiver = receiver\n${methods.join('\\n')}\n`;\n return acc;\n },\n {},\n );\n\n // Generate main client\n const apiImports = Object.keys(groups)\n .map(\n (name) =>\n `from .api.${snakecase(name)}_api import ${pascalcase(name)}Api`,\n )\n .join('\\\\n');\n\n const apiProperties = Object.keys(groups)\n .map(\n (name) =>\n ` self.${snakecase(name)} = ${pascalcase(name)}Api(dispatcher, receiver)`,\n )\n .join('\\\\n');\n\n const clientCode = `\\\"\\\"\\\"Main API client.\\\"\\\"\\\"\n\nfrom typing import Optional, List\nimport httpx\n\n${apiImports}\nfrom .http.dispatcher import Dispatcher, RequestConfig\nfrom .http.responses import Receiver\nfrom .http.interceptors import (\n Interceptor,\n BaseUrlInterceptor,\n LoggingInterceptor,\n AuthInterceptor,\n UserAgentInterceptor,\n)\n\n\nclass ${clientName}:\n \\\"\\\"\\\"Main API client for the SDK.\\\"\\\"\\\"\n\n def __init__(\n self,\n base_url: str,\n token: Optional[str] = None,\n api_key: Optional[str] = None,\n api_key_header: str = 'X-API-Key',\n enable_logging: bool = False,\n user_agent: Optional[str] = None,\n custom_interceptors: Optional[List[Interceptor]] = None,\n ):\n \\\"\\\"\\\"\n Initialize the API client.\n\n Args:\n base_url: Base URL for the API\n token: Bearer token for authentication\n api_key: API key for authentication\n api_key_header: Header name for API key authentication\n enable_logging: Enable request/response logging\n user_agent: Custom User-Agent header\n custom_interceptors: Additional custom interceptors\n \\\"\\\"\\\"\n self.base_url = base_url\n\n # Build interceptor chain\n interceptors = []\n\n # Base URL interceptor (always first)\n interceptors.append(BaseUrlInterceptor(base_url))\n\n # Authentication interceptor\n if token or api_key:\n interceptors.append(AuthInterceptor(token=token, api_key=api_key, api_key_header=api_key_header))\n\n # User agent interceptor\n if user_agent:\n interceptors.append(UserAgentInterceptor(user_agent))\n\n # Logging interceptor\n if enable_logging:\n interceptors.append(LoggingInterceptor())\n\n # Custom interceptors\n if custom_interceptors:\n interceptors.extend(custom_interceptors)\n\n # Initialize dispatcher and receiver\n self.dispatcher = Dispatcher(interceptors)\n self.receiver = Receiver(interceptors)\n\n # Initialize API clients\n${apiProperties}\n\n async def __aenter__(self):\n return self\n\n async def __aexit__(self, exc_type, exc_val, exc_tb):\n await self.close()\n\n async def close(self):\n \\\"\\\"\\\"Close the HTTP client.\\\"\\\"\\\"\n await self.dispatcher.close()\n`;\n\n // Write all files\n await settings.writer(output, {\n ...models,\n ...apiClasses,\n 'client.py': clientCode,\n 'http/dispatcher.py': dispatcherTxt,\n 'http/interceptors.py': interceptorsTxt,\n 'http/responses.py': responsesTxt,\n '__init__.py': `\\\"\\\"\\\"SDK package.\\\"\\\"\\\"\n\nfrom .client import ${clientName}\n\n__all__ = ['${clientName}']\n`,\n });\n\n // Generate requirements.txt if in full mode\n if (settings.mode === 'full') {\n const requirements = `# HTTP client\nhttpx>=0.24.0,<1.0.0\n\n# Data validation and serialization\npydantic>=2.0.0,<3.0.0\n\n# Enhanced type hints\ntyping-extensions>=4.0.0\n\n# Optional: For better datetime handling\npython-dateutil>=2.8.0\n`;\n\n await settings.writer(output, {\n 'requirements.txt': requirements,\n });\n }\n\n // Handle metadata and cleanup\n const metadata = await readWriteMetadata(\n settings.output,\n Array.from(writtenFiles),\n );\n\n if (settings.cleanup !== false && writtenFiles.size > 0) {\n await cleanFiles(metadata.content, settings.output, [\n '/__init__.py',\n 'requirements.txt',\n '/metadata.json',\n ]);\n }\n\n // Generate __init__.py files for packages\n await settings.writer(output, {\n 'models/__init__.py': await generateModuleInit(\n join(output, 'models'),\n settings.readFolder,\n ),\n 'inputs/__init__.py': await generateModuleInit(\n join(output, 'inputs'),\n settings.readFolder,\n ),\n 'outputs/__init__.py': await generateModuleInit(\n join(output, 'outputs'),\n settings.readFolder,\n ),\n 'api/__init__.py': await generateModuleInit(\n join(output, 'api'),\n settings.readFolder,\n ),\n 'http/__init__.py': `\\\"\\\"\\\"HTTP utilities.\\\"\\\"\\\"\n\nfrom .dispatcher import Dispatcher, RequestConfig\nfrom .interceptors import *\nfrom .responses import *\n\n__all__ = [\n 'Dispatcher',\n 'RequestConfig',\n 'ApiResponse',\n 'ErrorResponse',\n 'Interceptor',\n 'BaseUrlInterceptor',\n 'LoggingInterceptor',\n 'AuthInterceptor',\n]\n`,\n });\n\n // Run formatter if provided\n if (settings.formatCode) {\n await settings.formatCode({ output: settings.output });\n }\n}\n\nasync function generateModuleInit(\n folder: string,\n readFolder: ReadFolderFn,\n): Promise<string> {\n try {\n const files = await readFolder(folder);\n const pyFiles = files\n .filter(\n (file) =>\n file.fileName.endsWith('.py') && file.fileName !== '__init__.py',\n )\n .map((file) => file.fileName.replace('.py', ''));\n\n if (pyFiles.length === 0) {\n return '\\\"\\\"\\\"Package module.\\\"\\\"\\\"\\n';\n }\n\n const imports = pyFiles.map((name) => `from .${name} import *`).join('\\n');\n return `\\\"\\\"\\\"Package module.\\\"\\\"\\\"\\n\\n${imports}\\n`;\n } catch {\n return '\\\"\\\"\\\"Package module.\\\"\\\"\\\"\\\\n';\n }\n}\n\nfunction toInputs(\n spec: IR,\n { entry, operation }: { entry: any; operation: OperationObject },\n) {\n const inputName = (entry as any).inputName || 'Input';\n const haveInput =\n !isEmpty(operation.parameters) || !isEmpty(operation.requestBody);\n\n let contentType = 'json';\n if (operation.requestBody && !isRef(operation.requestBody)) {\n const content = operation.requestBody.content;\n if (content) {\n const contentTypes = Object.keys(content);\n if (contentTypes.some((type) => type.includes('multipart'))) {\n contentType = 'multipart';\n } else if (contentTypes.some((type) => type.includes('form'))) {\n contentType = 'form';\n }\n }\n }\n\n return {\n inputName,\n haveInput,\n contentType,\n };\n}\n\nfunction toOutput(spec: IR, operation: OperationObject) {\n if (!operation.responses) {\n return null;\n }\n\n // Find success response\n const successResponse = Object.entries(operation.responses).find(([code]) =>\n isSuccessStatusCode(Number(code)),\n );\n\n if (!successResponse) {\n return null;\n }\n\n const [statusCode, response] = successResponse;\n if (isRef(response)) {\n return null;\n }\n\n const content = response.content;\n if (!content) {\n return { returnType: 'None', successModel: null, errorModel: null };\n }\n\n // Find JSON content type\n const jsonContent = Object.entries(content).find(([type]) =>\n parseJsonContentType(type),\n );\n\n if (!jsonContent) {\n return {\n returnType: 'httpx.Response',\n successModel: null,\n errorModel: null,\n };\n }\n\n const [, mediaType] = jsonContent;\n const schema = (mediaType as any).schema;\n\n if (!schema || isRef(schema)) {\n return { returnType: 'Any', successModel: null, errorModel: null };\n }\n\n // Generate return type based on schema\n const emitter = new PythonEmitter(spec);\n const result = emitter.handle(schema, {});\n\n return {\n returnType: result.type || 'Any',\n successModel: result.type,\n errorModel: null, // TODO: Handle error models\n };\n}\n\nasync function serializeModels(\n spec: IR,\n emitter: PythonEmitter,\n): Promise<Record<string, string>> {\n const models: Record<string, string> = {};\n\n // Standard imports for all Python model files\n const standardImports = [\n 'from typing import Any, Dict, List, Optional, Union, Literal',\n 'from pydantic import BaseModel, Field',\n 'from datetime import datetime, date',\n 'from uuid import UUID',\n 'from enum import Enum',\n ].join('\\n');\n\n // Emit all schemas\n emitter.onEmit((name: string, content: string, schema: any) => {\n // Add imports to the content\n const fullContent = `${standardImports}\n${schema['x-inputname'] ? 'from ..http.dispatcher import RequestConfig' : ''}\n\n\n${content}`;\n\n if (schema['x-inputname']) {\n models[`inputs/${snakecase(name)}.py`] = fullContent;\n } else if (schema['x-response-name']) {\n models[`outputs/${snakecase(name)}.py`] = fullContent;\n } else {\n models[`models/${snakecase(name)}.py`] = fullContent;\n }\n });\n\n // Process all schemas in components\n if (spec.components?.schemas) {\n for (const [name, schema] of Object.entries(spec.components.schemas)) {\n if (!isRef(schema)) {\n emitter.handle(schema, { name });\n }\n }\n }\n\n return models;\n}\n", "\"\"\"HTTP dispatcher for making API requests.\"\"\"\n\nimport asyncio\nimport logging\nfrom typing import Any, Dict, List, Optional, Union\nfrom urllib.parse import urljoin, urlparse\n\nimport httpx\nfrom pydantic import BaseModel\n\nfrom .interceptors import Interceptor\nfrom .responses import ApiResponse, ErrorResponse\n\n\nclass RequestConfig(BaseModel):\n \"\"\"Configuration for an HTTP request.\"\"\"\n\n method: str\n url: str\n headers: Optional[Dict[str, str]] = None\n params: Optional[Dict[str, Any]] = None\n json_data: Optional[Dict[str, Any]] = None\n form_data: Optional[Dict[str, Any]] = None\n files: Optional[Dict[str, Any]] = None\n timeout: Optional[Union[float, httpx.Timeout]] = None\n \n class Config:\n \"\"\"Pydantic configuration.\"\"\"\n arbitrary_types_allowed = True\n\n\nclass Dispatcher:\n \"\"\"HTTP client dispatcher with interceptor support.\"\"\"\n\n def __init__(\n self, \n interceptors: Optional[List[Interceptor]] = None,\n client: Optional[httpx.AsyncClient] = None,\n timeout: Optional[Union[float, httpx.Timeout]] = None\n ):\n \"\"\"Initialize the dispatcher.\n \n Args:\n interceptors: List of interceptors to apply to requests/responses\n client: Custom httpx.AsyncClient instance (creates default if None)\n timeout: Default timeout for requests\n \"\"\"\n self.interceptors = interceptors or []\n self.client = client or httpx.AsyncClient(timeout=timeout)\n self.logger = logging.getLogger(__name__)\n\n async def __aenter__(self):\n \"\"\"Async context manager entry.\"\"\"\n return self\n\n async def __aexit__(self, exc_type, exc_val, exc_tb):\n \"\"\"Async context manager exit.\"\"\"\n await self.client.aclose()\n\n async def request(self, config: RequestConfig) -> httpx.Response:\n \"\"\"Execute an HTTP request with interceptor processing.\n \n Args:\n config: Request configuration\n \n Returns:\n HTTP response after processing through interceptors\n \n Raises:\n httpx.HTTPError: For HTTP-related errors\n ValueError: For invalid request configuration\n \"\"\"\n # Process request interceptors\n processed_config = config\n for interceptor in self.interceptors:\n processed_config = await interceptor.process_request(processed_config)\n\n # Prepare request arguments\n request_kwargs = self._prepare_request_kwargs(processed_config)\n\n try:\n # Execute request\n response = await self.client.request(**request_kwargs)\n \n # Process response interceptors (in reverse order)\n for interceptor in reversed(self.interceptors):\n response = await interceptor.process_response(response)\n\n return response\n \n except httpx.RequestError as e:\n self.logger.error(f\"Request failed: {e}\")\n raise\n except Exception as e:\n self.logger.error(f\"Unexpected error during request: {e}\")\n raise\n\n def _prepare_request_kwargs(self, config: RequestConfig) -> Dict[str, Any]:\n \"\"\"Prepare keyword arguments for httpx request.\n \n Args:\n config: Request configuration\n \n Returns:\n Dictionary of kwargs for httpx.request\n \n Raises:\n ValueError: If request configuration is invalid\n \"\"\"\n if not config.method:\n raise ValueError(\"Request method cannot be empty\")\n \n if not config.url:\n raise ValueError(\"Request URL cannot be empty\")\n\n request_kwargs = {\n 'method': config.method.upper(),\n 'url': config.url,\n 'headers': config.headers or {},\n 'params': config.params,\n 'timeout': config.timeout,\n }\n\n # Handle different content types\n content_type_set = False\n \n if config.json_data is not None:\n request_kwargs['json'] = config.json_data\n if 'Content-Type' not in request_kwargs['headers']:\n request_kwargs['headers']['Content-Type'] = 'application/json'\n content_type_set = True\n \n elif config.form_data is not None:\n request_kwargs['data'] = config.form_data\n if 'Content-Type' not in request_kwargs['headers']:\n request_kwargs['headers']['Content-Type'] = 'application/x-www-form-urlencoded'\n content_type_set = True\n \n elif config.files is not None:\n request_kwargs['files'] = config.files\n # Don't set Content-Type for multipart/form-data - httpx will handle it automatically\n content_type_set = True\n\n # Validate that only one content type is set\n content_fields = [config.json_data, config.form_data, config.files]\n non_none_count = sum(1 for field in content_fields if field is not None)\n \n if non_none_count > 1:\n raise ValueError(\n \"Only one of json_data, form_data, or files can be set in a single request\"\n )\n\n return request_kwargs\n\n async def json(self, config: RequestConfig) -> httpx.Response:\n \"\"\"Make a JSON request.\n \n Args:\n config: Request configuration\n \n Returns:\n HTTP response\n \"\"\"\n return await self.request(config)\n\n async def form(self, config: RequestConfig) -> httpx.Response:\n \"\"\"Make a form-encoded request.\n \n Args:\n config: Request configuration\n \n Returns:\n HTTP response\n \"\"\"\n return await self.request(config)\n\n async def multipart(self, config: RequestConfig) -> httpx.Response:\n \"\"\"Make a multipart/form-data request.\n \n Args:\n config: Request configuration\n \n Returns:\n HTTP response\n \"\"\"\n return await self.request(config)\n\n async def close(self):\n \"\"\"Close the HTTP client and clean up resources.\"\"\"\n await self.client.aclose()\n\n\nclass Receiver:\n \"\"\"Response processor with interceptor support.\"\"\"\n\n def __init__(\n self, \n interceptors: Optional[List[Interceptor]] = None,\n logger: Optional[logging.Logger] = None\n ):\n \"\"\"Initialize the receiver.\n \n Args:\n interceptors: List of interceptors to apply to responses\n logger: Custom logger instance\n \"\"\"\n self.interceptors = interceptors or []\n self.logger = logger or logging.getLogger(__name__)\n\n async def json(\n self, \n response: httpx.Response, \n success_model: Optional[type] = None, \n error_model: Optional[type] = None\n ) -> Any:\n \"\"\"Process a JSON response.\n \n Args:\n response: HTTP response to process\n success_model: Pydantic model for successful responses\n error_model: Pydantic model for error responses\n \n Returns:\n Parsed response data, optionally as model instances\n \n Raises:\n ErrorResponse: For HTTP error status codes\n ValueError: For response parsing errors\n \"\"\"\n # Process response interceptors\n processed_response = response\n for interceptor in self.interceptors:\n processed_response = await interceptor.process_response(processed_response)\n\n # Handle different status codes\n if 200 <= processed_response.status_code < 300:\n return await self._handle_success_response(\n processed_response, success_model\n )\n else:\n await self._handle_error_response(\n processed_response, error_model\n )\n\n async def _handle_success_response(\n self, \n response: httpx.Response, \n success_model: Optional[type] = None\n ) -> Any:\n \"\"\"Handle successful response.\n \n Args:\n response: HTTP response\n success_model: Pydantic model for successful responses\n \n Returns:\n Parsed response data\n \n Raises:\n ValueError: For parsing errors\n \"\"\"\n if not response.content:\n return None\n\n try:\n data = response.json()\n \n if success_model:\n if isinstance(data, list):\n return [success_model(**item) for item in data]\n else:\n return success_model(**data)\n \n return data\n \n except Exception as e:\n self.logger.error(f\"Failed to parse success response: {e}\")\n raise ValueError(f\"Failed to parse response: {e}\")\n\n async def _handle_error_response(\n self, \n response: httpx.Response, \n error_model: Optional[type] = None\n ) -> None:\n \"\"\"Handle error response.\n \n Args:\n response: HTTP response\n error_model: Pydantic model for error responses\n \n Raises:\n ErrorResponse: Always raises with error details\n \"\"\"\n error_data = {}\n \n if response.content:\n try:\n error_data = response.json()\n except Exception:\n # Fallback to text content if JSON parsing fails\n error_data = {'message': response.text}\n\n if error_model:\n try:\n error = error_model(**error_data)\n raise ErrorResponse(error, response.status_code, dict(response.headers))\n except Exception as e:\n self.logger.warning(f\"Failed to parse error with model {error_model}: {e}\")\n\n raise ErrorResponse(error_data, response.status_code, dict(response.headers))\n\n async def stream(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Return streaming response as-is.\n \n Args:\n response: HTTP response\n \n Returns:\n The unmodified streaming response\n \"\"\"\n return response\n\n async def text(self, response: httpx.Response) -> str:\n \"\"\"Get response as text.\n \n Args:\n response: HTTP response\n \n Returns:\n Response body as text\n \n Raises:\n ErrorResponse: For HTTP error status codes\n \"\"\"\n # Process response interceptors\n processed_response = response\n for interceptor in self.interceptors:\n processed_response = await interceptor.process_response(processed_response)\n\n if 200 <= processed_response.status_code < 300:\n return processed_response.text\n else:\n error_data = {'message': processed_response.text}\n raise ErrorResponse(error_data, processed_response.status_code, dict(processed_response.headers))\n\n async def bytes(self, response: httpx.Response) -> bytes:\n \"\"\"Get response as bytes.\n \n Args:\n response: HTTP response\n \n Returns:\n Response body as bytes\n \n Raises:\n ErrorResponse: For HTTP error status codes\n \"\"\"\n # Process response interceptors\n processed_response = response\n for interceptor in self.interceptors:\n processed_response = await interceptor.process_response(processed_response)\n\n if 200 <= processed_response.status_code < 300:\n return processed_response.content\n else:\n error_data = {'message': 'Binary response error'}\n raise ErrorResponse(error_data, processed_response.status_code, dict(processed_response.headers))\n\n\n# Convenience functions for common use cases\nasync def quick_request(\n method: str,\n url: str,\n interceptors: Optional[List[Interceptor]] = None,\n **kwargs\n) -> httpx.Response:\n \"\"\"Make a quick HTTP request with interceptors.\n \n Args:\n method: HTTP method\n url: Request URL\n interceptors: List of interceptors to apply\n **kwargs: Additional request configuration\n \n Returns:\n HTTP response\n \"\"\"\n config = RequestConfig(method=method, url=url, **kwargs)\n \n async with Dispatcher(interceptors=interceptors) as dispatcher:\n return await dispatcher.request(config)\n\n\nasync def quick_json_request(\n method: str,\n url: str,\n json_data: Optional[Dict[str, Any]] = None,\n interceptors: Optional[List[Interceptor]] = None,\n success_model: Optional[type] = None,\n error_model: Optional[type] = None,\n **kwargs\n) -> Any:\n \"\"\"Make a quick JSON HTTP request with interceptors.\n \n Args:\n method: HTTP method\n url: Request URL\n json_data: JSON data to send\n interceptors: List of interceptors to apply\n success_model: Pydantic model for successful responses\n error_model: Pydantic model for error responses\n **kwargs: Additional request configuration\n \n Returns:\n Parsed JSON response\n \"\"\"\n config = RequestConfig(method=method, url=url, json_data=json_data, **kwargs)\n \n async with Dispatcher(interceptors=interceptors) as dispatcher:\n response = await dispatcher.request(config)\n receiver = Receiver(interceptors=interceptors)\n return await receiver.json(response, success_model, error_model)\n", "\"\"\"HTTP interceptors for request/response processing.\"\"\"\n\nimport asyncio\nimport logging\nimport time\nfrom abc import ABC, abstractmethod\nfrom typing import Dict, Optional, List, Any, Union\nfrom urllib.parse import urljoin\n\nimport httpx\n\nfrom .dispatcher import RequestConfig\n\n\nclass Interceptor(ABC):\n \"\"\"Base class for HTTP interceptors.\"\"\"\n\n @abstractmethod\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Process an outgoing request.\n\n Args:\n config: The request configuration to process\n\n Returns:\n The modified request configuration\n \"\"\"\n pass\n\n @abstractmethod\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Process an incoming response.\n\n Args:\n response: The HTTP response to process\n\n Returns:\n The processed response\n \"\"\"\n pass\n\n\nclass BaseUrlInterceptor(Interceptor):\n \"\"\"Interceptor that prepends base URL to relative URLs.\"\"\"\n\n def __init__(self, base_url: str):\n \"\"\"Initialize the base URL interceptor.\n\n Args:\n base_url: The base URL to prepend to relative URLs\n \"\"\"\n self.base_url = base_url.rstrip('/')\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Prepend base URL if the request URL is relative.\n\n Args:\n config: The request configuration\n\n Returns:\n The modified request configuration with absolute URL\n \"\"\"\n if not config.url.startswith(('http://', 'https://')):\n # Use urljoin for proper URL joining, ensuring single slash\n config.url = urljoin(self.base_url + '/', config.url.lstrip('/'))\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\nclass LoggingInterceptor(Interceptor):\n \"\"\"Interceptor that logs requests and responses using Python's logging module.\"\"\"\n\n def __init__(\n self,\n enabled: bool = True,\n logger: Optional[logging.Logger] = None,\n log_level: int = logging.INFO,\n include_headers: bool = True,\n include_sensitive_headers: bool = False\n ):\n \"\"\"Initialize the logging interceptor.\n\n Args:\n enabled: Whether logging is enabled\n logger: Custom logger instance (creates default if None)\n log_level: Logging level to use\n include_headers: Whether to log request/response headers\n include_sensitive_headers: Whether to log sensitive headers like Authorization\n \"\"\"\n self.enabled = enabled\n self.logger = logger or logging.getLogger(__name__)\n self.log_level = log_level\n self.include_headers = include_headers\n self.include_sensitive_headers = include_sensitive_headers\n self._sensitive_headers = {'authorization', 'x-api-key', 'cookie', 'set-cookie'}\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Log outgoing request.\n\n Args:\n config: The request configuration\n\n Returns:\n The unmodified request configuration\n \"\"\"\n if not self.enabled:\n return config\n\n self.logger.log(self.log_level, f\"\u2192 {config.method.upper()} {config.url}\")\n\n if self.include_headers and config.headers:\n for key, value in config.headers.items():\n if (key.lower() in self._sensitive_headers and\n not self.include_sensitive_headers):\n self.logger.log(self.log_level, f\" {key}: [REDACTED]\")\n else:\n self.logger.log(self.log_level, f\" {key}: {value}\")\n\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Log incoming response.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n if not self.enabled:\n return response\n\n status_icon = \"\u2713\" if 200 <= response.status_code < 300 else \"\u2717\"\n self.logger.log(\n self.log_level,\n f\"\u2190 {status_icon} {response.status_code} {response.reason_phrase or ''}\"\n )\n\n if self.include_headers and response.headers:\n for key, value in response.headers.items():\n if (key.lower() in self._sensitive_headers and\n not self.include_sensitive_headers):\n self.logger.log(self.log_level, f\" {key}: [REDACTED]\")\n else:\n self.logger.log(self.log_level, f\" {key}: {value}\")\n\n return response\n\n\nclass AuthInterceptor(Interceptor):\n \"\"\"Interceptor that adds authentication headers.\"\"\"\n\n def __init__(\n self,\n token: Optional[str] = None,\n api_key: Optional[str] = None,\n api_key_header: str = 'X-API-Key',\n auth_type: str = 'Bearer'\n ):\n \"\"\"Initialize the authentication interceptor.\n\n Args:\n token: Bearer token for Authorization header\n api_key: API key value\n api_key_header: Header name for API key\n auth_type: Type of authentication (Bearer, Basic, etc.)\n \"\"\"\n self.token = token\n self.api_key = api_key\n self.api_key_header = api_key_header\n self.auth_type = auth_type\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Add authentication headers.\n\n Args:\n config: The request configuration\n\n Returns:\n The modified request configuration with auth headers\n \"\"\"\n if config.headers is None:\n config.headers = {}\n\n if self.token:\n config.headers['Authorization'] = f'{self.auth_type} {self.token}'\n elif self.api_key:\n config.headers[self.api_key_header] = self.api_key\n\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\nclass RetryInterceptor(Interceptor):\n \"\"\"Interceptor that retries failed requests with exponential backoff.\"\"\"\n\n def __init__(\n self,\n max_retries: int = 3,\n retry_delay: float = 1.0,\n backoff_factor: float = 2.0,\n retry_on_status: Optional[List[int]] = None,\n retry_on_exceptions: Optional[List[type]] = None\n ):\n \"\"\"Initialize the retry interceptor.\n\n Args:\n max_retries: Maximum number of retry attempts\n retry_delay: Initial delay between retries in seconds\n backoff_factor: Exponential backoff multiplier\n retry_on_status: HTTP status codes that should trigger retries\n retry_on_exceptions: Exception types that should trigger retries\n \"\"\"\n self.max_retries = max_retries\n self.retry_delay = retry_delay\n self.backoff_factor = backoff_factor\n self.retry_on_status = retry_on_status or [500, 502, 503, 504, 408, 429]\n self.retry_on_exceptions = retry_on_exceptions or [\n httpx.TimeoutException,\n httpx.ConnectError,\n httpx.RemoteProtocolError\n ]\n self._original_request_func = None\n self.logger = logging.getLogger(__name__)\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Store original request for potential retries.\n\n Args:\n config: The request configuration\n\n Returns:\n The unmodified request configuration\n \"\"\"\n # Store the original config for retries\n self._original_config = config.model_copy() if hasattr(config, 'model_copy') else config\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Check if response needs retry and handle accordingly.\n\n Args:\n response: The HTTP response\n\n Returns:\n The response (possibly after retries)\n \"\"\"\n # For retry logic to work properly, it needs to be integrated at the dispatcher level\n # This is a simplified version that just passes through\n # In a full implementation, the retry logic would need access to the original request method\n return response\n\n async def execute_with_retry(self, request_func, *args, **kwargs) -> httpx.Response:\n \"\"\"Execute a request function with retry logic.\n\n Args:\n request_func: Function that executes the HTTP request\n *args: Arguments to pass to request_func\n **kwargs: Keyword arguments to pass to request_func\n\n Returns:\n The HTTP response after potential retries\n\n Raises:\n The last exception encountered if all retries fail\n \"\"\"\n last_exception = None\n\n for attempt in range(self.max_retries + 1):\n try:\n response = await request_func(*args, **kwargs)\n\n # Check if response status requires retry\n if response.status_code not in self.retry_on_status:\n return response\n\n if attempt == self.max_retries:\n self.logger.warning(\n f\"Max retries ({self.max_retries}) reached for request. \"\n f\"Final status: {response.status_code}\"\n )\n return response\n\n # Wait before retry\n delay = self.retry_delay * (self.backoff_factor ** attempt)\n self.logger.info(\n f\"Retrying request (attempt {attempt + 1}/{self.max_retries + 1}) \"\n f\"after {delay:.2f}s due to status {response.status_code}\"\n )\n await asyncio.sleep(delay)\n\n except Exception as e:\n # Check if exception type requires retry\n if not any(isinstance(e, exc_type) for exc_type in self.retry_on_exceptions):\n raise e\n\n last_exception = e\n\n if attempt == self.max_retries:\n self.logger.error(\n f\"Max retries ({self.max_retries}) reached. \"\n f\"Final exception: {type(e).__name__}: {e}\"\n )\n raise e\n\n # Wait before retry\n delay = self.retry_delay * (self.backoff_factor ** attempt)\n self.logger.info(\n f\"Retrying request (attempt {attempt + 1}/{self.max_retries + 1}) \"\n f\"after {delay:.2f}s due to {type(e).__name__}: {e}\"\n )\n await asyncio.sleep(delay)\n\n\nclass UserAgentInterceptor(Interceptor):\n \"\"\"Interceptor that adds a User-Agent header.\"\"\"\n\n def __init__(self, user_agent: str):\n \"\"\"Initialize the User-Agent interceptor.\n\n Args:\n user_agent: The User-Agent string to set\n \"\"\"\n self.user_agent = user_agent\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Add User-Agent header if not already present.\n\n Args:\n config: The request configuration\n\n Returns:\n The modified request configuration with User-Agent header\n \"\"\"\n if config.headers is None:\n config.headers = {}\n\n # Only set User-Agent if not already present (case-insensitive check)\n has_user_agent = any(\n key.lower() == 'user-agent'\n for key in config.headers.keys()\n )\n\n if not has_user_agent:\n config.headers['User-Agent'] = self.user_agent\n\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\nclass TimeoutInterceptor(Interceptor):\n \"\"\"Interceptor that sets request timeouts.\"\"\"\n\n def __init__(self, timeout: Union[float, httpx.Timeout]):\n \"\"\"Initialize the timeout interceptor.\n\n Args:\n timeout: Timeout value in seconds or httpx.Timeout object\n \"\"\"\n self.timeout = timeout\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Set timeout for the request.\n\n Args:\n config: The request configuration\n\n Returns:\n The modified request configuration with timeout\n \"\"\"\n if config.timeout is None:\n config.timeout = self.timeout\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\nclass RateLimitInterceptor(Interceptor):\n \"\"\"Interceptor that implements client-side rate limiting.\"\"\"\n\n def __init__(self, max_requests: int, time_window: float = 60.0):\n \"\"\"Initialize the rate limit interceptor.\n\n Args:\n max_requests: Maximum number of requests allowed in the time window\n time_window: Time window in seconds\n \"\"\"\n self.max_requests = max_requests\n self.time_window = time_window\n self.requests = []\n self._lock = asyncio.Lock()\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Apply rate limiting before request.\n\n Args:\n config: The request configuration\n\n Returns:\n The unmodified request configuration\n \"\"\"\n async with self._lock:\n now = time.time()\n\n # Remove requests outside the time window\n self.requests = [req_time for req_time in self.requests\n if now - req_time < self.time_window]\n\n # Check if we've exceeded the rate limit\n if len(self.requests) >= self.max_requests:\n # Calculate how long to wait\n oldest_request = min(self.requests)\n wait_time = self.time_window - (now - oldest_request)\n\n if wait_time > 0:\n await asyncio.sleep(wait_time)\n\n # Record this request\n self.requests.append(now)\n\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\n# Factory functions for convenient interceptor creation\ndef create_base_url_interceptor(base_url: str) -> BaseUrlInterceptor:\n \"\"\"Create a BaseUrlInterceptor instance.\n\n Args:\n base_url: The base URL to prepend to relative URLs\n\n Returns:\n Configured BaseUrlInterceptor instance\n \"\"\"\n return BaseUrlInterceptor(base_url)\n\n\ndef create_logging_interceptor(\n enabled: bool = True,\n log_level: int = logging.INFO,\n include_headers: bool = True,\n include_sensitive_headers: bool = False\n) -> LoggingInterceptor:\n \"\"\"Create a LoggingInterceptor instance.\n\n Args:\n enabled: Whether logging is enabled\n log_level: Logging level to use\n include_headers: Whether to log headers\n include_sensitive_headers: Whether to log sensitive headers\n\n Returns:\n Configured LoggingInterceptor instance\n \"\"\"\n return LoggingInterceptor(\n enabled=enabled,\n log_level=log_level,\n include_headers=include_headers,\n include_sensitive_headers=include_sensitive_headers\n )\n\n\ndef create_auth_interceptor(\n token: Optional[str] = None,\n api_key: Optional[str] = None,\n api_key_header: str = 'X-API-Key',\n auth_type: str = 'Bearer'\n) -> AuthInterceptor:\n \"\"\"Create an AuthInterceptor instance.\n\n Args:\n token: Bearer token for Authorization header\n api_key: API key value\n api_key_header: Header name for API key\n auth_type: Type of authentication\n\n Returns:\n Configured AuthInterceptor instance\n \"\"\"\n return AuthInterceptor(\n token=token,\n api_key=api_key,\n api_key_header=api_key_header,\n auth_type=auth_type\n )\n\n\ndef create_retry_interceptor(\n max_retries: int = 3,\n retry_delay: float = 1.0,\n backoff_factor: float = 2.0,\n retry_on_status: Optional[List[int]] = None\n) -> RetryInterceptor:\n \"\"\"Create a RetryInterceptor instance.\n\n Args:\n max_retries: Maximum number of retry attempts\n retry_delay: Initial delay between retries in seconds\n backoff_factor: Exponential backoff multiplier\n retry_on_status: HTTP status codes that should trigger retries\n\n Returns:\n Configured RetryInterceptor instance\n \"\"\"\n return RetryInterceptor(\n max_retries=max_retries,\n retry_delay=retry_delay,\n backoff_factor=backoff_factor,\n retry_on_status=retry_on_status\n )\n\n\ndef create_user_agent_interceptor(user_agent: str) -> UserAgentInterceptor:\n \"\"\"Create a UserAgentInterceptor instance.\n\n Args:\n user_agent: The User-Agent string to set\n\n Returns:\n Configured UserAgentInterceptor instance\n \"\"\"\n return UserAgentInterceptor(user_agent)\n", "\"\"\"HTTP response models and exceptions.\"\"\"\n\nfrom typing import Any, Dict, Optional, Union\n\nimport httpx\nfrom pydantic import BaseModel\n\n\nclass ApiResponse(BaseModel):\n \"\"\"Base class for API responses.\"\"\"\n\n status_code: int\n headers: Dict[str, str]\n data: Any\n\n class Config:\n \"\"\"Pydantic configuration.\"\"\"\n arbitrary_types_allowed = True\n\n\nclass SuccessResponse(ApiResponse):\n \"\"\"Represents a successful API response.\"\"\"\n\n def __init__(self, data: Any, status_code: int = 200, headers: Optional[Dict[str, str]] = None):\n \"\"\"Initialize success response.\n\n Args:\n data: Response data\n status_code: HTTP status code\n headers: Response headers\n \"\"\"\n super().__init__(\n status_code=status_code,\n headers=headers or {},\n data=data\n )\n\n\nclass ErrorResponse(Exception):\n \"\"\"Exception raised for HTTP error responses.\"\"\"\n\n def __init__(\n self,\n data: Any,\n status_code: int,\n headers: Optional[Dict[str, str]] = None,\n message: Optional[str] = None\n ):\n \"\"\"Initialize error response.\n\n Args:\n data: Error response data\n status_code: HTTP status code\n headers: Response headers\n message: Custom error message\n \"\"\"\n self.data = data\n self.status_code = status_code\n self.headers = headers or {}\n self.message = message or f\"HTTP {status_code} Error\"\n\n super().__init__(self.message)\n\n def __str__(self) -> str:\n \"\"\"String representation of the error.\"\"\"\n return f\"ErrorResponse(status_code={self.status_code}, message='{self.message}')\"\n\n def __repr__(self) -> str:\n \"\"\"Detailed string representation of the error.\"\"\"\n return (\n f\"ErrorResponse(status_code={self.status_code}, \"\n f\"message='{self.message}', data={self.data})\"\n )\n\n\nclass TimeoutError(ErrorResponse):\n \"\"\"Exception raised for request timeouts.\"\"\"\n\n def __init__(self, message: str = \"Request timed out\"):\n \"\"\"Initialize timeout error.\n\n Args:\n message: Error message\n \"\"\"\n super().__init__(\n data={'error': 'timeout'},\n status_code=408,\n message=message\n )\n\n\nclass ConnectionError(ErrorResponse):\n \"\"\"Exception raised for connection errors.\"\"\"\n\n def __init__(self, message: str = \"Connection failed\"):\n \"\"\"Initialize connection error.\n\n Args:\n message: Error message\n \"\"\"\n super().__init__(\n data={'error': 'connection'},\n status_code=503,\n message=message\n )\n\n\nclass BadRequestError(ErrorResponse):\n \"\"\"Exception raised for 400 Bad Request errors.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Bad Request\"):\n \"\"\"Initialize bad request error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'bad_request'},\n status_code=400,\n message=message\n )\n\n\nclass UnauthorizedError(ErrorResponse):\n \"\"\"Exception raised for 401 Unauthorized errors.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Unauthorized\"):\n \"\"\"Initialize unauthorized error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'unauthorized'},\n status_code=401,\n message=message\n )\n\n\nclass ForbiddenError(ErrorResponse):\n \"\"\"Exception raised for 403 Forbidden errors.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Forbidden\"):\n \"\"\"Initialize forbidden error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'forbidden'},\n status_code=403,\n message=message\n )\n\n\nclass NotFoundError(ErrorResponse):\n \"\"\"Exception raised for 404 Not Found errors.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Not Found\"):\n \"\"\"Initialize not found error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'not_found'},\n status_code=404,\n message=message\n )\n\n\nclass InternalServerError(ErrorResponse):\n \"\"\"Exception raised for 500 Internal Server Error.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Internal Server Error\"):\n \"\"\"Initialize internal server error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'internal_server_error'},\n status_code=500,\n message=message\n )\n\n\ndef create_error_from_response(response: httpx.Response) -> ErrorResponse:\n \"\"\"Create appropriate error exception from HTTP response.\n\n Args:\n response: HTTP response\n\n Returns:\n Appropriate error exception\n \"\"\"\n status_code = response.status_code\n headers = dict(response.headers)\n\n # Try to parse error data\n try:\n data = response.json()\n except Exception:\n data = {'message': response.text}\n\n # Create specific error types based on status code\n error_classes = {\n 400: BadRequestError,\n 401: UnauthorizedError,\n 403: ForbiddenError,\n 404: NotFoundError,\n 500: InternalServerError,\n }\n\n error_class = error_classes.get(status_code, ErrorResponse)\n\n if error_class == ErrorResponse:\n return ErrorResponse(data, status_code, headers)\n else:\n return error_class(data)\n", "import type { ReferenceObject, SchemaObject } from 'openapi3-ts/oas31';\nimport { snakecase } from 'stringcase';\n\nimport { isRef, notRef, parseRef, pascalcase } from '@sdk-it/core';\nimport { type IR, isPrimitiveSchema } from '@sdk-it/spec';\n\nexport function coerceObject(schema: SchemaObject): SchemaObject {\n schema = structuredClone(schema);\n if (schema['x-properties']) {\n schema.properties = {\n ...(schema.properties ?? {}),\n ...(schema['x-properties'] ?? {}),\n };\n }\n if (schema['x-required']) {\n schema.required = Array.from(\n new Set([\n ...(Array.isArray(schema.required) ? schema.required : []),\n ...(schema['x-required'] || []),\n ]),\n );\n }\n return schema;\n}\n\ntype Context = Record<string, any>;\ntype Serialized = {\n nullable?: boolean;\n encode?: string;\n encodeV2?: string;\n use: string;\n matches?: string;\n fromJson: any;\n type?: string;\n literal?: unknown;\n content: string;\n simple?: boolean;\n};\ntype Emit = (name: string, content: string, schema: SchemaObject) => void;\n\n/**\n * Convert an OpenAPI (JSON Schema style) object into Python classes with Pydantic\n */\nexport class PythonEmitter {\n #spec: IR;\n #emitHandler?: Emit;\n #emitHistory = new Set<string>();\n #typeCache = new Map<string, Serialized>(); // Cache for resolved types\n\n #emit(name: string, content: string, schema: SchemaObject): void {\n if (this.#emitHistory.has(content)) {\n return;\n }\n this.#emitHistory.add(content);\n this.#emitHandler?.(name, content, schema);\n }\n\n constructor(spec: IR) {\n this.#spec = spec;\n }\n\n onEmit(emit: Emit): void {\n this.#emitHandler = emit;\n }\n\n #formatFieldName(name: string): string {\n // Convert to snake_case and handle special cases\n let fieldName = snakecase(name);\n\n // Handle reserved keywords\n const reservedKeywords = [\n 'class',\n 'def',\n 'if',\n 'else',\n 'elif',\n 'while',\n 'for',\n 'try',\n 'except',\n 'finally',\n 'with',\n 'as',\n 'import',\n 'from',\n 'global',\n 'nonlocal',\n 'lambda',\n 'yield',\n 'return',\n 'pass',\n 'break',\n 'continue',\n 'True',\n 'False',\n 'None',\n 'and',\n 'or',\n 'not',\n 'in',\n 'is',\n ];\n\n if (reservedKeywords.includes(fieldName)) {\n fieldName = `${fieldName}_`;\n }\n\n return fieldName;\n }\n\n #ref(ref: ReferenceObject): Serialized {\n const cacheKey = ref.$ref;\n if (this.#typeCache.has(cacheKey)) {\n return this.#typeCache.get(cacheKey)!;\n }\n\n const refInfo = parseRef(ref.$ref);\n const refName = refInfo.model;\n const className = pascalcase(refName);\n\n const result: Serialized = {\n type: className,\n content: '',\n use: className,\n fromJson: `${className}.parse_obj`,\n simple: false,\n };\n\n this.#typeCache.set(cacheKey, result);\n return result;\n }\n\n #oneOf(\n variants: (SchemaObject | ReferenceObject)[],\n context: Context,\n ): Serialized {\n const variantTypes = variants\n .map((variant) => this.handle(variant, context))\n .map((result) => result.type || 'Any')\n .filter((type, index, arr) => arr.indexOf(type) === index); // Remove duplicates\n\n if (variantTypes.length === 0) {\n return {\n type: 'Any',\n content: '',\n use: 'Any',\n fromJson: 'Any',\n simple: true,\n };\n }\n\n if (variantTypes.length === 1) {\n return {\n type: variantTypes[0],\n content: '',\n use: variantTypes[0],\n fromJson: variantTypes[0],\n simple: true,\n };\n }\n\n const unionType = `Union[${variantTypes.join(', ')}]`;\n return {\n type: unionType,\n content: '',\n use: unionType,\n fromJson: unionType,\n simple: true,\n };\n }\n\n #object(\n className: string,\n schema: SchemaObject,\n context: Context,\n ): Serialized {\n const { properties = {}, required = [] } = coerceObject(schema);\n\n const fields: string[] = [];\n\n // Handle allOf inheritance\n let baseClass = 'BaseModel';\n if (schema.allOf) {\n const bases = schema.allOf\n .filter(notRef)\n .map((s) => this.handle(s, context))\n .filter((result) => result.type)\n .map((result) => result.type);\n\n if (bases.length > 0 && bases[0]) {\n baseClass = bases[0];\n }\n }\n\n // Process properties\n for (const [propName, propSchema] of Object.entries(properties)) {\n if (isRef(propSchema)) {\n const refResult = this.#ref(propSchema);\n const refInfo = parseRef(propSchema.$ref);\n const refName = refInfo.model;\n const pythonType = pascalcase(refName);\n\n const fieldName = this.#formatFieldName(propName);\n const isRequired = required.includes(propName);\n const fieldType = isRequired ? pythonType : `Optional[${pythonType}]`;\n const defaultValue = isRequired ? '' : ' = None';\n\n fields.push(` ${fieldName}: ${fieldType}${defaultValue}`);\n } else {\n const result = this.handle(propSchema, { ...context, name: propName });\n const fieldName = this.#formatFieldName(propName);\n const isRequired = required.includes(propName);\n\n let fieldType = result.type || 'Any';\n if (!isRequired) {\n fieldType = `Optional[${fieldType}]`;\n }\n\n const defaultValue = isRequired ? '' : ' = None';\n let fieldDef = ` ${fieldName}: ${fieldType}${defaultValue}`;\n\n // Add Field() for alias or validation if needed\n if (fieldName !== propName) {\n fieldDef = ` ${fieldName}: ${fieldType} = Field(alias='${propName}'${defaultValue ? ', default=None' : ''})`;\n }\n\n // Add description as comment if available\n if (propSchema.description) {\n fieldDef += ` # ${propSchema.description}`;\n }\n\n fields.push(fieldDef);\n }\n }\n\n // Handle oneOf/anyOf as Union types using centralized logic\n if (schema.oneOf || schema.anyOf) {\n const unionResult = this.#oneOf(\n schema.oneOf || schema.anyOf || [],\n context,\n );\n fields.push(` value: ${unionResult.type}`);\n }\n\n // Handle additionalProperties\n if (\n schema.additionalProperties &&\n typeof schema.additionalProperties === 'object'\n ) {\n const addlResult = this.handle(schema.additionalProperties, context);\n fields.push(\n ` additional_properties: Optional[Dict[str, ${addlResult.type || 'Any'}]] = None`,\n );\n }\n\n // Generate class docstring\n const docstring = schema.description\n ? ` \\\"\\\"\\\"${schema.description}\\\"\\\"\\\"\\n`\n : '';\n\n // Generate to_request_config method for input models\n let requestConfigMethod = '';\n if (schema['x-inputname']) {\n requestConfigMethod = `\n def to_request_config(self, config: RequestConfig) -> RequestConfig:\n \\\"\\\"\\\"Convert this input model to request configuration.\\\"\\\"\\\"\n # Handle path parameters\n path_params = {}\n for key, value in self.dict(exclude_none=True).items():\n if key in config.url:\n path_params[key] = str(value)\n config.url = config.url.replace(f'{{{key}}}', str(value))\n\n # Handle query parameters\n query_params = {k: v for k, v in self.dict(exclude_none=True).items()\n if k not in path_params}\n if query_params:\n config.params = query_params\n\n return config\n`;\n }\n\n const content = `class ${className}(${baseClass}):\n${docstring}${fields.length > 0 ? fields.join('\\n') : ' pass'}${requestConfigMethod}\n`;\n\n this.#emit(className, content, schema);\n\n return {\n type: className,\n content,\n use: className,\n fromJson: `${className}.parse_obj`,\n simple: false,\n };\n }\n\n #primitive(schema: SchemaObject, context: Context): Serialized {\n const { type, format } = schema;\n const nullable = (schema as any).nullable; // Handle nullable as it may not be in the type definition\n\n let pythonType = 'Any';\n\n switch (type) {\n case 'string':\n if (format === 'date-time') {\n pythonType = 'datetime';\n } else if (format === 'date') {\n pythonType = 'date';\n } else if (format === 'uuid') {\n pythonType = 'UUID';\n } else if (format === 'binary' || format === 'byte') {\n pythonType = 'bytes';\n } else {\n pythonType = 'str';\n }\n break;\n\n case 'integer':\n if (format === 'int64') {\n pythonType = 'int'; // Python 3 ints are arbitrary precision\n } else {\n pythonType = 'int';\n }\n break;\n\n case 'number':\n pythonType = 'float';\n break;\n\n case 'boolean':\n pythonType = 'bool';\n break;\n\n default:\n pythonType = 'Any';\n }\n\n if (nullable) {\n pythonType = `Optional[${pythonType}]`;\n }\n\n return {\n type: pythonType,\n content: '',\n use: pythonType,\n fromJson: pythonType,\n simple: true,\n nullable,\n };\n }\n\n #array(schema: SchemaObject, context: Context): Serialized {\n const itemsSchema = schema.items;\n if (!itemsSchema) {\n return {\n type: 'List[Any]',\n content: '',\n use: 'List[Any]',\n fromJson: 'list',\n simple: true,\n };\n }\n\n const itemsResult = this.handle(itemsSchema, context);\n const listType = `List[${itemsResult.type || 'Any'}]`;\n\n return {\n type: listType,\n content: itemsResult.content,\n use: listType,\n fromJson: `List[${itemsResult.fromJson || itemsResult.type}]`,\n simple: true,\n };\n }\n\n #enum(schema: SchemaObject, context: Context): Serialized {\n const { enum: enumValues } = schema;\n if (!enumValues || enumValues.length === 0) {\n return this.#primitive(schema, context);\n }\n\n if (!context.name) {\n throw new Error('Enum schemas must have a name in context');\n }\n\n const className = pascalcase(context.name);\n\n const enumItems = enumValues.map((value, index) => {\n const name =\n typeof value === 'string'\n ? value.toUpperCase().replace(/[^A-Z0-9]/g, '_')\n : `VALUE_${index}`;\n\n const pythonValue =\n typeof value === 'string' ? `'${value}'` : String(value);\n return ` ${name} = ${pythonValue}`;\n });\n\n const content = `class ${className}(Enum):\n \\\"\\\"\\\"Enumeration for ${context.name}.\\\"\\\"\\\"\n${enumItems.join('\\n')}\n`;\n\n this.#emit(className, content, schema);\n\n return {\n type: className,\n content,\n use: className,\n fromJson: className,\n simple: false,\n };\n }\n\n #const(schema: SchemaObject, context: Context): Serialized {\n const { const: constValue } = schema;\n\n if (typeof constValue === 'string') {\n return {\n type: `Literal['${constValue}']`,\n content: '',\n use: `Literal['${constValue}']`,\n fromJson: `'${constValue}'`,\n simple: true,\n literal: constValue,\n };\n }\n\n return {\n type: `Literal[${JSON.stringify(constValue)}]`,\n content: '',\n use: `Literal[${JSON.stringify(constValue)}]`,\n fromJson: JSON.stringify(constValue),\n simple: true,\n literal: constValue,\n };\n }\n handle(\n schema: SchemaObject | ReferenceObject,\n context: Context = {},\n ): Serialized {\n if (isRef(schema)) {\n return this.#ref(schema);\n }\n\n // Handle const values\n if ('const' in schema && schema.const !== undefined) {\n return this.#const(schema, context);\n }\n\n // Handle enums\n if (schema.enum) {\n return this.#enum(schema, context);\n }\n\n // Handle arrays\n if (schema.type === 'array') {\n return this.#array(schema, context);\n }\n\n // Handle oneOf/anyOf at top level using centralized logic\n if (schema.oneOf || schema.anyOf) {\n return this.#oneOf(schema.oneOf || schema.anyOf || [], context);\n }\n\n // Handle objects\n if (\n schema.type === 'object' ||\n schema.properties ||\n schema.allOf ||\n schema.oneOf ||\n schema.anyOf\n ) {\n if (!context.name) {\n throw new Error('Object schemas must have a name in context');\n }\n const className = pascalcase(context.name);\n return this.#object(className, schema, context);\n }\n\n // Handle primitives\n if (isPrimitiveSchema(schema)) {\n return this.#primitive(schema, context);\n }\n\n // Fallback to Any\n return {\n type: 'Any',\n content: '',\n use: 'Any',\n fromJson: 'Any',\n simple: true,\n };\n }\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;AAErB,SAAS,aAAAA,kBAAiB;AAE1B,SAAS,SAAS,SAAAC,QAAO,cAAAC,mBAAkB;AAC3C;AAAA,EAGE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACpBP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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",
|
|
4
|
+
"sourcesContent": ["import { readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type {\n OpenAPIObject,\n OperationObject,\n ReferenceObject,\n SchemaObject,\n} from 'openapi3-ts/oas31';\nimport { snakecase } from 'stringcase';\n\nimport { isEmpty, isRef, pascalcase } from '@sdk-it/core';\nimport {\n type ReadFolderFn,\n type Writer,\n createWriterProxy,\n writeFiles,\n} from '@sdk-it/core/file-system.js';\nimport {\n type IR,\n cleanFiles,\n forEachOperation,\n isSuccessStatusCode,\n parseJsonContentType,\n readWriteMetadata,\n toIR,\n} from '@sdk-it/spec';\n\nimport dispatcherTxt from './http/dispatcher.txt';\nimport interceptorsTxt from './http/interceptors.txt';\nimport responsesTxt from './http/responses.txt';\nimport { PythonEmitter } from './python-emitter.ts';\n\nexport async function generate(\n openapi: OpenAPIObject,\n settings: {\n output: string;\n cleanup?: boolean;\n name?: string;\n writer?: Writer;\n readFolder?: ReadFolderFn;\n /**\n * full: generate a full project including requirements.txt\n * minimal: generate only the client sdk\n */\n mode?: 'full' | 'minimal';\n formatCode?: (options: { output: string }) => void | Promise<void>;\n },\n) {\n const spec = toIR({ spec: openapi }, true);\n\n const clientName = settings.name || 'Client';\n const output = settings.output;\n const { writer, files: writtenFiles } = createWriterProxy(\n settings.writer ?? writeFiles,\n settings.output,\n );\n settings.writer = writer;\n settings.readFolder ??= async (folder: string) => {\n const files = await readdir(folder, { withFileTypes: true });\n return files.map((file) => ({\n fileName: file.name,\n filePath: join(file.parentPath, file.name),\n isFolder: file.isDirectory(),\n }));\n };\n\n const groups: Record<\n string,\n {\n className: string;\n methods: string[];\n }\n > = {};\n\n // Process each operation and group by tags\n forEachOperation(spec, (entry, operation) => {\n console.log(`Processing ${entry.method} ${entry.path}`);\n const group = (groups[entry.tag] ??= {\n className: `${pascalcase(entry.tag)}Api`,\n methods: [],\n });\n\n const input = toInputs(spec, { entry, operation });\n const response = toOutput(spec, operation);\n\n // Generate method for this operation\n const methodName = snakecase(\n operation.operationId ||\n `${entry.method}_${entry.path.replace(/[^a-zA-Z0-9]/g, '_')}`,\n );\n const returnType = response ? response.returnType : 'httpx.Response';\n\n const docstring =\n operation.summary || operation.description\n ? ` \"\"\"${operation.summary || operation.description}\"\"\"`\n : '';\n\n group.methods.push(`\n async def ${methodName}(self${input.haveInput ? `, input_data: ${input.inputName}` : ''}) -> ${returnType}:\n${docstring}\n config = RequestConfig(\n method='${entry.method.toUpperCase()}',\n url='${entry.path}',\n )\n\n ${input.haveInput ? 'config = input_data.to_request_config(config)' : ''}\n\n response = await self.dispatcher.${input.contentType}(config)\n ${response ? `return await self.receiver.json(response, ${response.successModel || 'None'}, ${response.errorModel || 'None'})` : 'return response'}\n `);\n });\n\n // Generate models using the Python emitter\n const emitter = new PythonEmitter(spec);\n const models = await serializeModels(spec, emitter);\n\n // Generate API group classes\n const apiClasses = Object.entries(groups).reduce<Record<string, string>>(\n (acc, [name, { className, methods }]) => {\n const fileName = `api/${snakecase(name)}_api.py`;\n const imports = [\n 'from typing import Optional',\n 'import httpx',\n '',\n 'from ..http.dispatcher import Dispatcher, RequestConfig',\n 'from ..http.responses import Receiver',\n 'from ..inputs import *',\n 'from ..outputs import *',\n 'from ..models import *',\n '',\n ].join('\\n');\n\n acc[fileName] = `${imports}\nclass ${className}:\n \"\"\"API client for ${name} operations.\"\"\"\n\n def __init__(self, dispatcher: Dispatcher, receiver: Receiver):\n self.dispatcher = dispatcher\n self.receiver = receiver\n${methods.join('\\n')}\n`;\n return acc;\n },\n {},\n );\n\n // Generate main client\n const apiImports = Object.keys(groups)\n .map(\n (name) =>\n `from .api.${snakecase(name)}_api import ${pascalcase(name)}Api`,\n )\n .join('\\n');\n\n const apiProperties = Object.keys(groups)\n .map(\n (name) =>\n ` self.${snakecase(name)} = ${pascalcase(name)}Api(dispatcher, receiver)`,\n )\n .join('\\n');\n\n const clientCode = `\"\"\"Main API client.\"\"\"\n\nfrom typing import Optional, List\nimport httpx\n\n${apiImports}\nfrom .http.dispatcher import Dispatcher, RequestConfig\nfrom .http.responses import Receiver\nfrom .http.interceptors import (\n Interceptor,\n BaseUrlInterceptor,\n LoggingInterceptor,\n AuthInterceptor,\n UserAgentInterceptor,\n)\n\n\nclass ${clientName}:\n \"\"\"Main API client for the SDK.\"\"\"\n\n def __init__(\n self,\n base_url: str,\n token: Optional[str] = None,\n api_key: Optional[str] = None,\n api_key_header: str = 'X-API-Key',\n enable_logging: bool = False,\n user_agent: Optional[str] = None,\n custom_interceptors: Optional[List[Interceptor]] = None,\n ):\n \"\"\"\n Initialize the API client.\n\n Args:\n base_url: Base URL for the API\n token: Bearer token for authentication\n api_key: API key for authentication\n api_key_header: Header name for API key authentication\n enable_logging: Enable request/response logging\n user_agent: Custom User-Agent header\n custom_interceptors: Additional custom interceptors\n \"\"\"\n self.base_url = base_url\n\n # Build interceptor chain\n interceptors = []\n\n # Base URL interceptor (always first)\n interceptors.append(BaseUrlInterceptor(base_url))\n\n # Authentication interceptor\n if token or api_key:\n interceptors.append(AuthInterceptor(token=token, api_key=api_key, api_key_header=api_key_header))\n\n # User agent interceptor\n if user_agent:\n interceptors.append(UserAgentInterceptor(user_agent))\n\n # Logging interceptor\n if enable_logging:\n interceptors.append(LoggingInterceptor())\n\n # Custom interceptors\n if custom_interceptors:\n interceptors.extend(custom_interceptors)\n\n # Initialize dispatcher and receiver\n self.dispatcher = Dispatcher(interceptors)\n self.receiver = Receiver(interceptors)\n\n # Initialize API clients\n${apiProperties}\n\n async def __aenter__(self):\n return self\n\n async def __aexit__(self, exc_type, exc_val, exc_tb):\n await self.close()\n\n async def close(self):\n \"\"\"Close the HTTP client.\"\"\"\n await self.dispatcher.close()\n`;\n\n // Write all files\n await settings.writer(output, {\n ...models,\n ...apiClasses,\n 'client.py': clientCode,\n 'http/dispatcher.py': dispatcherTxt,\n 'http/interceptors.py': interceptorsTxt,\n 'http/responses.py': responsesTxt,\n '__init__.py': `\"\"\"SDK package.\"\"\"\n\nfrom .client import ${clientName}\n\n__all__ = ['${clientName}']\n`,\n });\n\n // Generate requirements.txt if in full mode\n if (settings.mode === 'full') {\n const requirements = `# HTTP client\nhttpx>=0.24.0,<1.0.0\n\n# Data validation and serialization\npydantic>=2.0.0,<3.0.0\n\n# Enhanced type hints\ntyping-extensions>=4.0.0\n\n# Optional: For better datetime handling\npython-dateutil>=2.8.0\n`;\n\n await settings.writer(output, {\n 'requirements.txt': requirements,\n });\n }\n\n // Handle metadata and cleanup\n const metadata = await readWriteMetadata(\n settings.output,\n Array.from(writtenFiles),\n );\n\n if (settings.cleanup !== false && writtenFiles.size > 0) {\n await cleanFiles(metadata.content, settings.output, [\n '/__init__.py',\n 'requirements.txt',\n '/metadata.json',\n ]);\n }\n\n // Generate __init__.py files for packages\n await settings.writer(output, {\n 'models/__init__.py': await generateModuleInit(\n join(output, 'models'),\n settings.readFolder,\n ),\n 'inputs/__init__.py': await generateModuleInit(\n join(output, 'inputs'),\n settings.readFolder,\n ),\n 'outputs/__init__.py': await generateModuleInit(\n join(output, 'outputs'),\n settings.readFolder,\n ),\n 'api/__init__.py': await generateModuleInit(\n join(output, 'api'),\n settings.readFolder,\n ),\n 'http/__init__.py': `\"\"\"HTTP utilities.\"\"\"\n\nfrom .dispatcher import Dispatcher, RequestConfig\nfrom .interceptors import *\nfrom .responses import *\n\n__all__ = [\n 'Dispatcher',\n 'RequestConfig',\n 'ApiResponse',\n 'ErrorResponse',\n 'Interceptor',\n 'BaseUrlInterceptor',\n 'LoggingInterceptor',\n 'AuthInterceptor',\n]\n`,\n });\n\n // Run formatter if provided\n if (settings.formatCode) {\n await settings.formatCode({ output: settings.output });\n }\n}\n\nasync function generateModuleInit(\n folder: string,\n readFolder: ReadFolderFn,\n): Promise<string> {\n try {\n const files = await readFolder(folder);\n const pyFiles = files\n .filter(\n (file) =>\n file.fileName.endsWith('.py') && file.fileName !== '__init__.py',\n )\n .map((file) => file.fileName.replace('.py', ''));\n\n if (pyFiles.length === 0) {\n return '\"\"\"Package module.\"\"\"\\n';\n }\n\n const imports = pyFiles.map((name) => `from .${name} import *`).join('\\n');\n return `\"\"\"Package module.\"\"\"\\n\\n${imports}\\n`;\n } catch {\n return '\"\"\"Package module.\"\"\"\\n';\n }\n}\n\nfunction toInputs(\n spec: IR,\n { entry, operation }: { entry: unknown; operation: OperationObject },\n) {\n const inputName = (entry as { inputName?: string }).inputName || 'Input';\n const haveInput =\n !isEmpty(operation.parameters) || !isEmpty(operation.requestBody);\n\n let contentType = 'json';\n if (operation.requestBody && !isRef(operation.requestBody)) {\n const content = operation.requestBody.content;\n if (content) {\n const contentTypes = Object.keys(content);\n if (contentTypes.some((type) => type.includes('multipart'))) {\n contentType = 'multipart';\n } else if (contentTypes.some((type) => type.includes('form'))) {\n contentType = 'form';\n }\n }\n }\n\n return {\n inputName,\n haveInput,\n contentType,\n };\n}\n\nfunction toOutput(spec: IR, operation: OperationObject) {\n if (!operation.responses) {\n return null;\n }\n\n // Find success response\n const successResponse = Object.entries(operation.responses).find(([code]) =>\n isSuccessStatusCode(Number(code)),\n );\n\n if (!successResponse) {\n return null;\n }\n\n const [, response] = successResponse;\n if (isRef(response)) {\n return null;\n }\n\n const content = response.content;\n if (!content) {\n return { returnType: 'None', successModel: null, errorModel: null };\n }\n\n // Find JSON content type\n const jsonContent = Object.entries(content).find(([type]) =>\n parseJsonContentType(type),\n );\n\n if (!jsonContent) {\n return {\n returnType: 'httpx.Response',\n successModel: null,\n errorModel: null,\n };\n }\n\n const [, mediaType] = jsonContent;\n const schema = (mediaType as { schema?: SchemaObject | ReferenceObject })\n .schema;\n\n if (!schema || isRef(schema)) {\n return { returnType: 'Any', successModel: null, errorModel: null };\n }\n\n // Generate return type based on schema\n const emitter = new PythonEmitter(spec);\n const result = emitter.handle(schema, {});\n\n return {\n returnType: result.type || 'Any',\n successModel: result.type,\n errorModel: null, // TODO: Handle error models\n };\n}\n\nasync function serializeModels(\n spec: IR,\n emitter: PythonEmitter,\n): Promise<Record<string, string>> {\n const models: Record<string, string> = {};\n\n // Standard imports for all Python model files\n const standardImports = [\n 'from typing import Any, Dict, List, Optional, Union, Literal',\n 'from pydantic import BaseModel, Field',\n 'from datetime import datetime, date',\n 'from uuid import UUID',\n 'from enum import Enum',\n ].join('\\n');\n\n // Emit all schemas\n emitter.onEmit((name: string, content: string, schema: SchemaObject) => {\n // Add imports to the content\n const fullContent = `${standardImports}\n${schema['x-inputname'] ? 'from ..http.dispatcher import RequestConfig' : ''}\n\n\n${content}`;\n\n if (schema['x-inputname']) {\n models[`inputs/${snakecase(name)}.py`] = fullContent;\n } else if (schema['x-response-name']) {\n models[`outputs/${snakecase(name)}.py`] = fullContent;\n } else {\n models[`models/${snakecase(name)}.py`] = fullContent;\n }\n });\n\n // Process all schemas in components\n if (spec.components?.schemas) {\n for (const [name, schema] of Object.entries(spec.components.schemas)) {\n if (!isRef(schema)) {\n emitter.handle(schema, { name });\n }\n }\n }\n\n return models;\n}\n", "\"\"\"HTTP dispatcher for making API requests.\"\"\"\n\nimport asyncio\nimport logging\nfrom typing import Any, Dict, List, Optional, Union\nfrom urllib.parse import urljoin, urlparse\n\nimport httpx\nfrom pydantic import BaseModel\n\nfrom .interceptors import Interceptor\nfrom .responses import ApiResponse, ErrorResponse\n\n\nclass RequestConfig(BaseModel):\n \"\"\"Configuration for an HTTP request.\"\"\"\n\n method: str\n url: str\n headers: Optional[Dict[str, str]] = None\n params: Optional[Dict[str, Any]] = None\n json_data: Optional[Dict[str, Any]] = None\n form_data: Optional[Dict[str, Any]] = None\n files: Optional[Dict[str, Any]] = None\n timeout: Optional[Union[float, httpx.Timeout]] = None\n \n class Config:\n \"\"\"Pydantic configuration.\"\"\"\n arbitrary_types_allowed = True\n\n\nclass Dispatcher:\n \"\"\"HTTP client dispatcher with interceptor support.\"\"\"\n\n def __init__(\n self, \n interceptors: Optional[List[Interceptor]] = None,\n client: Optional[httpx.AsyncClient] = None,\n timeout: Optional[Union[float, httpx.Timeout]] = None\n ):\n \"\"\"Initialize the dispatcher.\n \n Args:\n interceptors: List of interceptors to apply to requests/responses\n client: Custom httpx.AsyncClient instance (creates default if None)\n timeout: Default timeout for requests\n \"\"\"\n self.interceptors = interceptors or []\n self.client = client or httpx.AsyncClient(timeout=timeout)\n self.logger = logging.getLogger(__name__)\n\n async def __aenter__(self):\n \"\"\"Async context manager entry.\"\"\"\n return self\n\n async def __aexit__(self, exc_type, exc_val, exc_tb):\n \"\"\"Async context manager exit.\"\"\"\n await self.client.aclose()\n\n async def request(self, config: RequestConfig) -> httpx.Response:\n \"\"\"Execute an HTTP request with interceptor processing.\n \n Args:\n config: Request configuration\n \n Returns:\n HTTP response after processing through interceptors\n \n Raises:\n httpx.HTTPError: For HTTP-related errors\n ValueError: For invalid request configuration\n \"\"\"\n # Process request interceptors\n processed_config = config\n for interceptor in self.interceptors:\n processed_config = await interceptor.process_request(processed_config)\n\n # Prepare request arguments\n request_kwargs = self._prepare_request_kwargs(processed_config)\n\n try:\n # Execute request\n response = await self.client.request(**request_kwargs)\n \n # Process response interceptors (in reverse order)\n for interceptor in reversed(self.interceptors):\n response = await interceptor.process_response(response)\n\n return response\n \n except httpx.RequestError as e:\n self.logger.error(f\"Request failed: {e}\")\n raise\n except Exception as e:\n self.logger.error(f\"Unexpected error during request: {e}\")\n raise\n\n def _prepare_request_kwargs(self, config: RequestConfig) -> Dict[str, Any]:\n \"\"\"Prepare keyword arguments for httpx request.\n \n Args:\n config: Request configuration\n \n Returns:\n Dictionary of kwargs for httpx.request\n \n Raises:\n ValueError: If request configuration is invalid\n \"\"\"\n if not config.method:\n raise ValueError(\"Request method cannot be empty\")\n \n if not config.url:\n raise ValueError(\"Request URL cannot be empty\")\n\n request_kwargs = {\n 'method': config.method.upper(),\n 'url': config.url,\n 'headers': config.headers or {},\n 'params': config.params,\n 'timeout': config.timeout,\n }\n\n # Handle different content types\n content_type_set = False\n \n if config.json_data is not None:\n request_kwargs['json'] = config.json_data\n if 'Content-Type' not in request_kwargs['headers']:\n request_kwargs['headers']['Content-Type'] = 'application/json'\n content_type_set = True\n \n elif config.form_data is not None:\n request_kwargs['data'] = config.form_data\n if 'Content-Type' not in request_kwargs['headers']:\n request_kwargs['headers']['Content-Type'] = 'application/x-www-form-urlencoded'\n content_type_set = True\n \n elif config.files is not None:\n request_kwargs['files'] = config.files\n # Don't set Content-Type for multipart/form-data - httpx will handle it automatically\n content_type_set = True\n\n # Validate that only one content type is set\n content_fields = [config.json_data, config.form_data, config.files]\n non_none_count = sum(1 for field in content_fields if field is not None)\n \n if non_none_count > 1:\n raise ValueError(\n \"Only one of json_data, form_data, or files can be set in a single request\"\n )\n\n return request_kwargs\n\n async def json(self, config: RequestConfig) -> httpx.Response:\n \"\"\"Make a JSON request.\n \n Args:\n config: Request configuration\n \n Returns:\n HTTP response\n \"\"\"\n return await self.request(config)\n\n async def form(self, config: RequestConfig) -> httpx.Response:\n \"\"\"Make a form-encoded request.\n \n Args:\n config: Request configuration\n \n Returns:\n HTTP response\n \"\"\"\n return await self.request(config)\n\n async def multipart(self, config: RequestConfig) -> httpx.Response:\n \"\"\"Make a multipart/form-data request.\n \n Args:\n config: Request configuration\n \n Returns:\n HTTP response\n \"\"\"\n return await self.request(config)\n\n async def close(self):\n \"\"\"Close the HTTP client and clean up resources.\"\"\"\n await self.client.aclose()\n\n\nclass Receiver:\n \"\"\"Response processor with interceptor support.\"\"\"\n\n def __init__(\n self, \n interceptors: Optional[List[Interceptor]] = None,\n logger: Optional[logging.Logger] = None\n ):\n \"\"\"Initialize the receiver.\n \n Args:\n interceptors: List of interceptors to apply to responses\n logger: Custom logger instance\n \"\"\"\n self.interceptors = interceptors or []\n self.logger = logger or logging.getLogger(__name__)\n\n async def json(\n self, \n response: httpx.Response, \n success_model: Optional[type] = None, \n error_model: Optional[type] = None\n ) -> Any:\n \"\"\"Process a JSON response.\n \n Args:\n response: HTTP response to process\n success_model: Pydantic model for successful responses\n error_model: Pydantic model for error responses\n \n Returns:\n Parsed response data, optionally as model instances\n \n Raises:\n ErrorResponse: For HTTP error status codes\n ValueError: For response parsing errors\n \"\"\"\n # Process response interceptors\n processed_response = response\n for interceptor in self.interceptors:\n processed_response = await interceptor.process_response(processed_response)\n\n # Handle different status codes\n if 200 <= processed_response.status_code < 300:\n return await self._handle_success_response(\n processed_response, success_model\n )\n else:\n await self._handle_error_response(\n processed_response, error_model\n )\n\n async def _handle_success_response(\n self, \n response: httpx.Response, \n success_model: Optional[type] = None\n ) -> Any:\n \"\"\"Handle successful response.\n \n Args:\n response: HTTP response\n success_model: Pydantic model for successful responses\n \n Returns:\n Parsed response data\n \n Raises:\n ValueError: For parsing errors\n \"\"\"\n if not response.content:\n return None\n\n try:\n data = response.json()\n \n if success_model:\n if isinstance(data, list):\n return [success_model(**item) for item in data]\n else:\n return success_model(**data)\n \n return data\n \n except Exception as e:\n self.logger.error(f\"Failed to parse success response: {e}\")\n raise ValueError(f\"Failed to parse response: {e}\")\n\n async def _handle_error_response(\n self, \n response: httpx.Response, \n error_model: Optional[type] = None\n ) -> None:\n \"\"\"Handle error response.\n \n Args:\n response: HTTP response\n error_model: Pydantic model for error responses\n \n Raises:\n ErrorResponse: Always raises with error details\n \"\"\"\n error_data = {}\n \n if response.content:\n try:\n error_data = response.json()\n except Exception:\n # Fallback to text content if JSON parsing fails\n error_data = {'message': response.text}\n\n if error_model:\n try:\n error = error_model(**error_data)\n raise ErrorResponse(error, response.status_code, dict(response.headers))\n except Exception as e:\n self.logger.warning(f\"Failed to parse error with model {error_model}: {e}\")\n\n raise ErrorResponse(error_data, response.status_code, dict(response.headers))\n\n async def stream(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Return streaming response as-is.\n \n Args:\n response: HTTP response\n \n Returns:\n The unmodified streaming response\n \"\"\"\n return response\n\n async def text(self, response: httpx.Response) -> str:\n \"\"\"Get response as text.\n \n Args:\n response: HTTP response\n \n Returns:\n Response body as text\n \n Raises:\n ErrorResponse: For HTTP error status codes\n \"\"\"\n # Process response interceptors\n processed_response = response\n for interceptor in self.interceptors:\n processed_response = await interceptor.process_response(processed_response)\n\n if 200 <= processed_response.status_code < 300:\n return processed_response.text\n else:\n error_data = {'message': processed_response.text}\n raise ErrorResponse(error_data, processed_response.status_code, dict(processed_response.headers))\n\n async def bytes(self, response: httpx.Response) -> bytes:\n \"\"\"Get response as bytes.\n \n Args:\n response: HTTP response\n \n Returns:\n Response body as bytes\n \n Raises:\n ErrorResponse: For HTTP error status codes\n \"\"\"\n # Process response interceptors\n processed_response = response\n for interceptor in self.interceptors:\n processed_response = await interceptor.process_response(processed_response)\n\n if 200 <= processed_response.status_code < 300:\n return processed_response.content\n else:\n error_data = {'message': 'Binary response error'}\n raise ErrorResponse(error_data, processed_response.status_code, dict(processed_response.headers))\n\n\n# Convenience functions for common use cases\nasync def quick_request(\n method: str,\n url: str,\n interceptors: Optional[List[Interceptor]] = None,\n **kwargs\n) -> httpx.Response:\n \"\"\"Make a quick HTTP request with interceptors.\n \n Args:\n method: HTTP method\n url: Request URL\n interceptors: List of interceptors to apply\n **kwargs: Additional request configuration\n \n Returns:\n HTTP response\n \"\"\"\n config = RequestConfig(method=method, url=url, **kwargs)\n \n async with Dispatcher(interceptors=interceptors) as dispatcher:\n return await dispatcher.request(config)\n\n\nasync def quick_json_request(\n method: str,\n url: str,\n json_data: Optional[Dict[str, Any]] = None,\n interceptors: Optional[List[Interceptor]] = None,\n success_model: Optional[type] = None,\n error_model: Optional[type] = None,\n **kwargs\n) -> Any:\n \"\"\"Make a quick JSON HTTP request with interceptors.\n \n Args:\n method: HTTP method\n url: Request URL\n json_data: JSON data to send\n interceptors: List of interceptors to apply\n success_model: Pydantic model for successful responses\n error_model: Pydantic model for error responses\n **kwargs: Additional request configuration\n \n Returns:\n Parsed JSON response\n \"\"\"\n config = RequestConfig(method=method, url=url, json_data=json_data, **kwargs)\n \n async with Dispatcher(interceptors=interceptors) as dispatcher:\n response = await dispatcher.request(config)\n receiver = Receiver(interceptors=interceptors)\n return await receiver.json(response, success_model, error_model)\n", "\"\"\"HTTP interceptors for request/response processing.\"\"\"\n\nimport asyncio\nimport logging\nimport time\nfrom abc import ABC, abstractmethod\nfrom typing import Dict, Optional, List, Any, Union\nfrom urllib.parse import urljoin\n\nimport httpx\n\nfrom .dispatcher import RequestConfig\n\n\nclass Interceptor(ABC):\n \"\"\"Base class for HTTP interceptors.\"\"\"\n\n @abstractmethod\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Process an outgoing request.\n\n Args:\n config: The request configuration to process\n\n Returns:\n The modified request configuration\n \"\"\"\n pass\n\n @abstractmethod\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Process an incoming response.\n\n Args:\n response: The HTTP response to process\n\n Returns:\n The processed response\n \"\"\"\n pass\n\n\nclass BaseUrlInterceptor(Interceptor):\n \"\"\"Interceptor that prepends base URL to relative URLs.\"\"\"\n\n def __init__(self, base_url: str):\n \"\"\"Initialize the base URL interceptor.\n\n Args:\n base_url: The base URL to prepend to relative URLs\n \"\"\"\n self.base_url = base_url.rstrip('/')\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Prepend base URL if the request URL is relative.\n\n Args:\n config: The request configuration\n\n Returns:\n The modified request configuration with absolute URL\n \"\"\"\n if not config.url.startswith(('http://', 'https://')):\n # Use urljoin for proper URL joining, ensuring single slash\n config.url = urljoin(self.base_url + '/', config.url.lstrip('/'))\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\nclass LoggingInterceptor(Interceptor):\n \"\"\"Interceptor that logs requests and responses using Python's logging module.\"\"\"\n\n def __init__(\n self,\n enabled: bool = True,\n logger: Optional[logging.Logger] = None,\n log_level: int = logging.INFO,\n include_headers: bool = True,\n include_sensitive_headers: bool = False\n ):\n \"\"\"Initialize the logging interceptor.\n\n Args:\n enabled: Whether logging is enabled\n logger: Custom logger instance (creates default if None)\n log_level: Logging level to use\n include_headers: Whether to log request/response headers\n include_sensitive_headers: Whether to log sensitive headers like Authorization\n \"\"\"\n self.enabled = enabled\n self.logger = logger or logging.getLogger(__name__)\n self.log_level = log_level\n self.include_headers = include_headers\n self.include_sensitive_headers = include_sensitive_headers\n self._sensitive_headers = {'authorization', 'x-api-key', 'cookie', 'set-cookie'}\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Log outgoing request.\n\n Args:\n config: The request configuration\n\n Returns:\n The unmodified request configuration\n \"\"\"\n if not self.enabled:\n return config\n\n self.logger.log(self.log_level, f\"\u2192 {config.method.upper()} {config.url}\")\n\n if self.include_headers and config.headers:\n for key, value in config.headers.items():\n if (key.lower() in self._sensitive_headers and\n not self.include_sensitive_headers):\n self.logger.log(self.log_level, f\" {key}: [REDACTED]\")\n else:\n self.logger.log(self.log_level, f\" {key}: {value}\")\n\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Log incoming response.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n if not self.enabled:\n return response\n\n status_icon = \"\u2713\" if 200 <= response.status_code < 300 else \"\u2717\"\n self.logger.log(\n self.log_level,\n f\"\u2190 {status_icon} {response.status_code} {response.reason_phrase or ''}\"\n )\n\n if self.include_headers and response.headers:\n for key, value in response.headers.items():\n if (key.lower() in self._sensitive_headers and\n not self.include_sensitive_headers):\n self.logger.log(self.log_level, f\" {key}: [REDACTED]\")\n else:\n self.logger.log(self.log_level, f\" {key}: {value}\")\n\n return response\n\n\nclass AuthInterceptor(Interceptor):\n \"\"\"Interceptor that adds authentication headers.\"\"\"\n\n def __init__(\n self,\n token: Optional[str] = None,\n api_key: Optional[str] = None,\n api_key_header: str = 'X-API-Key',\n auth_type: str = 'Bearer'\n ):\n \"\"\"Initialize the authentication interceptor.\n\n Args:\n token: Bearer token for Authorization header\n api_key: API key value\n api_key_header: Header name for API key\n auth_type: Type of authentication (Bearer, Basic, etc.)\n \"\"\"\n self.token = token\n self.api_key = api_key\n self.api_key_header = api_key_header\n self.auth_type = auth_type\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Add authentication headers.\n\n Args:\n config: The request configuration\n\n Returns:\n The modified request configuration with auth headers\n \"\"\"\n if config.headers is None:\n config.headers = {}\n\n if self.token:\n config.headers['Authorization'] = f'{self.auth_type} {self.token}'\n elif self.api_key:\n config.headers[self.api_key_header] = self.api_key\n\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\nclass RetryInterceptor(Interceptor):\n \"\"\"Interceptor that retries failed requests with exponential backoff.\"\"\"\n\n def __init__(\n self,\n max_retries: int = 3,\n retry_delay: float = 1.0,\n backoff_factor: float = 2.0,\n retry_on_status: Optional[List[int]] = None,\n retry_on_exceptions: Optional[List[type]] = None\n ):\n \"\"\"Initialize the retry interceptor.\n\n Args:\n max_retries: Maximum number of retry attempts\n retry_delay: Initial delay between retries in seconds\n backoff_factor: Exponential backoff multiplier\n retry_on_status: HTTP status codes that should trigger retries\n retry_on_exceptions: Exception types that should trigger retries\n \"\"\"\n self.max_retries = max_retries\n self.retry_delay = retry_delay\n self.backoff_factor = backoff_factor\n self.retry_on_status = retry_on_status or [500, 502, 503, 504, 408, 429]\n self.retry_on_exceptions = retry_on_exceptions or [\n httpx.TimeoutException,\n httpx.ConnectError,\n httpx.RemoteProtocolError\n ]\n self._original_request_func = None\n self.logger = logging.getLogger(__name__)\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Store original request for potential retries.\n\n Args:\n config: The request configuration\n\n Returns:\n The unmodified request configuration\n \"\"\"\n # Store the original config for retries\n self._original_config = config.model_copy() if hasattr(config, 'model_copy') else config\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Check if response needs retry and handle accordingly.\n\n Args:\n response: The HTTP response\n\n Returns:\n The response (possibly after retries)\n \"\"\"\n # For retry logic to work properly, it needs to be integrated at the dispatcher level\n # This is a simplified version that just passes through\n # In a full implementation, the retry logic would need access to the original request method\n return response\n\n async def execute_with_retry(self, request_func, *args, **kwargs) -> httpx.Response:\n \"\"\"Execute a request function with retry logic.\n\n Args:\n request_func: Function that executes the HTTP request\n *args: Arguments to pass to request_func\n **kwargs: Keyword arguments to pass to request_func\n\n Returns:\n The HTTP response after potential retries\n\n Raises:\n The last exception encountered if all retries fail\n \"\"\"\n last_exception = None\n\n for attempt in range(self.max_retries + 1):\n try:\n response = await request_func(*args, **kwargs)\n\n # Check if response status requires retry\n if response.status_code not in self.retry_on_status:\n return response\n\n if attempt == self.max_retries:\n self.logger.warning(\n f\"Max retries ({self.max_retries}) reached for request. \"\n f\"Final status: {response.status_code}\"\n )\n return response\n\n # Wait before retry\n delay = self.retry_delay * (self.backoff_factor ** attempt)\n self.logger.info(\n f\"Retrying request (attempt {attempt + 1}/{self.max_retries + 1}) \"\n f\"after {delay:.2f}s due to status {response.status_code}\"\n )\n await asyncio.sleep(delay)\n\n except Exception as e:\n # Check if exception type requires retry\n if not any(isinstance(e, exc_type) for exc_type in self.retry_on_exceptions):\n raise e\n\n last_exception = e\n\n if attempt == self.max_retries:\n self.logger.error(\n f\"Max retries ({self.max_retries}) reached. \"\n f\"Final exception: {type(e).__name__}: {e}\"\n )\n raise e\n\n # Wait before retry\n delay = self.retry_delay * (self.backoff_factor ** attempt)\n self.logger.info(\n f\"Retrying request (attempt {attempt + 1}/{self.max_retries + 1}) \"\n f\"after {delay:.2f}s due to {type(e).__name__}: {e}\"\n )\n await asyncio.sleep(delay)\n\n\nclass UserAgentInterceptor(Interceptor):\n \"\"\"Interceptor that adds a User-Agent header.\"\"\"\n\n def __init__(self, user_agent: str):\n \"\"\"Initialize the User-Agent interceptor.\n\n Args:\n user_agent: The User-Agent string to set\n \"\"\"\n self.user_agent = user_agent\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Add User-Agent header if not already present.\n\n Args:\n config: The request configuration\n\n Returns:\n The modified request configuration with User-Agent header\n \"\"\"\n if config.headers is None:\n config.headers = {}\n\n # Only set User-Agent if not already present (case-insensitive check)\n has_user_agent = any(\n key.lower() == 'user-agent'\n for key in config.headers.keys()\n )\n\n if not has_user_agent:\n config.headers['User-Agent'] = self.user_agent\n\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\nclass TimeoutInterceptor(Interceptor):\n \"\"\"Interceptor that sets request timeouts.\"\"\"\n\n def __init__(self, timeout: Union[float, httpx.Timeout]):\n \"\"\"Initialize the timeout interceptor.\n\n Args:\n timeout: Timeout value in seconds or httpx.Timeout object\n \"\"\"\n self.timeout = timeout\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Set timeout for the request.\n\n Args:\n config: The request configuration\n\n Returns:\n The modified request configuration with timeout\n \"\"\"\n if config.timeout is None:\n config.timeout = self.timeout\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\nclass RateLimitInterceptor(Interceptor):\n \"\"\"Interceptor that implements client-side rate limiting.\"\"\"\n\n def __init__(self, max_requests: int, time_window: float = 60.0):\n \"\"\"Initialize the rate limit interceptor.\n\n Args:\n max_requests: Maximum number of requests allowed in the time window\n time_window: Time window in seconds\n \"\"\"\n self.max_requests = max_requests\n self.time_window = time_window\n self.requests = []\n self._lock = asyncio.Lock()\n\n async def process_request(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Apply rate limiting before request.\n\n Args:\n config: The request configuration\n\n Returns:\n The unmodified request configuration\n \"\"\"\n async with self._lock:\n now = time.time()\n\n # Remove requests outside the time window\n self.requests = [req_time for req_time in self.requests\n if now - req_time < self.time_window]\n\n # Check if we've exceeded the rate limit\n if len(self.requests) >= self.max_requests:\n # Calculate how long to wait\n oldest_request = min(self.requests)\n wait_time = self.time_window - (now - oldest_request)\n\n if wait_time > 0:\n await asyncio.sleep(wait_time)\n\n # Record this request\n self.requests.append(now)\n\n return config\n\n async def process_response(self, response: httpx.Response) -> httpx.Response:\n \"\"\"Pass through response unchanged.\n\n Args:\n response: The HTTP response\n\n Returns:\n The unmodified response\n \"\"\"\n return response\n\n\n# Factory functions for convenient interceptor creation\ndef create_base_url_interceptor(base_url: str) -> BaseUrlInterceptor:\n \"\"\"Create a BaseUrlInterceptor instance.\n\n Args:\n base_url: The base URL to prepend to relative URLs\n\n Returns:\n Configured BaseUrlInterceptor instance\n \"\"\"\n return BaseUrlInterceptor(base_url)\n\n\ndef create_logging_interceptor(\n enabled: bool = True,\n log_level: int = logging.INFO,\n include_headers: bool = True,\n include_sensitive_headers: bool = False\n) -> LoggingInterceptor:\n \"\"\"Create a LoggingInterceptor instance.\n\n Args:\n enabled: Whether logging is enabled\n log_level: Logging level to use\n include_headers: Whether to log headers\n include_sensitive_headers: Whether to log sensitive headers\n\n Returns:\n Configured LoggingInterceptor instance\n \"\"\"\n return LoggingInterceptor(\n enabled=enabled,\n log_level=log_level,\n include_headers=include_headers,\n include_sensitive_headers=include_sensitive_headers\n )\n\n\ndef create_auth_interceptor(\n token: Optional[str] = None,\n api_key: Optional[str] = None,\n api_key_header: str = 'X-API-Key',\n auth_type: str = 'Bearer'\n) -> AuthInterceptor:\n \"\"\"Create an AuthInterceptor instance.\n\n Args:\n token: Bearer token for Authorization header\n api_key: API key value\n api_key_header: Header name for API key\n auth_type: Type of authentication\n\n Returns:\n Configured AuthInterceptor instance\n \"\"\"\n return AuthInterceptor(\n token=token,\n api_key=api_key,\n api_key_header=api_key_header,\n auth_type=auth_type\n )\n\n\ndef create_retry_interceptor(\n max_retries: int = 3,\n retry_delay: float = 1.0,\n backoff_factor: float = 2.0,\n retry_on_status: Optional[List[int]] = None\n) -> RetryInterceptor:\n \"\"\"Create a RetryInterceptor instance.\n\n Args:\n max_retries: Maximum number of retry attempts\n retry_delay: Initial delay between retries in seconds\n backoff_factor: Exponential backoff multiplier\n retry_on_status: HTTP status codes that should trigger retries\n\n Returns:\n Configured RetryInterceptor instance\n \"\"\"\n return RetryInterceptor(\n max_retries=max_retries,\n retry_delay=retry_delay,\n backoff_factor=backoff_factor,\n retry_on_status=retry_on_status\n )\n\n\ndef create_user_agent_interceptor(user_agent: str) -> UserAgentInterceptor:\n \"\"\"Create a UserAgentInterceptor instance.\n\n Args:\n user_agent: The User-Agent string to set\n\n Returns:\n Configured UserAgentInterceptor instance\n \"\"\"\n return UserAgentInterceptor(user_agent)\n", "\"\"\"HTTP response models and exceptions.\"\"\"\n\nfrom typing import Any, Dict, Optional, Union\n\nimport httpx\nfrom pydantic import BaseModel\n\n\nclass ApiResponse(BaseModel):\n \"\"\"Base class for API responses.\"\"\"\n\n status_code: int\n headers: Dict[str, str]\n data: Any\n\n class Config:\n \"\"\"Pydantic configuration.\"\"\"\n arbitrary_types_allowed = True\n\n\nclass SuccessResponse(ApiResponse):\n \"\"\"Represents a successful API response.\"\"\"\n\n def __init__(self, data: Any, status_code: int = 200, headers: Optional[Dict[str, str]] = None):\n \"\"\"Initialize success response.\n\n Args:\n data: Response data\n status_code: HTTP status code\n headers: Response headers\n \"\"\"\n super().__init__(\n status_code=status_code,\n headers=headers or {},\n data=data\n )\n\n\nclass ErrorResponse(Exception):\n \"\"\"Exception raised for HTTP error responses.\"\"\"\n\n def __init__(\n self,\n data: Any,\n status_code: int,\n headers: Optional[Dict[str, str]] = None,\n message: Optional[str] = None\n ):\n \"\"\"Initialize error response.\n\n Args:\n data: Error response data\n status_code: HTTP status code\n headers: Response headers\n message: Custom error message\n \"\"\"\n self.data = data\n self.status_code = status_code\n self.headers = headers or {}\n self.message = message or f\"HTTP {status_code} Error\"\n\n super().__init__(self.message)\n\n def __str__(self) -> str:\n \"\"\"String representation of the error.\"\"\"\n return f\"ErrorResponse(status_code={self.status_code}, message='{self.message}')\"\n\n def __repr__(self) -> str:\n \"\"\"Detailed string representation of the error.\"\"\"\n return (\n f\"ErrorResponse(status_code={self.status_code}, \"\n f\"message='{self.message}', data={self.data})\"\n )\n\n\nclass TimeoutError(ErrorResponse):\n \"\"\"Exception raised for request timeouts.\"\"\"\n\n def __init__(self, message: str = \"Request timed out\"):\n \"\"\"Initialize timeout error.\n\n Args:\n message: Error message\n \"\"\"\n super().__init__(\n data={'error': 'timeout'},\n status_code=408,\n message=message\n )\n\n\nclass ConnectionError(ErrorResponse):\n \"\"\"Exception raised for connection errors.\"\"\"\n\n def __init__(self, message: str = \"Connection failed\"):\n \"\"\"Initialize connection error.\n\n Args:\n message: Error message\n \"\"\"\n super().__init__(\n data={'error': 'connection'},\n status_code=503,\n message=message\n )\n\n\nclass BadRequestError(ErrorResponse):\n \"\"\"Exception raised for 400 Bad Request errors.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Bad Request\"):\n \"\"\"Initialize bad request error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'bad_request'},\n status_code=400,\n message=message\n )\n\n\nclass UnauthorizedError(ErrorResponse):\n \"\"\"Exception raised for 401 Unauthorized errors.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Unauthorized\"):\n \"\"\"Initialize unauthorized error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'unauthorized'},\n status_code=401,\n message=message\n )\n\n\nclass ForbiddenError(ErrorResponse):\n \"\"\"Exception raised for 403 Forbidden errors.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Forbidden\"):\n \"\"\"Initialize forbidden error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'forbidden'},\n status_code=403,\n message=message\n )\n\n\nclass NotFoundError(ErrorResponse):\n \"\"\"Exception raised for 404 Not Found errors.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Not Found\"):\n \"\"\"Initialize not found error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'not_found'},\n status_code=404,\n message=message\n )\n\n\nclass InternalServerError(ErrorResponse):\n \"\"\"Exception raised for 500 Internal Server Error.\"\"\"\n\n def __init__(self, data: Any = None, message: str = \"Internal Server Error\"):\n \"\"\"Initialize internal server error.\n\n Args:\n data: Error data\n message: Error message\n \"\"\"\n super().__init__(\n data=data or {'error': 'internal_server_error'},\n status_code=500,\n message=message\n )\n\n\ndef create_error_from_response(response: httpx.Response) -> ErrorResponse:\n \"\"\"Create appropriate error exception from HTTP response.\n\n Args:\n response: HTTP response\n\n Returns:\n Appropriate error exception\n \"\"\"\n status_code = response.status_code\n headers = dict(response.headers)\n\n # Try to parse error data\n try:\n data = response.json()\n except Exception:\n data = {'message': response.text}\n\n # Create specific error types based on status code\n error_classes = {\n 400: BadRequestError,\n 401: UnauthorizedError,\n 403: ForbiddenError,\n 404: NotFoundError,\n 500: InternalServerError,\n }\n\n error_class = error_classes.get(status_code, ErrorResponse)\n\n if error_class == ErrorResponse:\n return ErrorResponse(data, status_code, headers)\n else:\n return error_class(data)\n", "import type { ReferenceObject, SchemaObject } from 'openapi3-ts/oas31';\nimport { snakecase } from 'stringcase';\n\nimport { isRef, notRef, parseRef, pascalcase } from '@sdk-it/core';\nimport { type IR, isPrimitiveSchema } from '@sdk-it/spec';\n\nexport function coerceObject(schema: SchemaObject): SchemaObject {\n schema = structuredClone(schema);\n if (schema['x-properties']) {\n schema.properties = {\n ...(schema.properties ?? {}),\n ...(schema['x-properties'] ?? {}),\n };\n }\n if (schema['x-required']) {\n schema.required = Array.from(\n new Set([\n ...(Array.isArray(schema.required) ? schema.required : []),\n ...(schema['x-required'] || []),\n ]),\n );\n }\n return schema;\n}\n\ntype Context = Record<string, unknown>;\ntype Serialized = {\n nullable?: boolean;\n encode?: string;\n encodeV2?: string;\n use: string;\n matches?: string;\n fromJson: unknown;\n type?: string;\n literal?: unknown;\n content: string;\n simple?: boolean;\n};\ntype Emit = (name: string, content: string, schema: SchemaObject) => void;\n\n/**\n * Convert an OpenAPI (JSON Schema style) object into Python classes with Pydantic\n */\nexport class PythonEmitter {\n #spec: IR;\n #emitHandler?: Emit;\n #emitHistory = new Set<string>();\n #typeCache = new Map<string, Serialized>(); // Cache for resolved types\n\n #emit(name: string, content: string, schema: SchemaObject): void {\n if (this.#emitHistory.has(content)) {\n return;\n }\n this.#emitHistory.add(content);\n this.#emitHandler?.(name, content, schema);\n }\n\n constructor(spec: IR) {\n this.#spec = spec;\n }\n\n onEmit(emit: Emit): void {\n this.#emitHandler = emit;\n }\n\n #formatFieldName(name: string): string {\n // Convert to snake_case and handle special cases\n let fieldName = snakecase(name);\n\n // Handle reserved keywords\n const reservedKeywords = [\n 'class',\n 'def',\n 'if',\n 'else',\n 'elif',\n 'while',\n 'for',\n 'try',\n 'except',\n 'finally',\n 'with',\n 'as',\n 'import',\n 'from',\n 'global',\n 'nonlocal',\n 'lambda',\n 'yield',\n 'return',\n 'pass',\n 'break',\n 'continue',\n 'True',\n 'False',\n 'None',\n 'and',\n 'or',\n 'not',\n 'in',\n 'is',\n ];\n\n if (reservedKeywords.includes(fieldName)) {\n fieldName = `${fieldName}_`;\n }\n\n return fieldName;\n }\n\n #ref(ref: ReferenceObject): Serialized {\n const cacheKey = ref.$ref;\n const cached = this.#typeCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const refInfo = parseRef(ref.$ref);\n const refName = refInfo.model;\n const className = pascalcase(refName);\n\n const result: Serialized = {\n type: className,\n content: '',\n use: className,\n fromJson: `${className}.parse_obj`,\n simple: false,\n };\n\n this.#typeCache.set(cacheKey, result);\n return result;\n }\n\n #oneOf(\n variants: (SchemaObject | ReferenceObject)[],\n context: Context,\n ): Serialized {\n const variantTypes = variants\n .map((variant) => this.handle(variant, context))\n .map((result) => result.type || 'Any')\n .filter((type, index, arr) => arr.indexOf(type) === index); // Remove duplicates\n\n if (variantTypes.length === 0) {\n return {\n type: 'Any',\n content: '',\n use: 'Any',\n fromJson: 'Any',\n simple: true,\n };\n }\n\n if (variantTypes.length === 1) {\n return {\n type: variantTypes[0],\n content: '',\n use: variantTypes[0],\n fromJson: variantTypes[0],\n simple: true,\n };\n }\n\n const unionType = `Union[${variantTypes.join(', ')}]`;\n return {\n type: unionType,\n content: '',\n use: unionType,\n fromJson: unionType,\n simple: true,\n };\n }\n\n #object(\n className: string,\n schema: SchemaObject,\n context: Context,\n ): Serialized {\n const { properties = {}, required = [] } = coerceObject(schema);\n\n const fields: string[] = [];\n\n // Handle allOf inheritance\n let baseClass = 'BaseModel';\n if (schema.allOf) {\n const bases = schema.allOf\n .filter(notRef)\n .map((s) => this.handle(s, context))\n .filter((result) => result.type)\n .map((result) => result.type);\n\n if (bases.length > 0 && bases[0]) {\n baseClass = bases[0];\n }\n }\n\n // Process properties\n for (const [propName, propSchema] of Object.entries(properties)) {\n if (isRef(propSchema)) {\n this.#ref(propSchema);\n const refInfo = parseRef(propSchema.$ref);\n const refName = refInfo.model;\n const pythonType = pascalcase(refName);\n\n const fieldName = this.#formatFieldName(propName);\n const isRequired = required.includes(propName);\n const fieldType = isRequired ? pythonType : `Optional[${pythonType}]`;\n const defaultValue = isRequired ? '' : ' = None';\n\n fields.push(` ${fieldName}: ${fieldType}${defaultValue}`);\n } else {\n const result = this.handle(propSchema, { ...context, name: propName });\n const fieldName = this.#formatFieldName(propName);\n const isRequired = required.includes(propName);\n\n let fieldType = result.type || 'Any';\n if (!isRequired) {\n fieldType = `Optional[${fieldType}]`;\n }\n\n const defaultValue = isRequired ? '' : ' = None';\n let fieldDef = ` ${fieldName}: ${fieldType}${defaultValue}`;\n\n // Add Field() for alias or validation if needed\n if (fieldName !== propName) {\n fieldDef = ` ${fieldName}: ${fieldType} = Field(alias='${propName}'${defaultValue ? ', default=None' : ''})`;\n }\n\n // Add description as comment if available\n if (propSchema.description) {\n fieldDef += ` # ${propSchema.description}`;\n }\n\n fields.push(fieldDef);\n }\n }\n\n // Handle oneOf/anyOf as Union types using centralized logic\n if (schema.oneOf || schema.anyOf) {\n const unionResult = this.#oneOf(\n schema.oneOf || schema.anyOf || [],\n context,\n );\n fields.push(` value: ${unionResult.type}`);\n }\n\n // Handle additionalProperties\n if (\n schema.additionalProperties &&\n typeof schema.additionalProperties === 'object'\n ) {\n const addlResult = this.handle(schema.additionalProperties, context);\n fields.push(\n ` additional_properties: Optional[Dict[str, ${addlResult.type || 'Any'}]] = None`,\n );\n }\n\n // Generate class docstring\n const docstring = schema.description\n ? ` \"\"\"${schema.description}\"\"\"\\n`\n : '';\n\n // Generate to_request_config method for input models\n let requestConfigMethod = '';\n if (schema['x-inputname']) {\n requestConfigMethod = `\n def to_request_config(self, config: RequestConfig) -> RequestConfig:\n \"\"\"Convert this input model to request configuration.\"\"\"\n # Handle path parameters\n path_params = {}\n for key, value in self.dict(exclude_none=True).items():\n if key in config.url:\n path_params[key] = str(value)\n config.url = config.url.replace(f'{{{key}}}', str(value))\n\n # Handle query parameters\n query_params = {k: v for k, v in self.dict(exclude_none=True).items()\n if k not in path_params}\n if query_params:\n config.params = query_params\n\n return config\n`;\n }\n\n const content = `class ${className}(${baseClass}):\n${docstring}${fields.length > 0 ? fields.join('\\n') : ' pass'}${requestConfigMethod}\n`;\n\n this.#emit(className, content, schema);\n\n return {\n type: className,\n content,\n use: className,\n fromJson: `${className}.parse_obj`,\n simple: false,\n };\n }\n\n #primitive(schema: SchemaObject): Serialized {\n const { type, format } = schema;\n const nullable = (schema as { nullable?: boolean }).nullable; // Handle nullable as it may not be in the type definition\n\n let pythonType = 'Any';\n\n switch (type) {\n case 'string':\n if (format === 'date-time') {\n pythonType = 'datetime';\n } else if (format === 'date') {\n pythonType = 'date';\n } else if (format === 'uuid') {\n pythonType = 'UUID';\n } else if (format === 'binary' || format === 'byte') {\n pythonType = 'bytes';\n } else {\n pythonType = 'str';\n }\n break;\n\n case 'integer':\n if (format === 'int64') {\n pythonType = 'int'; // Python 3 ints are arbitrary precision\n } else {\n pythonType = 'int';\n }\n break;\n\n case 'number':\n pythonType = 'float';\n break;\n\n case 'boolean':\n pythonType = 'bool';\n break;\n\n default:\n pythonType = 'Any';\n }\n\n if (nullable) {\n pythonType = `Optional[${pythonType}]`;\n }\n\n return {\n type: pythonType,\n content: '',\n use: pythonType,\n fromJson: pythonType,\n simple: true,\n nullable,\n };\n }\n\n #array(schema: SchemaObject, context: Context): Serialized {\n const itemsSchema = schema.items;\n if (!itemsSchema) {\n return {\n type: 'List[Any]',\n content: '',\n use: 'List[Any]',\n fromJson: 'list',\n simple: true,\n };\n }\n\n const itemsResult = this.handle(itemsSchema, context);\n const listType = `List[${itemsResult.type || 'Any'}]`;\n\n return {\n type: listType,\n content: itemsResult.content,\n use: listType,\n fromJson: `List[${itemsResult.fromJson || itemsResult.type}]`,\n simple: true,\n };\n }\n\n #enum(schema: SchemaObject, _context: Context): Serialized {\n const { enum: enumValues } = schema;\n if (!enumValues || enumValues.length === 0) {\n return this.#primitive(schema);\n }\n\n if (!_context.name || typeof _context.name !== 'string') {\n throw new Error('Enum schemas must have a name in context');\n }\n\n const className = pascalcase(_context.name as string);\n\n const enumItems = enumValues.map((value, index) => {\n const name =\n typeof value === 'string'\n ? value.toUpperCase().replace(/[^A-Z0-9]/g, '_')\n : `VALUE_${index}`;\n\n const pythonValue =\n typeof value === 'string' ? `'${value}'` : String(value);\n return ` ${name} = ${pythonValue}`;\n });\n\n const content = `class ${className}(Enum):\n \"\"\"Enumeration for ${_context.name}.\"\"\"\n${enumItems.join('\\n')}\n`;\n\n this.#emit(className, content, schema);\n\n return {\n type: className,\n content,\n use: className,\n fromJson: className,\n simple: false,\n };\n }\n\n #const(schema: SchemaObject): Serialized {\n const { const: constValue } = schema;\n\n if (typeof constValue === 'string') {\n return {\n type: `Literal['${constValue}']`,\n content: '',\n use: `Literal['${constValue}']`,\n fromJson: `'${constValue}'`,\n simple: true,\n literal: constValue,\n };\n }\n\n return {\n type: `Literal[${JSON.stringify(constValue)}]`,\n content: '',\n use: `Literal[${JSON.stringify(constValue)}]`,\n fromJson: JSON.stringify(constValue),\n simple: true,\n literal: constValue,\n };\n }\n handle(\n schema: SchemaObject | ReferenceObject,\n context: Context = {},\n ): Serialized {\n if (isRef(schema)) {\n return this.#ref(schema);\n }\n\n // Handle const values\n if ('const' in schema && schema.const !== undefined) {\n return this.#const(schema);\n }\n\n // Handle enums\n if (schema.enum) {\n return this.#enum(schema, context);\n }\n\n // Handle arrays\n if (schema.type === 'array') {\n return this.#array(schema, context);\n }\n\n // Handle oneOf/anyOf at top level using centralized logic\n if (schema.oneOf || schema.anyOf) {\n return this.#oneOf(schema.oneOf || schema.anyOf || [], context);\n }\n\n // Handle objects\n if (\n schema.type === 'object' ||\n schema.properties ||\n schema.allOf ||\n schema.oneOf ||\n schema.anyOf\n ) {\n if (!context.name || typeof context.name !== 'string') {\n throw new Error('Object schemas must have a name in context');\n }\n const className = pascalcase(context.name as string);\n return this.#object(className, schema, context);\n }\n\n // Handle primitives\n if (isPrimitiveSchema(schema)) {\n return this.#primitive(schema);\n }\n\n // Fallback to Any\n return {\n type: 'Any',\n content: '',\n use: 'Any',\n fromJson: 'Any',\n simple: true,\n };\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;AAOrB,SAAS,aAAAA,kBAAiB;AAE1B,SAAS,SAAS,SAAAC,QAAO,cAAAC,mBAAkB;AAC3C;AAAA,EAGE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACzBP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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",
|
|
6
6
|
"names": ["snakecase", "isRef", "pascalcase", "pascalcase", "snakecase", "isRef"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/lib/generate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/lib/generate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EAId,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,MAAM,EAGZ,MAAM,6BAA6B,CAAC;AAgBrC,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE;IACR,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE,iBAkSF"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { ReferenceObject, SchemaObject } from 'openapi3-ts/oas31';
|
|
2
2
|
import { type IR } from '@sdk-it/spec';
|
|
3
3
|
export declare function coerceObject(schema: SchemaObject): SchemaObject;
|
|
4
|
-
type Context = Record<string,
|
|
4
|
+
type Context = Record<string, unknown>;
|
|
5
5
|
type Serialized = {
|
|
6
6
|
nullable?: boolean;
|
|
7
7
|
encode?: string;
|
|
8
8
|
encodeV2?: string;
|
|
9
9
|
use: string;
|
|
10
10
|
matches?: string;
|
|
11
|
-
fromJson:
|
|
11
|
+
fromJson: unknown;
|
|
12
12
|
type?: string;
|
|
13
13
|
literal?: unknown;
|
|
14
14
|
content: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"python-emitter.d.ts","sourceRoot":"","sources":["../../src/lib/python-emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIvE,OAAO,EAAE,KAAK,EAAE,EAAqB,MAAM,cAAc,CAAC;AAE1D,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAiB/D;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"python-emitter.d.ts","sourceRoot":"","sources":["../../src/lib/python-emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIvE,OAAO,EAAE,KAAK,EAAE,EAAqB,MAAM,cAAc,CAAC;AAE1D,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAiB/D;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACvC,KAAK,UAAU,GAAG;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AACF,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;AAE1E;;GAEG;AACH,qBAAa,aAAa;;gBAcZ,IAAI,EAAE,EAAE;IAIpB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IA2XxB,MAAM,CACJ,MAAM,EAAE,YAAY,GAAG,eAAe,EACtC,OAAO,GAAE,OAAY,GACpB,UAAU;CAsDd"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdk-it/python",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -28,11 +28,8 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"openapi3-ts": "4.5.0",
|
|
31
|
-
"@sdk-it/core": "0.
|
|
32
|
-
"lodash-es": "^4.17.21",
|
|
31
|
+
"@sdk-it/core": "0.41.0",
|
|
33
32
|
"stringcase": "^4.3.1",
|
|
34
|
-
"@sdk-it/spec": "0.
|
|
35
|
-
"fast-content-type-parse": "^3.0.0",
|
|
36
|
-
"yaml": "^2.7.0"
|
|
33
|
+
"@sdk-it/spec": "0.41.0"
|
|
37
34
|
}
|
|
38
35
|
}
|