crossplane-function-pythonic 0.4.2__py3-none-any.whl → 0.5.0__py3-none-any.whl

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.
@@ -1,2 +1,2 @@
1
1
  # This is set at build time, using "hatch version"
2
- __version__ = "0.4.2"
2
+ __version__ = "0.5.0"
@@ -89,7 +89,7 @@ class Ready:
89
89
 
90
90
 
91
91
  class BaseComposite:
92
- def __init__(self, crossplane_v1, request, single_use, logger):
92
+ def __init__(self, crossplane_v1, request, logger):
93
93
  self.crossplane_v1 = crossplane_v1
94
94
  self.request = protobuf.Message(None, 'request', request.DESCRIPTOR, request, 'Function Request')
95
95
  response = fnv1.RunFunctionResponse(
@@ -104,10 +104,7 @@ class BaseComposite:
104
104
  )
105
105
  self.response = protobuf.Message(None, 'response', response.DESCRIPTOR, response)
106
106
  self.logger = logger
107
- if single_use:
108
- self.parameters = self.request.observed.composite.resource.spec.parameters
109
- else:
110
- self.parameters = self.request.input.parameters
107
+ self.parameters = self.request.input.parameters
111
108
  self.credentials = Credentials(self.request)
112
109
  self.context = self.response.context
113
110
  self.environment = self.context['apiextensions.crossplane.io/environment']
@@ -48,15 +48,13 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
48
48
  name.append(composite['metadata']['name'])
49
49
  logger = logging.getLogger('.'.join(name))
50
50
 
51
- if composite['apiVersion'] in ('pythonic.crossplane.io/v1alpha1', 'pythonic.fortra.com/v1alpha1') and composite['kind'] == 'Composite':
52
- if 'spec' not in composite or 'composite' not in composite['spec']:
53
- return self.fatal(request, logger, 'Missing spec "composite"')
54
- single_use = True
55
- composite = composite['spec']['composite']
51
+ if 'inlined' in request.input and request.input['inlined']:
52
+ if 'spec' not in composite or request.input['inlined'] not in composite['spec']:
53
+ return self.fatal(request, logger, f"Missing inlined spec.{request.input['inlined']}")
54
+ composite = composite['spec'][request.input['inlined']]
56
55
  else:
57
56
  if 'composite' not in request.input:
58
57
  return self.fatal(request, logger, 'Missing input "composite"')
59
- single_use = False
60
58
  composite = request.input['composite']
61
59
 
62
60
  # Ideally this is something the Function API provides
@@ -100,7 +98,7 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
100
98
  self.clazzes[composite] = clazz
101
99
 
102
100
  try:
103
- composite = clazz(self.crossplane_v1, request, single_use, logger)
101
+ composite = clazz(self.crossplane_v1, request, logger)
104
102
  except Exception as e:
105
103
  return self.fatal(request, logger, 'Instantiate', e)
106
104
 
@@ -796,8 +796,10 @@ class Value:
796
796
  def __contains__(self, item):
797
797
  match self._kind:
798
798
  case 'struct_value':
799
+ item = self._validate_key(item)
799
800
  return item in self._value.struct_value.fields or item in self._unknowns
800
801
  case 'Struct':
802
+ item = self._validate_key(item)
801
803
  return item in self._value.fields or item in self._unknowns
802
804
  case 'list_value' | 'ListValue':
803
805
  for value in self:
@@ -3,6 +3,7 @@ import asyncio
3
3
  import importlib
4
4
  import inflect
5
5
  import inspect
6
+ import kr8s
6
7
  import logging
7
8
  import pathlib
8
9
  import sys
@@ -16,6 +17,9 @@ from . import (
16
17
  protobuf,
17
18
  )
18
19
 
20
+ INFLECT = inflect.engine()
21
+ INFLECT.classical(all=False)
22
+
19
23
 
20
24
  class Command(command.Command):
21
25
  name = 'render'
@@ -54,7 +58,7 @@ class Command(command.Command):
54
58
  action='append',
55
59
  default=[],
56
60
  metavar='KEY=VALUE',
57
- help='Context key-value pairs to pass to the Function pipeline. Values must be YAML/JSON. Keys take precedence over --context-files.',
61
+ help='Context key-value pairs to pass to the Function pipeline. Values must be sYAML/JSON. Keys take precedence over --context-files.',
58
62
  )
