api-mocker 0.4.0__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,476 @@
1
+ """
2
+ WebSocket Mock Support System
3
+
4
+ This module provides comprehensive WebSocket mocking capabilities including:
5
+ - Real-time message handling
6
+ - Connection management
7
+ - Message routing and broadcasting
8
+ - Authentication and authorization
9
+ - Custom message handlers
10
+ - Connection state management
11
+ """
12
+
13
+ import asyncio
14
+ import json
15
+ import uuid
16
+ from typing import Any, Dict, List, Optional, Callable, Set
17
+ from dataclasses import dataclass, field
18
+ from enum import Enum
19
+ from datetime import datetime
20
+ import websockets
21
+ from websockets.server import WebSocketServerProtocol
22
+
23
+
24
+ class WebSocketMessageType(Enum):
25
+ """WebSocket message types"""
26
+ TEXT = "text"
27
+ BINARY = "binary"
28
+ PING = "ping"
29
+ PONG = "pong"
30
+ CLOSE = "close"
31
+
32
+
33
+ class WebSocketConnectionState(Enum):
34
+ """WebSocket connection states"""
35
+ CONNECTING = "connecting"
36
+ OPEN = "open"
37
+ CLOSING = "closing"
38
+ CLOSED = "closed"
39
+
40
+
41
+ @dataclass
42
+ class WebSocketMessage:
43
+ """Represents a WebSocket message"""
44
+ message_type: WebSocketMessageType
45
+ content: Any
46
+ timestamp: datetime = field(default_factory=datetime.now)
47
+ message_id: str = field(default_factory=lambda: str(uuid.uuid4()))
48
+ sender_id: Optional[str] = None
49
+ target_id: Optional[str] = None
50
+ room: Optional[str] = None
51
+ metadata: Dict[str, Any] = field(default_factory=dict)
52
+
53
+
54
+ @dataclass
55
+ class WebSocketConnection:
56
+ """Represents a WebSocket connection"""
57
+ connection_id: str
58
+ websocket: WebSocketServerProtocol
59
+ state: WebSocketConnectionState = WebSocketConnectionState.CONNECTING
60
+ user_id: Optional[str] = None
61
+ rooms: Set[str] = field(default_factory=set)
62
+ metadata: Dict[str, Any] = field(default_factory=dict)
63
+ created_at: datetime = field(default_factory=datetime.now)
64
+ last_activity: datetime = field(default_factory=datetime.now)
65
+ message_count: int = 0
66
+
67
+
68
+ @dataclass
69
+ class WebSocketRoom:
70
+ """Represents a WebSocket room for message broadcasting"""
71
+ name: str
72
+ connections: Set[str] = field(default_factory=set)
73
+ metadata: Dict[str, Any] = field(default_factory=dict)
74
+ created_at: datetime = field(default_factory=datetime.now)
75
+
76
+
77
+ @dataclass
78
+ class WebSocketMessageHandler:
79
+ """Represents a message handler for WebSocket messages"""
80
+ pattern: str
81
+ handler_func: Callable
82
+ description: Optional[str] = None
83
+ requires_auth: bool = False
84
+ rate_limit: Optional[int] = None # messages per minute
85
+
86
+
87
+ class WebSocketMockServer:
88
+ """Main WebSocket mock server implementation"""
89
+
90
+ def __init__(self, host: str = "localhost", port: int = 8765):
91
+ self.host = host
92
+ self.port = port
93
+ self.connections: Dict[str, WebSocketConnection] = {}
94
+ self.rooms: Dict[str, WebSocketRoom] = {}
95
+ self.message_handlers: List[WebSocketMessageHandler] = []
96
+ self.server: Optional[websockets.WebSocketServer] = None
97
+ self.running = False
98
+
99
+ # Statistics
100
+ self.total_connections = 0
101
+ self.total_messages = 0
102
+ self.active_connections = 0
103
+
104
+ # Setup default handlers
105
+ self._setup_default_handlers()
106
+
107
+ def _setup_default_handlers(self) -> None:
108
+ """Setup default message handlers"""
109
+ # Echo handler
110
+ self.add_message_handler(
111
+ pattern="echo",
112
+ handler_func=self._echo_handler,
113
+ description="Echo back the received message"
114
+ )
115
+
116
+ # Broadcast handler
117
+ self.add_message_handler(
118
+ pattern="broadcast",
119
+ handler_func=self._broadcast_handler,
120
+ description="Broadcast message to all connections"
121
+ )
122
+
123
+ # Room join handler
124
+ self.add_message_handler(
125
+ pattern="join",
126
+ handler_func=self._join_room_handler,
127
+ description="Join a room for group messaging"
128
+ )
129
+
130
+ # Room leave handler
131
+ self.add_message_handler(
132
+ pattern="leave",
133
+ handler_func=self._leave_room_handler,
134
+ description="Leave a room"
135
+ )
136
+
137
+ # Room message handler
138
+ self.add_message_handler(
139
+ pattern="room_message",
140
+ handler_func=self._room_message_handler,
141
+ description="Send message to a specific room"
142
+ )
143
+
144
+ # Stats handler
145
+ self.add_message_handler(
146
+ pattern="stats",
147
+ handler_func=self._stats_handler,
148
+ description="Get server statistics"
149
+ )
150
+
151
+ def add_message_handler(self, pattern: str, handler_func: Callable,
152
+ description: str = None, requires_auth: bool = False,
153
+ rate_limit: int = None) -> None:
154
+ """Add a custom message handler"""
155
+ handler = WebSocketMessageHandler(
156
+ pattern=pattern,
157
+ handler_func=handler_func,
158
+ description=description,
159
+ requires_auth=requires_auth,
160
+ rate_limit=rate_limit
161
+ )
162
+ self.message_handlers.append(handler)
163
+
164
+ async def start_server(self) -> None:
165
+ """Start the WebSocket server"""
166
+ if self.running:
167
+ return
168
+
169
+ self.server = await websockets.serve(
170
+ self._handle_connection,
171
+ self.host,
172
+ self.port
173
+ )
174
+ self.running = True
175
+ print(f"WebSocket mock server started on {self.host}:{self.port}")
176
+
177
+ async def stop_server(self) -> None:
178
+ """Stop the WebSocket server"""
179
+ if self.server:
180
+ self.server.close()
181
+ await self.server.wait_closed()
182
+ self.running = False
183
+ print("WebSocket mock server stopped")
184
+
185
+ async def _handle_connection(self, websocket: WebSocketServerProtocol, path: str) -> None:
186
+ """Handle a new WebSocket connection"""
187
+ connection_id = str(uuid.uuid4())
188
+ connection = WebSocketConnection(
189
+ connection_id=connection_id,
190
+ websocket=websocket,
191
+ state=WebSocketConnectionState.OPEN
192
+ )
193
+
194
+ self.connections[connection_id] = connection
195
+ self.total_connections += 1
196
+ self.active_connections += 1
197
+
198
+ print(f"New WebSocket connection: {connection_id}")
199
+
200
+ try:
201
+ # Send welcome message
202
+ await self._send_message(connection, {
203
+ "type": "welcome",
204
+ "connection_id": connection_id,
205
+ "message": "Connected to WebSocket mock server",
206
+ "timestamp": datetime.now().isoformat()
207
+ })
208
+
209
+ # Handle messages
210
+ async for message in websocket:
211
+ await self._process_message(connection, message)
212
+
213
+ except websockets.exceptions.ConnectionClosed:
214
+ print(f"WebSocket connection closed: {connection_id}")
215
+ except Exception as e:
216
+ print(f"WebSocket error: {e}")
217
+ finally:
218
+ await self._cleanup_connection(connection)
219
+
220
+ async def _process_message(self, connection: WebSocketConnection, message: str) -> None:
221
+ """Process an incoming WebSocket message"""
222
+ try:
223
+ # Parse message
224
+ if isinstance(message, str):
225
+ try:
226
+ data = json.loads(message)
227
+ except json.JSONDecodeError:
228
+ data = {"type": "text", "content": message}
229
+ else:
230
+ data = {"type": "binary", "content": message}
231
+
232
+ # Update connection activity
233
+ connection.last_activity = datetime.now()
234
+ connection.message_count += 1
235
+ self.total_messages += 1
236
+
237
+ # Find matching handler
238
+ handler = self._find_handler(data.get("type", ""))
239
+ if handler:
240
+ await handler.handler_func(connection, data)
241
+ else:
242
+ # Default handler - echo back
243
+ await self._echo_handler(connection, data)
244
+
245
+ except Exception as e:
246
+ print(f"Error processing message: {e}")
247
+ await self._send_error(connection, str(e))
248
+
249
+ def _find_handler(self, message_type: str) -> Optional[WebSocketMessageHandler]:
250
+ """Find a handler for the message type"""
251
+ for handler in self.message_handlers:
252
+ if handler.pattern == message_type:
253
+ return handler
254
+ return None
255
+
256
+ async def _echo_handler(self, connection: WebSocketConnection, data: Dict[str, Any]) -> None:
257
+ """Echo handler - echo back the received message"""
258
+ response = {
259
+ "type": "echo",
260
+ "original": data,
261
+ "timestamp": datetime.now().isoformat()
262
+ }
263
+ await self._send_message(connection, response)
264
+
265
+ async def _broadcast_handler(self, connection: WebSocketConnection, data: Dict[str, Any]) -> None:
266
+ """Broadcast handler - send message to all connections"""
267
+ message = data.get("message", "Broadcast message")
268
+ response = {
269
+ "type": "broadcast",
270
+ "message": message,
271
+ "sender": connection.connection_id,
272
+ "timestamp": datetime.now().isoformat()
273
+ }
274
+
275
+ # Send to all connections
276
+ for conn in self.connections.values():
277
+ if conn.state == WebSocketConnectionState.OPEN:
278
+ await self._send_message(conn, response)
279
+
280
+ async def _join_room_handler(self, connection: WebSocketConnection, data: Dict[str, Any]) -> None:
281
+ """Join room handler"""
282
+ room_name = data.get("room")
283
+ if not room_name:
284
+ await self._send_error(connection, "Room name required")
285
+ return
286
+
287
+ # Create room if it doesn't exist
288
+ if room_name not in self.rooms:
289
+ self.rooms[room_name] = WebSocketRoom(name=room_name)
290
+
291
+ # Add connection to room
292
+ self.rooms[room_name].connections.add(connection.connection_id)
293
+ connection.rooms.add(room_name)
294
+
295
+ response = {
296
+ "type": "room_joined",
297
+ "room": room_name,
298
+ "timestamp": datetime.now().isoformat()
299
+ }
300
+ await self._send_message(connection, response)
301
+
302
+ async def _leave_room_handler(self, connection: WebSocketConnection, data: Dict[str, Any]) -> None:
303
+ """Leave room handler"""
304
+ room_name = data.get("room")
305
+ if not room_name:
306
+ await self._send_error(connection, "Room name required")
307
+ return
308
+
309
+ # Remove connection from room
310
+ if room_name in self.rooms:
311
+ self.rooms[room_name].connections.discard(connection.connection_id)
312
+ connection.rooms.discard(room_name)
313
+
314
+ response = {
315
+ "type": "room_left",
316
+ "room": room_name,
317
+ "timestamp": datetime.now().isoformat()
318
+ }
319
+ await self._send_message(connection, response)
320
+
321
+ async def _room_message_handler(self, connection: WebSocketConnection, data: Dict[str, Any]) -> None:
322
+ """Room message handler"""
323
+ room_name = data.get("room")
324
+ message = data.get("message", "")
325
+
326
+ if not room_name:
327
+ await self._send_error(connection, "Room name required")
328
+ return
329
+
330
+ if room_name not in self.rooms:
331
+ await self._send_error(connection, "Room not found")
332
+ return
333
+
334
+ if connection.connection_id not in self.rooms[room_name].connections:
335
+ await self._send_error(connection, "Not in room")
336
+ return
337
+
338
+ # Send message to all connections in room
339
+ response = {
340
+ "type": "room_message",
341
+ "room": room_name,
342
+ "message": message,
343
+ "sender": connection.connection_id,
344
+ "timestamp": datetime.now().isoformat()
345
+ }
346
+
347
+ for conn_id in self.rooms[room_name].connections:
348
+ if conn_id in self.connections:
349
+ conn = self.connections[conn_id]
350
+ if conn.state == WebSocketConnectionState.OPEN:
351
+ await self._send_message(conn, response)
352
+
353
+ async def _stats_handler(self, connection: WebSocketConnection, data: Dict[str, Any]) -> None:
354
+ """Stats handler - return server statistics"""
355
+ stats = {
356
+ "type": "stats",
357
+ "total_connections": self.total_connections,
358
+ "active_connections": self.active_connections,
359
+ "total_messages": self.total_messages,
360
+ "rooms": len(self.rooms),
361
+ "handlers": len(self.message_handlers),
362
+ "timestamp": datetime.now().isoformat()
363
+ }
364
+ await self._send_message(connection, stats)
365
+
366
+ async def _send_message(self, connection: WebSocketConnection, data: Dict[str, Any]) -> None:
367
+ """Send a message to a connection"""
368
+ try:
369
+ if connection.state == WebSocketConnectionState.OPEN:
370
+ await connection.websocket.send(json.dumps(data))
371
+ except Exception as e:
372
+ print(f"Error sending message: {e}")
373
+
374
+ async def _send_error(self, connection: WebSocketConnection, error_message: str) -> None:
375
+ """Send an error message to a connection"""
376
+ error_data = {
377
+ "type": "error",
378
+ "message": error_message,
379
+ "timestamp": datetime.now().isoformat()
380
+ }
381
+ await self._send_message(connection, error_data)
382
+
383
+ async def _cleanup_connection(self, connection: WebSocketConnection) -> None:
384
+ """Cleanup a closed connection"""
385
+ connection.state = WebSocketConnectionState.CLOSED
386
+ self.active_connections -= 1
387
+
388
+ # Remove from all rooms
389
+ for room_name in connection.rooms.copy():
390
+ if room_name in self.rooms:
391
+ self.rooms[room_name].connections.discard(connection.connection_id)
392
+ # Remove empty rooms
393
+ if not self.rooms[room_name].connections:
394
+ del self.rooms[room_name]
395
+
396
+ # Remove connection
397
+ if connection.connection_id in self.connections:
398
+ del self.connections[connection.connection_id]
399
+
400
+ async def broadcast_to_room(self, room_name: str, message: Dict[str, Any]) -> None:
401
+ """Broadcast a message to all connections in a room"""
402
+ if room_name not in self.rooms:
403
+ return
404
+
405
+ for conn_id in self.rooms[room_name].connections:
406
+ if conn_id in self.connections:
407
+ conn = self.connections[conn_id]
408
+ if conn.state == WebSocketConnectionState.OPEN:
409
+ await self._send_message(conn, message)
410
+
411
+ async def send_to_connection(self, connection_id: str, message: Dict[str, Any]) -> None:
412
+ """Send a message to a specific connection"""
413
+ if connection_id in self.connections:
414
+ conn = self.connections[connection_id]
415
+ if conn.state == WebSocketConnectionState.OPEN:
416
+ await self._send_message(conn, message)
417
+
418
+ def get_connection_stats(self) -> Dict[str, Any]:
419
+ """Get connection statistics"""
420
+ return {
421
+ "total_connections": self.total_connections,
422
+ "active_connections": self.active_connections,
423
+ "total_messages": self.total_messages,
424
+ "rooms": len(self.rooms),
425
+ "handlers": len(self.message_handlers)
426
+ }
427
+
428
+ def get_room_info(self, room_name: str) -> Optional[Dict[str, Any]]:
429
+ """Get information about a room"""
430
+ if room_name not in self.rooms:
431
+ return None
432
+
433
+ room = self.rooms[room_name]
434
+ return {
435
+ "name": room.name,
436
+ "connections": len(room.connections),
437
+ "created_at": room.created_at.isoformat(),
438
+ "metadata": room.metadata
439
+ }
440
+
441
+
442
+ # Global WebSocket mock server instance
443
+ websocket_mock_server = WebSocketMockServer()
444
+
445
+
446
+ # Convenience functions
447
+ async def start_websocket_server(host: str = "localhost", port: int = 8765) -> WebSocketMockServer:
448
+ """Start a WebSocket mock server"""
449
+ server = WebSocketMockServer(host, port)
450
+ await server.start_server()
451
+ return server
452
+
453
+
454
+ def create_websocket_message_handler(pattern: str, handler_func: Callable) -> None:
455
+ """Add a custom WebSocket message handler"""
456
+ websocket_mock_server.add_message_handler(pattern, handler_func)
457
+
458
+
459
+ async def broadcast_message(room: str, message: str, message_type: str = "broadcast") -> None:
460
+ """Broadcast a message to a room"""
461
+ data = {
462
+ "type": message_type,
463
+ "message": message,
464
+ "timestamp": datetime.now().isoformat()
465
+ }
466
+ await websocket_mock_server.broadcast_to_room(room, data)
467
+
468
+
469
+ async def send_private_message(connection_id: str, message: str) -> None:
470
+ """Send a private message to a connection"""
471
+ data = {
472
+ "type": "private_message",
473
+ "message": message,
474
+ "timestamp": datetime.now().isoformat()
475
+ }
476
+ await websocket_mock_server.send_to_connection(connection_id, data)
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: api-mocker
3
- Version: 0.4.0
4
- Summary: 🚀 The Ultimate API Development Acceleration Tool - 3000+ Downloads! Production-ready FastAPI mock server with AI-powered generation, scenario-based mocking, smart response matching, enhanced analytics, comprehensive testing framework, and advanced mock response management system.
3
+ Version: 0.5.0
4
+ Summary: 🚀 The Ultimate API Development Acceleration Tool - 3000+ Downloads! Production-ready FastAPI mock server with AI-powered generation, scenario-based mocking, smart response matching, enhanced analytics, comprehensive testing framework, advanced mock response management, GraphQL mocking, WebSocket support, enterprise authentication, database integration, and machine learning capabilities.
5
5
  Author-email: sherin joseph roy <sherin.joseph2217@gmail.com>
