crossplane-function-pythonic 0.0.8__tar.gz → 0.0.9.post0__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 (94) hide show
  1. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/PKG-INFO +21 -18
  2. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/README.md +16 -16
  3. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/crossplane/pythonic/function.py +26 -15
  4. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/crossplane/pythonic/main.py +20 -15
  5. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/crossplane/pythonic/packages.py +3 -3
  6. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/crossplane/pythonic/protobuf.py +19 -29
  7. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/eks-cluster/functions.yaml +1 -1
  8. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/helm-copy-secret/functions.yaml +1 -1
  9. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/helm-copy-secret/vcluster.py +1 -1
  10. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/pyproject.toml +5 -2
  11. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/.gitignore +0 -0
  12. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/LICENSE +0 -0
  13. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/crossplane/pythonic/__init__.py +0 -0
  14. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/crossplane/pythonic/composite.py +9 -9
  15. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/.dev/functions.yaml +0 -0
  16. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/eks-cluster/composition-v2.yaml +0 -0
  17. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/eks-cluster/composition.yaml +0 -0
  18. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/eks-cluster/definition.yaml +0 -0
  19. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/eks-cluster/render-v2.sh +0 -0
  20. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/eks-cluster/render.sh +0 -0
  21. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/eks-cluster/xr.yaml +0 -0
  22. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/filing-system/README.md +0 -0
  23. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/filing-system/composition.yaml +0 -0
  24. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/filing-system/definition.yaml +0 -0
  25. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/filing-system/function.yaml +0 -0
  26. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/filing-system/kustomization.yaml +0 -0
  27. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/filing-system/runtime.yaml +0 -0
  28. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/filing-system/vcluster.py +0 -0
  29. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/filing-system/vcluster.yaml +0 -0
  30. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/conditions/composition.yaml +0 -0
  31. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/conditions/functions.yaml +0 -0
  32. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/conditions/render.sh +0 -0
  33. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/conditions/xr.yaml +0 -0
  34. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/conditions/xrd.yaml +0 -0
  35. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/context/composition.yaml +0 -0
  36. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/context/environmentConfigs.yaml +0 -0
  37. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/context/functions.yaml +0 -0
  38. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/context/render.sh +0 -0
  39. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/context/xr.yaml +0 -0
  40. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/context/xrd.yaml +0 -0
  41. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/extra-resources/composition.yaml +0 -0
  42. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/extra-resources/extraResources.yaml +0 -0
  43. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/extra-resources/functions.yaml +0 -0
  44. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/extra-resources/render.sh +0 -0
  45. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/extra-resources/xr.yaml +0 -0
  46. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/fromYaml/composition.yaml +0 -0
  47. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/fromYaml/functions.yaml +0 -0
  48. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/fromYaml/render.sh +0 -0
  49. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/fromYaml/xr.yaml +0 -0
  50. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getComposedResource/composition.yaml +0 -0
  51. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getComposedResource/functions.yaml +0 -0
  52. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getComposedResource/observed.yaml +0 -0
  53. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getComposedResource/render.sh +0 -0
  54. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getComposedResource/xr.yaml +0 -0
  55. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getCompositeResource/composition.yaml +0 -0
  56. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getCompositeResource/functions.yaml +0 -0
  57. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getCompositeResource/render.sh +0 -0
  58. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getCompositeResource/xr.yaml +0 -0
  59. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getResourceCondition/composition.yaml +0 -0
  60. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getResourceCondition/functions.yaml +0 -0
  61. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getResourceCondition/observed.yaml +0 -0
  62. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getResourceCondition/render.sh +0 -0
  63. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/getResourceCondition/xr.yaml +0 -0
  64. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/include/composition.yaml +0 -0
  65. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/include/functions.yaml +0 -0
  66. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/include/render.sh +0 -0
  67. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/include/xr.yaml +0 -0
  68. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/toYaml/composition.yaml +0 -0
  69. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/toYaml/functions.yaml +0 -0
  70. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/toYaml/render.sh +0 -0
  71. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/functions/toYaml/xr.yaml +0 -0
  72. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/inline/composition.yaml +0 -0
  73. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/inline/functions.yaml +0 -0
  74. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/inline/render.sh +0 -0
  75. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/inline/xr.yaml +0 -0
  76. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/recursive/composition-real.yaml +0 -0
  77. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/recursive/composition-wrapper.yaml +0 -0
  78. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/recursive/functions.yaml +0 -0
  79. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/recursive/render.sh +0 -0
  80. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/function-go-templating/recursive/xr.yaml +0 -0
  81. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/get-started-app/composition.yaml +0 -0
  82. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/get-started-app/definition.yaml +0 -0
  83. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/get-started-app/functions.yaml +0 -0
  84. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/get-started-app/render.sh +0 -0
  85. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/get-started-app/xr.yaml +0 -0
  86. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/helm-copy-secret/composition.yaml +0 -0
  87. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/helm-copy-secret/kustomization.yaml +0 -0
  88. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/helm-copy-secret/render.sh +0 -0
  89. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/helm-copy-secret/run-function.sh +0 -0
  90. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/helm-copy-secret/xr.yaml +0 -0
  91. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/helm-copy-secret/xrd.yaml +0 -0
  92. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/single-purpose/functions.yaml +0 -0
  93. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/single-purpose/render.sh +0 -0
  94. {crossplane_function_pythonic-0.0.8 → crossplane_function_pythonic-0.0.9.post0}/examples/single-purpose/xr.yaml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crossplane-function-pythonic