59
63
  parser.add_argument(
60
64
  '--observed-resources', '-o',
@@ -108,18 +112,18 @@ class Command(command.Command):
108
112
 
109
113
  async def run(self):
110
114
  if self.args.kube_context:
111
- kapi = Kr8sApi(self.args.kube_context, self.logger)
115
+ api = await kr8s.asyncio.api(context=self.args.kube_context)
112
116
  else:
113
- kapi = None
114
- composite = await self.setup_composite(kapi)
117
+ api = None
118
+ composite = await self.setup_composite(api)
115
119
  observed = self.collect_resources(self.args.observed_resources)
116
- composition = await self.setup_composition(composite, kapi)
120
+ composition = await self.setup_composition(composite, api)
117
121
  resources = self.collect_resources(self.args.required_resources)
118
122
  resources += self.collect_resources(self.args.secret_store)
119
123
  resources.sort(key=lambda resource: str(resource.metadata.name))
120
124
  context = self.setup_context()
121
125
 
122
- render = await self.render(composite, observed, composition, resources, context, kapi, self.args.render_unknowns, self.args.crossplane_v1)
126
+ render = await self.render(composite, observed, composition, resources, context, api, self.args.render_unknowns, self.args.crossplane_v1)
123
127
  if not render:
124
128
  sys.exit(1)
125
129
 
@@ -154,11 +158,11 @@ class Command(command.Command):
154
158
  print('---')
155
159
  print(str(render.context), end='')
156
160
 
157
- async def setup_composite(self, kapi=None):
161
+ async def setup_composite(self, api=None):
158
162
  # Obtain the Composite to render.
159
163
  if self.args.composite.is_file():
160
164
  return protobuf.Yaml(self.args.composite.read_text())
161
- if not kapi:
165
+ if not api:
162
166
  print(f"Composite \"{self.args.composite}\" is not a file", file=sys.stderr)
163
167
  sys.exit(1)
164
168
  composite = str(self.args.composite).split(':')
@@ -172,13 +176,13 @@ class Command(command.Command):
172
176
  else:
173
177
  print(f"Composite \"{self.args.composite}\" is not kind:apiVersion:namespace:name", file=sys.stderr)
174
178
  sys.exit(1)
175
- composite = await kapi.get(composite[0], composite[1], namespace, composite[-1])
179
+ composite = await self.kr8s_get(api, composite[0], composite[1], namespace, composite[-1])
176
180
  if not composite:
177
181
  print(f"Composite \"{self.args.composite}\" not found", file=sys.stderr)
178
182
  sys.exit(1)
179
183
  return composite
180
184
 
181
- async def setup_composition(self, composite, kapi=None):
185
+ async def setup_composition(self, composite, api=None):
182
186
  # Obtain the Composition that will be used to render the Composite.
183
187
  if not self.args.composition:
184
188
  return None
@@ -265,35 +269,35 @@ class Command(command.Command):
265
269
  context[key_value[0]] = protobuf.Yaml(key_value[1])
266
270
  return context
267
271
 
268
- async def render(self, composite, observed=[], composition=None, resources=[], context=None, kapi=None, render_unknowns=False, crossplane_v1=False):
272
+ async def render(self, composite, observed=[], composition=None, resources=[], context=None, api=None, render_unknowns=False, crossplane_v1=False, composite_observeds=True):
269
273
  # Create the request used when running Composition steps.
270
274
  request = protobuf.Message(None, 'request', fnv1.RunFunctionRequest.DESCRIPTOR, fnv1.RunFunctionRequest())
271
275
  if context is not None:
272
276
  request.context = context
273
277
 
274
278
  # Establish the request observed composite.
275
- await self.set_resource(composite, request.observed.composite, resources, kapi)
279
+ await self.set_resource(composite, request.observed.composite, resources, api)
276
280
  # Establish the manually configured observed resources.
277
281
  if observed:
278
282
  async with asyncio.TaskGroup() as group:
279
283
  for resource in observed:
280
284
  name = resource.metadata.annotations['crossplane.io/composition-resource-name']
281
285
  if name:
282
- group.create_task(self.set_resource(resource, request.observed.resources[name], resources, kapi))
283
- if kapi:
286
+ group.create_task(self.set_resource(resource, request.observed.resources[name], resources, api))
287
+ if api and composite_observeds:
284
288
  refs = composite.spec.crossplane.resourceRefs
285
289
  if not refs:
286
290
  refs = composite.spec.resourceRefs
287
291
  if refs:
288
292
  async with asyncio.TaskGroup() as group:
289
293
  for ref in refs:
290
- group.create_task(self.get_composite_ref(composite, ref, request, resources, kapi))
294
+ group.create_task(self.get_composite_ref(composite, ref, request, resources, api))
291
295
 
292
296
  if not composition:
293
297
  if composite.apiVersion in ('pythonic.crossplane.io/v1alpha1', 'pythonic.fortra.com/v1alpha1') and composite.kind == 'Composite':
294
298
  composition = self.create_composition(composite)
295
299
  else:
296
- if not kapi:
300
+ if not api:
297
301
  print('"composition" required', file=sys.stderr)
298
302
  return None
299
303
  revision = composite.spec.crossplane.compositionRevisionRef
@@ -303,7 +307,7 @@ class Command(command.Command):
303
307
  if not revision.name:
304
308
  print('Composite does not contain a CompositionRevision name', file=sys.stderr)
305
309
  return None
306
- composition = await kapi.get('CompositionRevision', 'apiextensions.crossplane.io/v1', None, revision.name)
310
+ composition = await self.kr8s_get(api, 'CompositionRevision', 'apiextensions.crossplane.io/v1', None, revision.name)
307
311
  if not composition:
308
312
  print(f"Compositioin \"{revision.name}\" not found", file=sys.stderr)
309
313
  return None
@@ -350,14 +354,14 @@ class Command(command.Command):
350
354
  # Fetch the step bootstrap resources specified.
351
355
  request.required_resources()
352
356
  for requirement in step.requirements.requiredResources:
353
- await self.set_required(requirement.requirementName, requirement, request.required_resources, resources, kapi)
357
+ await self.set_required(requirement.requirementName, requirement, request.required_resources, resources, api)
354
358
  # Fetch the required resources requested.
355
359
  for name, selector in requirements.resources:
356
- await self.set_required(name, selector, request.required_resources, resources, kapi)
360
+ await self.set_required(name, selector, request.required_resources, resources, api)
357
361
  # Fetch the now deprecated extra resources requested.
358
362
  request.extra_resources()
359
363
  for name, selector in requirements.extra_resources:
360
- await self.set_required(name, selector, request.extra_resources, resources, kapi)
364
+ await self.set_required(name, selector, request.extra_resources, resources, api)
361
365
  # Run the step using the function-pythonic function runner.
362
366
  response = protobuf.Message(
363
367
  None,
@@ -365,13 +369,15 @@ class Command(command.Command):
365
369
  fnv1.RunFunctionResponse.DESCRIPTOR,
366
370
  await runner.RunFunction(request._message, None),
367
371
  )
372
+ # Copy the response context to the request context to use in subsequent steps.
373
+ request.context = response.context
368
374
  # All done if there is a fatal result.
369
375
  for result in response.results:
370
376
  if result.severity == fnv1.Severity.SEVERITY_FATAL:
371
377
  fatal = True
372
378
  break
373
- # Copy the response context to the request context to use in subsequent steps.
374
- request.context = response.context
379
+ if fatal:
380
+ break
375
381
  # Exit this loop if the function has not requested additional extra/required resources.
376
382
  if response.requirements == requirements:
377
383
  break
@@ -479,21 +485,21 @@ class Command(command.Command):
479
485
  ),