6
6
  License: MIT License
7
7
 
@@ -70,6 +70,19 @@ Requires-Dist: httpx>=0.24.0
70
70
  Requires-Dist: rich>=13.0.0
71
71
  Requires-Dist: psutil>=5.9.0
72
72
  Requires-Dist: aiofiles>=23.0.0
73
+ Requires-Dist: websockets>=11.0.0
74
+ Requires-Dist: aiosqlite>=0.19.0
75
+ Requires-Dist: asyncpg>=0.29.0
76
+ Requires-Dist: motor>=3.3.0
77
+ Requires-Dist: pymongo>=4.6.0
78
+ Requires-Dist: redis>=5.0.0
79
+ Requires-Dist: scikit-learn>=1.3.0
80
+ Requires-Dist: pandas>=2.0.0
81
+ Requires-Dist: numpy>=1.24.0
82
+ Requires-Dist: pyotp>=2.9.0
83
+ Requires-Dist: qrcode>=7.4.0
84
+ Requires-Dist: PyJWT>=2.8.0
85
+ Requires-Dist: joblib>=1.3.0
73
86
  Provides-Extra: advanced
74
87
  Requires-Dist: PyJWT>=2.8.0; extra == "advanced"
75
88
  Requires-Dist: redis>=4.5.0; extra == "advanced"
