crossplane-function-pythonic 0.6.2__tar.gz → 0.7.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 (145) hide show
  1. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0}/PKG-INFO +23 -26
  2. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0}/README.md +11 -9
  3. crossplane_function_pythonic-0.7.0/pyproject.toml +65 -0
  4. crossplane_function_pythonic-0.7.0/pyproject.toml.orig +66 -0
  5. crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/__about__.py +6 -0
  6. crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/__init__.py +25 -0
  7. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/__main__.py +1 -0
  8. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/auto_ready.py +118 -61
  9. crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/command.py +136 -0
  10. crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/composite.py +1314 -0
  11. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/function.py +233 -130
  12. crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/grpc.py +174 -0
  13. crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/main.py +41 -0
  14. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/packages.py +82 -51
  15. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/proto/v1/run_function_pb2.py +3 -3
  16. crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/proto/v1/run_function_pb2.pyi +1172 -0
  17. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/proto/v1/run_function_pb2_grpc.py +4 -4
  18. crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/proto/v1/run_function_pb2_grpc.pyi +64 -0
  19. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/protobuf.py +589 -372
  20. crossplane_function_pythonic-0.7.0/src/crossplane/pythonic/render.py +1064 -0
  21. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/version.py +3 -4
  22. crossplane_function_pythonic-0.6.2/.gitignore +0 -219
  23. crossplane_function_pythonic-0.6.2/LICENSE +0 -201
  24. crossplane_function_pythonic-0.6.2/crossplane/pythonic/__about__.py +0 -2
  25. crossplane_function_pythonic-0.6.2/crossplane/pythonic/__init__.py +0 -17
  26. crossplane_function_pythonic-0.6.2/crossplane/pythonic/command.py +0 -125
  27. crossplane_function_pythonic-0.6.2/crossplane/pythonic/composite.py +0 -1086
  28. crossplane_function_pythonic-0.6.2/crossplane/pythonic/grpc.py +0 -150
  29. crossplane_function_pythonic-0.6.2/crossplane/pythonic/main.py +0 -28
  30. crossplane_function_pythonic-0.6.2/crossplane/pythonic/proto/v1/run_function_pb2.pyi +0 -306
  31. crossplane_function_pythonic-0.6.2/crossplane/pythonic/render.py +0 -731
  32. crossplane_function_pythonic-0.6.2/examples/aks-cluster/README.md +0 -129
  33. crossplane_function_pythonic-0.6.2/examples/aks-cluster/aks/kubernetescluster.py +0 -29
  34. crossplane_function_pythonic-0.6.2/examples/aks-cluster/aks/resourcegroup.py +0 -12
  35. crossplane_function_pythonic-0.6.2/examples/aks-cluster/cluster-function-pythonic.yaml +0 -63
  36. crossplane_function_pythonic-0.6.2/examples/aks-cluster/composition.yaml +0 -24
  37. crossplane_function_pythonic-0.6.2/examples/aks-cluster/definition.yaml +0 -45
  38. crossplane_function_pythonic-0.6.2/examples/aks-cluster/install.sh +0 -13
  39. crossplane_function_pythonic-0.6.2/examples/aks-cluster/kustomization.yaml +0 -20
  40. crossplane_function_pythonic-0.6.2/examples/aks-cluster/providers.yaml +0 -14
  41. crossplane_function_pythonic-0.6.2/examples/aks-cluster/render.sh +0 -4
  42. crossplane_function_pythonic-0.6.2/examples/aks-cluster/xr.yaml +0 -16
  43. crossplane_function_pythonic-0.6.2/examples/connection-details-composition/composition.yaml +0 -30
  44. crossplane_function_pythonic-0.6.2/examples/connection-details-composition/definition.yaml +0 -26
  45. crossplane_function_pythonic-0.6.2/examples/connection-details-composition/observed.yaml +0 -49
  46. crossplane_function_pythonic-0.6.2/examples/connection-details-composition/render.sh +0 -5
  47. crossplane_function_pythonic-0.6.2/examples/connection-details-composition/resources.yaml +0 -18
  48. crossplane_function_pythonic-0.6.2/examples/connection-details-composition/xr.yaml +0 -8
  49. crossplane_function_pythonic-0.6.2/examples/connections/composition.yaml +0 -39
  50. crossplane_function_pythonic-0.6.2/examples/connections/observed.yaml +0 -21
  51. crossplane_function_pythonic-0.6.2/examples/connections/render.sh +0 -7
  52. crossplane_function_pythonic-0.6.2/examples/connections/resources.yaml +0 -27
  53. crossplane_function_pythonic-0.6.2/examples/connections/xr.yaml +0 -8
  54. crossplane_function_pythonic-0.6.2/examples/eks-cluster/composition-v2.yaml +0 -138
  55. crossplane_function_pythonic-0.6.2/examples/eks-cluster/composition.yaml +0 -126
  56. crossplane_function_pythonic-0.6.2/examples/eks-cluster/definition.yaml +0 -47
  57. crossplane_function_pythonic-0.6.2/examples/eks-cluster/render-v2.sh +0 -3
  58. crossplane_function_pythonic-0.6.2/examples/eks-cluster/render.sh +0 -3
  59. crossplane_function_pythonic-0.6.2/examples/eks-cluster/xr.yaml +0 -13
  60. crossplane_function_pythonic-0.6.2/examples/filing-system/README.md +0 -13
  61. crossplane_function_pythonic-0.6.2/examples/filing-system/composition.yaml +0 -17
  62. crossplane_function_pythonic-0.6.2/examples/filing-system/definition.yaml +0 -21
  63. crossplane_function_pythonic-0.6.2/examples/filing-system/kustomization.yaml +0 -18
  64. crossplane_function_pythonic-0.6.2/examples/filing-system/runtime.yaml +0 -23
  65. crossplane_function_pythonic-0.6.2/examples/filing-system/vcluster.py +0 -38
  66. crossplane_function_pythonic-0.6.2/examples/filing-system/vcluster.yaml +0 -5
  67. crossplane_function_pythonic-0.6.2/examples/function-go-templating/conditions/composition.yaml +0 -31
  68. crossplane_function_pythonic-0.6.2/examples/function-go-templating/conditions/render.sh +0 -3
  69. crossplane_function_pythonic-0.6.2/examples/function-go-templating/conditions/xr.yaml +0 -5
  70. crossplane_function_pythonic-0.6.2/examples/function-go-templating/conditions/xrd.yaml +0 -25
  71. crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/composition.yaml +0 -31
  72. crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/render.sh +0 -3
  73. crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/resources.yaml +0 -10
  74. crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/xr.yaml +0 -5
  75. crossplane_function_pythonic-0.6.2/examples/function-go-templating/context/xrd.yaml +0 -25
  76. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/fromYaml/composition.yaml +0 -21
  77. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/fromYaml/render.sh +0 -3
  78. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/fromYaml/xr.yaml +0 -9
  79. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getComposedResource/composition.yaml +0 -28
  80. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getComposedResource/observed.yaml +0 -20
  81. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getComposedResource/render.sh +0 -3
  82. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getComposedResource/xr.yaml +0 -6
  83. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCompositeResource/composition.yaml +0 -23
  84. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCompositeResource/render.sh +0 -3
  85. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCompositeResource/xr.yaml +0 -7
  86. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCredentialData/composition.yaml +0 -27
  87. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCredentialData/render.sh +0 -3
  88. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCredentialData/resources.yaml +0 -9
  89. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getCredentialData/xr.yaml +0 -5
  90. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getResourceCondition/composition.yaml +0 -27
  91. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getResourceCondition/observed.yaml +0 -21
  92. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getResourceCondition/render.sh +0 -3
  93. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/getResourceCondition/xr.yaml +0 -12
  94. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/include/composition.yaml +0 -41
  95. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/include/render.sh +0 -3
  96. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/include/xr.yaml +0 -7
  97. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/toYaml/composition.yaml +0 -21
  98. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/toYaml/render.sh +0 -3
  99. crossplane_function_pythonic-0.6.2/examples/function-go-templating/functions/toYaml/xr.yaml +0 -12
  100. crossplane_function_pythonic-0.6.2/examples/function-go-templating/inline/composition.yaml +0 -35
  101. crossplane_function_pythonic-0.6.2/examples/function-go-templating/inline/render.sh +0 -3
  102. crossplane_function_pythonic-0.6.2/examples/function-go-templating/inline/xr.yaml +0 -6
  103. crossplane_function_pythonic-0.6.2/examples/function-go-templating/recursive/composition-real.yaml +0 -21
  104. crossplane_function_pythonic-0.6.2/examples/function-go-templating/recursive/composition.yaml +0 -23
  105. crossplane_function_pythonic-0.6.2/examples/function-go-templating/recursive/render.sh +0 -4
  106. crossplane_function_pythonic-0.6.2/examples/function-go-templating/recursive/xr.yaml +0 -8
  107. crossplane_function_pythonic-0.6.2/examples/function-go-templating/required-resources/composition.yaml +0 -29
  108. crossplane_function_pythonic-0.6.2/examples/function-go-templating/required-resources/render.sh +0 -3
  109. crossplane_function_pythonic-0.6.2/examples/function-go-templating/required-resources/resources.yaml +0 -14
  110. crossplane_function_pythonic-0.6.2/examples/function-go-templating/required-resources/xr.yaml +0 -6
  111. crossplane_function_pythonic-0.6.2/examples/function-sequencer/composition.yaml +0 -48
  112. crossplane_function_pythonic-0.6.2/examples/function-sequencer/observed.yaml +0 -27
  113. crossplane_function_pythonic-0.6.2/examples/function-sequencer/render.sh +0 -11
  114. crossplane_function_pythonic-0.6.2/examples/function-sequencer/sequencer.py +0 -12
  115. crossplane_function_pythonic-0.6.2/examples/function-sequencer/xr.yaml +0 -6
  116. crossplane_function_pythonic-0.6.2/examples/get-started-app/composition.yaml +0 -39
  117. crossplane_function_pythonic-0.6.2/examples/get-started-app/definition.yaml +0 -35
  118. crossplane_function_pythonic-0.6.2/examples/get-started-app/observed.yaml +0 -53
  119. crossplane_function_pythonic-0.6.2/examples/get-started-app/render.sh +0 -3
  120. crossplane_function_pythonic-0.6.2/examples/get-started-app/xr.yaml +0 -7
  121. crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/composition.yaml +0 -17
  122. crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/kustomization.yaml +0 -16
  123. crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/render.sh +0 -3
  124. crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/vcluster.py +0 -63
  125. crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/xr.yaml +0 -5
  126. crossplane_function_pythonic-0.6.2/examples/helm-copy-secret/xrd.yaml +0 -21
  127. crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/README.md +0 -35
  128. crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/composition.yaml +0 -68
  129. crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/definition.yaml +0 -39
  130. crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/observed.yaml +0 -6
  131. crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/provider-ec2.yaml +0 -20
  132. crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/render.sh +0 -4
  133. crossplane_function_pythonic-0.6.2/examples/import-existing-vpc/xr.yaml +0 -10
  134. crossplane_function_pythonic-0.6.2/examples/inlined/composition.yaml +0 -17
  135. crossplane_function_pythonic-0.6.2/examples/inlined/definition.yaml +0 -32
  136. crossplane_function_pythonic-0.6.2/examples/inlined/render.sh +0 -3
  137. crossplane_function_pythonic-0.6.2/examples/inlined/xr.yaml +0 -10
  138. crossplane_function_pythonic-0.6.2/examples/run-all.sh +0 -3
  139. crossplane_function_pythonic-0.6.2/examples/usages-extra/composition.yaml +0 -26
  140. crossplane_function_pythonic-0.6.2/examples/usages-extra/observed.yaml +0 -6
  141. crossplane_function_pythonic-0.6.2/examples/usages-extra/render.sh +0 -3
  142. crossplane_function_pythonic-0.6.2/examples/usages-extra/resources.yaml +0 -7
  143. crossplane_function_pythonic-0.6.2/examples/usages-extra/xr.yaml +0 -6
  144. crossplane_function_pythonic-0.6.2/pyproject.toml +0 -153
  145. {crossplane_function_pythonic-0.6.2 → crossplane_function_pythonic-0.7.0/src}/crossplane/pythonic/proto/v1/run_function.proto +0 -0