3
- Version: 0.0.8
3
+ Version: 0.0.9.post0
4
4
  Summary: A Python centric Crossplane Function
5
5
  Project-URL: Documentation, https://github.com/fortra/function-pythonic#readme
6
6
  Project-URL: Issues, https://github.com/fortra/function-pythonic/issues
@@ -15,8 +15,11 @@ Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
16
  Requires-Python: <3.14,>=3.11
17
17
  Requires-Dist: crossplane-function-sdk-python==0.9.0
18
- Requires-Dist: kopf==1.38.0
19
18
  Requires-Dist: pyyaml==6.0.2
19
+ Provides-Extra: packages
20
+ Requires-Dist: kopf==1.38.0; extra == 'packages'
21
+ Provides-Extra: pip-install
22
+ Requires-Dist: pip==25.2; extra == 'pip-install'
20
23
  Description-Content-Type: text/markdown
21
24
 
22
25
  # function-pythonic
@@ -49,7 +52,7 @@ spec:
49
52
  apiVersion: pythonic.fn.crossplane.io/v1beta1
50
53
  kind: Composite
51
54
  composite: |
52
- class Composite(BaseComposite):
55
+ class VpcComposite(BaseComposite):
53
56
  def compose(self):
54
57
  vpc = self.resources.vpc('ec2.aws.crossplane.io/v1beta1', 'VPC')
55
58
  vpc.spec.forProvider.region = self.spec.region
@@ -78,7 +81,7 @@ kind: Function
78
81
  metadata:
79
82
  name: function-pythonic
80
83
  spec:
81
- package: ghcr.io/fortra/function-pythonic:v0.0.7
84
+ package: ghcr.io/fortra/function-pythonic:v0.0.9
82
85
  ```
83
86
  ## Composed Resource Dependencies
84
87
 
@@ -179,13 +182,13 @@ proto = format(request, 'protobuf') # get the request as a protobuf string
179
182
  Composite composition is performed from a Composite orientation. A `BaseComposite` class
180
183
  is subclassed and the `compose` method is implemented.
181
184
  ```python
182
- class Composite(BaseComposite):
185
+ class MyComposite(BaseComposite):
183
186
  def compose(self):
184
187
  # Compose the Composite
185
188
  ```
186
189
  The compose method can also declare itself as performing async io:
187
190
  ```python
188
- class Composite(BaseComposite):
191
+ class MyAsyncComposite(BaseComposite):
189
192
  async def compose(self):
190
193
  # Compose the Composite using async io when needed
191
194
  ```
@@ -287,7 +290,7 @@ Each resource in the list is the following RequiredResource class:
287
290
 
288
291
  ### Conditions
289
292
 
290
- The `BaseCompsite.conditions`, `Resource.conditions`, and `RequiredResource.conditions` fields
293
+ The `BaseComposite.conditions`, `Resource.conditions`, and `RequiredResource.conditions` fields
291
294
  are maps of that entity's status conditions array, with the map key being the condition type.
292
295
  The fields are read only for `Resource.conditions` and `RequiredResource.conditions`.
293
296
 
@@ -328,7 +331,7 @@ metadata:
328
331
  name: composite-example
329
332
  spec:
330
333
  composite: |
331
- class Composite(BaseComposite):
334
+ class HelloComposite(BaseComposite):
332
335
  def compose(self):
333
336
  self.status.composite = 'Hello, World!'
334
337
  ```