@@ -2,11 +2,15 @@ api_mocker/__init__.py,sha256=4krN1yJyngDrqVf6weYU5n3cpHpej8tE97frHPXxYqM,168
2
2
  api_mocker/advanced.py,sha256=vf7pzC-ouVgT7PkSSMKLa423Z--Lj9ihC-OCNAhPOro,13429
3
3
  api_mocker/ai_generator.py,sha256=mdha8_9HKiD9CKOT2MnJvaPC_59RwFW5NcUsGuKPP2c,17420
4
4
  api_mocker/analytics.py,sha256=dO4uuoi-YmY6bSBYYahiwnXvTXOylR6RVDiq46UIMoA,14205
5
- api_mocker/cli.py,sha256=5DQlVRddvvEmajXHqlnOWkTc06mNvtZTyWe7WPftkYU,60718
5
+ api_mocker/auth_system.py,sha256=ePC0UVc0vQf5QOIVyPGyMZ41OdKgYldUxJllQyZgeHE,20496
6
+ api_mocker/cli.py,sha256=xRO5thj2hL7JzU0NOOTuDU8nPkl8eIfUWdMEAAv49q0,72678
6
7
  api_mocker/config.py,sha256=zNlJCk1Bs0BrGU-92wiFv2ZTBRu9dJQ6sF8Dh6kIhLQ,913