@@ -1,28 +1,23 @@
1
- Metadata-Version: 2.5
1
+ Metadata-Version: 2.4
2
2
  Name: crossplane-function-pythonic
3
- Version: 0.6.2
3
+ Version: 0.7.0
4
4
  Summary: A Python centric Crossplane Function
5
- Project-URL: Documentation, https://github.com/crossplane-contrib/function-pythonic#readme
6
- Project-URL: Issues, https://github.com/crossplane-contrib/function-pythonic/issues
7
- Project-URL: Source, https://github.com/crossplane-contrib/function-pythonic
5
+ Author: Patrick J McNerthney
8
6
  Author-email: Patrick J McNerthney <pat@mcnerthney.com>
9
7
  License-Expression: Apache-2.0
10
- License-File: LICENSE
11
- Classifier: Development Status :: 4 - Beta
12
- Classifier: Programming Language :: Python
13
- Classifier: Programming Language :: Python :: 3.12
14
- Classifier: Programming Language :: Python :: 3.13
15
- Classifier: Programming Language :: Python :: 3.14
16
- Requires-Python: <3.15,>=3.12
17
- Requires-Dist: grpcio==1.83.0
18
- Requires-Dist: inflect==7.5.0
8
+ Requires-Dist: grpcio==1.85.1
9
+ Requires-Dist: inflect==7.6.0
19
10
  Requires-Dist: kr8s==0.20.15
