devhelm 1.4.0__tar.gz → 1.5.0__tar.gz

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 (58) hide show
  1. {devhelm-1.4.0 → devhelm-1.5.0}/PKG-INFO +1 -1
  2. {devhelm-1.4.0 → devhelm-1.5.0}/pyproject.toml +1 -1
  3. {devhelm-1.4.0 → devhelm-1.5.0}/scripts/inject_strict_config.py +25 -7
  4. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/_generated.py +191 -188
  5. {devhelm-1.4.0 → devhelm-1.5.0}/uv.lock +1 -1
  6. {devhelm-1.4.0 → devhelm-1.5.0}/.github/workflows/ci.yml +0 -0
  7. {devhelm-1.4.0 → devhelm-1.5.0}/.github/workflows/release.yml +0 -0
  8. {devhelm-1.4.0 → devhelm-1.5.0}/.github/workflows/spec-check.yml +0 -0
  9. {devhelm-1.4.0 → devhelm-1.5.0}/.gitignore +0 -0
  10. {devhelm-1.4.0 → devhelm-1.5.0}/LICENSE +0 -0
  11. {devhelm-1.4.0 → devhelm-1.5.0}/Makefile +0 -0
  12. {devhelm-1.4.0 → devhelm-1.5.0}/README.md +0 -0
  13. {devhelm-1.4.0 → devhelm-1.5.0}/docs/openapi/monitoring-api.json +0 -0
  14. {devhelm-1.4.0 → devhelm-1.5.0}/scripts/emit_response_enums.py +0 -0
  15. {devhelm-1.4.0 → devhelm-1.5.0}/scripts/regen-from.sh +0 -0
  16. {devhelm-1.4.0 → devhelm-1.5.0}/scripts/release.sh +0 -0
  17. {devhelm-1.4.0 → devhelm-1.5.0}/scripts/typegen.sh +0 -0
  18. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/__init__.py +0 -0
  19. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/_enums.py +0 -0
  20. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/_errors.py +0 -0
  21. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/_http.py +0 -0
  22. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/_pagination.py +0 -0
  23. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/_validation.py +0 -0
  24. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/client.py +0 -0
  25. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/py.typed +0 -0
  26. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/__init__.py +0 -0
  27. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/alert_channels.py +0 -0
  28. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/api_keys.py +0 -0
  29. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/dependencies.py +0 -0
  30. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/deploy_lock.py +0 -0
  31. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/environments.py +0 -0
  32. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/forensics.py +0 -0
  33. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/incidents.py +0 -0
  34. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/maintenance_windows.py +0 -0
  35. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/monitors.py +0 -0
  36. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/notification_policies.py +0 -0
  37. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/resource_groups.py +0 -0
  38. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/secrets.py +0 -0
  39. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/services.py +0 -0
  40. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/status.py +0 -0
  41. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/status_pages.py +0 -0
  42. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/tags.py +0 -0
  43. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/resources/webhooks.py +0 -0
  44. {devhelm-1.4.0 → devhelm-1.5.0}/src/devhelm/types.py +0 -0
  45. {devhelm-1.4.0 → devhelm-1.5.0}/tests/__init__.py +0 -0
  46. {devhelm-1.4.0 → devhelm-1.5.0}/tests/run_sdk.py +0 -0
  47. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_api_keys.py +0 -0
  48. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_client.py +0 -0
  49. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_dependencies.py +0 -0
  50. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_errors.py +0 -0
  51. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_http.py +0 -0
  52. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_maintenance_windows.py +0 -0
  53. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_negative_validation.py +0 -0
  54. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_schemas.py +0 -0
  55. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_services.py +0 -0
  56. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_spec_parity.py +0 -0
  57. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_typing.py +0 -0
  58. {devhelm-1.4.0 → devhelm-1.5.0}/tests/test_validation_helpers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devhelm
3
- Version: 1.4.0
3
+ Version: 1.5.0
4
4
  Summary: DevHelm SDK for Python — typed client for monitors, incidents, alerting, and more