7
8
  api_mocker/core.py,sha256=K3rP5_cJIEpr02Qgcc_n1Ga3KPo4HumsA6Dlynaj_nQ,8478
8
9
  api_mocker/dashboard.py,sha256=OnZOTNgKXgDU3FON6avwZ4R7NRJjqCUhQePadvRBHHM,14000
10
+ api_mocker/database_integration.py,sha256=oB-mGNu21bpeGhapuyklGgKYDfYqQZk6DP0zSbPcrU0,20342
9
11
  api_mocker/enhanced_analytics.py,sha256=cSTLOft7oKZwDuy5ibUvfuSfRHmkAr9GQYU5DvtVOwI,23028
12
+ api_mocker/graphql_mock.py,sha256=Znxf_PcpCyGwOcuiTeoi__MXydlTGvVZsyCchHgg5SY,21744
13
+ api_mocker/ml_integration.py,sha256=exnOlqOOrdd4z7sj1LniYS5wVkJTPSI7FGIYEHdUREI,26348
10
14
  api_mocker/mock_responses.py,sha256=au9-aXBXqfct_hLhCbwYgbNEfxJqPTCmETqJVsbszqU,21153
11
15
  api_mocker/openapi.py,sha256=Pb1gKbBWosEV5i739rW0Nb3ArNq62lgMN0ecyvigNKY,7403