20
- Requires-Dist: protobuf==7.35.1
11
+ Requires-Dist: protobuf==7.36.2
21
12
  Requires-Dist: pyyaml==6.0.3
13
+ Requires-Dist: kopf==1.45.2 ; extra == 'packages'
14
+ Requires-Dist: pip==26.2.2 ; extra == 'pip-install'
15
+ Requires-Python: >=3.15, <3.16
16
+ Project-URL: Documentation, https://github.com/crossplane-contrib/function-pythonic#readme
17
+ Project-URL: Issues, https://github.com/crossplane-contrib/function-pythonic/issues
18
+ Project-URL: Source, https://github.com/crossplane-contrib/function-pythonic
22
19
  Provides-Extra: packages
23
- Requires-Dist: kopf==1.44.6; extra == 'packages'
24
20
  Provides-Extra: pip-install
25
- Requires-Dist: pip==26.2.1; extra == 'pip-install'
26
21
  Description-Content-Type: text/markdown
27
22
 
28
23
  # function-pythonic
@@ -213,7 +208,7 @@ message contents. For example, the following examples obtain the same value
213
208
  from the RunFunctionRequest message:
214
209
  ```python
215
210
  region = request.observed.composite.resource.spec.region
216
- region = request['observed']['composite']['resource']['spec']['region']
211
+ region = request["observed"]["composite"]["resource"]["spec"]["region"]
217
212
  ```