@@ -370,7 +373,7 @@ spec:
370
373
  apiVersion: pythonic.fn.fortra.com/v1alpha1
371
374
  kind: Composite
372
375
  composite: |
373
- class Composite(BaseComposite):
376
+ class GreetingComposite(BaseComposite):
374
377
  def compose(self):
375
378
  self.status.greeting = f"Hello, {self.spec.who}!"
376
379
  ```
@@ -383,7 +386,7 @@ metadata:
383
386
  annotations:
384
387
  render.crossplane.io/runtime: Development
385
388
  spec:
386
- package: ghcr.io/fortra/function-pythonic:v0.0.7
389
+ package: ghcr.io/fortra/function-pythonic:v0.0.9
387
390
  ```
388
391
  In one terminal session, run function-pythonic:
389
392
  ```shell
@@ -443,7 +446,7 @@ Then, in your Composition:
443
446
  kind: Composite
444
447
  composite: |
445
448
  from example.pythonic import features
446
- class Composite(BaseComposite):
449
+ class FetureComposite(BaseComposite):
447
450
  def compose(self):
448
451
  anything = features.anything()
449
452
  ...
@@ -485,7 +488,7 @@ kind: Function
485
488
  metadata:
486
489
  name: function-pythonic
487
490
  spec:
488
- package: ghcr.io/fortra/function-pythonic:v0.0.7
491
+ package: ghcr.io/fortra/function-pythonic:v0.0.9
489
492
  runtimeConfigRef:
490
493
  name: function-pythonic
491
494
  ---
@@ -514,12 +517,6 @@ kind: ClusterRole
514
517
  metadata:
515
518
  name: function-pythonic
516
519
  rules:
517
- - apiGroups:
518
- - ''
519
- resources:
520
- - events
521
- verbs:
522
- - create
523
520
  - apiGroups:
524
521
  - ''
525
522
  resources:
@@ -528,6 +525,12 @@ rules:
528
525
  - list
529
526
  - watch
530
527
  - patch
528
+ - apiGroups:
529
+ - ''
530
+ resources:
531
+ - events
532
+ verbs:
533
+ - create
531
534
  ---
532
535
  apiVersion: rbac.authorization.k8s.io/v1
533
536
  kind: ClusterRoleBinding
@@ -28,7 +28,7 @@ spec:
28
28
  apiVersion: pythonic.fn.crossplane.io/v1beta1
29
29
  kind: Composite
30
30
  composite: |
31
- class Composite(BaseComposite):
31
+ class VpcComposite(BaseComposite):
32
32
  def compose(self):
33
33
  vpc = self.resources.vpc('ec2.aws.crossplane.io/v1beta1', 'VPC')
34
34
  vpc.spec.forProvider.region = self.spec.region
@@ -57,7 +57,7 @@ kind: Function
57
57
  metadata:
58
58
  name: function-pythonic
59
59
  spec:
60
- package: ghcr.io/fortra/function-pythonic:v0.0.7
60
+ package: ghcr.io/fortra/function-pythonic:v0.0.9
61
61
  ```
62
62
  ## Composed Resource Dependencies
63
63
 
@@ -158,13 +158,13 @@ proto = format(request, 'protobuf') # get the request as a protobuf string
158
158
  Composite composition is performed from a Composite orientation. A `BaseComposite` class
159
159
  is subclassed and the `compose` method is implemented.
160
160
  ```python
161
- class Composite(BaseComposite):
161
+ class MyComposite(BaseComposite):
162
162
  def compose(self):
163
163
  # Compose the Composite
164
164
  ```
165
165
  The compose method can also declare itself as performing async io:
166
166
  ```python
167
- class Composite(BaseComposite):
167
+ class MyAsyncComposite(BaseComposite):
168
168
  async def compose(self):
169
169
  # Compose the Composite using async io when needed
170
170
  ```
@@ -266,7 +266,7 @@ Each resource in the list is the following RequiredResource class:
266
266
 
267
267
  ### Conditions
268
268
 
269
- The `BaseCompsite.conditions`, `Resource.conditions`, and `RequiredResource.conditions` fields
269
+ The `BaseComposite.conditions`, `Resource.conditions`, and `RequiredResource.conditions` fields
270
270
  are maps of that entity's status conditions array, with the map key being the condition type.
