crossplane-function-sdk-python 0.14.0__tar.gz → 0.15.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 (46) hide show
  1. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.github/workflows/ci.yml +9 -9
  2. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/PKG-INFO +5 -4
  3. crossplane_function_sdk_python-0.15.0/crossplane/function/__init__.py +65 -0
  4. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/__version__.py +1 -1
  5. crossplane_function_sdk_python-0.15.0/crossplane/function/cli.py +127 -0
  6. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/runtime.py +4 -1
  7. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/pyproject.toml +3 -2
  8. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  9. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  10. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  11. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.gitignore +0 -0
  12. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/CODEOWNERS +0 -0
  13. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/LICENSE +0 -0
  14. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/OWNERS.md +0 -0
  15. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/README.md +0 -0
  16. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/logging.py +0 -0
  17. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1/run_function.proto +0 -0
  18. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1/run_function_pb2.py +0 -0
  19. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1/run_function_pb2.pyi +0 -0
  20. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1/run_function_pb2_grpc.py +0 -0
  21. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1beta1/run_function.proto +0 -0
  22. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1beta1/run_function_pb2.py +0 -0
  23. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1beta1/run_function_pb2.pyi +0 -0
  24. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py +0 -0
  25. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/py.typed +0 -0
  26. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/request.py +0 -0
  27. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/resource.py +0 -0
  28. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/response.py +0 -0
  29. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/renovate.json +0 -0
  30. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/test_request.py +0 -0
  31. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/test_resource.py +0 -0
  32. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/test_response.py +0 -0
  33. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/test_runtime.py +0 -0
  34. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/k8s/api/__init__.py +0 -0
  35. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/k8s/api/resource/__init__.py +0 -0
  36. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/k8s/api/resource/v1.py +0 -0
  37. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/k8s/apimachinery/pkg/apis/__init__.py +0 -0
  38. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/k8s/apimachinery/pkg/apis/meta/__init__.py +0 -0
  39. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/k8s/apimachinery/pkg/apis/meta/v1.py +0 -0
  40. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/upbound/aws/s3/__init__.py +0 -0
  41. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/upbound/aws/s3/v1beta2.py +0 -0
  42. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/upbound/m/__init__.py +0 -0
  43. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/upbound/m/aws/__init__.py +0 -0
  44. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/upbound/m/aws/iam/__init__.py +0 -0
  45. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/upbound/m/aws/iam/accountalias/__init__.py +0 -0
  46. {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/upbound/m/aws/iam/accountalias/v1beta1.py +0 -0
@@ -38,10 +38,10 @@ jobs:
38
38
  runs-on: ubuntu-24.04
39
39
  steps:
40
40
  - name: Checkout
41
- uses: actions/checkout@v6
41
+ uses: actions/checkout@v7
42
42
 
43
43
  - name: Setup Python
44
- uses: actions/setup-python@v6
44
+ uses: actions/setup-python@v7
45
45
  with:
46
46
  python-version: ${{ env.PYTHON_VERSION }}
47
47
 
@@ -55,10 +55,10 @@ jobs:
55
55
  runs-on: ubuntu-24.04
56
56
  steps:
57
57
  - name: Checkout
58
- uses: actions/checkout@v6
58
+ uses: actions/checkout@v7
59
59
 
60
60
  - name: Setup Python
61
- uses: actions/setup-python@v6
61
+ uses: actions/setup-python@v7
62
62
  with:
63
63
  python-version: ${{ env.PYTHON_VERSION }}
64
64
 
@@ -73,10 +73,10 @@ jobs:
73
73
  runs-on: ubuntu-24.04
74
74
  steps:
75
75
  - name: Checkout
76
- uses: actions/checkout@v6
76
+ uses: actions/checkout@v7
77
77
 
78
78
  - name: Setup Python
79
- uses: actions/setup-python@v6
79
+ uses: actions/setup-python@v7
80
80
  with:
81
81
  python-version: ${{ env.PYTHON_VERSION }}
82
82
 
@@ -120,7 +120,7 @@ jobs:
120
120
  path: "dist"
121
121
 
122
122
  - name: Publish to PyPI
123
- uses: pypa/gh-action-pypi-publish@v1.14.0
123
+ uses: pypa/gh-action-pypi-publish@v1.14.2
124
124
  with:
125
125
  # Note that this is currently being pushed to the 'crossplane' PyPI
126
126
  # user (not org). See @negz if you need access - PyPI requires 2FA to
@@ -136,10 +136,10 @@ jobs:
136
136
  runs-on: ubuntu-24.04
137
137
  steps:
138
138
  - name: Checkout
139
- uses: actions/checkout@v6
139
+ uses: actions/checkout@v7
140
140
 
141
141
  - name: Setup Python
142
- uses: actions/setup-python@v6
142
+ uses: actions/setup-python@v7
143
143
  with:
144
144
  python-version: ${{ env.PYTHON_VERSION }}
145
145
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: crossplane-function-sdk-python
3
- Version: 0.14.0
3
+ Version: 0.15.0
4
4
  Summary: The Python SDK for Crossplane composition functions
5
5
  Project-URL: Documentation, https://github.com/crossplane/function-sdk-python#readme
6
6
  Project-URL: Issues, https://github.com/crossplane/function-sdk-python/issues
@@ -13,9 +13,10 @@ Classifier: Programming Language :: Python
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Typing :: Typed
15
15
  Requires-Python: >=3.11
16
+ Requires-Dist: click==8.*
16
17
  Requires-Dist: grpcio-reflection==1.*
17
- Requires-Dist: grpcio==1.81.0
18
- Requires-Dist: protobuf==7.35.0
18
+ Requires-Dist: grpcio==1.84.0
19
+ Requires-Dist: protobuf==7.36.1
19
20
  Requires-Dist: pydantic==2.*
20
21
  Requires-Dist: structlog==26.*
21
22
  Description-Content-Type: text/markdown
@@ -0,0 +1,65 @@
1
+ # Copyright 2025 The Crossplane Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Python SDK for writing Crossplane composition functions.
16
+
17
+ This SDK provides the building blocks for implementing [Crossplane composition
18
+ functions](https://docs.crossplane.io/latest/concepts/composition-functions) in
19
+ Python. Composition functions are serverless components that extend Crossplane's
20
+ composition capability, allowing you to programmatically control how infrastructure
21
+ is composed and managed.
22
+
23
+ ## Quick Start
24
+
25
+ Install the SDK:
26
+
27
+ ```shell
28
+ pip install crossplane-function-sdk-python
29
+ ```
30
+
31
+ Create a composition function by subclassing `Runtime`:
32
+
33
+ ```python
34
+ from crossplane.function.runtime import Runtime
35
+ from crossplane.function.request import RunFunctionRequest
36
+ from crossplane.function.response import RunFunctionResponse
37
+
38
+
39
+ class MyFunction(Runtime):
40
+ def Run(self, request: RunFunctionRequest) -> RunFunctionResponse:
41
+ # Your composition logic here
42
+ return response.to(request)
43
+ ```
44
+
45
+ ## Modules
46
+
47
+ - **runtime** — Base class for implementing the function runtime and gRPC server.
48
+ - **request** — Types and utilities for parsing `RunFunctionRequest` messages.
49
+ - **response** — Types and utilities for building `RunFunctionResponse` messages.
50
+ - **resource** — Kubernetes resource types used in function pipelines.
51
+ - **logging** — Structured logging utilities for function output.
52
+
53
+ ## Protobuf Types
54
+
55
+ The `RunFunctionRequest` and `RunFunctionResponse` types are generated from
56
+ proto3 schema definitions. See the proto modules for API reference:
57
+
58
+ - [`proto.v1.run_function_pb2`][crossplane.function.proto.v1.run_function_pb2]
59
+ — Current API
60
+ - [`proto.v1beta1.run_function_pb2`][crossplane.function.proto.v1beta1.run_function_pb2]
61
+ — Legacy API
62
+
63
+ Proto-generated fields behave like standard Python types but follow
64
+ [protobuf Python conventions](https://protobuf.dev/reference/python/python-generated/).
65
+ """
@@ -15,4 +15,4 @@
15
15
  """The version of function-sdk-python."""
16
16
 
17
17
  # This is set at build time, using "hatch version"
18
- __version__ = "0.14.0"
18
+ __version__ = "0.15.0"
@@ -0,0 +1,127 @@
1
+ # Copyright 2026 The Crossplane Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Standard CLI options for Python composition functions.
16
+
17
+ Provides reusable options and a run helper so that every composition
18
+ function shares a standard set of flags, environment variables, and defaults.
19
+
20
+ Usage in a function's main.py::
21
+
22
+ import click
23
+ from crossplane.function import cli as sdkcli
24
+ from function import fn
25
+
26
+ @click.command()
27
+ @sdkcli.standard_options
28
+ def cli(**kwargs):
29
+ sdkcli.run(fn.FunctionRunner(), **kwargs)
30
+
31
+ To add custom options, stack them with the decorator::
32
+
33
+ @click.command()
34
+ @sdkcli.standard_options
35
+ @click.option("--cache-size", default=100, envvar="CACHE_SIZE")
36
+ def cli(cache_size, **kwargs):
37
+ runner = fn.FunctionRunner(cache_size=cache_size)
38
+ sdkcli.run(runner, **kwargs)
39
+ """
40
+
41
+ import functools
42
+ from collections.abc import Callable
43
+ from typing import TypeVar
44
+
45
+ import click
46
+
47
+ from crossplane.function import logging, runtime
48
+ from crossplane.function.proto.v1 import run_function_pb2_grpc as grpcv1
49
+
50
+ F = TypeVar("F", bound=Callable)
51
+
52
+ DEFAULT_ADDRESS = "0.0.0.0:9443"
53
+ DEFAULT_MAX_RECV_MESSAGE_SIZE = 4 # MB
54
+
55
+
56
+ def standard_options(func: F) -> F:
57
+ """Apply the standard Composition Function CLI options to a Click command."""
58
+
59
+ @click.option(
60
+ "--max-recv-message-size",
61
+ type=int,
62
+ default=DEFAULT_MAX_RECV_MESSAGE_SIZE,
63
+ show_default=True,
64
+ envvar="MAX_RECV_MESSAGE_SIZE",
65
+ help="Maximum size of received gRPC messages in MB.",
66
+ )
67
+ @click.option(
68
+ "--insecure",
69
+ is_flag=True,
70
+ envvar="INSECURE",
71
+ help="Run without mTLS credentials. "
72
+ "If you supply this flag --tls-server-certs-dir will be ignored.",
73
+ )
74
+ @click.option(
75
+ "--tls-server-certs-dir",
76
+ "--tls-certs-dir",
77
+ "tls_certs_dir",
78
+ envvar="TLS_SERVER_CERTS_DIR",
79
+ help="Serve using mTLS certificates.",
80
+ )
81
+ @click.option(
82
+ "--address",
83
+ default=DEFAULT_ADDRESS,
84
+ show_default=True,
85
+ envvar="ADDRESS",
86
+ help="Address at which to listen for gRPC connections.",
87
+ )
88
+ @click.option(
89
+ "--debug",
90
+ "-d",
91
+ is_flag=True,
92
+ envvar="DEBUG",
93
+ help="Emit debug logs.",
94
+ )
95
+ @functools.wraps(func)
96
+ def wrapper(*args, **kwargs):
97
+ return func(*args, **kwargs)
98
+
99
+ return wrapper
100
+
101
+
102
+ def run( # noqa: PLR0913
103
+ function_runner: grpcv1.FunctionRunnerServiceServicer,
104
+ *,
105
+ debug: bool,
106
+ address: str,
107
+ tls_certs_dir: str | None,
108
+ insecure: bool,
109
+ max_recv_message_size: int,
110
+ ) -> None:
111
+ """Start a composition function gRPC server with standard options."""
112
+ level = logging.Level.DEBUG if debug else logging.Level.INFO
113
+ logging.configure(level=level)
114
+
115
+ size_bytes = max_recv_message_size * 1024 * 1024
116
+ options = [
117
+ ("grpc.max_receive_message_length", size_bytes),
118
+ ("grpc.max_send_message_length", size_bytes),
119
+ ]
120
+
121
+ runtime.serve(
122
+ function_runner,
123
+ address,
124
+ creds=runtime.load_credentials(tls_certs_dir),
125
+ insecure=insecure,
126
+ options=options,
127
+ )
@@ -95,7 +95,10 @@ def serve(
95
95
  are supplied.
96
96
  """
97
97
  # Define the loop before the server so everything uses the same loop.
98
- loop = asyncio.get_event_loop()
98
+ try:
99
+ loop = asyncio.get_event_loop()
100
+ except RuntimeError:
101
+ loop = asyncio.new_event_loop()
99
102
 
100
103
  server = grpc.aio.server(options=options)
101
104
 
@@ -18,9 +18,10 @@ classifiers = [
18
18
  ]
19
19
 
20
20
  dependencies = [
21
- "grpcio==1.81.0",
21
+ "click==8.*",
22
+ "grpcio==1.84.0",
22
23
  "grpcio-reflection==1.*",
23
- "protobuf==7.35.0", # Must be compatible with grpcio-tools.
24
+ "protobuf==7.36.1", # Must be compatible with grpcio-tools.
24
25
  "pydantic==2.*",
25
26
  "structlog==26.*",
26
27
  ]