218
213
  Getting values from free form map and list values will not throw
219
214
  errors for keys that do not exist, but will return an unknown placeholder
@@ -232,13 +227,15 @@ When setting fields, all intermediary unknown placeholders will automatically
232
227
  be created. For example, this will create all items needed to set the
233
228
  region on the desired resource:
234
229
  ```python
235
- response.desired.resources.vpc.resource.spec.forProvider.region = 'us-east-1'
230
+ response.desired.resources.vpc.resource.spec.forProvider.region = "us-east-1"
236
231
  ```
237
232
  Calling a message or map will clear it and will set any provided key word
238
233
  arguments. For example, this will either create or clear the resource
239
234
  and then set its apiVersion and kind:
240
235
  ```python
241
- response.desired.resources.vpc.resource(kind='VPC', apiVersion='ec2.aws.crossplane.io/v1beta1')
236
+ response.desired.resources.vpc.resource(
237
+ kind="VPC", apiVersion="ec2.aws.crossplane.io/v1beta1"
238
+ )
242
239
  ```
243
240
  The following functions are provided to create Protobuf structures:
244
241
  | Function | Description |
@@ -257,12 +254,12 @@ The following items are supported in all the Protobuf Message wrapper classes: `
257
254
 
258
255
  To convert a Protobuf message to a string value, use either `str` or `format`.
259
256
  ```python
260
- yaml = str(request) # get the request as yaml
261
- yaml = format(request) # also get the request as yaml
262
- yaml = format(request, 'yaml') # yet another get the request as yaml
263
- json = format(request, 'json') # get the request as json
264
- json = format(request, 'jsonc') # get the request as json compact
265
- proto = format(request, 'protobuf') # get the request as a protobuf string
257
+ yaml = str(request) # get the request as yaml
258
+ yaml = format(request) # also get the request as yaml
259
+ yaml = format(request, "yaml") # yet another get the request as yaml
260
+ json = format(request, "json") # get the request as json
261
+ json = format(request, "jsonc") # get the request as json compact
262
+ proto = format(request, "protobuf") # get the request as a protobuf string
266
263
  ```
267
264
  ## Composite Composition
268
265
 
@@ -186,7 +186,7 @@ message contents. For example, the following examples obtain the same value
186
186
  from the RunFunctionRequest message:
187
187
  ```python
188
188
  region = request.observed.composite.resource.spec.region
189
- region = request['observed']['composite']['resource']['spec']['region']
189
+ region = request["observed"]["composite"]["resource"]["spec"]["region"]
190
190
  ```
191
191
  Getting values from free form map and list values will not throw
192
192
  errors for keys that do not exist, but will return an unknown placeholder
@@ -205,13 +205,15 @@ When setting fields, all intermediary unknown placeholders will automatically
205
205
  be created. For example, this will create all items needed to set the
206
206
  region on the desired resource:
207
207
  ```python
208
- response.desired.resources.vpc.resource.spec.forProvider.region = 'us-east-1'
208
+ response.desired.resources.vpc.resource.spec.forProvider.region = "us-east-1"
209
209
  ```
210
210
  Calling a message or map will clear it and will set any provided key word
211
211
  arguments. For example, this will either create or clear the resource
212
212
  and then set its apiVersion and kind:
213
213
  ```python
214
- response.desired.resources.vpc.resource(kind='VPC', apiVersion='ec2.aws.crossplane.io/v1beta1')
214
+ response.desired.resources.vpc.resource(
215
+ kind="VPC", apiVersion="ec2.aws.crossplane.io/v1beta1"
216
+ )
215
217
  ```
216
218
  The following functions are provided to create Protobuf structures:
217
219
  | Function | Description |
@@ -230,12 +232,12 @@ The following items are supported in all the Protobuf Message wrapper classes: `
230
232
 
231
233
  To convert a Protobuf message to a string value, use either `str` or `format`.
232
234
  ```python
233
- yaml = str(request) # get the request as yaml
234
- yaml = format(request) # also get the request as yaml
235
- yaml = format(request, 'yaml') # yet another get the request as yaml
236
- json = format(request, 'json') # get the request as json
237
- json = format(request, 'jsonc') # get the request as json compact
238
- proto = format(request, 'protobuf') # get the request as a protobuf string
235
+ yaml = str(request) # get the request as yaml
236
+ yaml = format(request) # also get the request as yaml
237
+ yaml = format(request, "yaml") # yet another get the request as yaml
238
+ json = format(request, "json") # get the request as json
239
+ json = format(request, "jsonc") # get the request as json compact
240
+ proto = format(request, "protobuf") # get the request as a protobuf string
239
241
  ```
