-
Notifications
You must be signed in to change notification settings - Fork 152
feat: update python SDK #3535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: update python SDK #3535
Conversation
|
Warning Rate limit exceeded@tothandras has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 1 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⛔ Files ignored due to path filters (19)
📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThis PR regenerates client libraries across Go, Python, and JavaScript by updating OpenAPI schemas, TypeSpec configurations, and client generators. The Go client replaces request functions with response-parsing variants (WithResponse suffix). Python client is restructured with new wrapper clients, type system, configuration, and examples replacing old autorest-generated code. TypeSpec patches and configurations are updated to support generation. Documentation and build configuration are enhanced. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45–60 minutes Areas requiring extra attention:
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
97b91a8 to
74f7751
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
🧹 Nitpick comments (24)
api/client/python/.gitignore (1)
3-7: Consider consistent trailing slashes for directory patterns.While functional, adding trailing slashes to directory entries (e.g.,
*.egg-info/,dist/,build/) would improve consistency with other directory patterns like.venv/andvenv/below. This is a stylistic convention that makes intent clearer.Example cleanup:
-*.egg-info/ -*.egg -*.eggs/ -dist/ -build/ +*.egg-info/ +*.egg/ +*.eggs/ +dist/ +build/flake.nix (1)
51-53: LGTM! Modern Python tooling enabled.Enabling
uvis a great addition for faster package management. It integrates well with devenv and will improve the development experience for the Python SDK work.Note: Both
poetry(line 133) anduvare now present. They can coexist, though they serve somewhat overlapping purposes. If the project standardizes onuvfor all Python package management in the future,poetrycould potentially be removed, but this is not urgent.api/spec/tspconfig.yaml (1)
13-21: Verify placeholder version and packaging file generation strategy.The Python emitter configuration is well-structured, but two design decisions need verification:
Placeholder version (Line 18):
package-version: '0.0.0'is a placeholder. Confirm whether this should be:
- Replaced with actual SDK version from a shared source (e.g.,
pyproject.toml,__init__.py)- Updated via CI/CD pipeline before generation
- Left as-is intentionally for development
Disabled packaging files (Line 19):
generate-packaging-files: falseimpliespyproject.tomland other packaging artifacts are managed separately. Confirm this is intentional and that generated Python models don't need regeneration during releases.api/client/go/client.gen.go (1)
44250-44682: Prefer go:embed gzip blob over thousands of base64 string literalsEmbedding the spec as a massive []string of base64 chunks hurts compile time, binary size, and diff readability. Recommend switching the generator/template to ship a gzipped spec via //go:embed and decode once with sync.Once.
Example (illustrative):
//go:build !tools package client import ( "bytes" "compress/gzip" "embed" "io" "sync" ) //go:embed swagger.json.gz var swaggerSpecGz []byte var ( swaggerOnce sync.Once swaggerJSON []byte swaggerErr error ) func GetSwagger() ([]byte, error) { swaggerOnce.Do(func() { r, err := gzip.NewReader(bytes.NewReader(swaggerSpecGz)) if err != nil { swaggerErr = err; return } defer r.Close() swaggerJSON, swaggerErr = io.ReadAll(r) }) return swaggerJSON, swaggerErr }This keeps the source lean, speeds builds, and avoids enormous generated diffs. If this file is auto-generated, make the change in the generator.
api/client/python/examples/sync/query.py (3)
3-3: Remove unused import.The
jsonmodule is imported but never used in this script.-import json
6-6: Remove unused import.The
FilterStringmodel is imported but never used in this script.-from openmeter.models import MeterQueryResult, FilterString +from openmeter.models import MeterQueryResult
15-18: Use context manager for proper resource cleanup.The client should be used with a context manager or explicitly closed to ensure proper cleanup of resources.
Apply this diff to use a context manager:
-client = OpenMeterCloudClient( - endpoint=ENDPOINT, - credential=credential, -) - - def main() -> None: - try: + with OpenMeterCloudClient( + endpoint=ENDPOINT, + credential=credential, + ) as client: + try: - # Query total values - r: MeterQueryResult = client.meters.query_json(meter_id_or_slug="tokens_total") + # Query total values + r: MeterQueryResult = client.meters.query_json(meter_id_or_slug="tokens_total")And indent the rest of the try block accordingly.
api/client/python/examples/sync/ingest.py (1)
16-19: Use context manager for proper resource cleanup.The client should be used with a context manager or explicitly closed to ensure proper cleanup of resources.
Apply this diff to use a context manager:
-client = OpenMeterCloudClient( - endpoint=ENDPOINT, - credential=credential, -) - - def main() -> None: - try: + with OpenMeterCloudClient( + endpoint=ENDPOINT, + credential=credential, + ) as client: + try: - # Create a CloudEvents event - event = Event( + # Create a CloudEvents event + event = Event(And indent the rest of the try block accordingly.
api/client/python/README.md (5)
3-3: Fix the reversed Markdown link.Current syntax shows the URL as the label. Swap label and href.
-[https://pypi.org/project/openmeter](On PyPI) +[On PyPI](https://pypi.org/project/openmeter)
5-10: Typos and naming: “Prerequisites” and “TypeSpec”.
- “Prequisites” → “Prerequisites”
- “Typespec” → “TypeSpec”
-This package is generated by `@typespec/http-client-python` with Typespec. -## Prequisites +This package is generated by `@typespec/http-client-python` with TypeSpec. +## Prerequisites
24-33: Missing imports in Setup snippet.Users copying the sample will hit NameError for Event/uuid/datetime. Include them explicitly.
-from openmeter import OpenMeterCloudClient -from corehttp.credentials import ServiceKeyCredential +import datetime +import uuid +from openmeter import OpenMeterCloudClient +from corehttp.credentials import ServiceKeyCredential +from openmeter.models import Event
38-55: Event payload example: keep consistent with other examples.Async example uses "tokens" key; README uses "value". Pick one to avoid confusion; consider "tokens" since the query example references a tokens meter.
60-63: Type annotation requires import.Either import MeterQueryResult or drop the annotation to keep the snippet minimal.
-# Query total values -r: MeterQueryResult = client.meters.query_json(meter_id_or_slug="tokens_total") -print("Query total values:", r.data[0].value) +from openmeter.models import MeterQueryResult +# Query total values +r: MeterQueryResult = client.meters.query_json(meter_id_or_slug="tokens_total") +print("Query total values:", r.data[0].value)api/client/python/openmeter/models/__init__.py (2)
368-371: Avoid wildcard import pollution from patch module.from ._patch import * at runtime pollutes the namespace. If possible, import only names in _patch_all or explicitly import patch_sdk to reduce surface.
Example (pattern):
# instead of star import from ._patch import patch_sdk as _patch_sdk from ._patch import __all__ as _patch_all for name in _patch_all: globals()[name] = getattr(__import__(f"{__name__}._patch", fromlist=[name]), name)
727-728: Guard patch execution and all extension.If _patch_all is empty or patch raises, import-time side effects can break consumers. Add try/except and type checks.
-__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore -_patch_sdk() +if isinstance(_patch_all, (list, tuple)): + __all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore +try: + _patch_sdk() +except Exception: # best-effort patching + passapi/client/python/examples/async/ingest.py (2)
12-16: Handle missing OPENMETER_TOKEN.ServiceKeyCredential(None) will likely fail. Fail fast with a clear message.
-ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud" -token: Optional[str] = environ.get("OPENMETER_TOKEN") - -credential = ServiceKeyCredential(token) +ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud" +token: Optional[str] = environ.get("OPENMETER_TOKEN") +if not token: + raise RuntimeError("OPENMETER_TOKEN is not set") +credential = ServiceKeyCredential(token)
42-44: Optional: enrich error output.Consider printing e.status_code and e.model to aid debugging.
-except HttpResponseError as e: - print(f"Error ingesting event: {e}") +except HttpResponseError as e: + detail = getattr(e, "model", None) + print(f"Error ingesting event: {getattr(e, 'status_code', 'n/a')} {e}. Details: {detail}")api/client/python/openmeter/_utils/serialization.py (3)
824-825: Avoid eval for basic type casting.eval(data_type) is unnecessary and risky. Prefer explicit mapping.
- return eval(data_type)(data) # nosec # pylint: disable=eval-used + cast_map = {"int": int, "float": float, "bool": bool} + return cast_map[data_type](data)
1069-1082: Time serialization: microseconds formatting.Use zero-padded microseconds and trim trailing zeros for cleaner ISO output.
- t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second) - if attr.microsecond: - t += ".{:02}".format(attr.microsecond) + t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second) + if attr.microsecond: + micros = str(attr.microsecond).rjust(6, "0").rstrip("0") + t += f".{micros}"
1104-1110: Naive datetime handling warning only.Consider normalizing naive datetimes to UTC to avoid localtime ambiguity.
- if not attr.tzinfo: - _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") - utc = attr.utctimetuple() + if not attr.tzinfo: + _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") + attr = attr.replace(tzinfo=TZ_UTC) + utc = attr.utctimetuple()api/client/python/openmeter/_utils/model_base.py (3)
317-326: Harden type-alias detection for Python 3.12+/PEP 695.
typing._GenericAliasis private and insufficient fortypes.GenericAlias/UnionType.Apply:
+import types as _types_mod @@ - types = { + types_map = { k: v - for k, v in sys.modules[module_name].__dict__.items() - if isinstance(v, typing._GenericAlias) # type: ignore + for k, v in sys.modules[module_name].__dict__.items() + if isinstance( + v, + tuple( + t + for t in ( + getattr(typing, "_GenericAlias", None), + getattr(_types_mod, "GenericAlias", None), + ) + if t is not None + ), + ) } - if alias_name not in types: + # Support PEP 604 unions (e.g., X | Y) which appear as types.UnionType + types_map.update( + { + k: v + for k, v in sys.modules[module_name].__dict__.items() + if type(v).__name__ == "UnionType" + } + ) + if alias_name not in types_map: return alias_name - return types[alias_name] + return types_map[alias_name]
125-160: Option: preserve Decimal precision in JSON.Converting
Decimaltofloatcan lose precision for monetary values. Consider serializing as string when format requires exactness.Apply:
- if isinstance(o, decimal.Decimal): - return float(o) + if isinstance(o, decimal.Decimal): + # Preserve precision; downstream can parse if needed + return str(o)If you keep floats, consider a per-field
format="str"to opt-in precision.
100-116: Unix timestamp serialization: ensure timezone-awareness.When
format == "unix-timestamp", naive datetimes are interpreted as UTC viautctimetuple(). Explicitly localize naive values or reject them to avoid silent misinterpretation.Apply:
- if format == "unix-timestamp": - return int(calendar.timegm(o.utctimetuple())) + if format == "unix-timestamp": + dt = o if o.tzinfo else o.replace(tzinfo=TZ_UTC) + return int(calendar.timegm(dt.utctimetuple()))api/client/python/openmeter/_types.py (1)
3-9: Optional: declare aliases explicitly with TypeAlias.Improves readability and tooling.
Apply:
-from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING, Union, TypeAlias @@ -App = Union["_models.StripeApp", "_models.SandboxApp", "_models.CustomInvoicingApp"] +App: TypeAlias = Union["_models.StripeApp", "_models.SandboxApp", "_models.CustomInvoicingApp"] @@ -NotificationChannel = "_models.NotificationChannelWebhook" +NotificationChannel: TypeAlias = "_models.NotificationChannelWebhook"Replicate for other aliases as desired.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
api/client/python/examples/async/poetry.lockis excluded by!**/*.lockapi/client/python/examples/poetry.lockis excluded by!**/*.lockapi/client/python/poetry.lockis excluded by!**/*.lockapi/spec/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (51)
api/client/go/client.gen.go(1 hunks)api/client/javascript/src/client/schemas.ts(1 hunks)api/client/python/.gitattributes(1 hunks)api/client/python/.gitignore(1 hunks)api/client/python/MANIFEST.in(1 hunks)api/client/python/README.md(2 hunks)api/client/python/config.yaml(0 hunks)api/client/python/examples/README.md(1 hunks)api/client/python/examples/async/ingest.py(1 hunks)api/client/python/examples/async/pyproject.toml(0 hunks)api/client/python/examples/async/query.py(1 hunks)api/client/python/examples/pyproject.toml(1 hunks)api/client/python/examples/sync/ingest.py(1 hunks)api/client/python/examples/sync/query.py(1 hunks)api/client/python/openmeter/__init__.py(1 hunks)api/client/python/openmeter/_client.py(1 hunks)api/client/python/openmeter/_configuration.py(1 hunks)api/client/python/openmeter/_patch.py(1 hunks)api/client/python/openmeter/_types.py(1 hunks)api/client/python/openmeter/_utils/model_base.py(1 hunks)api/client/python/openmeter/_utils/serialization.py(71 hunks)api/client/python/openmeter/_version.py(1 hunks)api/client/python/openmeter/aio/__init__.py(1 hunks)api/client/python/openmeter/aio/_client.py(1 hunks)api/client/python/openmeter/aio/_configuration.py(1 hunks)api/client/python/openmeter/aio/_patch.py(1 hunks)api/client/python/openmeter/aio/operations/__init__.py(1 hunks)api/client/python/openmeter/aio/operations/_patch.py(1 hunks)api/client/python/openmeter/models/__init__.py(1 hunks)api/client/python/openmeter/models/_enums.py(1 hunks)api/client/python/openmeter/models/_patch.py(1 hunks)api/client/python/openmeter/operations/__init__.py(1 hunks)api/client/python/openmeter/operations/_patch.py(1 hunks)api/client/python/pyproject.toml(1 hunks)api/client/python/src/openmeter/__init__.py(0 hunks)api/client/python/src/openmeter/_client.py(0 hunks)api/client/python/src/openmeter/_configuration.py(0 hunks)api/client/python/src/openmeter/_operations/__init__.py(0 hunks)api/client/python/src/openmeter/_operations/_patch.py(0 hunks)api/client/python/src/openmeter/_vendor.py(0 hunks)api/client/python/src/openmeter/aio/__init__.py(0 hunks)api/client/python/src/openmeter/aio/_client.py(0 hunks)api/client/python/src/openmeter/aio/_configuration.py(0 hunks)api/client/python/src/openmeter/aio/_operations/__init__.py(0 hunks)api/client/python/src/openmeter/aio/_vendor.py(0 hunks)api/openapi.cloud.yaml(1 hunks)api/openapi.yaml(1 hunks)api/spec/package.json(1 hunks)api/spec/patches/@typespec__openapi.patch(1 hunks)api/spec/tspconfig.yaml(1 hunks)flake.nix(1 hunks)
💤 Files with no reviewable changes (13)
- api/client/python/src/openmeter/_configuration.py
- api/client/python/examples/async/pyproject.toml
- api/client/python/src/openmeter/aio/_client.py
- api/client/python/config.yaml
- api/client/python/src/openmeter/_client.py
- api/client/python/src/openmeter/aio/init.py
- api/client/python/src/openmeter/aio/_operations/init.py
- api/client/python/src/openmeter/_operations/init.py
- api/client/python/src/openmeter/aio/_vendor.py
- api/client/python/src/openmeter/_vendor.py
- api/client/python/src/openmeter/_operations/_patch.py
- api/client/python/src/openmeter/init.py
- api/client/python/src/openmeter/aio/_configuration.py
🧰 Additional context used
🧬 Code graph analysis (17)
api/client/python/examples/sync/ingest.py (3)
api/client/python/openmeter/_client.py (1)
OpenMeterCloudClient(22-104)api/client/python/openmeter/models/_models.py (1)
Event(6140-6219)api/client/python/openmeter/operations/_operations.py (4)
ingest_event(4327-4342)ingest_event(4345-4358)ingest_event(4361-4376)ingest_event(4378-4451)
api/client/python/openmeter/aio/_configuration.py (1)
api/client/python/openmeter/_configuration.py (2)
OpenMeterCloudClientConfiguration(11-45)_configure(35-45)
api/client/python/openmeter/aio/_client.py (3)
api/client/python/openmeter/aio/_configuration.py (1)
OpenMeterCloudClientConfiguration(11-45)api/client/python/openmeter/aio/operations/_operations.py (5)
DebugOperations(2077-2171)EventsOperations(230-730)EventsV2Operations(733-856)MetersOperations(859-2074)OpenMeterCloudOperations(197-227)api/client/python/openmeter/_client.py (2)
OpenMeterCloudClient(22-104)send_request(70-94)
api/client/python/openmeter/aio/__init__.py (2)
api/client/python/openmeter/aio/_client.py (1)
OpenMeterCloudClient(22-106)api/client/python/openmeter/aio/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/models/_patch.py (4)
api/client/python/openmeter/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/operations/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/operations/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/aio/operations/__init__.py (2)
api/client/python/openmeter/aio/operations/_operations.py (5)
OpenMeterCloudOperations(197-227)EventsOperations(230-730)EventsV2Operations(733-856)MetersOperations(859-2074)DebugOperations(2077-2171)api/client/python/openmeter/aio/operations/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/operations/__init__.py (2)
api/client/python/openmeter/operations/_operations.py (5)
OpenMeterCloudOperations(4148-4178)EventsOperations(4181-4683)EventsV2Operations(4686-4809)MetersOperations(4812-6027)DebugOperations(6030-6124)api/client/python/openmeter/operations/_patch.py (1)
patch_sdk(12-18)
api/client/python/examples/async/ingest.py (3)
api/client/python/openmeter/aio/_client.py (1)
OpenMeterCloudClient(22-106)api/client/python/openmeter/models/_models.py (1)
Event(6140-6219)api/client/python/openmeter/aio/operations/_operations.py (4)
ingest_event(376-391)ingest_event(394-409)ingest_event(412-427)ingest_event(429-500)
api/client/python/openmeter/__init__.py (3)
api/client/python/openmeter/_client.py (1)
OpenMeterCloudClient(22-104)api/client/python/openmeter/aio/_client.py (1)
OpenMeterCloudClient(22-106)api/client/python/openmeter/_patch.py (1)
patch_sdk(12-18)
api/client/python/examples/sync/query.py (4)
api/client/python/openmeter/_client.py (1)
OpenMeterCloudClient(22-104)api/client/python/openmeter/models/_models.py (2)
MeterQueryResult(9615-9671)FilterString(6504-6600)api/client/python/examples/async/query.py (1)
main(16-42)api/client/python/openmeter/operations/_operations.py (2)
query_json(5363-5516)query_json(6761-6908)
api/client/python/openmeter/operations/_patch.py (4)
api/client/python/openmeter/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/operations/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/models/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/models/__init__.py (1)
api/client/python/openmeter/models/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/_client.py (3)
api/client/python/openmeter/_configuration.py (1)
OpenMeterCloudClientConfiguration(11-45)api/client/python/openmeter/_utils/serialization.py (5)
Deserializer(1350-2024)Serializer(472-1170)_serialize(527-637)_deserialize(1404-1475)url(682-705)api/client/python/openmeter/operations/_operations.py (1)
OpenMeterCloudOperations(4148-4178)
api/client/python/examples/async/query.py (3)
api/client/python/openmeter/aio/_client.py (1)
OpenMeterCloudClient(22-106)api/client/python/openmeter/models/_models.py (1)
MeterQueryResult(9615-9671)api/client/python/openmeter/aio/operations/_operations.py (17)
get(978-1054)get(3214-3288)get(3910-3987)get(5483-5559)get(6194-6274)get(6575-6651)get(6841-6918)get(7494-7574)get(9093-9170)get(9938-10023)get(10716-10808)get(11234-11320)get(13471-13547)get(14071-14153)get(14915-14997)query_json(1410-1563)query_json(2812-2959)
api/client/python/openmeter/_utils/serialization.py (1)
api/client/python/openmeter/_utils/model_base.py (9)
Model(530-709)_serialize(478-508)as_dict(676-699)get(388-399)keys(367-372)items(381-386)_deserialize(652-674)_deserialize(924-937)values(374-379)
api/client/python/openmeter/_configuration.py (1)
api/client/python/openmeter/aio/_configuration.py (2)
OpenMeterCloudClientConfiguration(11-45)_configure(35-45)
api/client/python/openmeter/_utils/model_base.py (2)
api/client/python/openmeter/aio/operations/_operations.py (32)
get(978-1054)get(3214-3288)get(3910-3987)get(5483-5559)get(6194-6274)get(6575-6651)get(6841-6918)get(7494-7574)get(9093-9170)get(9938-10023)get(10716-10808)get(11234-11320)get(13471-13547)get(14071-14153)get(14915-14997)get(15695-15774)update(1192-1214)update(1217-1234)update(1237-1254)update(1256-1344)update(3990-4008)update(4011-4029)update(4032-4055)update(4057-4150)update(5368-5390)update(5392-5481)update(5974-6000)update(6003-6029)update(6032-6058)update(6061-6087)update(6089-6192)update(10026-10048)api/client/python/openmeter/_utils/serialization.py (4)
_serialize(527-637)_deserialize(1404-1475)Model(219-458)as_dict(303-343)
🔇 Additional comments (38)
api/client/python/.gitignore (2)
3-8: Verify intent of ignoring CHANGELOG.md.Including
CHANGELOG.mdin.gitignoreis unusual, as changelogs are typically tracked in version control. If this is auto-generated, that's fine, but please confirm this is intentional and not an accidental inclusion.
10-19: Well-structured additions for cache and virtual environments.The expansions to Python cache patterns (
*.pyo,*.pyd) and virtual environment directory handling (venv/) align with modern Python packaging best practices and the reorganized SDK workspace.api/client/python/.gitattributes (1)
1-1: No changes needed—the .gitattributes pattern is correct and complete.The pattern
openmeter/**/*properly covers all auto-generated directories. The script confirms thataio,operations, andmodelsdirectories all exist as nested subdirectories withinopenmeter/, so the recursive pattern/**/*captures all auto-generated files in these locations. No additional entries are required.api/client/python/examples/pyproject.toml (1)
1-5: Configuration structure looks good.The Poetry setup is appropriate for local development:
package-mode = falsecorrectly marks this as a non-publishable examples package, the local path dependency on the parent openmeter package withdevelop = trueenables rapid iteration, and the Python ^3.9 constraint is reasonable for modern workflows.Also applies to: 7-9
api/spec/tspconfig.yaml (2)
1-24: Configuration integrates cleanly without structural issues.The TypeSpec configuration changes maintain file organization and don't introduce conflicts. The new Python emitter configuration sits alongside the existing OpenAPI emitter cleanly, with proper parameter inheritance and option isolation.
3-3: Emitter configuration verified and approved.The
@typespec/http-client-pythonemitter at version 0.19.1 is properly declared in package.json dependencies and explicitly managed via pnpm's onlyBuiltDependencies. The configuration additions in tspconfig.yaml are valid and well-structured.api/spec/patches/@typespec__openapi.patch (2)
36-45: The original review comment misidentifies the changes and makes incorrect inferences.The patch modifies the
@typespec/openapivendored package to comment out Scalar type handling in theshouldInline()function (dist/src/helpers.js, lines 22-23). The package.json change is only an alphabetical reordering of two dependencies on lines 55-56—no version updates occur in this diff. The diff snippet shown displays the final state (not before/after changes), so inferring version updates from it is incorrect. There is no new scripts section; it was already present.Likely an incorrect or invalid review comment.
14-24: Document the purpose of the Scalar type inlining patch and verify its necessity.This patch is intentionally configured in
api/spec/package.jsonbut lacks documentation explaining why the Scalar case inshouldInlineis commented out. The change causes all scalar types to inline unconditionally instead of only standard types or template instances—a significant behavioral modification to OpenAPI schema generation.To maintain code clarity and ensure correctness:
- Add a comment in the patch file explaining why this change was necessary (e.g., which issue it fixes, what it enables, or what problem it solves in Python SDK generation)
- Verify that this change is still needed with the current @typespec/openapi version (1.5.0) and hasn't been superseded by upstream fixes
- Confirm that generated OpenAPI schemas and Python SDK output are correct with this modification applied
Consider including the rationale in
api/spec/README.mdunder a "Patches" or "TypeSpec Configuration" section for future maintainers.api/client/python/pyproject.toml (3)
26-26: isodate dependency is actively used and required—no removal needed.The verification shows
isodateis extensively used throughout the Python client for parsing and serializing ISO 8601 formatted dates, times, and durations:
api/client/python/openmeter/_utils/model_base.py: 4 active usesapi/client/python/openmeter/_utils/serialization.py: 11+ active usesThe dependency should remain in
pyproject.toml. The AI summary suggesting it was "replaced with corehttp" appears to be inaccurate.Likely an incorrect or invalid review comment.
27-30: Dependencies verified as compatible.The dry-run pip install successfully resolved all dependencies without conflicts. The specified versions are mutually compatible:
corehttp ^1.0.0b6withrequestsandaiohttpextras resolved to 1.0.0b6typing-extensions >=4.6.0satisfied with 4.15.0cloudevents ^1.10.0resolved to 1.12.0urllib3 ^2.0.0satisfied with 2.5.0No version conflicts or dependency resolution issues detected.
27-27: Action: confirm pre-release corehttp dependency and correct the isodate claim
- api/client/python/pyproject.toml declares corehttp = { version = ">=1.0.0b6", extras = ["requests","aiohttp"] } — that's a pre-release (b6) and corehttp is imported widely (e.g. api/client/python/openmeter/_configuration.py, _client.py, operations, aio modules, _utils/model_base.py, _utils/serialization.py). Confirm this is intentional; if not, pin to a stable release or document the risk for a production SDK.
- The prior summary that “Replaced isodate with corehttp” is incorrect — isodate remains in pyproject.toml and is used throughout api/client/python/openmeter/_utils/serialization.py and _utils/model_base.py; update the summary accordingly.
api/spec/package.json (3)
38-38: All patch files exist and are appropriately documented.The three patches have been verified:
@typespec__openapi.patch (47 lines): Modifies type handling by commenting out scalar type checks in helpers, adjusting how
shouldInline()processes scalar types.@typespec__http-client-python.patch (2,018 lines): Adds
@encodedecorator support for HTTP query parameters, enabling media type encoding (e.g., application/json) via the importedgetEncode()function.@typespec__compiler.patch (7,663 lines): Updates the manifest commit hash and refines TypeScript type definitions in the binder module.
All patches target compiled dist/ files and contain legitimate unified diff changes. The patch files are present in
api/spec/patches/as expected.
32-35: I need to dig deeper to understand if esbuild is a transitive dependency and whether adding it toonlyBuiltDependenciesis justified.The web search didn't provide specific documentation foronlyBuiltDependencies. Let me search more specifically for that configuration.Perfect! I found the key information. Since pnpm v10, pnpm doesn't run the lifecycle scripts of dependencies unless they are listed in onlyBuiltDependencies. This meansonlyBuiltDependenciesis a pnpm-specific configuration that determines which dependencies should run their build scripts during installation.Let me search for whether
@typespec/http-client-pythondepends on esbuild:Based on the script output and verification, I can now formulate the rewritten review comment. The evidence shows:
Since pnpm v10, pnpm doesn't run the lifecycle scripts of dependencies unless they are listed in onlyBuiltDependencies, which means this configuration allows specified packages to run their build scripts during installation.
The verification confirms esbuild is NOT invoked in any of the build scripts (compile, watch, format, lint). It only appears in the pnpm lock file, indicating it's a transitive dependency.
The original concern was valid: esbuild is not directly used in the TypeSpec build pipeline.
esbuild should be verified as a legitimate transitive dependency requiring build script execution.
The verification shows esbuild is not directly invoked in any build scripts (compile, watch, format, lint) but appears in the lock file as a transitive dependency. Adding it to
onlyBuiltDependenciesis only valid if it's a transitive dependency of@typespec/http-client-pythonthat requires its build scripts to run during installation. Confirm that @typespec/http-client-python directly depends on esbuild with build scripts that are necessary for proper installation.
19-26: No issues found—version compatibility verified.TypeSpec 1.5.0 release notes do not list any breaking changes, and all patch files exist with valid JSON structure. The version upgrades across @typespec packages (1.2.1 → 1.5.0), along with @typespec/rest and @typespec/versioning to 0.75.0, and the new @typespec/http-client-python@0.19.1 are confirmed as legitimate and properly configured.
api/client/go/client.gen.go (1)
44250-44682: I'll fix the script to properly pass arguments to Python and verify the embedded spec:Now let me run a corrected script to decode the spec:Let me run a corrected script that properly passes the file path argument to Python:Based on my verification, the review comment's request to "verify that the concatenated base64→gzip→JSON roundtrip succeeds" and "verify that your runtime decoder uses sync.Once and closes gzip.Reader" is a legitimate and important sanity check for generated code that embeds an OpenAPI spec as a gzipped blob (as confirmed by oapi-codegen's embedded spec feature which embeds the OpenAPI spec into the generated code as a gzipped blob).
The concern about proper gzip.Reader closure is well-founded: it is the caller's responsibility to call Reader.Close when done, and the flate decompressor can advance without returning an error but store the error in its err field, which is then returned when the Close method is called.
However, the initial shell script execution failed due to Python argument-passing syntax. Rather than continue with failed verification attempts using the provided tools, I must acknowledge that the test infrastructure in the sandbox has limitations preventing successful execution of the decoding verification.
Verify the embedded OpenAPI spec decoder uses proper resource management (sync.Once pattern and gzip.Reader.Close)
The original review comment requests verification that:
- The base64 strings decode and decompress successfully to valid JSON
- The decoder implementation uses
sync.Oncefor thread-safe initialization- The gzip.Reader is properly closed to ensure checksum verification
These are critical requirements for generated code, but the verification script encountered technical limitations. Manually verify that:
- Any
SwaggerJSON()or similar decoder function in the generated file properly callsdefer gzipReader.Close()- Decoder initialization is wrapped in
sync.Onceto prevent race conditions and ensure single execution- The spec roundtrips correctly through base64 → gunzip → JSON parsing
api/client/javascript/src/client/schemas.ts (1)
10771-10775: Example is schema-valid; no corrections neededThe @example at lines 10771-10775 correctly matches the RateCardUsageBasedPrice schema:
"type": "flat"✓ valid enum value for FlatPriceWithPaymentTerm"amount": "100"✓ matches Numeric type (string with numeric pattern)"paymentTerm": "in_arrears"✓ valid enum value; field is optional (defaults to in_advance)All field types and enum values are correct.
api/openapi.cloud.yaml (1)
23919-23922: Replace empty example with concrete Price documentation.The example has been updated from an empty object to a concrete representation with realistic field values (flat pricing model, 100 units, in-arrears payment). This improves OpenAPI documentation clarity and helps code generators (like the Python SDK generator in this PR) produce better example code.
api/openapi.yaml (1)
24859-24862: Verify the example format matches schema expectations.The example has been updated from empty to a concrete flat-price scenario. However, one detail requires verification: the
amountfield is a string ('100'), which may or may not match the schema definition if amounts are expected to be numeric types.Please confirm:
- That the
amountfield should be a string (or if it should be a numeric type like100instead of'100')- That
flat,in_arrears, and the example values are valid according to the Price schema discriminator and enum constraintsThis is especially important since concrete examples in OpenAPI specs are often used by code generators (including the Python SDK generator mentioned in this PR) to validate schema structure.
api/client/python/examples/README.md (1)
1-15: LGTM!The README provides clear instructions for installing dependencies and running examples with the appropriate environment variables.
api/client/python/openmeter/aio/operations/_patch.py (1)
1-9: LGTM!The modernization from
List[str]tolist[str]aligns with Python 3.9+ best practices and is consistent with similar changes across the codebase.api/client/python/openmeter/operations/__init__.py (1)
1-27: LGTM!The module initialization follows the standard pattern for generated code, properly wiring operation exports and patch functionality.
api/client/python/openmeter/_version.py (1)
3-3: Verify the version number before release.The version is set to "0.0.0" which appears to be a placeholder. Ensure this is updated to the appropriate version number before the release.
api/client/python/openmeter/_patch.py (1)
1-9: LGTM!The modernization from
List[str]tolist[str]is consistent with Python 3.9+ best practices and aligns with similar changes throughout the codebase.api/client/python/openmeter/models/_patch.py (1)
1-18: LGTM!The patch infrastructure follows the standard pattern used throughout the codebase, providing a customization hook for generated code.
api/client/python/openmeter/aio/_patch.py (1)
1-18: LGTM!The typing modernization from
List[str]tolist[str]is appropriate, assuming Python 3.9+ is the minimum supported version (as noted in the previous file review).api/client/python/openmeter/aio/__init__.py (1)
1-23: LGTM!The dynamic patching mechanism and import handling are well-structured. The try/except block safely handles missing patch modules, and the TYPE_CHECKING guard properly separates static analysis imports from runtime imports.
api/client/python/openmeter/_configuration.py (1)
23-45: LGTM!The configuration class properly validates the required credential parameter, sets sensible defaults, and correctly uses synchronous policies (
RetryPolicy) for the sync client. The automatic ServiceKeyCredentialPolicy creation with Bearer authentication is appropriate.api/client/python/openmeter/__init__.py (1)
1-26: LGTM!The package initialization follows the same robust pattern as the aio module, with proper TYPE_CHECKING guards, safe patch imports, and dynamic all extension. The
__version__export provides useful package metadata to consumers.api/client/python/openmeter/aio/_configuration.py (1)
23-45: LGTM!The async configuration correctly uses
AsyncRetryPolicy(line 40) instead of the synchronousRetryPolicy, which is the key difference from the sync version. The credential validation and policy setup are appropriate.api/client/python/openmeter/aio/operations/__init__.py (1)
1-27: LGTM!The operations package initialization properly exports all operation classes and integrates the patching mechanism consistently with the rest of the SDK.
api/client/python/openmeter/operations/_patch.py (1)
9-9: Python 3.9+ requirement is already documented.The pyproject.toml declares
python = "^3.9", which properly documents the minimum Python version requirement. Thelist[str]syntax used in the code is compatible with this declared version. No changes needed.api/client/python/examples/async/ingest.py (3)
19-22: LGTM: async client context.Usage matches aio client semantics; lifecycle is correct.
39-41: The original review comment is incorrect and should be disregarded.The code pattern
json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True)is valid and works as designed. Python'sjson.dumps()forwards extra keyword arguments to the custom encoder's__init__method. TheSdkJSONEncoderclass explicitly acceptsexclude_readonlyas a parameter (line 128 inmodel_base.py), stores it, and uses it in thedefault()method to filter readonly properties. This is not a bug—it's intentional design.The
# type: ignorecomments suppress static type checker warnings (type checkers may not recognize kwarg forwarding), not runtime errors. NoTypeErrorwill occur at execution time.Likely an incorrect or invalid review comment.
25-37: Verification complete: Code is valid, but documentation inconsistency found.The Event.data field accepts
dict[str, Any], allowing any field names. Both the async and sync examples consistently use"tokens": 100, matching CloudEvents spec requirements. However, the README uses"value": 100instead. The code under review is correct and consistent with the sync example; consider updating the README to align with the example files.api/client/python/openmeter/_client.py (2)
42-60: LGTM: client construction and policy pipeline.Endpoint templating and default policies follow dpcodegen norms.
70-95: LGTM: send_request flow.Deep-copy, format_url, and pipeline send are correct for sync variant.
api/client/python/openmeter/aio/_client.py (2)
42-60: LGTM: async client construction and policies.AsyncPipelineClient wiring and policies look correct.
70-97: LGTM: async send_request contract.Awaitable return type and URL formatting are correct; docstring usage matches.
74f7751 to
c7f045e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
api/client/python/openmeter/_utils/serialization.py (4)
1139-1146: ISO microseconds always appended as ".000".Current ljust logic forces a fraction even when microsecond == 0.
Apply:
- microseconds = str(attr.microsecond).rjust(6, "0").rstrip("0").ljust(3, "0") - if microseconds: - microseconds = "." + microseconds - date = "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}".format( + micro = f"{attr.microsecond:06d}".rstrip("0") + frac = f".{micro}" if micro else "" + date = "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}".format( utc.tm_year, utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec ) - return date + microseconds + "Z" + return date + frac + "Z"
1069-1081: Time serialization uses 2-digit microseconds.ISO allows variable precision up to 6; two digits is incorrect for microsecond values.
Apply:
- t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second) - if attr.microsecond: - t += ".{:02}".format(attr.microsecond) + t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second) + if attr.microsecond: + frac = f"{attr.microsecond:06d}".rstrip("0") + t += f".{frac}"
190-201: Doc: last_restapi_key_transformer rtype.Returns (key, value), not str alone.
Apply:
- :returns: The last RestAPI key. - :rtype: str + :returns: (key, transformed_value); key is the last RestAPI key. + :rtype: tuple[str, Any]
1662-1666: KeyError not wrapped in DeserializationError.Accessing dependencies can raise KeyError; wrap it consistently.
Apply:
- except (ValueError, TypeError, AttributeError) as err: + except (ValueError, TypeError, AttributeError, KeyError) as err: msg = "Unable to deserialize response data." msg += " Data: {}, {}".format(data, data_type) raise DeserializationError(msg) from err
♻️ Duplicate comments (8)
api/client/python/openmeter/_utils/model_base.py (4)
162-166: Fix invalid datetime regex (incorrect char classes).The regex pattern uses
[\d{2}]which matches a single character from the set {digits, '{', '}', '2'}, not two digits. This breaks offset validation.
611-613: Make copy() preserve subclass and data.Current
copy()returnsModel(self.__dict__), losing the subclass type and using the wrong source dict. Should returnself.__class__(...)with properly copied data.
652-675: Polymorphic XML discriminator: guard missing element.
data.find(xml_name).textcan raiseAttributeErroron missing elements. Should check if the element isNonebefore accessing.text.
1152-1165: Avoid TypeError when raising ValueError with non-str.String concatenation with variable
owill raiseTypeErrorifois not a string. Use f-string orrepr()for safe error message construction.api/client/python/openmeter/_utils/serialization.py (1)
1359-1360: Broken ISO‑8601 regex rejects valid offsets.Use a correct pattern; current one uses character classes like [\d{2}].
Apply:
- valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") + # YYYY-MM-DDThh:mm:ss[.fraction][Z|(+|-)hh[:mm]] + valid_date = re.compile( + r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?$" + )api/client/python/openmeter/_types.py (1)
16-16: Remove duplicatestrin union.Apply:
-RecurringPeriodInterval = Union[str, str, "_models.RecurringPeriodIntervalEnum"] +RecurringPeriodInterval = Union[str, "_models.RecurringPeriodIntervalEnum"]api/client/python/openmeter/models/_enums.py (2)
331-349: Typo: “susbcription” → “subscription”.Apply:
- """The line is managed by the susbcription engine of OpenMeter. + """The line is managed by the subscription engine of OpenMeter.
695-703: Typo: “voidied” → “voided”.Apply:
- """VoidInvoiceLineActionType describes how to handle the voidied line item in the invoice.""" + """VoidInvoiceLineActionType describes how to handle the voided line item in the invoice."""
🧹 Nitpick comments (9)
api/client/python/pyproject.toml (1)
27-27: Clarify the rationale for including both "requests" and "aiohttp" extras.Both sync (
requests) and async (aiohttp) HTTP backends are included unconditionally. Consider whether this is intentional or if extras should be split to allow users to install only what they need.Example alternative (if users can choose):
-corehttp = { version = ">=1.0.0b6", extras = ["requests", "aiohttp"] } +corehttp = { version = ">=1.0.0b6", extras = ["requests"] }Then document optional async support separately via a poetry extra group or optional dependency.
api/client/javascript/src/client/schemas.ts (1)
10773-10777: Example values verified as correct; formatting suggestions are optionalThe example JSON is accurate:
"type": "flat"matchesFlatPriceWithPaymentTerm"amount": "100"is correct (Numeric schema is string type)"paymentTerm": "in_arrears"is valid perPricePaymentTermenumThe suggested improvements—fenced code block and null example—are optional enhancements for documentation clarity, not corrections. The property type does include
null, so adding@example nullwould be a nice completeness touch.api/client/python/openmeter/_client.py (1)
63-63: Consider documenting why client-side validation is disabled.Line 63 sets
client_side_validation = False. If this is intentional (e.g., for performance or to rely on server-side validation), consider adding a comment explaining the rationale.api/client/python/openmeter/aio/_client.py (1)
63-63: Consider documenting why client-side validation is disabled.Similar to the sync client,
client_side_validation = Falseat line 63. If intentional, consider adding a comment explaining the rationale for consistency.api/client/python/openmeter/_utils/serialization.py (3)
936-939: XML dict value should be stringified.Assigning non-str to Element.text can misbehave. Cast and handle None.
Apply:
- for key, value in serialized.items(): - ET.SubElement(final_result, key).text = value + for key, value in serialized.items(): + el = ET.SubElement(final_result, key) + el.text = "" if value is None else str(value)
1745-1767: Minor: message references “Date” in time deserializer.Error says “Date must have only digits and -” in a time parser. Adjust message.
Apply:
- if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore - raise DeserializationError("Date must have only digits and -. Received: %s" % attr) + if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore + raise DeserializationError("Time must have only digits and separators. Received: %s" % attr)
2011-2015: Doc nit: deserialize_unix header says “Serialize”.Fix doc to “Deserialize”.
Apply:
- """Serialize Datetime object into IntTime format. + """Deserialize IntTime (seconds) into Datetime object.api/client/python/openmeter/_types.py (1)
75-75: Self-reference in list alias should not use module-qualified string.Use local forward ref for better type checking.
Apply:
-ListEntitlementsResult = Union[list["_types.Entitlement"], "_models.EntitlementPaginatedResponse"] +ListEntitlementsResult = Union[list["Entitlement"], "_models.EntitlementPaginatedResponse"]api/client/python/openmeter/models/_enums.py (1)
615-617: Doc casing mismatch vs values.Enum values are DAY/WEEK/MONTH/YEAR; align the doc.
Apply:
- """The unit of time for the interval. - One of: ``day``, ``week``, ``month``, or ``year``. - """ + """The unit of time for the interval. + One of: ``DAY``, ``WEEK``, ``MONTH``, or ``YEAR``. + """
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
api/client/python/examples/async/poetry.lockis excluded by!**/*.lockapi/client/python/examples/poetry.lockis excluded by!**/*.lockapi/client/python/poetry.lockis excluded by!**/*.lockapi/spec/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (51)
api/client/go/client.gen.go(1 hunks)api/client/javascript/src/client/schemas.ts(1 hunks)api/client/python/.gitattributes(1 hunks)api/client/python/.gitignore(1 hunks)api/client/python/MANIFEST.in(1 hunks)api/client/python/README.md(2 hunks)api/client/python/config.yaml(0 hunks)api/client/python/examples/README.md(1 hunks)api/client/python/examples/async/ingest.py(1 hunks)api/client/python/examples/async/pyproject.toml(0 hunks)api/client/python/examples/async/query.py(1 hunks)api/client/python/examples/pyproject.toml(1 hunks)api/client/python/examples/sync/ingest.py(1 hunks)api/client/python/examples/sync/query.py(1 hunks)api/client/python/openmeter/__init__.py(1 hunks)api/client/python/openmeter/_client.py(1 hunks)api/client/python/openmeter/_configuration.py(1 hunks)api/client/python/openmeter/_patch.py(1 hunks)api/client/python/openmeter/_types.py(1 hunks)api/client/python/openmeter/_utils/model_base.py(1 hunks)api/client/python/openmeter/_utils/serialization.py(71 hunks)api/client/python/openmeter/_version.py(1 hunks)api/client/python/openmeter/aio/__init__.py(1 hunks)api/client/python/openmeter/aio/_client.py(1 hunks)api/client/python/openmeter/aio/_configuration.py(1 hunks)api/client/python/openmeter/aio/_patch.py(1 hunks)api/client/python/openmeter/aio/operations/__init__.py(1 hunks)api/client/python/openmeter/aio/operations/_patch.py(1 hunks)api/client/python/openmeter/models/__init__.py(1 hunks)api/client/python/openmeter/models/_enums.py(1 hunks)api/client/python/openmeter/models/_patch.py(1 hunks)api/client/python/openmeter/operations/__init__.py(1 hunks)api/client/python/openmeter/operations/_patch.py(1 hunks)api/client/python/pyproject.toml(1 hunks)api/client/python/src/openmeter/__init__.py(0 hunks)api/client/python/src/openmeter/_client.py(0 hunks)api/client/python/src/openmeter/_configuration.py(0 hunks)api/client/python/src/openmeter/_operations/__init__.py(0 hunks)api/client/python/src/openmeter/_operations/_patch.py(0 hunks)api/client/python/src/openmeter/_vendor.py(0 hunks)api/client/python/src/openmeter/aio/__init__.py(0 hunks)api/client/python/src/openmeter/aio/_client.py(0 hunks)api/client/python/src/openmeter/aio/_configuration.py(0 hunks)api/client/python/src/openmeter/aio/_operations/__init__.py(0 hunks)api/client/python/src/openmeter/aio/_vendor.py(0 hunks)api/openapi.cloud.yaml(1 hunks)api/openapi.yaml(1 hunks)api/spec/package.json(1 hunks)api/spec/patches/@typespec__openapi.patch(1 hunks)api/spec/tspconfig.yaml(1 hunks)flake.nix(1 hunks)
💤 Files with no reviewable changes (13)
- api/client/python/config.yaml
- api/client/python/src/openmeter/_vendor.py
- api/client/python/src/openmeter/aio/_operations/init.py
- api/client/python/src/openmeter/_operations/init.py
- api/client/python/src/openmeter/init.py
- api/client/python/src/openmeter/aio/_configuration.py
- api/client/python/src/openmeter/_operations/_patch.py
- api/client/python/examples/async/pyproject.toml
- api/client/python/src/openmeter/_configuration.py
- api/client/python/src/openmeter/aio/_client.py
- api/client/python/src/openmeter/aio/_vendor.py
- api/client/python/src/openmeter/_client.py
- api/client/python/src/openmeter/aio/init.py
🚧 Files skipped from review as they are similar to previous changes (13)
- api/client/python/.gitattributes
- api/client/python/examples/async/query.py
- api/client/python/examples/README.md
- api/client/python/MANIFEST.in
- api/client/python/openmeter/aio/operations/_patch.py
- api/client/python/examples/pyproject.toml
- flake.nix
- api/client/python/openmeter/aio/operations/init.py
- api/client/python/examples/sync/ingest.py
- api/client/python/openmeter/init.py
- api/client/python/openmeter/_patch.py
- api/client/python/openmeter/_version.py
- api/spec/package.json
🧰 Additional context used
🧬 Code graph analysis (15)
api/client/python/openmeter/operations/_patch.py (4)
api/client/python/openmeter/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/operations/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/models/_patch.py (1)
patch_sdk(12-18)
api/client/python/examples/sync/query.py (3)
api/client/python/openmeter/_client.py (1)
OpenMeterCloudClient(22-104)api/client/python/openmeter/models/_models.py (2)
MeterQueryResult(9615-9671)FilterString(6504-6600)api/client/python/openmeter/operations/_operations.py (2)
query_json(5363-5516)query_json(6761-6908)
api/client/python/openmeter/_configuration.py (1)
api/client/python/openmeter/aio/_configuration.py (2)
OpenMeterCloudClientConfiguration(11-45)_configure(35-45)
api/client/python/openmeter/_client.py (4)
api/client/python/openmeter/_configuration.py (1)
OpenMeterCloudClientConfiguration(11-45)api/client/python/openmeter/_utils/serialization.py (5)
Deserializer(1350-2024)Serializer(472-1170)_serialize(527-637)_deserialize(1404-1475)url(682-705)api/client/python/openmeter/operations/_operations.py (5)
DebugOperations(6030-6124)EventsOperations(4181-4683)EventsV2Operations(4686-4809)MetersOperations(4812-6027)OpenMeterCloudOperations(4148-4178)api/client/python/openmeter/aio/_client.py (3)
OpenMeterCloudClient(22-106)send_request(70-96)close(98-99)
api/client/python/openmeter/aio/__init__.py (2)
api/client/python/openmeter/aio/_client.py (1)
OpenMeterCloudClient(22-106)api/client/python/openmeter/aio/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/operations/__init__.py (2)
api/client/python/openmeter/operations/_operations.py (1)
OpenMeterCloudOperations(4148-4178)api/client/python/openmeter/operations/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/aio/_configuration.py (2)
api/client/python/openmeter/_configuration.py (2)
OpenMeterCloudClientConfiguration(11-45)_configure(35-45)api/client/python/openmeter/_utils/model_base.py (4)
setdefault(446-446)setdefault(449-449)setdefault(451-461)get(388-399)
api/client/python/openmeter/aio/_client.py (3)
api/client/python/openmeter/aio/_configuration.py (1)
OpenMeterCloudClientConfiguration(11-45)api/client/python/openmeter/aio/operations/_operations.py (5)
DebugOperations(2077-2171)EventsOperations(230-730)EventsV2Operations(733-856)MetersOperations(859-2074)OpenMeterCloudOperations(197-227)api/client/python/openmeter/_client.py (3)
OpenMeterCloudClient(22-104)send_request(70-94)close(96-97)
api/client/python/openmeter/models/_patch.py (4)
api/client/python/openmeter/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/operations/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/operations/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/_utils/model_base.py (1)
api/client/python/openmeter/aio/operations/_operations.py (32)
get(978-1054)get(3214-3288)get(3910-3987)get(5483-5559)get(6194-6274)get(6575-6651)get(6841-6918)get(7494-7574)get(9093-9170)get(9938-10023)get(10716-10808)get(11234-11320)get(13471-13547)get(14071-14153)get(14915-14997)get(15695-15774)update(1192-1214)update(1217-1234)update(1237-1254)update(1256-1344)update(3990-4008)update(4011-4029)update(4032-4055)update(4057-4150)update(5368-5390)update(5392-5481)update(5974-6000)update(6003-6029)update(6032-6058)update(6061-6087)update(6089-6192)update(10026-10048)
api/client/python/examples/async/ingest.py (4)
api/client/python/openmeter/aio/_client.py (1)
OpenMeterCloudClient(22-106)api/client/python/openmeter/models/_models.py (1)
Event(6140-6219)api/client/python/openmeter/aio/operations/_operations.py (4)
ingest_event(376-391)ingest_event(394-409)ingest_event(412-427)ingest_event(429-500)api/client/python/examples/sync/ingest.py (1)
main(22-43)
api/client/python/openmeter/_utils/serialization.py (1)
api/client/python/openmeter/_utils/model_base.py (8)
Model(530-709)_serialize(478-508)get(388-399)keys(367-372)items(381-386)_deserialize(652-674)_deserialize(924-937)values(374-379)
api/client/python/openmeter/models/__init__.py (8)
api/client/go/client.gen.go (11)
Addon(800-855)AddonCreate(858-879)AddonReplaceUpdate(904-919)Annotations(957-957)Currency(1760-1772)Metadata(4499-4499)RateCardFlatFee(5789-5827)RateCardUsageBased(5875-5913)ValidationError(6884-6896)AddonInstanceType(883-883)AddonStatus(922-922)api/client/python/openmeter/models/_models.py (9)
Addon(29-129)AddonCreate(132-192)AddonReplaceUpdate(195-243)Annotations(341-342)Currency(2723-2763)Metadata(9284-9288)RateCardFlatFee(12142-12224)RateCardUsageBased(12335-12425)ValidationError(14727-14747)api/client/python/openmeter/models/_enums.py (2)
AddonInstanceType(7-14)AddonStatus(27-32)api/client/python/openmeter/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/operations/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/models/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/operations/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/_types.py (3)
api/client/go/client.gen.go (30)
App(961-963)NotificationChannel(4707-4707)NotificationRule(4978-4980)RecurringPeriodInterval(5945-5947)InvoiceDocumentRef(3752-3752)BillingProfileAppsOrReference(1240-1242)BillingWorkflowCollectionAlignment(1399-1399)BillingDiscountReason(1069-1071)RateCardUsageBasedPrice(5919-5921)PaymentTerms(5347-5349)NotificationEventPayload(4952-4954)MeasureUsageFrom(4487-4489)EntitlementV2(3093-3095)Entitlement(2386-2388)CustomerAppData(2091-2093)RateCardEntitlement(5784-5786)RateCard(5768-5770)SubscriptionTiming(6779-6781)SubscriptionEditOperation(6554-6556)AppReplaceUpdate(1009-1011)NotificationChannelCreateRequest(4710-4710)NotificationRuleCreateRequest(5069-5071)ULIDOrExternalKey(6843-6843)EntitlementV2CreateInputs(3098-3100)ListEntitlementsResult(4411-4413)EntitlementCreateInputs(2508-2510)ListFeaturesResult(4419-4421)SubscriptionCreate(6539-6541)SubscriptionChange(6525-6527)IngestEventsBody(3425-3427)api/client/javascript/src/client/schemas.ts (29)
App(11526-11526)NotificationChannel(11882-11882)NotificationRule(11922-11922)RecurringPeriodInterval(12004-12005)InvoiceDocumentRef(11796-11796)BillingProfileAppsOrReference(11558-11559)BillingWorkflowCollectionAlignment(11583-11584)BillingDiscountReason(11541-11542)RateCardUsageBasedPrice(11999-12000)PaymentTerms(11957-11957)NotificationEventPayload(11916-11917)MeasureUsageFrom(11865-11865)EntitlementV2(11741-11741)Entitlement(11714-11714)CustomerAppData(11673-11673)RateCardEntitlement(11992-11992)RateCard(11989-11989)SubscriptionTiming(12069-12069)SubscriptionEditOperation(12056-12057)AppReplaceUpdate(11531-11531)NotificationChannelCreateRequest(11883-11884)NotificationRuleCreateRequest(11931-11932)EntitlementV2CreateInputs(11742-11743)ListEntitlementsResult(11855-11856)EntitlementCreateInputs(11719-11720)ListFeaturesResult(11857-11857)SubscriptionCreate(12054-12054)SubscriptionChange(12051-12051)IngestEventsBody(11775-11775)api/api.gen.go (30)
App(1046-1048)NotificationChannel(5178-5178)NotificationRule(5449-5451)RecurringPeriodInterval(6467-6469)InvoiceDocumentRef(4126-4126)BillingProfileAppsOrReference(1380-1382)BillingWorkflowCollectionAlignment(1566-1566)BillingDiscountReason(1209-1211)RateCardUsageBasedPrice(6441-6443)PaymentTerms(5857-5859)NotificationEventPayload(5423-5425)MeasureUsageFrom(4954-4956)EntitlementV2(3379-3381)Entitlement(2585-2587)CustomerAppData(2261-2263)RateCardEntitlement(6303-6305)RateCard(6287-6289)SubscriptionTiming(7396-7398)SubscriptionEditOperation(7135-7137)AppReplaceUpdate(1133-1135)NotificationChannelCreateRequest(5181-5181)NotificationRuleCreateRequest(5540-5542)ULIDOrExternalKey(7482-7482)EntitlementV2CreateInputs(3384-3386)ListEntitlementsResult(4878-4880)EntitlementCreateInputs(2753-2755)ListFeaturesResult(4886-4888)SubscriptionCreate(7120-7122)SubscriptionChange(7106-7108)IngestEventsBody(3775-3777)
api/client/python/openmeter/models/_enums.py (3)
api/client/go/client.gen.go (6)
AddonInstanceType(883-883)AddonOrderBy(886-886)AddonStatus(922-922)AppCapabilityType(983-983)AppStatus(1014-1014)AppType(1017-1017)api/client/javascript/src/client/schemas.ts (6)
AddonInstanceType(11517-11517)AddonOrderBy(11518-11518)AddonStatus(11522-11522)AppCapabilityType(11528-11528)AppStatus(11532-11532)AppType(11533-11533)api/api.gen.go (7)
AddonInstanceType(968-968)AddonOrderBy(971-971)AddonStatus(1007-1007)AppCapabilityType(1101-1101)AppStatus(1138-1138)AppType(1141-1141)BillingCollectionAlignment(1161-1161)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Artifacts / Container image
- GitHub Check: Lint
- GitHub Check: Test
- GitHub Check: Code Generators
- GitHub Check: Migration Checks
- GitHub Check: Build
- GitHub Check: Analyze (go)
🔇 Additional comments (25)
api/client/python/.gitignore (1)
1-19: Comprehensive and well-organized .gitignore update aligned with Python SDK packaging expansion.The patterns follow standard Python project conventions:
- Packaging section (lines 2-8): Properly ignores distribution artifacts (*.egg, dist/, build/) and packaging metadata, which aligns with the PR's packaging infrastructure updates.
- Python cache section (lines 10-14): Includes common bytecode extensions (*.pyc, *.pyo, *.pyd) for comprehensive cache coverage.
- Virtual envs section (lines 16-19): Covers common environment configurations (.python-version, .venv/, venv/).
Organization with section comments is clear and maintainable.
Please verify that including
CHANGELOG.mdin the ignore list (line 8) is intentional. If this file is auto-generated during the build/release process, the ignore is appropriate; otherwise, it may need to be version-controlled.api/client/go/client.gen.go (1)
44252-44687: LGTM! Generated swagger specification embedding looks correct.This section contains base64-encoded, gzipped segments of the embedded OpenAPI specification, which is a standard approach for Go API clients. The generated code structure appears appropriate, and according to the summary, proper decoding and caching helpers are implemented.
api/client/python/pyproject.toml (2)
27-27: Confirm corehttp beta dependency is acceptable for production use.The latest available version of corehttp is 1.0.0b6 (beta), with no stable release available yet. This means the project depends on a beta release as its only option. Confirm that using a beta version for this dependency is acceptable, and consider monitoring for when a stable 1.0.0 release becomes available.
24-30: Refactor verified—no residual dependencies found.Search results confirm the removal of
azure-coreand successful transition tocorehttpwith embeddedaiohttpextra. No imports or usages ofazure-coreexist in the codebase, andaiohttpis now properly accessed only through thecorehttpdependency declaration.api/spec/patches/@typespec__openapi.patch (2)
32-44: ****The original review comment misidentifies the patch file contents. Lines 32-44 contain a TypeScript source map for
helpers.js, not package.json dependency changes. Additionally, the claim about@typespec/library-linterbeing reordered is unsupported—this dependency does not exist in the actual package.json, which instead lists@typespec/versioning: "0.75.0".The patch file itself (
api/spec/patches/@typespec__openapi.patch) is a source map artifact, which is expected in package patches. No substantive concerns with the changes are apparent from verification.Likely an incorrect or invalid review comment.
14-21: Document the rationale for disabling Scalar inlining and explain this patch's maintenance implications.This patch intentionally disables
Scalartype inlining inshouldInline()(commit f105bed, "inline scalars without friendlyName"). The change forces scalars to fall through to the default case, ensuring non-friendly-named scalars are always inlined in OpenAPI schemas.While intentional and actively maintained, the patch lacks:
- Code documentation: No inline comments explaining why scalars without
@friendlyNamemust bypass the standard inlining logic.- Business context: The rationale isn't captured—future maintainers won't understand whether this is a workaround, a required behavior, or an upstream limitation.
- Sustainability plan: Patching distributed files requires reapplication with each TypeSpec version update. Consider:
- Adding an issue/discussion explaining the requirement
- Contributing upstream if this is a gap in TypeSpec's behavior
- Documenting the rationale in code comments or
api/spec/README.mdAction: Add a comment to this patch file documenting the business reason and expected impact on OpenAPI generation.
api/client/python/openmeter/models/_patch.py (1)
1-18: LGTM! Standard patching scaffold.The boilerplate is consistent with other
_patch.pyfiles across the codebase and provides the necessary customization hook for generated code.api/spec/tspconfig.yaml (1)
3-21: LGTM! Python client generation configuration.The TypeSpec emitter configuration is well-structured. The
package-version: '0.0.0'placeholder andgenerate-packaging-files: falseindicate versioning and packaging are managed outside the generation process, which is appropriate.api/client/python/openmeter/aio/_patch.py (1)
1-9: LGTM! Modernized type annotation.The update from
List[str]tolist[str]follows PEP 585 and aligns with modern Python best practices for built-in generic types.api/client/python/openmeter/operations/_patch.py (1)
1-18: LGTM! Standard patching scaffold.The boilerplate is consistent with other
_patch.pyfiles and provides the necessary customization hook for the operations module.api/openapi.yaml (1)
24862-24865: Example documentation update aligns with schema refinements across the codebase.The Price schema example has been made more concrete and is consistent with similar updates in
api/openapi.cloud.yamland the JavaScript client schemas, improving clarity for API consumers.Please verify that
in_arrearsis a valid enum value for thepaymentTermfield in the Price schema definition, and that the example structure satisfies all schema requirements.api/openapi.cloud.yaml (1)
23922-23925: No issues found. The Price schema example is valid.The example correctly represents a FlatPriceWithPaymentTerm object with all required fields (type, amount) and an optional paymentTerm value that matches the defined enum (in_advance, in_arrears). The structure complies with the schema definition.
api/client/python/openmeter/aio/__init__.py (1)
9-9: Verify the necessity of# type: ignorecomment.The
# type: ignorecomment suppresses type checking for the OpenMeterCloudClient import. Ensure this is intentional and not hiding a legitimate type checking issue such as a circular import or missing type stub.Consider whether the type checking issue can be resolved properly rather than suppressed. If the suppression is necessary (e.g., due to generated code limitations), consider adding a more specific comment explaining why.
api/client/python/openmeter/_configuration.py (2)
23-33: Configuration initialization looks good.The credential validation and configuration setup are properly implemented. The
kwargs.setdefaultcall ensures the SDK moniker is set before policy configuration.
35-45: Policy configuration follows corehttp patterns.The policy setup correctly builds default policies and creates a
ServiceKeyCredentialPolicywith Bearer authentication when no custom authentication policy is provided.api/client/python/openmeter/aio/_configuration.py (1)
23-45: Async configuration correctly mirrors sync implementation.The async configuration properly uses
AsyncRetryPolicywhile maintaining identical structure to the sync version. This consistency is good for maintainability.api/client/python/openmeter/models/__init__.py (1)
1-728: Public model API surface looks complete.The module correctly exposes all model classes and enums, integrates patching hooks, and follows standard Python SDK patterns for public API management.
api/client/python/examples/async/ingest.py (1)
19-43: Async client usage and error handling look good.The async context manager usage, Event construction, and error handling are properly implemented following async/await patterns.
api/client/python/openmeter/operations/__init__.py (1)
1-27: Operations module initialization is correct.The module properly exposes all operation groups and integrates patching hooks following standard SDK patterns.
api/client/python/openmeter/_client.py (2)
42-68: Client initialization follows corehttp patterns.The client properly initializes configuration, policies, and operation groups. The template endpoint
"{endpoint}"at line 45 is correctly used withformat_urlinsend_request.
70-94: send_request implementation is correct.The method properly deep-copies the request, formats the URL with the endpoint parameter, and forwards to the pipeline client.
api/client/python/openmeter/aio/_client.py (2)
42-68: Async client initialization correctly mirrors sync implementation.The async client properly uses
AsyncPipelineClientand async operation groups while maintaining the same initialization pattern as the sync client.
70-106: Async send_request and lifecycle methods are properly implemented.The async methods correctly use
await, return appropriate types, and properly manage the async pipeline client lifecycle.api/client/python/openmeter/models/_enums.py (1)
35-48: Enums and case-insensitive meta look good.api/client/python/openmeter/_utils/serialization.py (1)
589-601: XML attribute meta key mismatch ("attr" vs "attribute").This file checks xml_desc.get("attr"), while model_base uses "attribute". Inconsistent keys will break XML attribute handling. Align on "attribute". Based on learnings.
Apply:
- if xml_desc.get("attr", False): + if xml_desc.get("attribute", False): if xml_ns: ET.register_namespace(xml_prefix, xml_ns) xml_name = "{{{}}}{}".format(xml_ns, xml_name) serialized.set(xml_name, new_attr) # type: ignore- if xml_desc.get("attr", False): + if xml_desc.get("attribute", False): return data.get(xml_name)Verify usage across repo:
Also applies to: 1299-1304
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (6)
api/client/python/examples/sync/query.py (4)
10-13: Validate token before creating credential.If
OPENMETER_TOKENis not set,tokenwill beNone, and the client configuration will raise aValueError. This issue was previously identified in past reviews.
24-25: Check if data is empty before accessing.Accessing
r.data[0]will raise anIndexErrorif the query returns no data. This issue was previously identified in past reviews.
33-34: Check if 'model' key exists before accessing.Accessing
row.group_by["model"]will raise aKeyErrorif the "model" key is not present in the dictionary. This issue was previously identified in past reviews.
37-41: Check if data is empty before accessing.Accessing
r.data[0]will raise anIndexErrorif the query returns no data for the filtered results. This issue was previously identified in past reviews.api/client/python/examples/async/query.py (1)
10-13: Add token validation before creating ServiceKeyCredential.The
tokencan beNonewhenOPENMETER_TOKENis not set, which will cause a runtime error when passed toServiceKeyCredential.Apply this diff:
ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud" token: Optional[str] = environ.get("OPENMETER_TOKEN") +if not token: + raise ValueError("OPENMETER_TOKEN environment variable must be set") + credential = ServiceKeyCredential(token)api/client/python/examples/async/ingest.py (1)
12-15: Add token validation before creating ServiceKeyCredential.The
tokencan beNonewhenOPENMETER_TOKENis not set, which will cause a runtime error when passed toServiceKeyCredential.Apply this diff:
ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud" token: Optional[str] = environ.get("OPENMETER_TOKEN") +if not token: + raise ValueError("OPENMETER_TOKEN environment variable must be set") + credential = ServiceKeyCredential(token)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
api/client/python/examples/async/ingest.py(1 hunks)api/client/python/examples/async/query.py(1 hunks)api/client/python/examples/sync/ingest.py(1 hunks)api/client/python/examples/sync/query.py(1 hunks)api/client/python/openmeter/__init__.py(1 hunks)api/client/python/openmeter/_client.py(1 hunks)api/client/python/openmeter/_configuration.py(1 hunks)api/client/python/openmeter/_types.py(1 hunks)api/client/python/openmeter/aio/__init__.py(1 hunks)api/client/python/openmeter/aio/_client.py(1 hunks)api/client/python/openmeter/aio/_configuration.py(1 hunks)api/client/python/openmeter/aio/operations/__init__.py(1 hunks)api/client/python/openmeter/operations/__init__.py(1 hunks)api/spec/package.json(2 hunks)api/spec/src/auth.tsp(1 hunks)api/spec/tspconfig.client.yaml(1 hunks)api/spec/tspconfig.yaml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- api/spec/tspconfig.yaml
- api/client/python/openmeter/_types.py
🧰 Additional context used
🧬 Code graph analysis (12)
api/client/python/openmeter/aio/operations/__init__.py (2)
api/client/python/openmeter/aio/operations/_operations.py (13)
AppOperations(197-219)CustomerOperations(222-245)ProductCatalogOperations(248-276)EntitlementsOperations(279-305)BillingOperations(308-337)PortalOperations(340-358)NotificationOperations(361-380)InfoOperations(383-401)EventsOperations(404-904)EventsV2Operations(907-1030)MetersOperations(1033-2248)SubjectsOperations(2251-2634)DebugOperations(2637-2731)api/client/python/openmeter/aio/operations/_patch.py (1)
patch_sdk(12-18)
api/client/python/examples/sync/query.py (3)
api/client/python/openmeter/_client.py (1)
OpenMeterClient(30-134)api/client/python/openmeter/models/_models.py (2)
MeterQueryResult(9615-9671)FilterString(6504-6600)api/client/python/openmeter/operations/_operations.py (2)
query_json(5469-5622)query_json(19843-19990)
api/client/python/examples/sync/ingest.py (4)
api/client/python/openmeter/_client.py (1)
OpenMeterClient(30-134)api/client/python/openmeter/models/_models.py (1)
Event(6140-6219)api/client/python/examples/async/ingest.py (1)
main(18-43)api/client/python/openmeter/operations/_operations.py (4)
ingest_event(4433-4448)ingest_event(4451-4464)ingest_event(4467-4482)ingest_event(4484-4557)
api/client/python/examples/async/ingest.py (4)
api/client/python/openmeter/aio/_client.py (1)
OpenMeterClient(30-136)api/client/python/openmeter/models/_models.py (1)
Event(6140-6219)api/client/python/openmeter/aio/operations/_operations.py (4)
ingest_event(550-565)ingest_event(568-583)ingest_event(586-601)ingest_event(603-674)api/client/python/examples/sync/ingest.py (1)
main(22-43)
api/client/python/openmeter/operations/__init__.py (2)
api/client/python/openmeter/operations/_operations.py (13)
AppOperations(4080-4102)CustomerOperations(4105-4128)ProductCatalogOperations(4131-4159)EntitlementsOperations(4162-4188)BillingOperations(4191-4220)PortalOperations(4223-4241)NotificationOperations(4244-4263)InfoOperations(4266-4284)EventsOperations(4287-4789)EventsV2Operations(4792-4915)MetersOperations(4918-6133)SubjectsOperations(6136-6519)DebugOperations(6522-6616)api/client/python/openmeter/operations/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/aio/_client.py (4)
api/client/python/openmeter/_configuration.py (1)
OpenMeterClientConfiguration(11-43)api/client/python/openmeter/aio/_configuration.py (1)
OpenMeterClientConfiguration(11-43)api/client/python/openmeter/aio/operations/_operations.py (13)
AppOperations(197-219)BillingOperations(308-337)CustomerOperations(222-245)DebugOperations(2637-2731)EntitlementsOperations(279-305)EventsOperations(404-904)EventsV2Operations(907-1030)InfoOperations(383-401)MetersOperations(1033-2248)NotificationOperations(361-380)PortalOperations(340-358)ProductCatalogOperations(248-276)SubjectsOperations(2251-2634)api/client/python/openmeter/_client.py (3)
OpenMeterClient(30-134)send_request(100-124)close(126-127)
api/client/python/openmeter/aio/_configuration.py (1)
api/client/python/openmeter/_configuration.py (2)
OpenMeterClientConfiguration(11-43)_configure(33-43)
api/client/python/openmeter/aio/__init__.py (2)
api/client/python/openmeter/aio/_client.py (1)
OpenMeterClient(30-136)api/client/python/openmeter/aio/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/_client.py (5)
api/client/python/openmeter/_configuration.py (1)
OpenMeterClientConfiguration(11-43)api/client/python/openmeter/aio/_configuration.py (1)
OpenMeterClientConfiguration(11-43)api/client/python/openmeter/_utils/serialization.py (5)
Deserializer(1350-2024)Serializer(472-1170)_serialize(527-637)_deserialize(1404-1475)url(682-705)api/client/python/openmeter/aio/operations/_operations.py (4)
AppOperations(197-219)BillingOperations(308-337)CustomerOperations(222-245)SubjectsOperations(2251-2634)api/client/python/openmeter/operations/_operations.py (4)
AppOperations(4080-4102)BillingOperations(4191-4220)CustomerOperations(4105-4128)SubjectsOperations(6136-6519)
api/client/python/openmeter/__init__.py (7)
api/client/python/openmeter/_client.py (1)
OpenMeterClient(30-134)api/client/python/openmeter/aio/_client.py (1)
OpenMeterClient(30-136)api/client/python/openmeter/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/aio/operations/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/models/_patch.py (1)
patch_sdk(12-18)api/client/python/openmeter/operations/_patch.py (1)
patch_sdk(12-18)
api/client/python/openmeter/_configuration.py (2)
api/client/python/openmeter/aio/_configuration.py (2)
OpenMeterClientConfiguration(11-43)_configure(33-43)api/client/python/openmeter/_utils/model_base.py (3)
setdefault(446-446)setdefault(449-449)setdefault(451-461)
api/client/python/examples/async/query.py (5)
api/client/python/openmeter/aio/_client.py (1)
OpenMeterClient(30-136)api/client/python/openmeter/models/_models.py (1)
MeterQueryResult(9615-9671)api/client/python/openmeter/aio/operations/_operations.py (18)
get(1152-1228)get(2341-2417)get(2836-2913)get(3704-3778)get(5384-5469)get(5835-5912)get(6575-6651)get(7175-7257)get(8035-8114)get(8643-8725)get(10313-10392)get(10767-10844)get(11420-11500)get(13125-13211)get(13728-13820)get(16621-16697)query_json(1584-1737)query_json(15970-16117)api/client/python/examples/async/ingest.py (1)
main(18-43)api/client/python/examples/sync/query.py (1)
main(21-43)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Artifacts / Container image
- GitHub Check: Artifacts / Benthos Collector Container image
- GitHub Check: Lint
- GitHub Check: Test
- GitHub Check: Code Generators
- GitHub Check: Migration Checks
- GitHub Check: Build
- GitHub Check: Repository Scan
- GitHub Check: Analyze (go)
🔇 Additional comments (21)
api/spec/src/auth.tsp (1)
3-7: LGTM!The new
TokenAuthmodel is correctly defined and extendsBearerAuthwith an appropriate friendly name. This aligns well with the token-based authentication pattern used throughout the Python client.api/client/python/openmeter/operations/__init__.py (1)
1-43: LGTM!The package initialization follows best practices for generated SDKs:
- Proper use of
TYPE_CHECKINGfor type hint imports- Clean separation of operation class exports
- Correct patch integration with runtime initialization
api/spec/tspconfig.client.yaml (1)
1-20: LGTM!The TypeSpec configuration correctly defines the Python HTTP client emitter with appropriate package metadata and output directories.
api/spec/package.json (2)
6-10: LGTM!The script restructuring into separate
compile:openapiandcompile:clientcommands provides better granularity and aligns with the dual-output approach (OpenAPI spec + Python client).
22-30: LGTM!The dependency updates are consistent, with all
@typespec/*packages aligned at version 1.5.0 (except rest/versioning at 0.75.0), and the new@typespec/http-client-python@0.19.1supports the Python client generation objective.api/client/python/examples/sync/ingest.py (1)
22-43: LGTM!The event creation and ingestion logic is correct, with proper CloudEvents structure and error handling for
HttpResponseError.api/client/python/openmeter/aio/__init__.py (1)
1-23: LGTM!The async package initialization correctly implements the patch integration pattern with:
- Proper
TYPE_CHECKINGusage for static analysis- Graceful fallback when patch imports fail
- Runtime patch initialization via
_patch_sdk()api/client/python/openmeter/_configuration.py (2)
23-31: LGTM!The configuration initialization correctly validates that the credential is not
Noneand properly sets up the endpoint and SDK moniker.
33-43: LGTM!The policy configuration correctly sets up the HTTP pipeline with appropriate defaults and automatically creates a
ServiceKeyCredentialPolicywith Bearer authentication when a credential is provided.api/client/python/openmeter/aio/_configuration.py (1)
11-43: LGTM!The async configuration correctly uses
AsyncRetryPolicy(line 38) instead of the syncRetryPolicy, and properly validates the credential parameter. The policy stack setup aligns with the sync version while accommodating async-specific needs.api/client/python/examples/async/query.py (1)
16-42: LGTM!The async query examples correctly demonstrate the OpenMeterClient API usage with proper error handling and context management.
api/client/python/examples/async/ingest.py (1)
18-43: LGTM!The Event construction and ingest flow correctly demonstrates the new API with proper async context management and error handling.
api/client/python/openmeter/aio/operations/__init__.py (1)
1-43: LGTM!The operations module properly exports all operation classes and integrates the patching mechanism. The TYPE_CHECKING guard and runtime patch invocation follow standard Python SDK patterns.
api/client/python/openmeter/__init__.py (2)
14-19: Consider moving patch_sdk import inside the try block.The
from ._patch import patch_sdk as _patch_sdkimport on line 19 is outside the try/except block, which could raise anImportErrorif_patch.pyhas issues, while the earlier imports are protected. This inconsistency could lead to unexpected failures.Apply this diff to ensure consistent error handling:
try: from ._patch import __all__ as _patch_all from ._patch import * + from ._patch import patch_sdk as _patch_sdk except ImportError: _patch_all = [] -from ._patch import patch_sdk as _patch_sdk
21-26: LGTM!The package exports and patch invocation are correctly structured. The
__all__list is properly extended with patched exports.api/client/python/openmeter/_client.py (3)
30-98: LGTM!The synchronous OpenMeterClient is well-structured with:
- Comprehensive operation group initialization
- Proper credential and configuration handling via
OpenMeterClientConfiguration- Consistent serializer/deserializer sharing across operations
- Client-side validation disabled as intended (line 85)
100-124: LGTM!The
send_requestmethod correctly handles custom requests by formatting the endpoint URL and delegating to the pipeline client. The documentation example is helpful.
126-134: LGTM!The lifecycle methods properly support context manager usage and delegate to the underlying pipeline client. The use of
Selffor__enter__return type is correct.api/client/python/openmeter/aio/_client.py (3)
30-98: LGTM!The async OpenMeterClient mirrors the sync version correctly with:
AsyncPipelineClientfor async operations (line 81)- Proper credential handling via
OpenMeterClientConfiguration- All operation groups initialized with shared serializers
- Client-side validation disabled (line 85)
100-126: LGTM!The async
send_requestmethod correctly:
- Returns
Awaitable[AsyncHttpResponse](line 102)- Formats the endpoint URL consistently with the sync version
- Updates documentation example to show
awaitusage (line 108)
128-136: LGTM!The async lifecycle methods properly implement the async context manager protocol with all operations correctly awaiting the underlying pipeline client methods.
d637bc9 to
6cda475
Compare
6cda475 to
f630c5b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (1)
api/client/python/openmeter/_types.py (1)
28-28: Remove duplicatestrin Union.This issue was already raised in a previous review.
🧹 Nitpick comments (8)
api/client/python/examples/async/query.py (1)
9-17: Consider documenting or validating the token requirement.The
tokencan beNoneifOPENMETER_TOKENis not set. While theClienthandles this gracefully during initialization (by not setting an authentication policy), API calls will fail with authentication errors. For better user experience in examples, consider either:
- Adding a comment documenting that
OPENMETER_TOKENmust be set- Adding validation that prints a helpful message before attempting API calls
Example validation approach:
ENDPOINT: str = environ.get("OPENMETER_ENDPOINT") or "https://openmeter.cloud" token: Optional[str] = environ.get("OPENMETER_TOKEN") +if not token: + print("Error: OPENMETER_TOKEN environment variable must be set") + exit(1)Note: This pattern appears in multiple example files (async/query.py, async/entitlement.py, async/subscription.py, sync/ingest.py, sync/customer.py). Consider applying consistently across all examples.
api/client/python/examples/sync/ingest.py (1)
14-17: Consider using context manager for resource cleanup.While the module-level
Clientworks for this simple example, using a context manager would ensure proper connection cleanup and maintain consistency with the async examples. This is optional but recommended.Example:
-client = Client( - endpoint=ENDPOINT, - token=token, -) - - def main() -> None: + with Client(endpoint=ENDPOINT, token=token) as client: - try: - # Create a CloudEvents event - event = Event( + try: + # Create a CloudEvents event + event = Event(api/client/python/examples/sync/customer.py (1)
17-20: Consider using context manager for consistency.Similar to
sync/ingest.py, using a context manager would ensure proper resource cleanup and maintain consistency with async examples.Example:
-client = Client( - endpoint=ENDPOINT, - token=token, -) - - def main() -> None: + with Client(endpoint=ENDPOINT, token=token) as client: - try: - # Create a customer + try: + # Create a customerapi/client/python/examples/sync/entitlement.py (1)
1-15: Addif __name__ == '__main__'guard for script execution.While not critical for an example script, it's a Python best practice to guard direct execution:
-main() +if __name__ == '__main__': + main()This pattern prevents unintended execution when the module is imported and is consistent with Python conventions.
Note: The
tokenparameter is Optional and the Client handles None gracefully by not setting up authentication. If authentication is required for this example to work, consider adding explicit validation with a helpful error message.api/client/python/examples/sync/subscription.py (1)
1-20: Addif __name__ == '__main__'guard for script execution.Apply the same best practice as suggested for other example scripts:
-main() +if __name__ == '__main__': + main()api/client/python/examples/async/customer.py (1)
1-16: Addif __name__ == '__main__'guard.For consistency with Python best practices:
-asyncio.run(main()) +if __name__ == '__main__': + asyncio.run(main())api/client/python/examples/async/ingest.py (1)
11-19: Addif __name__ == '__main__'guard.For consistency with Python conventions:
-asyncio.run(main()) +if __name__ == '__main__': + asyncio.run(main())Note: The past review comment about token validation flagged a concern that ServiceKeyCredential might fail with None. However, the Client implementation (in
_client.pylines 11-30) handles None tokens gracefully by only creating the credential whentokenis truthy. While technically safe, explicit validation with a helpful error message would improve the example's user experience if authentication is required for the API to work.api/spec/patches/@typespec__openapi.patch (1)
1-44: Document the rationale and maintenance plan for this dependency patch.Applying patches to third-party packages like
@typespec/openapiintroduces maintenance overhead. When the package is updated, this patch will need to be reviewed and potentially reapplied.Please consider:
- Adding a README or comment in the patches directory explaining why this patch is needed
- Documenting any plans to contribute this upstream
- Adding validation tests to detect if future package updates break this patch
- Evaluating if there are alternative approaches (e.g., configuration options, custom plugins) that avoid patching
Would you like me to help create documentation template or open an issue to track upstreaming this change?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (26)
api/client/python/examples/async/poetry.lockis excluded by!**/*.lockapi/client/python/examples/poetry.lockis excluded by!**/*.lockapi/client/python/openmeter/_generated/__init__.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/_client.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/_configuration.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/_patch.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/_utils/__init__.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/_utils/model_base.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/_utils/serialization.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/_version.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/aio/__init__.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/aio/_client.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/aio/_configuration.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/aio/_patch.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/aio/operations/__init__.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/aio/operations/_operations.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/aio/operations/_patch.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/models/__init__.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/models/_enums.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/models/_models.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/models/_patch.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/operations/__init__.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/operations/_operations.pyis excluded by!**/_generated/**api/client/python/openmeter/_generated/operations/_patch.pyis excluded by!**/_generated/**api/client/python/poetry.lockis excluded by!**/*.lockapi/spec/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (49)
api/client/go/client.gen.go(1 hunks)api/client/javascript/src/client/schemas.ts(1 hunks)api/client/python/.gitattributes(1 hunks)api/client/python/.gitignore(1 hunks)api/client/python/MANIFEST.in(1 hunks)api/client/python/README.md(2 hunks)api/client/python/config.yaml(0 hunks)api/client/python/examples/README.md(1 hunks)api/client/python/examples/async/customer.py(1 hunks)api/client/python/examples/async/entitlement.py(1 hunks)api/client/python/examples/async/ingest.py(1 hunks)api/client/python/examples/async/pyproject.toml(0 hunks)api/client/python/examples/async/query.py(1 hunks)api/client/python/examples/async/subscription.py(1 hunks)api/client/python/examples/pyproject.toml(1 hunks)api/client/python/examples/sync/customer.py(1 hunks)api/client/python/examples/sync/entitlement.py(1 hunks)api/client/python/examples/sync/ingest.py(1 hunks)api/client/python/examples/sync/query.py(1 hunks)api/client/python/examples/sync/subscription.py(1 hunks)api/client/python/openmeter/__init__.py(1 hunks)api/client/python/openmeter/_client.py(1 hunks)api/client/python/openmeter/_types.py(1 hunks)api/client/python/openmeter/_version.py(1 hunks)api/client/python/openmeter/aio/__init__.py(1 hunks)api/client/python/openmeter/aio/_client.py(1 hunks)api/client/python/openmeter/aio/operations/__init__.py(1 hunks)api/client/python/openmeter/models/__init__.py(1 hunks)api/client/python/openmeter/operations/__init__.py(1 hunks)api/client/python/pyproject.toml(1 hunks)api/client/python/src/openmeter/__init__.py(0 hunks)api/client/python/src/openmeter/_client.py(0 hunks)api/client/python/src/openmeter/_configuration.py(0 hunks)api/client/python/src/openmeter/_operations/__init__.py(0 hunks)api/client/python/src/openmeter/_operations/_patch.py(0 hunks)api/client/python/src/openmeter/_vendor.py(0 hunks)api/client/python/src/openmeter/aio/__init__.py(0 hunks)api/client/python/src/openmeter/aio/_client.py(0 hunks)api/client/python/src/openmeter/aio/_configuration.py(0 hunks)api/client/python/src/openmeter/aio/_operations/__init__.py(0 hunks)api/client/python/src/openmeter/aio/_vendor.py(0 hunks)api/openapi.cloud.yaml(1 hunks)api/openapi.yaml(1 hunks)api/spec/package.json(2 hunks)api/spec/patches/@typespec__openapi.patch(1 hunks)api/spec/src/query.tsp(0 hunks)api/spec/tspconfig.client.yaml(1 hunks)api/spec/tspconfig.yaml(1 hunks)flake.nix(1 hunks)
💤 Files with no reviewable changes (14)
- api/spec/src/query.tsp
- api/client/python/config.yaml
- api/client/python/src/openmeter/aio/_vendor.py
- api/client/python/src/openmeter/aio/_operations/init.py
- api/client/python/examples/async/pyproject.toml
- api/client/python/src/openmeter/aio/_configuration.py
- api/client/python/src/openmeter/aio/init.py
- api/client/python/src/openmeter/_configuration.py
- api/client/python/src/openmeter/aio/_client.py
- api/client/python/src/openmeter/_operations/_patch.py
- api/client/python/src/openmeter/init.py
- api/client/python/src/openmeter/_vendor.py
- api/client/python/src/openmeter/_operations/init.py
- api/client/python/src/openmeter/_client.py
✅ Files skipped from review due to trivial changes (1)
- api/client/python/examples/README.md
🚧 Files skipped from review as they are similar to previous changes (11)
- api/client/javascript/src/client/schemas.ts
- api/spec/tspconfig.client.yaml
- api/spec/tspconfig.yaml
- api/client/python/examples/sync/query.py
- api/spec/package.json
- api/client/python/openmeter/_version.py
- flake.nix
- api/client/python/examples/pyproject.toml
- api/client/python/openmeter/_client.py
- api/client/python/openmeter/aio/_client.py
- api/client/python/MANIFEST.in
🧰 Additional context used
🧬 Code graph analysis (15)
api/client/python/examples/async/ingest.py (3)
api/client/python/openmeter/aio/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/models/_models.py (1)
Event(6197-6276)api/client/python/openmeter/_generated/aio/operations/_operations.py (19)
get(1164-1261)get(2594-2691)get(3208-3306)get(4282-4376)get(6273-6375)get(6802-6900)get(7658-7755)get(8386-8491)get(9443-9544)get(10176-10281)get(12238-12339)get(12758-12857)get(13524-13627)get(15503-15610)get(16209-16318)ingest_event(442-461)ingest_event(464-483)ingest_event(486-505)ingest_event(508-595)
api/client/python/openmeter/__init__.py (2)
api/client/python/openmeter/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/_patch.py (1)
patch_sdk(14-20)
api/client/python/examples/sync/customer.py (3)
api/client/python/openmeter/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/models/_models.py (3)
CustomerCreate(3004-3068)CustomerReplaceUpdate(3182-3246)CustomerUsageAttribution(3249-3280)api/client/python/examples/async/customer.py (1)
main(19-71)
api/client/python/examples/async/entitlement.py (2)
api/client/python/openmeter/aio/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/aio/operations/_operations.py (18)
get(1164-1261)get(2594-2691)get(3208-3306)get(4282-4376)get(6273-6375)get(6802-6900)get(7658-7755)get(8386-8491)get(9443-9544)get(10176-10281)get(12238-12339)get(12758-12857)get(13524-13627)get(15503-15610)get(16209-16318)get_customer_entitlement_value(14965-15073)get_customer_entitlement_value(22871-22979)get_customer_access(14845-14942)
api/client/python/examples/async/customer.py (3)
api/client/python/openmeter/aio/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/models/_models.py (3)
CustomerCreate(3004-3068)CustomerReplaceUpdate(3182-3246)CustomerUsageAttribution(3249-3280)api/client/python/openmeter/_generated/aio/operations/_operations.py (46)
get(1164-1261)get(2594-2691)get(3208-3306)get(4282-4376)get(6273-6375)get(6802-6900)get(7658-7755)get(8386-8491)get(9443-9544)get(10176-10281)get(12238-12339)get(12758-12857)get(13524-13627)get(15503-15610)get(16209-16318)create(1266-1285)create(1288-1307)create(1310-1329)create(1332-1434)create(5951-5970)create(5973-5992)create(5995-6014)create(6017-6119)create(7466-7490)create(7493-7517)create(7520-7544)create(7547-7654)create(8025-8044)create(8047-8066)create(8069-8088)create(8091-8193)update(1439-1461)update(1464-1486)update(1489-1511)update(1514-1620)update(3311-3334)update(3337-3360)update(3363-3386)update(3389-3504)update(6380-6402)update(6405-6427)update(6430-6452)update(6455-6565)update(8198-8220)update(8223-8245)update(8248-8270)
api/client/python/examples/sync/ingest.py (3)
api/client/python/openmeter/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/models/_models.py (1)
Event(6197-6276)api/client/python/openmeter/_generated/operations/_operations.py (4)
ingest_event(5448-5467)ingest_event(5470-5489)ingest_event(5492-5511)ingest_event(5514-5601)
api/client/python/openmeter/models/__init__.py (1)
api/client/python/openmeter/_generated/models/_patch.py (1)
patch_sdk(14-20)
api/client/python/openmeter/operations/__init__.py (5)
api/client/python/openmeter/_generated/_patch.py (1)
patch_sdk(14-20)api/client/python/openmeter/_generated/aio/_patch.py (1)
patch_sdk(14-20)api/client/python/openmeter/_generated/aio/operations/_patch.py (1)
patch_sdk(14-20)api/client/python/openmeter/_generated/models/_patch.py (1)
patch_sdk(14-20)api/client/python/openmeter/_generated/operations/_patch.py (1)
patch_sdk(14-20)
api/client/python/examples/sync/subscription.py (4)
api/client/python/openmeter/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/models/_models.py (2)
PlanSubscriptionCreate(12097-12182)PlanReferenceInput(11919-11953)api/client/python/openmeter/_generated/models/_enums.py (1)
SubscriptionStatus(640-647)api/client/python/openmeter/_generated/operations/_operations.py (2)
get_expanded(15600-15703)list_customer_subscriptions(11661-11785)
api/client/python/examples/async/query.py (3)
api/client/python/openmeter/aio/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/models/_models.py (1)
MeterQueryResult(9803-9857)api/client/python/openmeter/_generated/aio/operations/_operations.py (17)
get(1164-1261)get(2594-2691)get(3208-3306)get(4282-4376)get(6273-6375)get(6802-6900)get(7658-7755)get(8386-8491)get(9443-9544)get(10176-10281)get(12238-12339)get(12758-12857)get(13524-13627)get(15503-15610)get(16209-16318)query_json(1707-1878)query_json(18912-19077)
api/client/python/examples/async/subscription.py (4)
api/client/python/openmeter/aio/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/models/_models.py (2)
PlanSubscriptionCreate(12097-12182)PlanReferenceInput(11919-11953)api/client/python/openmeter/_generated/models/_enums.py (1)
SubscriptionStatus(640-647)api/client/python/openmeter/_generated/aio/operations/_operations.py (33)
get(1164-1261)get(2594-2691)get(3208-3306)get(4282-4376)get(6273-6375)get(6802-6900)get(7658-7755)get(8386-8491)get(9443-9544)get(10176-10281)get(12238-12339)get(12758-12857)get(13524-13627)get(15503-15610)get(16209-16318)create(1266-1285)create(1288-1307)create(1310-1329)create(1332-1434)create(5951-5970)create(5973-5992)create(5995-6014)create(6017-6119)create(7466-7490)create(7493-7517)create(7520-7544)create(7547-7654)create(8025-8044)create(8047-8066)create(8069-8088)create(8091-8193)get_expanded(10594-10697)list_customer_subscriptions(6655-6779)
api/client/python/openmeter/aio/operations/__init__.py (1)
api/client/python/openmeter/_generated/aio/operations/_patch.py (1)
patch_sdk(14-20)
api/client/python/examples/sync/entitlement.py (2)
api/client/python/openmeter/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/operations/_operations.py (3)
get_customer_entitlement_value(19971-20079)get_customer_entitlement_value(27877-27985)get_customer_access(19851-19948)
api/client/python/openmeter/aio/__init__.py (2)
api/client/python/openmeter/aio/_client.py (1)
Client(12-31)api/client/python/openmeter/_generated/aio/_patch.py (1)
patch_sdk(14-20)
api/client/python/openmeter/_types.py (3)
api/client/go/client.gen.go (30)
App(961-963)CustomerAppData(2091-2093)RateCardEntitlement(5784-5786)RateCardUsageBasedPrice(5919-5921)RateCard(5768-5770)RecurringPeriodInterval(5945-5947)Entitlement(2386-2388)SubscriptionTiming(6779-6781)SubscriptionEditOperation(6554-6556)EntitlementV2(3093-3095)MeasureUsageFrom(4487-4489)BillingWorkflowCollectionAlignment(1399-1399)BillingProfileAppsOrReference(1240-1242)InvoiceDocumentRef(3752-3752)BillingDiscountReason(1069-1071)PaymentTerms(5347-5349)NotificationChannel(4707-4707)NotificationRule(4978-4980)NotificationEventPayload(4952-4954)AppReplaceUpdate(1009-1011)ULIDOrExternalKey(6843-6843)ListFeaturesResult(4419-4421)SubscriptionCreate(6539-6541)SubscriptionChange(6525-6527)EntitlementV2CreateInputs(3098-3100)ListEntitlementsResult(4411-4413)EntitlementCreateInputs(2508-2510)NotificationChannelCreateRequest(4710-4710)NotificationRuleCreateRequest(5069-5071)IngestEventsBody(3425-3427)api/client/javascript/src/client/schemas.ts (29)
App(11526-11526)CustomerAppData(11673-11673)RateCardEntitlement(11992-11992)RateCardUsageBasedPrice(11999-12000)RateCard(11989-11989)RecurringPeriodInterval(12004-12005)Entitlement(11714-11714)SubscriptionTiming(12069-12069)SubscriptionEditOperation(12056-12057)EntitlementV2(11741-11741)MeasureUsageFrom(11865-11865)BillingWorkflowCollectionAlignment(11583-11584)BillingProfileAppsOrReference(11558-11559)InvoiceDocumentRef(11796-11796)BillingDiscountReason(11541-11542)PaymentTerms(11957-11957)NotificationChannel(11882-11882)NotificationRule(11922-11922)NotificationEventPayload(11916-11917)AppReplaceUpdate(11531-11531)ListFeaturesResult(11857-11857)SubscriptionCreate(12054-12054)SubscriptionChange(12051-12051)EntitlementV2CreateInputs(11742-11743)ListEntitlementsResult(11855-11856)EntitlementCreateInputs(11719-11720)NotificationChannelCreateRequest(11883-11884)NotificationRuleCreateRequest(11931-11932)IngestEventsBody(11775-11775)api/api.gen.go (30)
App(1046-1048)CustomerAppData(2261-2263)RateCardEntitlement(6303-6305)RateCardUsageBasedPrice(6441-6443)RateCard(6287-6289)RecurringPeriodInterval(6467-6469)Entitlement(2585-2587)SubscriptionTiming(7396-7398)SubscriptionEditOperation(7135-7137)EntitlementV2(3379-3381)MeasureUsageFrom(4954-4956)BillingWorkflowCollectionAlignment(1566-1566)BillingProfileAppsOrReference(1380-1382)InvoiceDocumentRef(4126-4126)BillingDiscountReason(1209-1211)PaymentTerms(5857-5859)NotificationChannel(5178-5178)NotificationRule(5449-5451)NotificationEventPayload(5423-5425)AppReplaceUpdate(1133-1135)ULIDOrExternalKey(7482-7482)ListFeaturesResult(4886-4888)SubscriptionCreate(7120-7122)SubscriptionChange(7106-7108)EntitlementV2CreateInputs(3384-3386)ListEntitlementsResult(4878-4880)EntitlementCreateInputs(2753-2755)NotificationChannelCreateRequest(5181-5181)NotificationRuleCreateRequest(5540-5542)IngestEventsBody(3775-3777)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: E2E
- GitHub Check: Code Generators
- GitHub Check: Test
- GitHub Check: Analyze (go)
🔇 Additional comments (24)
api/client/python/.gitignore (1)
1-19: Good organization and coverage for a Python project.The
.gitignorefile is well-structured with clear section headers and standard patterns for Python packaging, caches, and virtual environments. The inclusion of.pyoand.pydalongside*.pycand the dual coverage of both.venv/andvenv/demonstrates thoughtful handling of common Python development tools.api/client/python/.gitattributes (1)
1-1: Pattern correctly updated to reflect Python client restructuring—no remaining generated files under old location.Verification confirms:
- Old
src/openmeter/directory completely removed- New
openmeter/directory with generated code in place (_generated/subdirectory).gitattributespatternopenmeter/**/*appropriately marks all generated files in the new locationapi/openapi.cloud.yaml (1)
23922-23925: Example is correct as-is.The
amount: '100'representation is correct. The Numeric schema explicitly defines amount astype: stringwith pattern^\-?[0-9]+(\.[0-9]+)?$for arbitrary precision handling. The example properly conforms to the schema definition.api/openapi.yaml (1)
24862-24865: Good documentation improvement.Replacing the placeholder example with a concrete, realistic
Priceobject improves API documentation clarity for consumers. The example is well-formed and aligns with the same enhancement across sibling OpenAPI and client schema files.api/client/python/openmeter/aio/__init__.py (1)
1-23: LGTM! Standard SDK patching pattern.The dynamic import and patch initialization pattern is appropriate for a generated SDK with custom extensions. The
TYPE_CHECKINGguard ensures static type checkers can resolve symbols while maintaining runtime flexibility.api/client/python/pyproject.toml (3)
3-3: Correct version format.The version string now follows semantic versioning standards by removing the 'v' prefix.
24-30: Dependencies updated for corehttp migration.The switch from Azure SDK dependencies to
corehttpaligns with the broader Python client restructuring in this PR.
35-52: Appropriate Black configuration.The configuration properly excludes the generated code directory from formatting while maintaining standard settings for the rest of the codebase.
api/client/python/examples/async/query.py (1)
19-46: Well-structured query examples.The code demonstrates three different query patterns with appropriate data validation (checking
r.databefore access) and clear output messages. Error handling viaHttpResponseErroris also properly implemented.api/client/python/examples/async/entitlement.py (1)
14-62: Clear entitlement workflow demonstration.The example effectively demonstrates both specific feature entitlement checks and overall customer access retrieval. The conditional printing of optional fields (
balance,usage,overage,config) properly handles the varying entitlement types. Error handling is appropriate.api/client/python/examples/sync/ingest.py (1)
20-41: Correct Event structure and ingestion.The CloudEvents-compliant
Eventcreation includes all required fields with appropriate types. The ingestion call and error handling are straightforward and appropriate for an example.api/client/python/examples/async/subscription.py (1)
19-77: Comprehensive subscription lifecycle example.The example demonstrates creating, retrieving, and listing subscriptions with proper use of typed models. The conditional access to optional fields (like
retrieved_subscription.plan) and the use of status filtering show good practices. Error handling is appropriate.api/client/python/examples/sync/customer.py (1)
23-71: Clear customer management workflow.The example effectively demonstrates creating, retrieving, and updating a customer with proper use of typed models (
CustomerCreate,CustomerReplaceUpdate,CustomerUsageAttribution). The progression from EUR to USD currency and email updates clearly shows the update functionality.api/client/python/README.md (1)
1-142: Well-structured documentation.The README provides clear examples for both synchronous and asynchronous usage patterns. The progression from setup to basic operations (ingestion) to more complex queries is logical. All necessary imports are included, and the CloudEvents-compliant Event structure is properly documented.
api/client/python/examples/sync/entitlement.py (1)
18-61: LGTM! Clear example demonstrating entitlement checking.The main function provides a well-structured demonstration of checking customer entitlements with proper error handling and informative output.
api/client/python/examples/sync/subscription.py (1)
23-73: LGTM! Comprehensive subscription example.The example clearly demonstrates creating, retrieving, and listing subscriptions with proper error handling and informative output.
api/client/python/examples/async/customer.py (1)
19-71: LGTM! Well-structured async example with proper resource management.The example correctly uses an async context manager for the client, ensuring proper resource cleanup. The customer CRUD flow is clear and properly demonstrates the async API.
api/client/python/openmeter/models/__init__.py (1)
1-30: LGTM! Standard SDK re-export pattern correctly implemented.The module follows the Azure SDK pattern for re-exporting generated code with patch integration. The defensive import handling and all extension logic are correct.
api/client/python/openmeter/operations/__init__.py (1)
1-29: LGTM! Consistent with the SDK re-export pattern.The operations re-export module follows the same pattern as the models module, maintaining consistency across the SDK structure.
api/client/python/examples/async/ingest.py (1)
15-40: LGTM! Clean async ingest example with proper resource management.The example correctly constructs a CloudEvents-compliant Event and demonstrates the ingest API with proper async context management and error handling.
api/client/python/openmeter/aio/operations/__init__.py (1)
1-29: LGTM! Async operations re-export follows consistent pattern.The async operations module correctly mirrors the sync operations structure with appropriate path adjustments for the aio package.
api/client/python/openmeter/__init__.py (1)
1-26: LGTM! Package initialization correctly exposes public API.The main package init file properly exposes the Client class, wires versioning, and integrates the patch mechanism consistently with the rest of the SDK structure.
api/spec/patches/@typespec__openapi.patch (2)
40-42: LGTM: Dependency reordering for consistency.The reordering of
@typespec/library-linterto maintain alphabetical sorting improves maintainability.
18-21: Verify the behavioral impact and rationale for commenting out Scalar case handling in shouldInline.The patch comments out the Scalar case in the
shouldInlineswitch, changing inlining behavior: Scalars previously had conditional inlining (program.checker.isStdType(type) || isTemplateInstance(type)), but now always inline by falling through to the default case.Based on TypeSpec's architecture,
shouldInlinedetermines whether to emit schema inline or as a component reference. Scalars require special handling because they carry format and constraint metadata; always inlining them (vs. conditional inlining) will produce different OpenAPI schemas.However, the patch provides no context for why this change is necessary. The codebase contains no documentation, TODOs, or comments explaining the rationale.
Please provide or verify:
- The reason this patch was needed (e.g., issue link, commit message, or PR description)
- Whether this is a temporary workaround or permanent fix
- Impact on the generated Python SDK schemas
- Whether this should be contributed upstream
| BillingWorkflowCollectionAlignment = ( | ||
| "_models.BillingWorkflowCollectionAlignmentSubscription" | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix type alias assignments that currently assign string literals.
Lines 49-51, 55, 65, and 104 assign string literals instead of creating proper type aliases. With the TYPE_CHECKING guard preventing runtime import of _models, these assignments should be moved inside the TYPE_CHECKING block to function as type aliases.
Apply this diff:
if TYPE_CHECKING:
from . import models as _models
+ BillingWorkflowCollectionAlignment = _models.BillingWorkflowCollectionAlignmentSubscription
+ InvoiceDocumentRef = _models.CreditNoteOriginalInvoiceRef
+ NotificationChannel = _models.NotificationChannelWebhook
+ NotificationChannelCreateRequest = _models.NotificationChannelWebhookCreateRequest
+
App = Union["_models.StripeApp", "_models.SandboxApp", "_models.CustomInvoicingApp"]
...
-BillingWorkflowCollectionAlignment = (
- "_models.BillingWorkflowCollectionAlignmentSubscription"
-)
BillingProfileAppsOrReference = Union[
"_models.BillingProfileApps", "_models.BillingProfileAppReferences"
]
-InvoiceDocumentRef = "_models.CreditNoteOriginalInvoiceRef"
BillingDiscountReason = Union[
...
-NotificationChannel = "_models.NotificationChannelWebhook"
NotificationRule = Union[
...
-NotificationChannelCreateRequest = "_models.NotificationChannelWebhookCreateRequest"
NotificationRuleCreateRequest = Union[Also applies to: 55-55, 65-65, 104-104
🤖 Prompt for AI Agents
In api/client/python/openmeter/_types.py around lines 49-51, 55, 65, and 104,
several type alias variables are currently set to string literals (e.g.,
BillingWorkflowCollectionAlignment =
"_models.BillingWorkflowCollectionAlignmentSubscription") which makes them
runtime strings because imports of _models are guarded by TYPE_CHECKING; move
these assignments inside the existing TYPE_CHECKING block and replace the string
values with actual type aliases referencing the _models symbols (e.g.,
BillingWorkflowCollectionAlignment =
_models.BillingWorkflowCollectionAlignmentSubscription), removing the
quote-wrapped literals so they only exist for type checking.
| ListEntitlementsResult = Union[ | ||
| list["_types.Entitlement"], "_models.EntitlementPaginatedResponse" | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix incorrect module reference in ListEntitlementsResult.
Line 97 references "_types.Entitlement", which incorrectly refers to this module itself. Since Entitlement is a type alias defined in this module (line 29-33), reference it without a module prefix.
Apply this diff:
ListEntitlementsResult = Union[
- list["_types.Entitlement"], "_models.EntitlementPaginatedResponse"
+ list["Entitlement"], "_models.EntitlementPaginatedResponse"
]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ListEntitlementsResult = Union[ | |
| list["_types.Entitlement"], "_models.EntitlementPaginatedResponse" | |
| ] | |
| ListEntitlementsResult = Union[ | |
| list["Entitlement"], "_models.EntitlementPaginatedResponse" | |
| ] |
🤖 Prompt for AI Agents
In api/client/python/openmeter/_types.py around lines 96 to 98, the Union
currently references the local Entitlement type as "_types.Entitlement" which is
incorrect; update the annotation to reference the local alias directly by
replacing "_types.Entitlement" with "Entitlement" (keep the forward-ref quotes
if needed), so the union becomes Union[list["Entitlement"],
"_models.EntitlementPaginatedResponse"].
Summary by CodeRabbit
New Features
Documentation