cognite-function-apps 0.12.0__tar.gz → 0.13.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/workflows/copilot-setup-steps.yml +1 -1
  2. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/workflows/python-package.yml +1 -1
  3. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/workflows/repo-assist.lock.yml +7 -7
  4. cognite_function_apps-0.13.0/.release-please-manifest.json +3 -0
  5. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/CHANGELOG.md +24 -0
  6. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/PKG-INFO +1 -1
  7. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/function-client.md +13 -20
  8. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs-src/function-client.py +12 -19
  9. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/pyproject.toml +4 -3
  10. cognite_function_apps-0.13.0/src/cognite_function_apps/_version.py +1 -0
  11. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/app.py +9 -2
  12. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/cli.py +5 -5
  13. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/client.py +36 -15
  14. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/convert.py +18 -10
  15. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/mcp.py +7 -5
  16. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/models.py +6 -6
  17. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/schema.py +3 -1
  18. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/service.py +11 -2
  19. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/conftest.py +1 -1
  20. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_client_generation.py +157 -1
  21. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_convert.py +57 -2
  22. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_convert_edge_cases.py +1 -1
  23. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_handle_function.py +22 -0
  24. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_mcp.py +2 -2
  25. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_schema.py +34 -1
  26. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_trace_propagation.py +1 -1
  27. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/uv.lock +84 -106
  28. cognite_function_apps-0.12.0/.release-please-manifest.json +0 -3
  29. cognite_function_apps-0.12.0/src/cognite_function_apps/_version.py +0 -1
  30. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.claude/settings.json +0 -0
  31. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.gemini/config.yaml +0 -0
  32. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.gitattributes +0 -0
  33. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/agents/agentic-workflows.agent.md +0 -0
  34. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/aw/actions-lock.json +0 -0
  35. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/renovate.json +0 -0
  36. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/workflows/conventional-pr-title.yml +0 -0
  37. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/workflows/deploy-docs.yml +0 -0
  38. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/workflows/python-publish.yml +0 -0
  39. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/workflows/release-please.yml +0 -0
  40. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.github/workflows/repo-assist.md +0 -0
  41. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.gitignore +0 -0
  42. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.nojekyll +0 -0
  43. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.pre-commit-config.yaml +0 -0
  44. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/.python-version +0 -0
  45. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/AGENTS.md +0 -0
  46. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/CLAUDE.md +0 -0
  47. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/CODEOWNERS +0 -0
  48. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/LICENSE +0 -0
  49. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/README.md +0 -0
  50. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/RELEASING.md +0 -0
  51. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/Terminology.md +0 -0
  52. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/adr/response-customization.md +0 -0
  53. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/codecov.yml +0 -0
  54. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/api-reference.md +0 -0
  55. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/app-composition.md +0 -0
  56. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/architecture.md +0 -0
  57. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/async-support.md +0 -0
  58. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/contributing.md +0 -0
  59. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/dependency-injection.md +0 -0
  60. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/dev-server.md +0 -0
  61. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/error-handling.md +0 -0
  62. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/index.md +0 -0
  63. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/introspection.md +0 -0
  64. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/logging.md +0 -0
  65. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/mcp.md +0 -0
  66. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/release-process.md +0 -0
  67. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/response-customization.md +0 -0
  68. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/stylesheets/extra.css +0 -0
  69. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/tracing.md +0 -0
  70. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs/type-safety.md +0 -0
  71. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs-src/error-handling.py +0 -0
  72. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs-src/introspection.py +0 -0
  73. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs-src/logging.py +0 -0
  74. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs-src/tracing.py +0 -0
  75. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/docs-src/type-safety.py +0 -0
  76. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/examples/dev.py +0 -0
  77. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/examples/handler.py +0 -0
  78. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/examples/requirements.txt +0 -0
  79. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/justfile +0 -0
  80. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/mkdocs.yml +0 -0
  81. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/release-please-config.json +0 -0
  82. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/__init__.py +0 -0
  83. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/base_client.py +0 -0
  84. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/client_generation.py +0 -0
  85. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/dependency_registry.py +0 -0
  86. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/devserver/__init__.py +0 -0
  87. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/devserver/asgi.py +0 -0
  88. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/devserver/auth.py +0 -0
  89. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/devserver/swagger.py +0 -0
  90. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/formatting.py +0 -0
  91. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/introspection.py +0 -0
  92. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/logger.py +0 -0
  93. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/py.typed +0 -0
  94. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/routing.py +0 -0
  95. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/templates/__init__.py +0 -0
  96. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/templates/client_template.jinja2 +0 -0
  97. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/src/cognite_function_apps/tracer.py +0 -0
  98. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/__init__.py +0 -0
  99. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/conftest_pep563.py +0 -0
  100. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_app.py +0 -0
  101. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_asgi.py +0 -0
  102. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_async_handlers.py +0 -0
  103. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_cli.py +0 -0
  104. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_convert_property_based.py +0 -0
  105. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_dependency_injection.py +0 -0
  106. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_error_handling.py +0 -0
  107. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_formatting.py +0 -0
  108. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_introspection.py +0 -0
  109. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_logger.py +0 -0
  110. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_models.py +0 -0
  111. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_routing.py +0 -0
  112. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_swagger.py +0 -0
  113. {cognite_function_apps-0.12.0 → cognite_function_apps-0.13.0}/tests/test_tracer.py +0 -0