480
486
  )
481
487
 
482
- async def get_composite_ref(self, composite, ref, request, resources, kapi):
488
+ async def get_composite_ref(self, composite, ref, request, resources, api):
483
489
  namespace = ref.namespace
484
490
  if not namespace:
485
491
  namespace = composite.metadata.namespace
486
492
  if not namespace:
487
493
  namespace = None
488
- source = await kapi.get(ref.kind, ref.apiVersion, namespace, ref.name)
494
+ source = await self.kr8s_get(api, ref.kind, ref.apiVersion, namespace, ref.name)
489
495
  if source:
490
496
  name = source.metadata.annotations['crossplane.io/composition-resource-name']
491
497
  if name:
492
498
  destination = request.observed.resources[name]
493
499
  if not destination: # Do not override manual observed
494
- await self.set_resource(source, destination, resources, kapi)
500
+ await self.set_resource(source, destination, resources, api)
495
501
 
496
- async def set_required(self, name, selector, requireds, resources=[], kapi=None):
502
+ async def set_required(self, name, selector, requireds, resources=[], api=None):
497
503
  if not name:
498
504
  return
499
505
  name = str(name)
@@ -505,23 +511,23 @@ class Command(command.Command):
505
511
  or (selector.namespace == resource.metadata.namespace)