271
271
  The fields are read only for `Resource.conditions` and `RequiredResource.conditions`.
272
272
 
@@ -307,7 +307,7 @@ metadata:
307
307
  name: composite-example
308
308
  spec:
309
309
  composite: |
310
- class Composite(BaseComposite):
310
+ class HelloComposite(BaseComposite):
311
311
  def compose(self):
312
312
  self.status.composite = 'Hello, World!'
313
313
  ```
@@ -349,7 +349,7 @@ spec:
349
349
  apiVersion: pythonic.fn.fortra.com/v1alpha1
350
350
  kind: Composite
351
351
  composite: |
352
- class Composite(BaseComposite):
352
+ class GreetingComposite(BaseComposite):
353
353
  def compose(self):
354
354
  self.status.greeting = f"Hello, {self.spec.who}!"
355
355
  ```
@@ -362,7 +362,7 @@ metadata:
362
362
  annotations:
363
363
  render.crossplane.io/runtime: Development
364
364
  spec:
365
- package: ghcr.io/fortra/function-pythonic:v0.0.7
365
+ package: ghcr.io/fortra/function-pythonic:v0.0.9
366
366
  ```
367
367
  In one terminal session, run function-pythonic:
368
368
  ```shell
@@ -422,7 +422,7 @@ Then, in your Composition:
422
422
  kind: Composite
423
423
  composite: |
424
424
  from example.pythonic import features
425
- class Composite(BaseComposite):
425
+ class FetureComposite(BaseComposite):
426
426
  def compose(self):
427
427
  anything = features.anything()
428
428
  ...
@@ -464,7 +464,7 @@ kind: Function
464
464
  metadata:
465
465
  name: function-pythonic
466
466
  spec:
467
- package: ghcr.io/fortra/function-pythonic:v0.0.7
467
+ package: ghcr.io/fortra/function-pythonic:v0.0.9
468
468
  runtimeConfigRef:
469
469
  name: function-pythonic
470
470
  ---
@@ -493,12 +493,6 @@ kind: ClusterRole
493
493
  metadata:
494
494
  name: function-pythonic
495
495
  rules:
496
- - apiGroups:
497
- - ''
498
- resources:
499
- - events
500
- verbs:
501
- - create
502
496
  - apiGroups:
503
497
  - ''
504
498
  resources:
@@ -507,6 +501,12 @@ rules:
507
501
  - list
508
502
  - watch
509
503
  - patch
504
+ - apiGroups:
505
+ - ''
506
+ resources:
507
+ - events
508
+ verbs:
509
+ - create
510
510
  ---
511
511
  apiVersion: rbac.authorization.k8s.io/v1
512
512
  kind: ClusterRoleBinding
@@ -87,7 +87,18 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
87
87
  logger.exception('Exec exception')
88
88
  crossplane.function.response.fatal(response, f"Exec exception: {e}")
89
89
  return response
90
- composite = ['<script>', 'Composite']
90
+ for field in dir(module):
91
+ value = getattr(module, field)
92
+ if inspect.isclass(value) and issubclass(value, BaseComposite):
93
+ if clazz:
94
+ logger.error('Composite script has multiple BaseComposite classes')
95
+ crossplane.function.response.fatal(response, 'Composite script has multiple BaseComposite classes')
96
+ return response
97
+ clazz = value
98
+ if not clazz:
99
+ logger.error('Composite script does not have have a BaseComposite class')
100
+ crossplane.function.response.fatal(response, 'Composite script does have have a BaseComposite class')
101
+ return response
91
102
  else:
92
103
  composite = composite.rsplit('.', 1)
93
104
  if len(composite) == 1:
@@ -100,20 +111,20 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
100
111
  logger.error(str(e))
101
112
  crossplane.function.response.fatal(response, f"Import module exception: {e}")
102
113
  return response