@@ -21,6 +21,6 @@ jobs:
21
21
  - name: Checkout repository
22
22
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
23
23
  - name: Install gh-aw extension
24
- uses: github/gh-aw-actions/setup-cli@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5
24
+ uses: github/gh-aw-actions/setup-cli@23453ecc01928d28ee1e773e403b216b29e89a5b # v0.74.2
25
25
  with:
26
26
  version: v0.71.5
@@ -52,7 +52,7 @@ jobs:
52
52
 
53
53
  - name: Upload coverage reports to Codecov
54
54
  if: matrix.python-version == '3.13'
55
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6
55
+ uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6
56
56
  with:
57
57
  token: ${{ secrets.CODECOV_TOKEN }}
58
58
  slug: cognitedata/cognite-function-apps
@@ -134,7 +134,7 @@ jobs:
134
134
  steps:
135
135
  - name: Setup Scripts
136
136
  id: setup
137
- uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5
137
+ uses: github/gh-aw-actions/setup@23453ecc01928d28ee1e773e403b216b29e89a5b # v0.74.2
138
138
  with:
139
139
  destination: ${{ runner.temp }}/gh-aw/actions
140
140
  job-name: ${{ github.job }}
@@ -453,7 +453,7 @@ jobs:
453
453
  steps:
454
454
  - name: Setup Scripts
455
455
  id: setup
456
- uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5
456
+ uses: github/gh-aw-actions/setup@23453ecc01928d28ee1e773e403b216b29e89a5b # v0.74.2
457
457
  with:
458
458
  destination: ${{ runner.temp }}/gh-aw/actions
459
459
  job-name: ${{ github.job }}
@@ -1249,7 +1249,7 @@ jobs:
1249
1249
  steps:
1250
1250
  - name: Setup Scripts
1251
1251
  id: setup
1252
- uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5
1252
+ uses: github/gh-aw-actions/setup@23453ecc01928d28ee1e773e403b216b29e89a5b # v0.74.2
1253
1253
  with:
1254
1254
  destination: ${{ runner.temp }}/gh-aw/actions
1255
1255
  job-name: ${{ github.job }}
@@ -1418,7 +1418,7 @@ jobs:
1418
1418
  steps:
1419
1419
  - name: Setup Scripts
1420
1420
  id: setup
1421
- uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5
1421
+ uses: github/gh-aw-actions/setup@23453ecc01928d28ee1e773e403b216b29e89a5b # v0.74.2
1422
1422
  with:
1423
1423
  destination: ${{ runner.temp }}/gh-aw/actions
1424
1424
  job-name: ${{ github.job }}
@@ -1601,7 +1601,7 @@ jobs:
1601
1601
  steps:
1602
1602
  - name: Setup Scripts
1603
1603
  id: setup
1604
- uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5
1604
+ uses: github/gh-aw-actions/setup@23453ecc01928d28ee1e773e403b216b29e89a5b # v0.74.2
1605
1605
  with:
1606
1606
  destination: ${{ runner.temp }}/gh-aw/actions
1607
1607
  job-name: ${{ github.job }}
@@ -1654,7 +1654,7 @@ jobs:
1654
1654
  steps:
1655
1655
  - name: Setup Scripts
1656
1656
  id: setup
1657
- uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5
1657
+ uses: github/gh-aw-actions/setup@23453ecc01928d28ee1e773e403b216b29e89a5b # v0.74.2
1658
1658
  with:
1659
1659
  destination: ${{ runner.temp }}/gh-aw/actions
1660
1660
  job-name: ${{ github.job }}
@@ -1753,7 +1753,7 @@ jobs:
1753
1753
  steps:
1754
1754
  - name: Setup Scripts
1755
1755
  id: setup