240
242
  ## Composite Composition
241
243
 
@@ -0,0 +1,65 @@
1
+ [project]
2
+ name = "crossplane-function-pythonic"
3
+ version = "0.7.0"
4
+ description = "A Python centric Crossplane Function"
5
+ license = "Apache-2.0"
6
+ readme = "README.md"
7
+ requires-python = ">=3.15,<3.16"
8
+ dependencies = [
9
+ "grpcio==1.85.1",
10
+ "inflect==7.6.0",
11
+ "kr8s==0.20.15",
12
+ "protobuf==7.36.2",
13
+ "pyyaml==6.0.3",
14
+ ]
15
+
16
+ [[project.authors]]
17
+ name = "Patrick J McNerthney"
18
+ email = "pat@mcnerthney.com"
19
+
20
+ [project.optional-dependencies]
21
+ packages = ["kopf==1.45.2"]
22
+ pip-install = ["pip==26.2.2"]
23
+
24
+ [project.urls]
25
+ Documentation = "https://github.com/crossplane-contrib/function-pythonic#readme"
26
+ Issues = "https://github.com/crossplane-contrib/function-pythonic/issues"
27
+ Source = "https://github.com/crossplane-contrib/function-pythonic"
28
+
29
+ [project.scripts]
30
+ function-pythonic = "crossplane.pythonic.main:main"
31
+
32
+ [build-system]
33
+ requires = ["uv_build>=0.12.23,<0.13.0"]
34
+ build-backend = "uv_build"
35
+
36
+ [dependency-groups]
37
+ dev = [
38
+ "grpcio-tools>=1.85.0",
39
+ "kopf==1.45.2",
40
+ "mypy-protobuf>=5.1.0",
41
+ "poethepoet>=0.48.0",
42
+ "pip==26.2.2",
43
+ "pytest>=9.1.1",
44
+ "pytest-asyncio>=1.4.0",
45
+ "pytest-cov>=7.1.0",
46
+ "ruff>=0.17.0",
47
+ "ty>=0.0.92",
48
+ "types-grpcio>=1.84.0.20261021",
49
+ ]
50
+
51
+ [tool.uv.build-backend]
52
+ module-name = "crossplane.pythonic"
53
+
54
+ [tool.ty.src]
55
+ exclude = ["tests/test_protobuf_messages.py"]
56
+
57
+ [tool.poe.tasks]
58
+ generate-protoc = "python -m grpc_tools.protoc --proto_path=src --python_out=src --mypy_out=src --grpc_python_out=src --mypy_grpc_out=src crossplane/pythonic/proto/v1/run_function.proto"
59
+ test = "pytest tests -x --verbose --verbose --cov --cov-report=term --cov-report=html:reports"
60
+ test-protobuf = "pytest tests/test_protobuf_*.py -x --verbose --verbose --cov --cov-report=term --cov-report=html:reports"
61
+ test-ci = "pytest tests --verbose --verbose --junitxml=reports/pytest-junit.xml --cov --cov-report=term --cov-report=xml:reports/pytest-coverage.xml"
62
+
63
+ [tool.pytest.ini_options]
64
+ pythonpath = ["."]
65
+ filterwarnings = ["ignore:'asyncio.iscoroutinefunction' is deprecated:DeprecationWarning:kr8s._vendored.asyncache"]
@@ -0,0 +1,66 @@
1
+ [project]
2
+ name = "crossplane-function-pythonic"
3
+ version = "0.7.0"
4
+ description = "A Python centric Crossplane Function"
5
+ license = "Apache-2.0"
6
+ readme = "README.md"
7
+ authors = [
8
+ { name = "Patrick J McNerthney", email = "pat@mcnerthney.com" }
9
+ ]
10
+ requires-python = ">=3.15,<3.16"
11
+ dependencies = [
12
+ "grpcio==1.85.1",
13
+ "inflect==7.6.0",
14
+ "kr8s==0.20.15",
15
+ "protobuf==7.36.2",
16
+ "pyyaml==6.0.3",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ packages = ["kopf==1.45.2"]
21
+ pip-install = ["pip==26.2.2"]
22
+
23
+ [project.urls]
24
+ Documentation = "https://github.com/crossplane-contrib/function-pythonic#readme"
25
+ Issues = "https://github.com/crossplane-contrib/function-pythonic/issues"
26
+ Source = "https://github.com/crossplane-contrib/function-pythonic"
27
+
28
+ [project.scripts]
29
+ function-pythonic = "crossplane.pythonic.main:main"
30
+
31
+ [build-system]
32
+ requires = ["uv_build>=0.12.23,<0.13.0"]
33
+ build-backend = "uv_build"
34
+
35
+ [dependency-groups]
36
+ dev = [
37
+ "grpcio-tools>=1.85.0",
38
+ "kopf==1.45.2",
39
+ "mypy-protobuf>=5.1.0",
40
+ "poethepoet>=0.48.0",
41
+ "pip==26.2.2",
42
+ "pytest>=9.1.1",
43
+ "pytest-asyncio>=1.4.0",
44
+ "pytest-cov>=7.1.0",
45
+ "ruff>=0.17.0",
46
+ "ty>=0.0.92",
47
+ "types-grpcio>=1.84.0.20261021",
48
+ ]
49
+
50
+ [tool.uv.build-backend]
51
+ module-name = "crossplane.pythonic"
52
+
53
+ [tool.ty.src]
54
+ exclude = ["tests/test_protobuf_messages.py"]
55
+
56
+ [tool.poe.tasks]
57
+ generate-protoc = "python -m grpc_tools.protoc --proto_path=src --python_out=src --mypy_out=src --grpc_python_out=src --mypy_grpc_out=src crossplane/pythonic/proto/v1/run_function.proto"
58
+ test = "pytest tests -x --verbose --verbose --cov --cov-report=term --cov-report=html:reports"
59
+ test-protobuf = "pytest tests/test_protobuf_*.py -x --verbose --verbose --cov --cov-report=term --cov-report=html:reports"
60
+ test-ci = "pytest tests --verbose --verbose --junitxml=reports/pytest-junit.xml --cov --cov-report=term --cov-report=xml:reports/pytest-coverage.xml"
61
+
62
+ [tool.pytest.ini_options]
63
+ pythonpath = ["."]
64
+ filterwarnings = [
65
+ "ignore:'asyncio.iscoroutinefunction' is deprecated:DeprecationWarning:kr8s._vendored.asyncache",
66
+ ]
@@ -0,0 +1,6 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
3
+ try:
4
+ __version__ = version("crossplane-function-pythonic")
5
+ except PackageNotFoundError:
6
+ __version__ = "0.0.0"
@@ -0,0 +1,25 @@
1
+ from .composite import BaseComposite
2
+ from .protobuf import (
3
+ B64Decode,
4
+ B64Encode,
5
+ Json,
6
+ List,
7
+ Map,
8
+ Unknown,
9
+ Yaml,
10
+ YamlAll,
11
+ append,
12
+ )
13
+
14
+ __all__ = [
15
+ "B64Decode",
16
+ "B64Encode",
17
+ "BaseComposite",
18
+ "Json",
19
+ "List",
20
+ "Map",
21
+ "Unknown",
22
+ "Yaml",
23
+ "YamlAll",
24
+ "append",
25
+ ]
@@ -1,14 +1,24 @@
1
+ from typing import TYPE_CHECKING
1
2
 
2
- def resource_ready(resource):
3
+ if TYPE_CHECKING:
4
+ from . import (
5
+ composite,
6
+ protobuf,
7
+ )
8
+
9
+
10
+ def resource_ready(resource: composite.Resource) -> protobuf.Value | None:
3
11
  if not resource.observed:
4
12
  return None
5
- return _checks.get((resource.observed.apiVersion, resource.observed.kind), _check_default).ready(resource)
13
+ return _checks.get(
14
+ (resource.observed.apiVersion, resource.observed.kind), _check_default
15
+ ).ready(resource)
6
16
 
7
17
 
8
18
  class ReadyCondition:
9
- def ready(self, resource):
19
+ def ready(self, resource: composite.Resource) -> protobuf.Value | None:
10
20
  ready = resource.conditions.Ready
11
- if not ready._find_condition():
21
+ if not ready._get_condition():
12
22
  return None
13
23
  if ready.status:
14
24
  return resource.observed.metadata.name
@@ -16,36 +26,43 @@ class ReadyCondition:
16
26
  return resource.status.notReadyCondition[ready.reason]
17
27
  return resource.status.notReadyCondition
18
28
 
19
- _checks = {}
29
+
30
+ _checks: dict[tuple, Check] = {}
20
31
  _check_default = ReadyCondition()
21
32
 
33
+
22
34
  class Check:
23
35
  @classmethod
24
36
  def __init_subclass__(cls, **kwargs):
25
37
  super().__init_subclass__(**kwargs)
26
- if hasattr(cls, 'apiVersion'):
38
+ if hasattr(cls, "apiVersion"):
27
39
  _checks[(cls.apiVersion, cls.__name__)] = cls()
28
40
 
29
- def ready(self, resource):
41
+ def ready(self, resource: composite.Resource) -> protobuf.Value | None:
30
42
  raise NotImplementedError()
31
43
 
44
+
32
45
  class AlwaysReady(Check):
33
- def ready(self, resource):
46
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
34
47
  return resource.observed.metadata.name
35
48
 
36
49
 
37
50
  class ClusterRole(AlwaysReady):
38
- apiVersion = 'rbac.authorization.k8s.io/v1'
51
+ apiVersion = "rbac.authorization.k8s.io/v1"
52
+
39
53
 
40
54
  class ClusterRoleBinding(AlwaysReady):
41
- apiVersion = 'rbac.authorization.k8s.io/v1'
55
+ apiVersion = "rbac.authorization.k8s.io/v1"
56
+
42
57
 
43
58
  class ConfigMap(AlwaysReady):
44
- apiVersion = 'v1'
59
+ apiVersion = "v1"
60
+
45
61
 
46
62
  class CronJob(Check):
47
- apiVersion = 'batch/v1'
48
- def ready(self, resource):
63
+ apiVersion = "batch/v1"
64
+
65
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
49
66
  if resource.observed.spec.suspend and len(resource.observed.spec.suspend):
50
67
  return resource.observed.metadata.name
51
68
  if not resource.status.lastScheduleTime:
@@ -54,17 +71,21 @@ class CronJob(Check):
54
71
  return resource.observed.metadata.name
55
72
  if not resource.status.lastSuccessfulTime:
56
73
  return resource.status.lastSuccessfulTime
57
- if str(resource.status.lastSuccessfulTime) < str(resource.status.lastScheduleTime):
74
+ if str(resource.status.lastSuccessfulTime) < str(
75
+ resource.status.lastScheduleTime
76
+ ):
58
77
  return resource.status.successfulBeforeSchedule
59
78
  return resource.observed.metadata.name
60
79
 
80
+
61
81
  class DaemonSet(Check):
62
- apiVersion = 'apps/v1'
63
- def ready(self, resource):
82
+ apiVersion = "apps/v1"
83
+
84
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
64
85
  scheduled = resource.status.desiredNumberScheduled
65
86
  if not scheduled:
66
87
  return scheduled
67
- for field in ('numberReady', 'updatedNumberScheduled', 'numberAvailable'):
88
+ for field in ("numberReady", "updatedNumberScheduled", "numberAvailable"):
68
89
  value = resource.status[field]
69
90
  if not value:
70
91
  return value
@@ -72,16 +93,18 @@ class DaemonSet(Check):
72
93
  return resource.status[f"{field}NotScheduled"]
73
94
  return resource.observed.metadata.name
74
95
 
96
+
75
97
  class Deployment(Check):
76
- apiVersion = 'apps/v1'
77
- def ready(self, resource):
98
+ apiVersion = "apps/v1"
99
+
100
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
78
101
  replicas = resource.observed.spec.replicas or 1
79
- for field in ('updatedReplicas', 'availableReplicas'):
102
+ for field in ("updatedReplicas", "availableReplicas"):
80
103
  value = resource.status[field]
81
104
  if not value:
82
105
  return value
83
106
  if replicas != value:
84
- return resource.status[F"{field}NotReplicas"]
107
+ return resource.status[f"{field}NotReplicas"]
85
108
  available = resource.conditions.Available
86
109
  if not available:
87
110
  return resource.status.notAvailable
@@ -91,28 +114,39 @@ class Deployment(Check):
91
114
  return resource.status.notAvailable
92
115
  return resource.observed.metadata.name
93
116
 
117
+
94
118
  class HorizontalPodAutoscaler(Check):
95
- apiVersion = 'autoscaling/v2'
96
- def ready(self, resource):
97
- for type in ('FailedGetScale', 'FailedUpdateScale', 'FailedGetResourceMetric', 'InvalidSelector'):
119
+ apiVersion = "autoscaling/v2"
120
+
121
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
122
+ for type in (
123
+ "FailedGetScale",
124
+ "FailedUpdateScale",
125
+ "FailedGetResourceMetric",
126
+ "InvalidSelector",
127
+ ):
98
128
  if resource.conditions[type].status:
99
129
  return resource.status[f"is{type}"]
100
- for type in ('ScalingActive', 'ScalingLimited'):
130
+ for type in ("ScalingActive", "ScalingLimited"):
101
131
  if resource.conditions[type].status:
102
132
  return resource.observed.metadata.name
103
- return resource.status.notScalingActiveOrLimiited
133
+ return resource.status.notScalingActiveOrLimited
134
+
104
135
 
105
136
  class Ingress(Check):
106
- apiVersion = 'networking.k8s.io/v1'
107
- def ready(self, resource):
137
+ apiVersion = "networking.k8s.io/v1"
138
+
139
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
108
140
  if not len(resource.status.loadBalancer.ingress):
109
- return resource.status.noLoadBalanceIngresses
141
+ return resource.status.noLoadBalancerIngresses
110
142
  return resource.observed.metadata.name
111
143
 
144
+
112
145
  class Job(Check):
113
- apiVersion = 'batch/v1'
114
- def ready(self, resource):
115
- for type in ('Failed', 'Suspended'):
146
+ apiVersion = "batch/v1"
147
+
148
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
149
+ for type in ("Failed", "Suspended"):
116
150
  if resource.conditions[type].status:
117
151
  return resource.status[f"is{type}"]
118
152
  complete = resource.conditions.Complete
@@ -124,71 +158,94 @@ class Job(Check):
124
158
  return resource.status.notComplete
125
159
  return resource.observed.metadata.name
126
160
 
161
+
127
162
  class Namespace(AlwaysReady):
128
- apiVersion = 'v1'
163
+ apiVersion = "v1"
164
+
129
165
 
130
166
  class PersistentVolumeClaim(Check):
131
- apiVersion = 'v1'
132
- def ready(self, resource):
133
- if resource.status.phase != 'Bound':
167
+ apiVersion = "v1"
168
+
169
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
170
+ if resource.status.phase != "Bound":
134
171
  return resource.status.phaseNotBound
135
172
  return resource.observed.metadata.name
136
173
 
174
+
137
175
  class Pod(Check):
138
- apiVersion = 'v1'
139
- def ready(self, resource):
140
- if resource.status.phase == 'Succeeded':
176
+ apiVersion = "v1"
177
+
178
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
179
+ if resource.status.phase == "Succeeded":
180
+ return resource.observed.metadata.name
181
+ if (
182
+ resource.status.phase == "Running"
183
+ and resource.observed.spec.restartPolicy == "Always"
184
+ and resource.conditions.Ready.status
185
+ ):
141
186
  return resource.observed.metadata.name
142
- if resource.status.phase == 'Running':
143
- if resource.observed.spec.restartPolicy == 'Always':
144
- if resource.conditions.Ready.status:
145
- return resource.observed.metadata.name
146
187
  return resource.status.notSucceededOrRunning
147
188
 
189
+
148
190
  class ReplicaSet(Check):
149
- apiVersion = 'v1'
150
- def ready(self, resource):
151
- if int(resource.status.observedGeneration) < int(resource.observed.metadata.generation):
191
+ apiVersion = "apps/v1"
192
+
193
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
194
+ if int(resource.status.observedGeneration) < int(
195
+ resource.observed.metadata.generation
196
+ ):
152
197
  return resource.status.priorObservedGeneration
153
198
  if resource.conditions.ReplicaFailure.status:
154
199
  if resource.conditions.ReplicaFailure.reason:
155
- return resource.status.isReplicaFailure[resource.conditions.ReplicaFailure.reason]
200
+ return resource.status.isReplicaFailure[
201
+ resource.conditions.ReplicaFailure.reason
202
+ ]
156
203
  return resource.status.isReplicaFailure
157
- if int(resource.status.availableReplicas) < int(resource.observed.spec.replicas or 1):
158
- return resource.status.tooFewavailableReplicas
204
+ if int(resource.status.availableReplicas) < int(
205
+ resource.observed.spec.replicas or 1
206
+ ):
207
+ return resource.status.tooFewAvailableReplicas
159
208
  return resource.observed.metadata.name
160
209
 
210
+
161
211
  class Role(AlwaysReady):
162
- apiVersion = 'rbac.authorization.k8s.io/v1'
212
+ apiVersion = "rbac.authorization.k8s.io/v1"
213
+
163
214
 
164
215
  class RoleBinding(AlwaysReady):
165
- apiVersion = 'rbac.authorization.k8s.io/v1'
216
+ apiVersion = "rbac.authorization.k8s.io/v1"
217
+
166
218
 
167
219
  class Secret(AlwaysReady):
168
- apiVersion = 'v1'
220
+ apiVersion = "v1"
221
+
169
222
 
170
223
  class Service(Check):
171
- apiVersion = 'v1'
172
- def ready(self, resource):
173
- if resource.observed.spec.type != 'LoadBalancer':
224
+ apiVersion = "v1"
225
+
226
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
227
+ if resource.observed.spec.type != "LoadBalancer":
174
228
  return resource.observed.metadata.name
175
229
  if not len(resource.status.loadBalancer.ingress):
176
230
  return resource.status.noLoadBalancerIngresses
177
231
  return resource.observed.metadata.name
178
232
 
233
+
179
234
  class ServiceAccount(AlwaysReady):
180
- apiVersion = 'v1'
235
+ apiVersion = "v1"
236
+
181
237
 
182
238
  class StatefulSet(Check):
183
- apiVersion = 'apps/v1'
184
- def ready(self, resource):
239
+ apiVersion = "apps/v1"
240
+
241
+ def ready(self, resource: composite.Resource) -> protobuf.Value:
185
242
  replicas = resource.observed.spec.replicas or 1
186
- for field in ('readyReplicas', 'currentReplicas'):
243
+ for field in ("readyReplicas", "currentReplicas"):
187
244
  value = resource.status[field]
188
245
  if not value:
189
246
  return value
190
247
  if replicas != value:
191
- return resource.status[F"{field}NotReplicas"]
248
+ return resource.status[f"{field}NotReplicas"]
192
249
  if resource.status.currentRevision != resource.status.updateRevision:
193
- return resource.status.currentRevisionNotUpdateReivsion
250
+ return resource.status.currentRevisionNotUpdateRevision
194
251
  return resource.observed.metadata.name