506
512
  ):
507
513
  if selector.match_name == resource.metadata.name:
508
- await self.set_resource(resource, items[protobuf.append], resources, kapi)
514
+ await self.set_resource(resource, items[protobuf.append], resources, api)
509
515
  elif selector.match_labels.labels:
510
516
  for key, value in selector.match_labels.labels:
511
517
  if value != resource.metadata.labels[key]:
512
518
  break
513
519
  else:
514
- await self.set_resource(resource, items[protobuf.append], resources, kapi)
515
- if not len(items) and kapi:
520
+ await self.set_resource(resource, items[protobuf.append], resources, api)
521
+ if not len(items) and api:
516
522
  if len(selector.match_name):
517
- resource = await kapi.get(selector.kind, selector.api_version, selector.namespace, selector.match_name)
523
+ resource = await self.kr8s_get(api, selector.kind, selector.api_version, selector.namespace, selector.match_name)
518
524
  if resource:
519
- await self.set_resource(resource, items[protobuf.append], resources, kapi)
525
+ await self.set_resource(resource, items[protobuf.append], resources, api)
520
526
  elif len(selector.match_labels.labels):
521
- for resource in await kapi.list(selector.kind, selector.api_version, selector.namespace, selector.match_labels.labels):
522
- await self.set_resource(resource, items[protobuf.append], resources, kapi)
527
+ for resource in await self.kr8s_list(api, selector.kind, selector.api_version, selector.namespace, selector.match_labels.labels):
528
+ await self.set_resource(resource, items[protobuf.append], resources, api)
523
529
 
524
- async def set_resource(self, source, destination, resources=[], kapi=None):
530
+ async def set_resource(self, source, destination, resources=[], api=None):
525
531
  destination.resource = source
526
532
  namespace = source.spec.writeConnectionSecretToRef.namespace or source.metadata.namespace
527
533
  name = source.spec.writeConnectionSecretToRef.name
@@ -533,8 +539,8 @@ class Command(command.Command):
533
539
  connection = resource
534
540
  break
535
541
  else:
536
- if kapi:
537
- connection = await kapi.get('Secret', 'v1', namespace, name)
542
+ if api:
543
+ connection = await self.kr8s_get(api, 'Secret', 'v1', namespace, name)
538
544
  if connection:
539
545
  destination.connection_details()
540
546
  for key, value in connection.data:
@@ -572,69 +578,53 @@ class Command(command.Command):
572
578
  condition['message'] = message
573
579
  return condition
574
580
 
575
-
576
- class Kr8sApi:
577
- def __init__(self, context=None, logger=None):
578
- self.kr8s = importlib.import_module('kr8s')
579
- self.inflect = inflect.engine()
580
- self.inflect.classical(all=False)
581
- self.context = context
582
- self.logger = logger
583
- self._api = None
584
-
585
- async def api(self):
586
- if not self._api:
587
- self._api = await self.kr8s.asyncio.api(context=self.context)
588
- return self._api
589
-
590
- async def get(self, kind, apiVersion, namespace, name):
591
- clazz = self._get_clazz(kind, apiVersion, namespace)
581
+ async def kr8s_get(self, api, kind, apiVersion, namespace, name):
582
+ namespaced = namespace and len(namespace)
583
+ clazz = self.kr8s_class(kind, apiVersion, namespaced)
592
584
  try:
593
585
  fullName = [str(kind), str(apiVersion), str(name)]
594
- if namespace and len(namespace):
586
+ if namespaced:
595
587
  fullName.insert(-1, str(namespace))
596
- resource = await clazz.get(str(name), namespace=str(namespace), api=await self.api())
588
+ resource = await clazz.get(str(name), namespace=str(namespace), api=api)
597
589
  else:
