fixturify 0.1.9__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.
Files changed (71) hide show
  1. fixturify/__init__.py +21 -0
  2. fixturify/_utils/__init__.py +7 -0
  3. fixturify/_utils/_constants.py +10 -0
  4. fixturify/_utils/_fixture_discovery.py +165 -0
  5. fixturify/_utils/_path_resolver.py +135 -0
  6. fixturify/http_d/__init__.py +80 -0
  7. fixturify/http_d/_config.py +214 -0
  8. fixturify/http_d/_decorator.py +267 -0
  9. fixturify/http_d/_exceptions.py +153 -0
  10. fixturify/http_d/_fixture_discovery.py +33 -0
  11. fixturify/http_d/_matcher.py +372 -0
  12. fixturify/http_d/_mock_context.py +154 -0
  13. fixturify/http_d/_models.py +205 -0
  14. fixturify/http_d/_patcher.py +524 -0
  15. fixturify/http_d/_player.py +222 -0
  16. fixturify/http_d/_recorder.py +1350 -0
  17. fixturify/http_d/_stubs/__init__.py +8 -0
  18. fixturify/http_d/_stubs/_aiohttp.py +220 -0
  19. fixturify/http_d/_stubs/_connection.py +478 -0
  20. fixturify/http_d/_stubs/_httpcore.py +269 -0
  21. fixturify/http_d/_stubs/_tornado.py +95 -0
  22. fixturify/http_d/_utils.py +194 -0
  23. fixturify/json_assert/__init__.py +13 -0
  24. fixturify/json_assert/_actual_saver.py +67 -0
  25. fixturify/json_assert/_assert.py +173 -0
  26. fixturify/json_assert/_comparator.py +183 -0
  27. fixturify/json_assert/_diff_formatter.py +265 -0
  28. fixturify/json_assert/_normalizer.py +83 -0
  29. fixturify/object_mapper/__init__.py +5 -0
  30. fixturify/object_mapper/_deserializers/__init__.py +19 -0
  31. fixturify/object_mapper/_deserializers/_base.py +186 -0
  32. fixturify/object_mapper/_deserializers/_dataclass.py +52 -0
  33. fixturify/object_mapper/_deserializers/_plain.py +55 -0
  34. fixturify/object_mapper/_deserializers/_pydantic_v1.py +38 -0
  35. fixturify/object_mapper/_deserializers/_pydantic_v2.py +41 -0
  36. fixturify/object_mapper/_deserializers/_sqlalchemy.py +72 -0
  37. fixturify/object_mapper/_deserializers/_sqlmodel.py +43 -0
  38. fixturify/object_mapper/_detectors/__init__.py +5 -0
  39. fixturify/object_mapper/_detectors/_type_detector.py +186 -0
  40. fixturify/object_mapper/_serializers/__init__.py +19 -0
  41. fixturify/object_mapper/_serializers/_base.py +260 -0
  42. fixturify/object_mapper/_serializers/_dataclass.py +55 -0
  43. fixturify/object_mapper/_serializers/_plain.py +49 -0
  44. fixturify/object_mapper/_serializers/_pydantic_v1.py +49 -0
  45. fixturify/object_mapper/_serializers/_pydantic_v2.py +49 -0
  46. fixturify/object_mapper/_serializers/_sqlalchemy.py +70 -0
  47. fixturify/object_mapper/_serializers/_sqlmodel.py +54 -0
  48. fixturify/object_mapper/mapper.py +256 -0
  49. fixturify/read_d/__init__.py +5 -0
  50. fixturify/read_d/_decorator.py +193 -0
  51. fixturify/read_d/_fixture_loader.py +88 -0
  52. fixturify/sql_d/__init__.py +7 -0
  53. fixturify/sql_d/_config.py +30 -0
  54. fixturify/sql_d/_decorator.py +373 -0
  55. fixturify/sql_d/_driver_registry.py +133 -0
  56. fixturify/sql_d/_executor.py +82 -0
  57. fixturify/sql_d/_fixture_discovery.py +55 -0
  58. fixturify/sql_d/_phase.py +10 -0
  59. fixturify/sql_d/_strategies/__init__.py +11 -0
  60. fixturify/sql_d/_strategies/_aiomysql.py +63 -0
  61. fixturify/sql_d/_strategies/_aiosqlite.py +29 -0
  62. fixturify/sql_d/_strategies/_asyncpg.py +34 -0
  63. fixturify/sql_d/_strategies/_base.py +118 -0
  64. fixturify/sql_d/_strategies/_mysql.py +70 -0
  65. fixturify/sql_d/_strategies/_psycopg.py +35 -0
  66. fixturify/sql_d/_strategies/_psycopg2.py +40 -0
  67. fixturify/sql_d/_strategies/_registry.py +109 -0
  68. fixturify/sql_d/_strategies/_sqlite.py +33 -0
  69. fixturify-0.1.9.dist-info/METADATA +122 -0
  70. fixturify-0.1.9.dist-info/RECORD +71 -0
  71. fixturify-0.1.9.dist-info/WHEEL +4 -0
