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.
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.github/workflows/ci.yml +9 -9
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/PKG-INFO +5 -4
- crossplane_function_sdk_python-0.15.0/crossplane/function/__init__.py +65 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/__version__.py +1 -1
- crossplane_function_sdk_python-0.15.0/crossplane/function/cli.py +127 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/runtime.py +4 -1
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/pyproject.toml +3 -2
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/.gitignore +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/CODEOWNERS +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/LICENSE +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/OWNERS.md +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/README.md +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/logging.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1/run_function.proto +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1/run_function_pb2.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1/run_function_pb2.pyi +0 -0
- {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
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1beta1/run_function.proto +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1beta1/run_function_pb2.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/proto/v1beta1/run_function_pb2.pyi +0 -0
- {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
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/py.typed +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/request.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/resource.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/crossplane/function/response.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/renovate.json +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/test_request.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/test_resource.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/test_response.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/test_runtime.py +0 -0
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/k8s/api/__init__.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/tests/testdata/models/io/upbound/m/__init__.py +0 -0
- {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
- {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
- {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
- {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@
|
|
41
|
+
uses: actions/checkout@v7
|
|
42
42
|
|
|
43
43
|
- name: Setup Python
|
|
44
|
-
uses: actions/setup-python@
|
|
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@
|
|
58
|
+
uses: actions/checkout@v7
|
|
59
59
|
|
|
60
60
|
- name: Setup Python
|
|
61
|
-
uses: actions/setup-python@
|
|
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@
|
|
76
|
+
uses: actions/checkout@v7
|
|
77
77
|
|
|
78
78
|
- name: Setup Python
|
|
79
|
-
uses: actions/setup-python@
|
|
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.
|
|
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@
|
|
139
|
+
uses: actions/checkout@v7
|
|
140
140
|
|
|
141
141
|
- name: Setup Python
|
|
142
|
-
uses: actions/setup-python@
|
|
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.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: crossplane-function-sdk-python
|
|
3
|
-
Version: 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.
|
|
18
|
-
Requires-Dist: protobuf==7.
|
|
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
|
+
"""
|
|
@@ -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
|
-
|
|
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
|
|
{crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/pyproject.toml
RENAMED
|
@@ -18,9 +18,10 @@ classifiers = [
|
|
|
18
18
|
]
|
|
19
19
|
|
|
20
20
|
dependencies = [
|
|
21
|
-
"
|
|
21
|
+
"click==8.*",
|
|
22
|
+
"grpcio==1.84.0",
|
|
22
23
|
"grpcio-reflection==1.*",
|
|
23
|
-
"protobuf==7.
|
|
24
|
+
"protobuf==7.36.1", # Must be compatible with grpcio-tools.
|
|
24
25
|
"pydantic==2.*",
|
|
25
26
|
"structlog==26.*",
|
|
26
27
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{crossplane_function_sdk_python-0.14.0 → crossplane_function_sdk_python-0.15.0}/renovate.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|