598
- resource = await clazz.get(str(name), api=await self.api())
590
+ resource = await clazz.get(str(name), api=api)
599
591
  resource = protobuf.Value(None, None, resource.raw)
600
592
  result = 'found'
601
- except self.kr8s.NotFoundError:
593
+ except kr8s.NotFoundError:
602
594
  resource = None
603
595
  result = 'missing'
604
- if self.logger:
605
- self.logger.debug(f"Resource {result}: {':'.join(fullName)}")
596
+ self.logger.debug(f"Resource {result}: {':'.join(fullName)}")
606
597
  return resource
607
598
 
608
- async def list(self, kind, apiVersion, namespace, labelSelector):
609
- clazz = self._get_clazz(kind, apiVersion, namespace)
599
+ async def kr8s_list(self, api, kind, apiVersion, namespace, labelSelector):
600
+ namespaced = namespace and len(namespace)
601
+ clazz = self.kr8s_class(kind, apiVersion, namespaced)
610
602
  resources = [
611
603
  protobuf.Value(None, None, resource.raw)
612
604
  async for resource in clazz.list(
613
- namespace=str(namespace) if namespace and len(namespace) else None,
614
- label_selector={
615
- label: str(value)
616
- for label, value in labelSelector
617
- },
618
- api=await self.api()
605
+ namespace=str(namespace) if namespaced else None,
606
+ label_selector={
607
+ label: str(value)
608
+ for label, value in labelSelector
609
+ },
610
+ api=api,
619
611
  )
620
612
  ]
621
613
  if self.logger.isEnabledFor(logging.DEBUG):
622
614
  fullName = [str(kind), str(apiVersion)]
623
- if namespace and len(namespace):
615
+ if namespaced:
624
616
  fullName.append(str(namespace))
625
617
  fullName.append('&'.join(f"{label}={value}" for label, value in labelSelector))
626
618
  if resources:
627
- result = f"found {self.inflect.number_to_words(len(resources))}"
619
+ result = f"found {INFLECT.number_to_words(len(resources))}"
628
620
  else:
629
621
  result = 'missing'
630
622
  self.logger.debug(f"Resources {result}: {':'.join(fullName)}")
631
623
  return resources
632
624
 
633
- def _get_clazz(self, kind, apiVersion, namespaced):
634
- kind = str(kind)
635
- apiVersion = str(apiVersion)
625
+ def kr8s_class(self, kind, apiVersion, namespaced):
636
626
  try:
637
- return self.kr8s.asyncio.objects.get_class(kind, apiVersion, True)
627
+ return kr8s.asyncio.objects.get_class(str(kind), str(apiVersion), True)
638
628
  except KeyError:
639
629
  pass
640
- return self.kr8s.asyncio.objects.new_class(kind, apiVersion, True, bool(namespaced) and len(namespaced), plural=self.inflect.plural_noun(kind))
630
+ return kr8s.asyncio.objects.new_class(str(kind), str(apiVersion), True, namespaced, plural=INFLECT.plural_noun(str(kind)).lower())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crossplane-function-pythonic
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: A Python centric Crossplane Function
5
5
  Project-URL: Documentation, https://github.com/crossplane-contrib/function-pythonic#readme
6
6
  Project-URL: Issues, https://github.com/crossplane-contrib/function-pythonic/issues
@@ -199,6 +199,11 @@ subnet.spec.forProvider.cidrBlock = '10.0.0.0/20'
199
199
  ```
200
200
  Will generate the appropriate Crossplane Usage resource.
201
201
 
202
+ ## API Documentation
203
+
204
+ - [Composite API (`composite.py`)](https://github.com/crossplane-contrib/function-pythonic/blob/main/docs/composite.md)
205
+ - [Protobuf Wrapper API (`protobuf.py`)](https://github.com/crossplane-contrib/function-pythonic/blob/main/docs/protobuf.md)
206
+
202
207
  ## Pythonic access of Protobuf Messages
203
208
 
204
209
  All Protobuf messages are wrapped by a set of python classes which enable using
@@ -406,25 +411,78 @@ optionally to the Claim.
406
411
  | Result.message | String | Human-readable details about the result |
407
412
  | Result.claim | Boolean | Also apply the result to the claim |
408
413
 
409
- ## Single use Composites
414
+ ## Inlined Composites
410
415
 
411
416
  Tired of creating a CompositeResourceDefinition, a Composition, and a Composite
412
- just to run that Composition once in a single use or initialize task?
417
+ just to run that Composition once in a setup or initialize task?
413
418
 
414
- function-pythonic installs a `Composite` CompositeResourceDefinition that enables
415
- creating such tasks using a single Composite resource:
419
+ function-pythonic supports "inlined" Compositions, where the python module
420
+ is obtained from a field in the Composite's spec.
416
421
  ```yaml