@@ -0,0 +1,205 @@
1
+ """WireMock-compatible data models for HTTP recording and playback."""
2
+
3
+ import base64
4
+ import json
5
+ from dataclasses import dataclass, field
6
+ from typing import Any, Dict, List, Optional
7
+
8
+
9
+ @dataclass
10
+ class HttpRequest:
11
+ """
12
+ Represents an HTTP request in WireMock format.
13
+
14
+ Attributes:
15
+ method: HTTP method (GET, POST, PUT, DELETE, etc.)
16
+ url: Full URL including scheme and host
17
+ headers: Request headers as dict
18
+ queryParameters: Query parameters as dict
19
+ body: Request body (string or None)
20
+ bodyEncoding: Encoding for binary bodies ('base64' or None for text)
21
+ """
22
+
23
+ method: str
24
+ url: str
25
+ headers: Dict[str, str] = field(default_factory=dict)
26
+ queryParameters: Dict[str, str] = field(default_factory=dict)
27
+ body: Optional[str] = None
28
+ bodyEncoding: Optional[str] = None
29
+
30
+ def to_dict(self) -> Dict[str, Any]:
31
+ """Convert to dictionary for JSON serialization."""
32
+ result: Dict[str, Any] = {
33
+ "method": self.method.upper(),
34
+ "url": self.url,
35
+ "headers": self.headers,
36
+ "queryParameters": self.queryParameters,
37
+ }
38
+ if self.body is not None:
39
+ result["body"] = self.body
40
+ if self.bodyEncoding is not None:
41
+ result["bodyEncoding"] = self.bodyEncoding
42
+ return result
43
+
44
+ @classmethod
45
+ def from_dict(cls, data: Dict[str, Any]) -> "HttpRequest":
46
+ """Create from dictionary."""
47
+ return cls(
48
+ method=data.get("method", "GET"),
49
+ url=data.get("url", ""),
50
+ headers=data.get("headers", {}),
51
+ queryParameters=data.get("queryParameters", {}),
52
+ body=data.get("body"),
53
+ bodyEncoding=data.get("bodyEncoding"),
54
+ )
55
+
56
+
57
+ @dataclass
58
+ class HttpResponse:
59
+ """
60
+ Represents an HTTP response in WireMock format.
61
+
62
+ Attributes:
63
+ status: HTTP status code
64
+ headers: Response headers as dict
65
+ body: Response body (string or None if using bodyFileName)
66
+ bodyFileName: Optional filename for binary content (stored separately)
67
+ bodyEncoding: Encoding for inline binary bodies ('base64' or None for text)
68
+ """
69
+
70
+ status: int
71
+ headers: Dict[str, str] = field(default_factory=dict)
72
+ body: Optional[str] = ""
73
+ bodyFileName: Optional[str] = None
74
+ bodyEncoding: Optional[str] = None
75
+ # Runtime field: actual bytes loaded from file (not serialized)
76
+ _body_bytes: Optional[bytes] = field(default=None, repr=False)
77
+
78
+ def to_dict(self) -> Dict[str, Any]:
79
+ """Convert to dictionary for JSON serialization."""
80
+ result: Dict[str, Any] = {
81
+ "status": self.status,
82
+ "headers": self.headers,
83
+ }
84
+ if self.bodyFileName is not None:
85
+ result["bodyFileName"] = self.bodyFileName
86
+ else:
87
+ result["body"] = self.body if self.body is not None else ""
88
+ if self.bodyEncoding is not None:
89
+ result["bodyEncoding"] = self.bodyEncoding
90
+ return result
91
+
92
+ @classmethod
93
+ def from_dict(cls, data: Dict[str, Any]) -> "HttpResponse":
94
+ """Create from dictionary."""
95
+ return cls(
96
+ status=data.get("status", 200),
97
+ headers=data.get("headers", {}),
98
+ body=data.get("body", ""),
99
+ bodyFileName=data.get("bodyFileName"),
100
+ bodyEncoding=data.get("bodyEncoding"),
101
+ )
102
+
103
+ def get_body_bytes(self) -> bytes:
104
+ """
105
+ Get response body as bytes, handling base64 encoding and file references.
106
+
107
+ Returns:
108
+ Response body as bytes
109
+
110
+ Note:
111
+ If bodyFileName is used, _body_bytes must be set by the player
112
+ after loading the file.
113
+ """
114
+ # If body was loaded from file
115
+ if self._body_bytes is not None:
116
+ return self._body_bytes
117
+
118
+ # If using base64 encoding
119
+ if self.bodyEncoding == "base64" and self.body:
120
+ return base64.b64decode(self.body)
121
+
122
+ # Regular text body
123
+ return (self.body or "").encode("utf-8")
124
+
125
+ def set_body_bytes(self, data: bytes) -> None:
126
+ """Set body bytes (used when loading from file)."""
127
+ self._body_bytes = data
128
+
129
+
130
+ @dataclass
131
+ class HttpMapping:
132
+ """
133
+ Represents a request-response mapping in WireMock format.
134
+
135
+ Attributes:
136
+ request: The HTTP request
137
+ response: The HTTP response
138
+ """
139
+
140
+ request: HttpRequest
141
+ response: HttpResponse
142
+
143
+ def to_dict(self) -> Dict[str, Any]:
144
+ """Convert to dictionary for JSON serialization."""
145
+ return {
146
+ "request": self.request.to_dict(),
147
+ "response": self.response.to_dict(),
148
+ }
149
+
150
+ @classmethod
151
+ def from_dict(cls, data: Dict[str, Any]) -> "HttpMapping":
152
+ """Create from dictionary."""
153
+ return cls(
154
+ request=HttpRequest.from_dict(data.get("request", {})),
155
+ response=HttpResponse.from_dict(data.get("response", {})),
156
+ )
157
+
158
+
159
+ @dataclass
160
+ class HttpMappings:
161
+ """
162
+ Container for multiple HTTP mappings in WireMock format.
163
+
164
+ The JSON structure is:
165
+ {
166
+ "mappings": [
167
+ { "request": {...}, "response": {...} },
168
+ ...
169
+ ]
170
+ }
171
+ """
172
+
173
+ mappings: List[HttpMapping] = field(default_factory=list)
174
+
175
+ def to_dict(self) -> Dict[str, Any]:
176
+ """Convert to dictionary for JSON serialization."""
177
+ return {"mappings": [m.to_dict() for m in self.mappings]}
178
+
179
+ def to_json(self, indent: int = 2) -> str:
180
+ """Convert to JSON string."""
181
+ return json.dumps(self.to_dict(), indent=indent, ensure_ascii=False)
182
+
183
+ @classmethod
184
+ def from_dict(cls, data: Dict[str, Any]) -> "HttpMappings":
185
+ """Create from dictionary."""
186
+ mappings_data = data.get("mappings", [])
187
+ return cls(mappings=[HttpMapping.from_dict(m) for m in mappings_data])
188
+
189
+ @classmethod
190
+ def from_json(cls, json_str: str) -> "HttpMappings":
191
+ """Create from JSON string."""
192
+ data = json.loads(json_str)
193
+ return cls.from_dict(data)
194
+
195
+ def add(self, mapping: HttpMapping) -> None:
196
+ """Add a mapping."""
197
+ self.mappings.append(mapping)
198
+
199
+ def __len__(self) -> int:
200
+ """Return number of mappings."""
201
+ return len(self.mappings)
202
+
203
+ def __iter__(self):
204
+ """Iterate over mappings."""
205
+ return iter(self.mappings)