5
5
  Project-URL: Homepage, https://github.com/devhelmhq/sdk-python
6
6
  Project-URL: Repository, https://github.com/devhelmhq/sdk-python.git
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "devhelm"
3
- version = "1.4.0"
3
+ version = "1.5.0"
4
4
  description = "DevHelm SDK for Python — typed client for monitors, incidents, alerting, and more"
5
5
  authors = [{ name = "DevHelm", email = "hello@devhelm.io" }]
6
6
  license = "MIT"
@@ -50,7 +50,24 @@ from pathlib import Path
50
50
  # `root-model-extra`), so skip them. Their behavior is governed by the
51
51
  # inner type, which on its own enforces strict validation.
52
52
  CLASS_RE = re.compile(r"^class\s+([A-Za-z_][\w]*)\s*\(\s*(BaseModel)\s*\)\s*:\s*$")
53
- CONFIG_LINE = " model_config = ConfigDict(extra='forbid', populate_by_name=True)"
53
+ CONFIG_LINE_STRICT = " model_config = ConfigDict(extra='forbid', populate_by_name=True)"
54
+ CONFIG_LINE_TOLERANT = " model_config = ConfigDict(extra='ignore', populate_by_name=True)"
55
+
56
+
57
+ def _is_response_shape(class_name: str) -> bool:
58
+ """Response-shape classes tolerate unknown fields (Postel's Law)."""
59
+ if class_name[0].islower():
60
+ return False
61
+ if class_name.endswith(("Request", "Params")):
62
+ return False
63
+ return bool(
64
+ class_name.endswith(("Dto", "Response"))
65
+ or class_name.startswith(("SingleValueResponse", "TableValueResult", "CursorPage"))
66
+ )
67
+
68
+
69
+ # Keep the old name for backward compat in case anything imports it
70
+ CONFIG_LINE = CONFIG_LINE_STRICT
54
71
 
55
72
  # Doc-banner injections keyed by class name. Inserted as a leading docstring
56
73
  # inside the target class so the note shows up in IDE hovers and stays put
@@ -130,6 +147,7 @@ def inject(source: str) -> tuple[str, int]:
130
147
  i += 1
131
148
  continue
132
149
  class_name = m.group(1)
150
+ config_line = CONFIG_LINE_TOLERANT if _is_response_shape(class_name) else CONFIG_LINE_STRICT
133
151
  # Look at the very next line. If it's already model_config or pass,
134
152
  # leave the class alone (idempotency / empty class).
135
153
  next_idx = i + 1
@@ -141,10 +159,10 @@ def inject(source: str) -> tuple[str, int]:
141
159
  out.append(f' """{banner}"""\n')
142
160
  modified += 1
143
161
  if "model_config" in next_line:
144
- # Upgrade the existing config line to include populate_by_name=True
145
- # if it isn't already there. Idempotent across re-runs.
146
- if "populate_by_name" not in next_line:
147
- out.append(CONFIG_LINE + "\n")
162
+ # Replace the existing config line if it doesn't match the
163
+ # desired strictness or is missing populate_by_name.
164
+ if next_line.strip() != config_line.strip():
165
+ out.append(config_line + "\n")
148
166
  i += 2 # replace the existing model_config line
149
167
  modified += 1
150
168
  continue
@@ -154,11 +172,11 @@ def inject(source: str) -> tuple[str, int]:
154
172
  # exact match (NOT startswith) — fields like `passed: Annotated[...]`
155
173
  # also start with "pass" but are not empty class markers.
156
174
  if next_line.strip() in ("pass", "pass\n"):
157
- out.append(CONFIG_LINE + "\n")
175
+ out.append(config_line + "\n")
158
176
  i += 2 # skip the pass
159
177
  modified += 1
160
178
  continue
161
- out.append(CONFIG_LINE + "\n")
179
+ out.append(config_line + "\n")
162
180
  modified += 1
163
181
  i += 1
164
182
  return "".join(out), modified