417
- apiVersion: pythonic.fn.crossplane.io/v1alpha1
418
- kind: Composite
422
+ apiVersion: inlined.example.org/v1alpha1
423
+ kind: Step
419
424
  metadata:
420
- name: composite-example
425
+ name: inlined-example
421
426
  spec:
422
427
  composite: |
423
428
  class HelloComposite(BaseComposite):
424
429
  def compose(self):
425
- self.status.composite = 'Hello, World!'
430
+ self.status.step = 'Hello, World!'
431
+ ```
432
+ The CompositeResourceDefinition and Composition to support the above example:
433
+ ```yaml
434
+ apiVersion: apiextensions.crossplane.io/v1
435
+ kind: CompositeResourceDefinition
436
+ metadata:
437
+ name: inlined.example.org/v1alpha1
438
+ spec:
439
+ group: inlined.example.org
440
+ names:
441
+ kind: Step
442
+ plural: steps
443
+ defaultCompositionRef:
444
+ name: steps.inlined.example.org
445
+ versions:
446
+ - name: v1alpha1
447
+ served: true
448
+ referenceable: true
449
+ schema:
450
+ openAPIV3Schema:
451
+ type: object
452
+ properties:
453
+ spec:
454
+ type: object
455
+ properties:
456
+ composite:
457
+ type: string
458
+ description: 'A Python module that defines a class with the signature: class Composite(BaseComposite)'
459
+ required:
460
+ - composite
461
+ status:
462
+ type: object
463
+ properties:
464
+ composite:
465
+ x-kubernetes-preserve-unknown-fields: true
466
+ ```
467
+ ```yaml
468
+ apiVersion: apiextensions.crossplane.io/v1
469
+ kind: Composition
470
+ metadata:
471
+ name: steps.inlined.example.org
472
+ spec:
473
+ compositeTypeRef:
474
+ apiVersion: inlined.example.org/v1alpha1
475
+ kind: Step
476
+ mode: Pipeline
477
+ pipeline:
478
+ - step: inlined
479
+ functionRef:
480
+ name: function-pythonic
481
+ input:
482
+ apiVersion: pythonic.fn.crossplane.io/v1alpha1
483
+ kind: Composite
484
+ inlined: composite
426
485
  ```
427
-
428
486
  ## Quick Start Development
429
487
 
430
488
  function-pythonic includes a pure python implementation of the `crossplane render ...`
@@ -0,0 +1,18 @@
1
+ crossplane/pythonic/__about__.py,sha256=S3lIpABQ5LwPMuO5TLfDrT_eJOQtFzsph4TXcfnx4OY,73
2
+ crossplane/pythonic/__init__.py,sha256=cRk12kc18RLhc9s5dW6sQqZSCEp80fooRO5zxXqc1oA,292
3
+ crossplane/pythonic/__main__.py,sha256=6vYRlYDJtqFgLyiTamnl3htiNOtz8QlDl5WlIP98I8o,31
4
+ crossplane/pythonic/auto_ready.py,sha256=sPetUuJRhwZbg9muaDmbdqmtTIIUDmY4qoadoJA0EtQ,7201
5
+ crossplane/pythonic/command.py,sha256=aT58WBrhU_scaOGeqmsBfofIDnXyW1CQOpCktVGBj5s,4211
6
+ crossplane/pythonic/composite.py,sha256=niq-JSVZ8NB53Q7khkMqH9vQTJPb6yB-13O-wa2Is1U,30311
7
+ crossplane/pythonic/function.py,sha256=CL2j_Br0eYWbn_8r8md9O9ErfHizz78H1KR8l2oV1IA,17964
8
+ crossplane/pythonic/grpc.py,sha256=9ZQceboDju37NB6AhcUSWpBx_hZQ5W7uo7CZF6ynhfI,4451
9
+ crossplane/pythonic/main.py,sha256=ujUa_FYElQSGqnhZ-0NJrD3kSyYjfRbIp79FV2Yl7hs,599
10
+ crossplane/pythonic/packages.py,sha256=4TxyT6V79R0m4tJbC8R1gwU_vgHGLXKSBzeTTKd8xGo,5120
11
+ crossplane/pythonic/protobuf.py,sha256=nmVf-Xn_-ER8BEfEbqd8uQo2gdhmNYyQh9QlhcaYebs,53083
12
+ crossplane/pythonic/render.py,sha256=Y1-fdjQxvPBSkGjfJnNwCOOg6I-bX7Ys9X4jXkqIZp4,30140
13
+ crossplane/pythonic/version.py,sha256=-RiB0p146ayqJj0SXfYxTNv49u9Fx9pPgm59Ji2blhc,214
14
+ crossplane_function_pythonic-0.5.0.dist-info/METADATA,sha256=yMVu_bg-pjIrhuHDjeTAmKEa082V2pq5NroKIeo9poI,33133
15
+ crossplane_function_pythonic-0.5.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
16
+ crossplane_function_pythonic-0.5.0.dist-info/entry_points.txt,sha256=jJ4baywFDviB9WyAhyhNYF2VOCb6XtbRSjKf7bnBwhg,68
17
+ crossplane_function_pythonic-0.5.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
18
+ crossplane_function_pythonic-0.5.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.28.0
2
+ Generator: hatchling 1.29.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,18 +0,0 @@
1
- crossplane/pythonic/__about__.py,sha256=6nxRspkiSsLkBLqHh-X--3FLozkQy1xfr-6ZG0LORbs,73
2
- crossplane/pythonic/__init__.py,sha256=cRk12kc18RLhc9s5dW6sQqZSCEp80fooRO5zxXqc1oA,292
3
- crossplane/pythonic/__main__.py,sha256=6vYRlYDJtqFgLyiTamnl3htiNOtz8QlDl5WlIP98I8o,31
4
- crossplane/pythonic/auto_ready.py,sha256=sPetUuJRhwZbg9muaDmbdqmtTIIUDmY4qoadoJA0EtQ,7201
5
- crossplane/pythonic/command.py,sha256=aT58WBrhU_scaOGeqmsBfofIDnXyW1CQOpCktVGBj5s,4211
6
- crossplane/pythonic/composite.py,sha256=f9SYjtXVqRx7vse7nQ06Ic6EnnfCYVtpoOmBhR9Jv6g,30451
7
- crossplane/pythonic/function.py,sha256=uILb3XICcWi2JA5W1xuq37nFQA3_TqbdJefo0-gUUNI,18063
8
- crossplane/pythonic/grpc.py,sha256=9ZQceboDju37NB6AhcUSWpBx_hZQ5W7uo7CZF6ynhfI,4451
9
- crossplane/pythonic/main.py,sha256=ujUa_FYElQSGqnhZ-0NJrD3kSyYjfRbIp79FV2Yl7hs,599
10
- crossplane/pythonic/packages.py,sha256=4TxyT6V79R0m4tJbC8R1gwU_vgHGLXKSBzeTTKd8xGo,5120
11
- crossplane/pythonic/protobuf.py,sha256=YcKqH0Y4Vj63m6pUb1ptL4gIKcdNLU-WR8TaQ5eGW-M,52987
12
- crossplane/pythonic/render.py,sha256=e_WQF5TlemfsZim2oGkTPAWuU6BDJztexJ9iBbuFQbo,30417
13
- crossplane/pythonic/version.py,sha256=-RiB0p146ayqJj0SXfYxTNv49u9Fx9pPgm59Ji2blhc,214
14
- crossplane_function_pythonic-0.4.2.dist-info/METADATA,sha256=KemrVxbVM4-B6I1P-Gq3hDdqE67sPMCRyfu9gjBY4_w,31561
15
- crossplane_function_pythonic-0.4.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
- crossplane_function_pythonic-0.4.2.dist-info/entry_points.txt,sha256=jJ4baywFDviB9WyAhyhNYF2VOCb6XtbRSjKf7bnBwhg,68
17
- crossplane_function_pythonic-0.4.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
18
- crossplane_function_pythonic-0.4.2.dist-info/RECORD,,