12
16
  api_mocker/plugins.py,sha256=OK3OVHJszDky46JHntMVsZUH1ajBjBhAKq3TCDYuxWI,8178
@@ -15,9 +19,10 @@ api_mocker/scenarios.py,sha256=wadcxu4Gp8w7i-UlPr6PlbcYnrSd1ehZA81e9dxGTgc,13392
15
19
  api_mocker/server.py,sha256=xfczRj4xFXGVaGn2pVPgGvYyv3IHUlYTEz3Hop1KQu0,3812
16
20
  api_mocker/smart_matching.py,sha256=DvTSKQwo4MhPEUHWdV3zF_H_dmp-l-47I59zz41tNe0,15067
17
21
  api_mocker/testing.py,sha256=z4yJqS5MaSBOThpf3GtUY4dCzXTgopmnGnCuvnmKkF4,24949
18
- api_mocker-0.4.0.dist-info/licenses/LICENSE,sha256=FzyeLcPe623lrwpFx3xQ3W0Hb_S2sbHqLzhSXaTmcGg,1074
19
- api_mocker-0.4.0.dist-info/METADATA,sha256=TIiH9dsICsrjrA69vqUuQT1y8VgMrJc3Ixi5y4F3hlI,14554
20
- api_mocker-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
- api_mocker-0.4.0.dist-info/entry_points.txt,sha256=dj0UIkQ36Uq3oeSjGzmRRUQKFriq4WMCzg7TCor7wkM,51
22
- api_mocker-0.4.0.dist-info/top_level.txt,sha256=ZcowEudKsJ6xbvOXIno2zZcPhjB-gGO1w7uzoUKRKDM,11
23
- api_mocker-0.4.0.dist-info/RECORD,,
22
+ api_mocker/websocket_mock.py,sha256=bz923XE8NHFGOYvtMEinCBjYYvS0OHLL2hDWsjs9cik,17094
23
+ api_mocker-0.5.0.dist-info/licenses/LICENSE,sha256=FzyeLcPe623lrwpFx3xQ3W0Hb_S2sbHqLzhSXaTmcGg,1074
24
+ api_mocker-0.5.0.dist-info/METADATA,sha256=FlId0Vxf8Kh-wzv3JbHSwO6me_JYhgfLg5aSRJ7w1HE,15054
25
+ api_mocker-0.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
+ api_mocker-0.5.0.dist-info/entry_points.txt,sha256=dj0UIkQ36Uq3oeSjGzmRRUQKFriq4WMCzg7TCor7wkM,51
27
+ api_mocker-0.5.0.dist-info/top_level.txt,sha256=ZcowEudKsJ6xbvOXIno2zZcPhjB-gGO1w7uzoUKRKDM,11
28
+ api_mocker-0.5.0.dist-info/RECORD,,