103
- clazz = getattr(module, composite[1], None)
104
- if not clazz:
105
- logger.error(f"{composite[0]} did not define: {composite[1]}")
106
- crossplane.function.response.fatal(response, f"{composite[0]} did not define: {composite[1]}")
107
- return response
108
- composite = '.'.join(composite)
109
- if not inspect.isclass(clazz):
110
- logger.error(f"{composite} is not a class")
111
- crossplane.function.response.fatal(response, f"{composite} is not a class")
112
- return response
113
- if not issubclass(clazz, BaseComposite):
114
- logger.error(f"{composite} is not a subclass of BaseComposite")
115
- crossplane.function.response.fatal(response, f"{composite} is not a subclass of BaseComposite")
116
- return response
114
+ clazz = getattr(module, composite[1], None)
115
+ if not clazz:
116
+ logger.error(f"{composite[0]} did not define: {composite[1]}")
117
+ crossplane.function.response.fatal(response, f"{composite[0]} did not define: {composite[1]}")
118
+ return response
119
+ composite = '.'.join(composite)
120
+ if not inspect.isclass(clazz):
121
+ logger.error(f"{composite} is not a class")
122
+ crossplane.function.response.fatal(response, f"{composite} is not a class")
123
+ return response
124
+ if not issubclass(clazz, BaseComposite):
125
+ logger.error(f"{composite} is not a subclass of BaseComposite")
126
+ crossplane.function.response.fatal(response, f"{composite} is not a subclass of BaseComposite")
127
+ return response
117
128
  self.clazzes[composite] = clazz
118
129
 
119
130
  try:
@@ -13,7 +13,6 @@ import traceback
13
13
  import crossplane.function.logging
14
14
  import crossplane.function.proto.v1.run_function_pb2_grpc as grpcv1
15
15
  import grpc
16
- import pip._internal.cli.main
17
16
 
18
17
  from . import function
19
18
 
@@ -24,7 +23,7 @@ def main():
24
23
 
25
24
  class Main:
26
25
  async def main(self):