1756
- uses: github/gh-aw-actions/setup@b8068426813005612b960b5ab0b8bd2c27142323 # v0.71.5
1756
+ uses: github/gh-aw-actions/setup@23453ecc01928d28ee1e773e403b216b29e89a5b # v0.74.2
1757
1757
  with:
1758
1758
  destination: ${{ runner.temp }}/gh-aw/actions
1759
1759
  job-name: ${{ github.job }}
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.13.0"
3
+ }
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.13.0](https://github.com/cognitedata/cognite-function-apps/compare/cognite-function-apps-v0.12.0...cognite-function-apps-v0.13.0) (2026-05-22)
4
+
5
+
6
+ ### Features
7
+
8
+ * add __repr__ to FunctionApp and FunctionService [Repo Assist] ([#152](https://github.com/cognitedata/cognite-function-apps/issues/152)) ([6e7e437](https://github.com/cognitedata/cognite-function-apps/commit/6e7e4373968bc215f6d5c4313cde70f6fd260c77))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * add __name__ attribute to FunctionService for Cognite SDK compatibility [Repo Assist] ([#151](https://github.com/cognitedata/cognite-function-apps/issues/151)) ([e4d5521](https://github.com/cognitedata/cognite-function-apps/commit/e4d5521232131a207342dbda754cf4ed962ed822))
14
+ * pass type_hints to get_dependency_param_names for deferred annotation support ([#145](https://github.com/cognitedata/cognite-function-apps/issues/145)) ([9a3df86](https://github.com/cognitedata/cognite-function-apps/commit/9a3df868e6f2757fa771b68701a2951d094f155d))
15
+ * support Sequence[T] and tuple[T, ...] in type conversion [Repo Assist] ([#134](https://github.com/cognitedata/cognite-function-apps/issues/134)) ([5b10356](https://github.com/cognitedata/cognite-function-apps/commit/5b10356e682dcb71e353e5dc4b7ee36ecf6f0741))
16
+
17
+
18
+ ### Documentation
19
+
20
+ * update authentication examples to use OAuthInteractive.default_for_entra_id ([#149](https://github.com/cognitedata/cognite-function-apps/issues/149)) ([8ce6662](https://github.com/cognitedata/cognite-function-apps/commit/8ce666278e3c6b2cab6a4814393ba266055dd3fa))
21
+
22
+
23
+ ### Code Refactoring
24
+
25
+ * rename internal Json type alias to JsonValue ([#162](https://github.com/cognitedata/cognite-function-apps/issues/162)) ([f7c768d](https://github.com/cognitedata/cognite-function-apps/commit/f7c768dc40c53504810c7310375736debbd0323e))
26
+
3
27
  ## [0.12.0](https://github.com/cognitedata/cognite-function-apps/compare/cognite-function-apps-v0.11.0...cognite-function-apps-v0.12.0) (2026-05-19)
4
28
 
5
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite-function-apps
3
- Version: 0.12.0
3
+ Version: 0.13.0
4
4
  Summary: FastAPI-style framework for building composable Function Apps for Cognite Functions with automatic OpenAPI schema generation and MCP integration
5
5
  Project-URL: Repository, https://github.com/cognitedata/cognite-function-apps
6
6
  Project-URL: Changelog, https://github.com/cognitedata/cognite-function-apps/releases
@@ -1,4 +1,4 @@
1
- <!-- Auto-generated from docs-src/function-client.py at 2026-03-20T14:15:31Z, do not edit -->
1
+ <!-- Auto-generated from docs-src/function-client.py at 2026-05-17T20:22:06Z, do not edit -->
2
2
 
3
3
  # FunctionClient Design
4
4
 
@@ -585,44 +585,37 @@ Deployed function support requires:
585
585
  ### Authentication
586
586
 
587
587
  ```python
588
- # Option 1: Interactive authentication with MSAL
589
- # from msal import PublicClientApplication
588
+ # Option 1: Interactive authentication (opens browser)
590
589
  # from cognite.client import ClientConfig, CogniteClient
591
- # from cognite.client.credentials import Token
590
+ # from cognite.client.credentials import OAuthInteractive
592
591
  #
593
- # def authenticate_azure() -> dict[str, str]:
594
- # authority_host_uri = "https://login.microsoftonline.com"
595
- # tenant = "your-tenant-id"
596
- # client_id = "your-client-id"
597
- # authority_uri = authority_host_uri + "/" + tenant
598
- # scopes = ["https://your-cluster.cognitedata.com/.default"]
599
- # app = PublicClientApplication(client_id=client_id, authority=authority_uri)
600
- # port = 53000
601
- # creds: dict[str, str] = app.acquire_token_interactive(scopes=scopes, port=port)
602
- # return creds
603
- #
604
- # creds = authenticate_azure()
605
592
  # cognite_client = CogniteClient(
606
593
  # config=ClientConfig(
607
594
  # client_name="notebook-client",
608
595
  # project="your-project",
609
596
  # base_url="https://your-cluster.cognitedata.com",
610
- # credentials=Token(creds["access_token"]),
597
+ # credentials=OAuthInteractive.default_for_entra_id(
598
+ # tenant_id="your-tenant-id",
599
+ # client_id="your-client-id",
600
+ # cdf_cluster="your-cluster",
601
+ # ),
611
602
  # )
612
603
  # )
613
604
 
614
605
  # Option 2: Client credentials (service accounts)
606
+ # from cognite.client import ClientConfig, CogniteClient
615
607
  # from cognite.client.credentials import OAuthClientCredentials
608
+ #
616
609
  # cognite_client = CogniteClient(
617
610
  # config=ClientConfig(
618
611
  # client_name="service-client",
619
612
  # project="your-project",
620
613
  # base_url="https://your-cluster.cognitedata.com",
621
- # credentials=OAuthClientCredentials(
622
- # token_url="https://login.microsoftonline.com/your-tenant-id/oauth2/v2.0/token",
614
+ # credentials=OAuthClientCredentials.default_for_entra_id(
615
+ # tenant_id="your-tenant-id",
623
616
  # client_id="your-client-id",
624
617
  # client_secret="your-client-secret",
625
- # scopes=["https://your-cluster.cognitedata.com/.default"],
618
+ # cdf_cluster="your-cluster",
626
619
  # ),
627
620
  # )
628
621
  # )
@@ -600,44 +600,37 @@ Deployed function support requires:
600
600
  """
601
601
 
602
602
  # %%
603
- # Option 1: Interactive authentication with MSAL
604
- # from msal import PublicClientApplication
603
+ # Option 1: Interactive authentication (opens browser)
605
604
  # from cognite.client import ClientConfig, CogniteClient
606
- # from cognite.client.credentials import Token
605
+ # from cognite.client.credentials import OAuthInteractive
607
606
  #
608
- # def authenticate_azure() -> dict[str, str]:
609
- # authority_host_uri = "https://login.microsoftonline.com"
610
- # tenant = "your-tenant-id"
611
- # client_id = "your-client-id"
612
- # authority_uri = authority_host_uri + "/" + tenant
613
- # scopes = ["https://your-cluster.cognitedata.com/.default"]
614
- # app = PublicClientApplication(client_id=client_id, authority=authority_uri)
615
- # port = 53000
616
- # creds: dict[str, str] = app.acquire_token_interactive(scopes=scopes, port=port)
617
- # return creds
618
- #
619
- # creds = authenticate_azure()
620
607
  # cognite_client = CogniteClient(
621
608
  # config=ClientConfig(
622
609
  # client_name="notebook-client",
623
610
  # project="your-project",
624
611
  # base_url="https://your-cluster.cognitedata.com",
625
- # credentials=Token(creds["access_token"]),
612
+ # credentials=OAuthInteractive.default_for_entra_id(
613
+ # tenant_id="your-tenant-id",
614
+ # client_id="your-client-id",
615
+ # cdf_cluster="your-cluster",
616
+ # ),
626
617
  # )
627
618
  # )
628
619
 
629
620
  # Option 2: Client credentials (service accounts)
621
+ # from cognite.client import ClientConfig, CogniteClient
630
622
  # from cognite.client.credentials import OAuthClientCredentials
623
+ #
631
624
  # cognite_client = CogniteClient(
632
625
  # config=ClientConfig(
633
626
  # client_name="service-client",
634
627
  # project="your-project",
635
628
  # base_url="https://your-cluster.cognitedata.com",
636
- # credentials=OAuthClientCredentials(
637
- # token_url="https://login.microsoftonline.com/your-tenant-id/oauth2/v2.0/token",
629
+ # credentials=OAuthClientCredentials.default_for_entra_id(
630
+ # tenant_id="your-tenant-id",
638
631
  # client_id="your-client-id",
639
632
  # client_secret="your-client-secret",
640
- # scopes=["https://your-cluster.cognitedata.com/.default"],
633
+ # cdf_cluster="your-cluster",
641
634
  # ),
642
635
  # )
643
636
  # )
@@ -42,9 +42,9 @@ fun = "cognite_function_apps.cli:main"
42
42
 
43
43
  [dependency-groups]
44
44
  dev = [
45
- "pytest>=7.0.0",
46
- "pytest-asyncio>=0.21.0",
47
- "pytest-cov>=4.0.0",
45
+ "pytest>=8.3.0",
46
+ "pytest-asyncio>=0.23.0",
47
+ "pytest-cov>=6.0.0",
48
48
  "openapi-spec-validator>=0.7.0",
49
49
  "pre-commit>=4.3.0",
50
50
  "pyright>=1.1.406",
@@ -76,6 +76,7 @@ testpaths = ["tests"]
76
76
  python_files = "test_*.py"
77
77
  python_classes = "Test*"
78
78
  python_functions = "test_*"
79
+ asyncio_mode = "strict"
79
80
  filterwarnings = [
80
81
  # Ignore TracerProvider warning in tests where TracingApp is instantiated
81
82
  # after session fixture has already configured the provider
@@ -0,0 +1 @@
1
+ __version__ = "0.13.0" # x-release-please-version
@@ -192,6 +192,11 @@ class FunctionApp:
192
192
 
193
193
  self.router = Router()
194
194
 
195
+ def __repr__(self) -> str:
196
+ """Return a string representation of this FunctionApp."""
197
+ route_count = sum(len(methods) for methods in self.router.routes.values())
198
+ return f"FunctionApp(title={self.title!r}, version={self.version!r}, routes={route_count})"
199
+
195
200
  @property
196
201
  def downstream_apps(self) -> Sequence["FunctionApp"]:
197
202
  """Get all downstream apps by walking the next_app chain.
@@ -452,8 +457,10 @@ class FunctionApp:
452
457
  "Registry not initialized. App must be composed with create_function_service() before use."
453
458
  )
454
459
 
455
- # Get dependency names early (needed for body param detection and conversion)
456
- dependency_names = self.registry.get_dependency_param_names(route_match.signature)
460
+ # Get dependency names early (needed for body param detection and conversion).
461
+ # Pass type_hints so deferred/quoted annotations (PEP 563 / from __future__ import annotations)
462
+ # are resolved before dependency detection.
463
+ dependency_names = self.registry.get_dependency_param_names(route_match.signature, route_match.type_hints)
457
464
 
458
465
  # GET and DELETE do not have a request body — ignore any body that was sent.
459
466
  # This matches the schema (body params are not documented for these methods)
@@ -316,13 +316,13 @@ app = create_asgi_app(handler_module.handle)
316
316
 
317
317
  @app.command()
318
318
  def serve(
319
- path: Annotated[Path, typer.Argument(help="Path to the directory containing handler.py")],
320
- host: Annotated[str, typer.Option(help="Host to bind to")] = "127.0.0.1",
321
- port: Annotated[int, typer.Option(help="Port to bind to")] = 8000,
322
- reload: Annotated[bool, typer.Option(help="Enable auto-reload on code changes")] = True,
319
+ path: Annotated[Path, typer.Argument(help="Path to the directory containing handler.py")], # pyright: ignore[reportUnknownMemberType]
320
+ host: Annotated[str, typer.Option(help="Host to bind to")] = "127.0.0.1", # pyright: ignore[reportUnknownMemberType]
321
+ port: Annotated[int, typer.Option(help="Port to bind to")] = 8000, # pyright: ignore[reportUnknownMemberType]
322
+ reload: Annotated[bool, typer.Option(help="Enable auto-reload on code changes")] = True, # pyright: ignore[reportUnknownMemberType]
323
323
  log_level: Annotated[
324
324
  str,
325
- typer.Option(help="Log level"),
325
+ typer.Option(help="Log level"), # pyright: ignore[reportUnknownMemberType]
326
326
  ] = "info",
327
327
  ) -> None:
328
328
  """Start a development server for local testing.
@@ -85,7 +85,7 @@ from .models import (
85
85
  CogniteFunctionError,
86
86
  CogniteFunctionResponse,
87
87
  HTTPMethod,
88
- Json,
88
+ JsonValue,
89
89
  )
90
90
  from .schema import (
91
91
  ClientMethodsMetadata,
@@ -121,6 +121,11 @@ def _augment_imports(
121
121
  detect all types. This client-side pass scans the received model sources AND
122
122
  method return/parameter types, then patches the import lines to include any
123
123
  missing names.
124
+
125
+ Each element of `imports` must be a complete, single import statement (one
126
+ logical line). The server always emits imports this way. Parenthesised names
127
+ on a single line — e.g. ``from pydantic import (Field, validator)`` — are
128
+ supported; multi-line statements split across several list elements are not.
124
129
  """
125
130
  # Combine all text to scan: model sources + method return types + param types
126
131
  parts = [m.get("source", "") for m in models]
@@ -136,12 +141,14 @@ def _augment_imports(
136
141
  remaining: list[str] = []
137
142
  for imp in imports:
138
143
  if imp.startswith("from pydantic import"):
139
- for name in imp.removeprefix("from pydantic import").split(","):
144
+ raw = imp.removeprefix("from pydantic import").strip().strip("()")
145
+ for name in raw.split(","):
140
146
  name = name.strip()
141
147
  if name:
142
148
  pydantic_names.add(name)
143
149
  elif imp.startswith("from typing import"):
144
- for name in imp.removeprefix("from typing import").split(","):
150
+ raw = imp.removeprefix("from typing import").strip().strip("()")
151
+ for name in raw.split(","):
145
152
  name = name.strip()
146
153
  if name:
147
154
  typing_names.add(name)
@@ -161,24 +168,38 @@ def _augment_imports(
161
168
  return sorted(new_imports)
162
169
 
163
170
 
171
+ # Json[T] → unwrap to T (HTTP layer has already decoded the JSON on the client side)
172
+ _json_with_args = re.compile(r"\bJson\[([A-Za-z_][\w\s,\[\]]*)\]")
173
+ # bare Json (no type args) → Any (not valid Python syntax as a standalone annotation)
174
+ _bare_json = re.compile(r"\bJson\b(?!\[)")
175
+
176
+
164
177
  def _normalize_method_types(methods: list[dict[str, Any]]) -> list[dict[str, Any]]:
165
- """Replace pydantic field-annotation types that are invalid in method signatures.
178
+ """Replace pydantic Json annotations with the correct client-side types.
166
179
 
167
- Some types (e.g. pydantic.Json) are valid in Pydantic model fields but not
168
- as bare method return/parameter types. Json is a generic alias that requires
169
- type arguments (Json[T]), and on the client side the HTTP layer has already
170
- decoded the JSON, so Any is the correct substitute.
180
+ pydantic.Json is valid in model fields but inappropriate in generated method
181
+ signatures for two reasons:
182
+ - Json[T] is semantically wrong on the client side: the HTTP layer has already
183
+ decoded the JSON, so the actual type is T.
184
+ - bare Json (no type args) is not valid Python syntax and causes a SyntaxError.
185
+
186
+ Both forms are normalised: Json[T] → T, bare Json → Any.
171
187
  """
172
- # Match bare `Json` not followed by `[` (i.e. not Json[T] which would be intentional)
173
- _bare_json = re.compile(r"\bJson\b(?!\[)")
188
+
189
+ def _fix(t: str) -> str:
190
+ while True:
191
+ t2 = _json_with_args.sub(r"\1", t)
192
+ t2 = _bare_json.sub("Any", t2)
193
+ if t2 == t:
194
+ return t
195
+ t = t2
174
196
 
175
197
  normalized: list[dict[str, Any]] = []
176
198
  for method in methods:
177
199
  method = dict(method)
178
- method["return_type"] = _bare_json.sub("Any", method.get("return_type", ""))
179
- method["parameters"] = [
180
- {**p, "type": _bare_json.sub("Any", p.get("type", ""))} for p in method.get("parameters", [])
181
- ]
200
+ method["return_type"] = _fix(method.get("return_type") or "Any")
201
+ params: list[dict[str, Any]] = method.get("parameters") or []
202
+ method["parameters"] = [{**p, "type": _fix(p.get("type") or "Any")} for p in params]
182
203
  normalized.append(method)
183
204
  return normalized
184
205
 
@@ -775,7 +796,7 @@ class FunctionClient(BaseFunctionClient):
775
796
 
776
797
  return deps
777
798
 
778
- def _extract_refs(self, obj: Json, refs: set[str]) -> None:
799
+ def _extract_refs(self, obj: JsonValue, refs: set[str]) -> None:
779
800
  """Recursively extract all $ref model names from a schema.
780
801
 
781
802
  Args:
@@ -80,7 +80,7 @@ manual parameter processing in Cognite Functions.
80
80
  import inspect
81
81
  import types
82
82
  import warnings
83
- from collections.abc import Mapping
83
+ from collections.abc import Mapping, Sequence
84
84
  from typing import Any, Union, cast, get_args, get_origin
85
85
 
86
86
  from pydantic import BaseModel
@@ -180,26 +180,34 @@ def convert_value_to_type(value: Any, target_type: Any, param_path: str = "") ->
180
180
 
181
181
  raise ConvertError(f"Cannot convert {type(value)} to {target_type.__name__} at {param_path}")
182
182
 
183
- # Handle list types. Note that Python's type introspection treats
183
+ # Handle list and sequence types. Note that Python's type introspection treats
184
184
  # legacy and modern collection syntax identically.
185
- if origin is list:
186
- if not isinstance(value, list):
187
- raise ConvertError(f"Expected list but got {type(value)} at {param_path}")
185
+ # We accept both list[T] (origin is list) and Sequence[T] / tuple[T, ...] etc.
186
+ # (origin is a subclass of Sequence that is not str/bytes).
187
+ is_sequence_type = origin is not None and (
188
+ inspect.isclass(origin) and issubclass(origin, Sequence) and not issubclass(origin, (str, bytes))
189
+ )
190
+ if is_sequence_type:
191
+ if not isinstance(value, (list, tuple)):
192
+ raise ConvertError(
193
+ f"Unsupported sequence type {type(value).__name__} at {param_path}, supported types are: (list, tuple)."
194
+ )
188
195
 
189
196
  if not generic_args:
190
- warnings.warn(f"Untyped list at {param_path}, returning as-is")
191
- return value # type: ignore # Untyped list, return as-is
197
+ warnings.warn(f"Untyped sequence at {param_path}, returning as-is")
198
+ return value # type: ignore # Untyped sequence, return as-is
192
199
 
193
200
  element_type = generic_args[0]
194
201
  try:
195
- return [
202
+ converted = [
196
203
  convert_value_to_type(item, element_type, f"{param_path}[{i}]")
197
204
  for i, item in enumerate(cast(list[Any], value))
198
205
  ]
206
+ return tuple(converted) if origin is tuple else converted
199
207
  except Exception as e:
200
208
  if _is_top_level_param(param_path):
201
- raise ConvertError(f"Validation error for parameter '{param_path}' (list): {e!s}") from e
202
- raise ConvertError(f"List conversion error at {param_path}: {e}") from e
209
+ raise ConvertError(f"Validation error for parameter '{param_path}' (sequence): {e!s}") from e
210
+ raise ConvertError(f"Sequence conversion error at {param_path}: {e}") from e
203
211
 
204
212
  # Handle dict types. Python's type introspection treats legacy and
205
213
  # modern collection syntax identically:
@@ -50,7 +50,7 @@ from ._version import __version__
50
50
  from .app import FunctionApp, call_endpoint
51
51
  from .convert import ConvertError, convert_arguments_to_typed_params
52
52
  from .dependency_registry import DependencyRegistry, resolve_dependencies
53
- from .models import CogniteFunctionError, DataDict, FunctionCallInfo, Json, SecretsMapping, TypedResponse
53
+ from .models import CogniteFunctionError, DataDict, FunctionCallInfo, JsonValue, SecretsMapping, TypedResponse
54
54
 
55
55
  _P = ParamSpec("_P")
56
56
  _R = TypeVar("_R")
@@ -165,7 +165,9 @@ class MCPApp(FunctionApp):
165
165
  # Convert MCP arguments to typed parameters with pre-computed signature and type hints
166
166
  converted_params = convert_arguments_to_typed_params(
167
167
  kwargs,
168
- dependency_names=self.registry.get_dependency_param_names(_sig) if self.registry else frozenset(),
168
+ dependency_names=(
169
+ self.registry.get_dependency_param_names(_sig, _type_hints) if self.registry else frozenset()
170
+ ),
169
171
  signature=_sig,
170
172
  type_hints=_type_hints,
171
173
  )
@@ -409,8 +411,8 @@ def _create_mcp_error(error_type: str, message: str, details: DataDict) -> DataD
409
411
  return CogniteFunctionError(error_type=error_type, message=message, details=details).model_dump(by_alias=True)
410
412
 
411
413
 
412
- def _typed_response_to_json(value: TypedResponse) -> Json:
413
- """Recursively convert TypedResponse to Json by converting all BaseModel instances."""
414
+ def _typed_response_to_json(value: TypedResponse) -> JsonValue:
415
+ """Recursively convert TypedResponse to JsonValue by converting all BaseModel instances."""
414
416
  match value:
415
417
  case BaseModel():
416
418
  # Use by_alias=True to ensure field aliases are used in serialization
@@ -421,7 +423,7 @@ def _typed_response_to_json(value: TypedResponse) -> Json:
421
423
  return [_typed_response_to_json(item) for item in value]
422
424
  case _:
423
425
  # Primitives pass through
424
- return cast(Json, value)
426
+ return cast(JsonValue, value)
425
427
 
426
428
 
427
429
  def _format_mcp_response(result: TypedResponse, tool_name: str) -> DataDict:
@@ -152,7 +152,7 @@ class FunctionCallInfo(TypedDict):
152
152
 
153
153
  if TYPE_CHECKING:
154
154
  # Recursive types for type checking (pyright). These work with pyright, but not for Pydantic (RecursionError)
155
- Json: TypeAlias = Mapping[str, "Json"] | Sequence["Json"] | str | int | float | bool | None
155
+ JsonValue: TypeAlias = Mapping[str, "JsonValue"] | Sequence["JsonValue"] | str | int | float | bool | None
156
156
  TypedResponse: TypeAlias = (
157
157
  BaseModel | Sequence["TypedResponse"] | Mapping[str, "TypedResponse"] | str | int | float | bool | None
158
158
  )
@@ -162,9 +162,9 @@ else:
162
162
  # These work for Pydantic, but not with pyright. Note the use of `Mapping` and
163
163
  # `Sequence` to make them covariant. For more information, see
164
164
  # https://docs.pydantic.dev/2.11/concepts/types/#named-recursive-types
165
- Json = TypeAliasType(
166
- "Json",
167
- "Mapping[str, Json] | Sequence[Json] | str | int | float | bool | None",
165
+ JsonValue = TypeAliasType(
166
+ "JsonValue",
167
+ "Mapping[str, JsonValue] | Sequence[JsonValue] | str | int | float | bool | None",
168
168
  )
169
169
  TypedResponse = TypeAliasType(
170
170
  "TypedResponse",
@@ -173,7 +173,7 @@ else:
173
173
 
174
174
 
175
175
  # Type aliases for better readability
176
- DataDict: TypeAlias = Mapping[str, Json]
176
+ DataDict: TypeAlias = Mapping[str, JsonValue]
177
177
  SecretsMapping: TypeAlias = Mapping[str, str]
178
178
  RequestHeaders: TypeAlias = Mapping[str, str] # Header names are lowercase (HTTP/2 convention)
179
179
 
@@ -206,7 +206,7 @@ class Handle(Protocol):
206
206
  data: DataDict,
207
207
  secrets: SecretsMapping | None = None,
208
208
  function_call_info: FunctionCallInfo | None = None,
209
- ) -> Json:
209
+ ) -> JsonValue:
210
210
  """Call the handler."""
211
211
  ...
212
212
 
@@ -945,7 +945,9 @@ class SchemaGenerator:
945
945
 
946
946
  # Get dependency parameter names for filtering
947
947
  dependency_param_names = (
948
- registry.get_dependency_param_names(route_info.signature) if registry else frozenset[str]()
948
+ registry.get_dependency_param_names(route_info.signature, route_info.type_hints)
949
+ if registry
950
+ else frozenset[str]()
949
951
  )
950
952
 
951
953
  # Generate response schema from return type
@@ -30,7 +30,7 @@ from .models import (
30
30
  CogniteFunctionError,
31
31
  DataDict,
32
32
  FunctionCallInfo,
33
- Json,
33
+ JsonValue,
34
34
  RequestData,
35
35
  SecretsMapping,
36
36
  )
@@ -85,6 +85,8 @@ class FunctionService:
85
85
  async_handle: The async implementation for optimal devserver performance
86
86
  """
87
87
 
88
+ __name__ = "handle"
89
+
88
90
  def __init__(
89
91
  self,
90
92
  asgi_app: ASGIApp,
@@ -96,6 +98,13 @@ class FunctionService:
96
98
  """
97
99
  self.asgi_app = asgi_app
98
100
 
101
+ def __repr__(self) -> str:
102
+ """Return a string representation of this FunctionService."""
103
+ if not isinstance(self.asgi_app, FunctionApp):
104
+ return "FunctionService(routes=0)"
105
+ route_count = sum(len(methods) for methods in self.asgi_app.all_routes.values())
106
+ return f"FunctionService(routes={route_count})"
107
+
99
108
  def _collect_all_routes(self) -> list[dict[str, Any]]:
100
109
  """Collect all routes from all apps in the composition chain.
101
110
 
@@ -231,7 +240,7 @@ class FunctionService:
231
240
  data: DataDict,
232
241
  secrets: SecretsMapping | None = None,
233
242
  function_call_info: FunctionCallInfo | None = None,
234
- ) -> Json:
243
+ ) -> JsonValue:
235
244
  """Call the synchronous handle (Handle protocol implementation).
236
245
 
237
246
  This makes FunctionService callable and compatible with the Cognite
@@ -48,7 +48,7 @@ def convert_with_di(
48
48
  # Convert user-provided arguments
49
49
  converted_params = convert_arguments_to_typed_params(
50
50
  arguments,
51
- dependency_names=registry.get_dependency_param_names(sig),
51
+ dependency_names=registry.get_dependency_param_names(sig, type_hints),
52
52
  signature=sig,
53
53
  type_hints=type_hints,
54
54
  )