27
- parser = argparse.ArgumentParser('Forta Crossplane Function')
26
+ parser = argparse.ArgumentParser('Crossplane Function Pythonic')
28
27
  parser.add_argument(
29
28
  '--debug', '-d',
30
29
  action='store_true',
@@ -40,18 +39,18 @@ class Main:
40
39
  parser.add_argument(
41
40
  '--address',
42
41
  default='0.0.0.0:9443',
43
- help='Address at which to listen for gRPC connections, default: 0.0.0.0:9443',
42
+ help='Address to listen on for gRPC connections, default: 0.0.0.0:9443',
44
43
  )
45
44
  parser.add_argument(
46
45
  '--tls-certs-dir',
47
46
  default=os.getenv('TLS_SERVER_CERTS_DIR'),
48
47
  metavar='DIRECTORY',
49
- help='Serve using mTLS certificates.',
48
+ help='Serve using TLS certificates.',
50
49
  )
51
50
  parser.add_argument(
52
51
  '--insecure',
53
52
  action='store_true',
54
- help='Run without mTLS credentials. If you supply this flag --tls-certs-dir will be ignored.',
53
+ help='Run without mTLS credentials, --tls-certs-dir will be ignored.',
55
54
  )
56
55
  parser.add_argument(
57
56
  '--packages',
@@ -94,15 +93,23 @@ class Main:
94
93
  help='Allow oversized protobuf messages'
95
94
  )
96
95
  args = parser.parse_args()
96
+ if not args.tls_certs_dir and not args.insecure:
97
+ print('Either --tls-certs-dir or --insecure must be specified', file=sys.stderr)
98
+ sys.exit(1)
99
+
97
100
  self.configure_logging(args)
101
+ # enables read only volumes or mismatched uid volumes
102
+ sys.dont_write_bytecode = True
103
+ await self.run(args)
98
104
 
105
+ # Allow for independent running of function-pythonic
106
+ async def run(self, args):
99
107
  if args.pip_install:
100
- pip._internal.cli.main.main(['install', *shlex.split(args.pip_install)])
108
+ import pip._internal.cli.main
109
+ pip._internal.cli.main.main(['install', '--user', *shlex.split(args.pip_install)])
101
110
 
102
- # enables read only volumes or mismatched uid volumes
103
- sys.dont_write_bytecode = True
104
111
  for path in reversed(args.python_path):
105
- sys.path.insert(0, str(pathlib.Path(path).resolve()))
112
+ sys.path.insert(0, str(pathlib.Path(path).expanduser().resolve()))
106
113
 
107
114
  if args.allow_oversize_protos:
108
115
  from google.protobuf.internal import api_implementation
@@ -113,8 +120,10 @@ class Main:
113
120
  grpc_runner = function.FunctionRunner(args.debug)
114
121
  grpc_server = grpc.aio.server()
115
122
  grpcv1.add_FunctionRunnerServiceServicer_to_server(grpc_runner, grpc_server)
116
- if args.tls_certs_dir:
117
- certs = pathlib.Path(args.tls_certs_dir)
123
+ if args.insecure:
124
+ grpc_server.add_insecure_port(args.address)
125
+ else:
126
+ certs = pathlib.Path(args.tls_certs_dir).expanduser().resolve()
118
127
  grpc_server.add_secure_port(
119
128
  args.address,
120
129
  grpc.ssl_server_credentials(
@@ -126,10 +135,6 @@ class Main:
126
135
  require_client_auth=True,
127
136
  ),
128
137
  )
129
- else:
130
- if not args.insecure:
131
- raise ValueError('Either --tls-certs-dir or --insecure must be specified')
132
- grpc_server.add_insecure_port(args.address)
133
138
  await grpc_server.start()
134
139
 
135
140
  if args.packages:
@@ -14,7 +14,7 @@ PACKAGE_LABEL = {'function-pythonic.package': kopf.PRESENT}
14
14
  PACKAGES_DIR = None
15
15
 
16
16
 
17
- def operator(grpc_server, grpc_runner, packages_secrets, packages_namespace, packages_dir):
17
+ def operator(grpc_server, grpc_runner, packages_secrets, packages_namespaces, packages_dir):
18
18
  logging.getLogger('kopf.objects').setLevel(logging.INFO)
19
19
  global GRPC_SERVER, GRPC_RUNNER, PACKAGES_DIR
20
20
  GRPC_SERVER = grpc_server
@@ -28,8 +28,8 @@ def operator(grpc_server, grpc_runner, packages_secrets, packages_namespace, pac
28
28
  kopf.on.delete('', 'v1', 'secrets', labels=PACKAGE_LABEL)(delete)
29
29
  return kopf.operator(
30
30
  standalone=True,
31
- clusterwide=not packages_namespace,
32
- namespaces=packages_namespace,
31
+ clusterwide=not packages_namespaces,
32
+ namespaces=packages_namespaces,
33
33
  )
34
34
 
35
35
 
@@ -25,10 +25,16 @@ append = sys.maxsize
25
25
 
26
26
 
27
27
  def Map(**kwargs):
28
- return Values(None, None, None, Values.Type.MAP)(**kwargs)
28
+ map = Values(None, None, None, Values.Type.MAP)
29
+ for name, value in kwargs.items():
30
+ map[name] = value
31
+ return map
29
32
 
30
33
  def List(*args):
31
- return Values(None, None, None, Values.Type.LIST)(*args)
34
+ list = Values(None, None, None, Values.Type.LIST)
35
+ for ix, value in enumerate(args):
36
+ list[ix] = value
37
+ return list
32
38
 
33
39
  def Unknown():
34
40
  return Values(None, None, None, Values.Type.UNKNOWN)
@@ -683,36 +689,16 @@ class Values:
683
689
  def __call__(self, *args, **kwargs):
684
690
  if self._readOnly:
685
691
  raise ValueError(f"{self._readOnly} is read only")
692
+ self.__dict__['_values'] = None
693
+ self.__dict__['_type'] = self.Type.UNKNOWN
686
694
  self._cache.clear()
687
695
  self._unknowns.clear()
688
696
  if len(kwargs):
689
- if not self._isMap:
690
- if not self._isUnknown:
691
- raise ValueError('Cannot specify kwargs on lists')
692
- self.__dict__['_type'] = self.Type.MAP
693
697
  if len(args):
694
- raise ValueError('Connect specify args on maps')
695
- if self._values is None:
696
- if self._parent is None:
697
- self.__dict__['_values'] = google.protobuf.struct_pb2.Struct()
698
- else:
699
- self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
700
- self._values.Clear()
698
+ raise ValueError('Connect specify both kwargs and args')
701
699
  for key, value in kwargs.items():
702
700
  self[key] = value
703
701
  elif len(args):
704
- if not self._isList:
705
- if not self._isUnknown:
706
- raise ValueError('Cannot specify args on maps')
707
- self.__dict__['_type'] = self.Type.LIST
708
- if len(kwargs):
709
- raise ValueError('Connect specify kwargs on lists')
710
- if self._values is None:
711
- if self._parent is None:
712
- self.__dict__['_values'] = google.protobuf.struct_pb2.ListValue()
713
- else:
714
- self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
715
- self._values.Clear()
716
702
  for key in range(len(args)):
717
703
  self[key] = args[key]
718
704
  return self
@@ -767,17 +753,21 @@ class Values:
767
753
  values[key].number_value = value
768
754
  elif isinstance(value, dict):
769
755
  values[key].struct_value.Clear()
770
- self[key](**value)
756
+ for k, v in value.items():
757
+ self[key][k] = v
771
758
  elif isinstance(value, (list, tuple)):
772
759
  values[key].list_value.Clear()
773
- self[key](*value)
760
+ for ix, v in enumerate(value):
761
+ self[key][ix] = v
774
762
  elif isinstance(value, Values):
775
763
  if value._isMap:
776
764
  values[key].struct_value.Clear()
777
- self[key](**{k:v for k,v in value})
765
+ for k, v in value:
766
+ self[key][k] = v
778
767
  elif value._isList:
779
768
  values[key].list_value.Clear()
780
- self[key](*[v for v in value])
769
+ for ix, v in enumerate(value):
770
+ self[key][ix] = v
781
771
  else:
782
772
  self._unknowns[key] = value
783
773
  if self._isMap:
@@ -5,4 +5,4 @@ metadata:
5
5
  annotations:
6
6
  render.crossplane.io/runtime: Development
7
7
  spec:
8
- package: ghcr.io/fortra/function-pythonic:v0.0.7
8
+ package: ghcr.io/fortra/function-pythonic:v0.0.9
@@ -5,4 +5,4 @@ metadata:
5
5
  annotations:
6
6
  render.crossplane.io/runtime: Development
7
7
  spec:
8
- package: ghcr.io/fortra/function-pythonic:v0.0.7
8
+ package: ghcr.io/fortra/function-pythonic:v0.0.9
@@ -15,7 +15,7 @@ class VClusterComposite(BaseComposite):
15
15
  release.spec.forProvider.namespace = namespace
16
16
  release.spec.forProvider.values.controlPlane.proxy.extraSANs[0] = f'{name}.{namespace}'
17
17
  vcluster_secret = self.requireds.vcluster_secret('v1', 'Secret', namespace, secret_name)[0]
18
- argocd_secret = self.resources.argocd_secret('v1', 'Secret', 'default', secret_name)
18
+ argocd_secret = self.resources.argocd_secret('v1', 'Secret', 'argocd', secret_name)
19
19
  argocd_secret.metadata.labels['argocd.argoproj.io/secret-type'] = 'cluster'
20
20
  argocd_secret.type = 'Opaque'
21
21
  argocd_secret.data.name = B64Encode(name)
@@ -14,13 +14,16 @@ classifiers = [
14
14
  "Programming Language :: Python :: 3.13",
15
15
  ]
16
16
 
17
+ dynamic = ["version"]
18
+
17
19
  dependencies = [
18
20
  "crossplane-function-sdk-python==0.9.0",
19
- "kopf==1.38.0",
20
21
  "pyyaml==6.0.2",
21
22
  ]
22
23
 
23
- dynamic = ["version"]
24
+ [project.optional-dependencies]
25
+ packages = ["kopf==1.38.0"]
26
+ pip-install = ["pip==25.2"]
24
27
 
25
28
  [project.urls]
26
29
  Documentation = "https://github.com/fortra/function-pythonic#readme"
@@ -637,15 +637,6 @@ class Event:
637
637
  else:
638
638
  self._result.severity = fnv1.Severity.SEVERITY_NORMAL
639
639
 
640
- @property
641
- def message(self):
642
- return self._result.message if bool(self) else None
643
-
644
- @message.setter
645
- def message(self, message):
646
- if bool(self):
647
- self._result.message = message
648
-
649
640
  @property
650
641
  def reason(self):
651
642
  return self._result.reason if bool(self) else None
@@ -655,6 +646,15 @@ class Event:
655
646
  if bool(self):
656
647
  self._result.reason = reason
657
648
 
649
+ @property
650
+ def message(self):
651
+ return self._result.message if bool(self) else None
652
+
653
+ @message.setter
654
+ def message(self, message):
655
+ if bool(self):
656
+ self._result.message = message
657
+
658
658
  @property
659
659
  def claim(self):
660
660
  return bool(self) and self._result == fnv1.Target.TARGET_COMPOSITE_AND_CLAIM