port-ocean 0.5.13__py3-none-any.whl → 0.5.15__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.
@@ -5,7 +5,7 @@ define run_checks
5
5
  cd $1; \
6
6
  poetry check || exit_code=$$?;\
7
7
  mypy . || exit_code=$$?; \
8
- ruff . || exit_code=$$?; \
8
+ ruff check . || exit_code=$$?; \
9
9
  black --check . || exit_code=$$?; \
10
10
  if [ $$exit_code -eq 1 ]; then \
11
11
  echo "\033[0;31mOne or more checks failed with exit code $$exit_code\033[0m"; \
@@ -173,27 +173,33 @@ class EntityClientMixin:
173
173
  handle_status_code(response)
174
174
  return [Entity.parse_obj(result) for result in response.json()["entities"]]
175
175
 
176
- async def does_integration_has_ownership_over_entity(
177
- self, entity: Entity, user_agent_type: UserAgentType
178
- ) -> bool:
179
- logger.info(f"Validating ownership on entity {entity.identifier}")
180
- found_entities: list[Entity] = await self.search_entities(
176
+ async def search_batch_entities(
177
+ self, user_agent_type: UserAgentType, entities_to_search: list[Entity]
178
+ ) -> list[Entity]:
179
+ search_rules = []
180
+ for entity in entities_to_search:
181
+ search_rules.append(
182
+ {
183
+ "combinator": "and",
184
+ "rules": [
185
+ {
186
+ "property": "$identifier",
187
+ "operator": "=",
188
+ "value": entity.identifier,
189
+ },
190
+ {
191
+ "property": "$blueprint",
192
+ "operator": "=",
193
+ "value": entity.blueprint,
194
+ },
195
+ ],
196
+ }
197
+ )
198
+
199
+ return await self.search_entities(
181
200
  user_agent_type,
182
201
  {
183
202
  "combinator": "and",
184
- "rules": [
185
- {
186
- "property": "$identifier",
187
- "operator": "contains",
188
- "value": entity.identifier,
189
- },
190
- {
191
- "property": "$blueprint",
192
- "operator": "contains",
193
- "value": entity.blueprint,
194
- },
195
- ],
203
+ "rules": [{"combinator": "or", "rules": search_rules}],
196
204
  },
197
205
  )
198
-
199
- return len(found_entities) > 0
@@ -7,7 +7,7 @@ from port_ocean.core.handlers.base import BaseHandler
7
7
  from port_ocean.core.handlers.port_app_config.models import ResourceConfig
8
8
  from port_ocean.core.models import Entity
9
9
  from port_ocean.core.ocean_types import (
10
- RawEntity,
10
+ RAW_ITEM,
11
11
  EntitySelectorDiff,
12
12
  )
13
13
 
@@ -38,7 +38,7 @@ class BaseEntityProcessor(BaseHandler):
38
38
  async def _parse_items(
39
39
  self,
40
40
  mapping: ResourceConfig,
41
- raw_data: list[RawEntity],
41
+ raw_data: list[RAW_ITEM],
42
42
  parse_all: bool = False,
43
43
  ) -> EntitySelectorDiff:
44
44
  pass
@@ -46,7 +46,7 @@ class BaseEntityProcessor(BaseHandler):
46
46
  async def parse_items(
47
47
  self,
48
48
  mapping: ResourceConfig,
49
- raw_data: list[RawEntity],
49
+ raw_data: list[RAW_ITEM],
50
50
  parse_all: bool = False,
51
51
  ) -> EntitySelectorDiff:
52
52
  """Public method to parse raw entity data and map it to an EntityDiff.
@@ -10,7 +10,7 @@ from port_ocean.core.handlers.entity_processor.base import BaseEntityProcessor
10
10
  from port_ocean.core.handlers.port_app_config.models import ResourceConfig
11
11
  from port_ocean.core.models import Entity
12
12
  from port_ocean.core.ocean_types import (
13
- RawEntity,
13
+ RAW_ITEM,
14
14
  EntitySelectorDiff,
15
15
  )
16
16
  from port_ocean.exceptions.core import EntityProcessorException
@@ -122,7 +122,7 @@ class JQEntityProcessor(BaseEntityProcessor):
122
122
  async def _parse_items(
123
123
  self,
124
124
  mapping: ResourceConfig,
125
- raw_results: list[RawEntity],
125
+ raw_results: list[RAW_ITEM],
126
126
  parse_all: bool = False,
127
127
  ) -> EntitySelectorDiff:
128
128
  raw_entity_mappings: dict[str, Any] = mapping.port.entity.mappings.dict(
@@ -153,4 +153,4 @@ class JQEntityProcessor(BaseEntityProcessor):
153
153
  else:
154
154
  failed_entities.append(parsed_entity)
155
155
 
156
- return {"passed": passed_entities, "failed": failed_entities}
156
+ return EntitySelectorDiff(passed=passed_entities, failed=failed_entities)
@@ -22,9 +22,8 @@ from port_ocean.core.ocean_types import (
22
22
  RAW_RESULT,
23
23
  RESYNC_RESULT,
24
24
  RawEntityDiff,
25
- EntityDiff,
26
25
  ASYNC_GENERATOR_RESYNC_TYPE,
27
- RawEntity,
26
+ RAW_ITEM,
28
27
  EntitySelectorDiff,
29
28
  )
30
29
  from port_ocean.core.utils import zip_and_sum
@@ -123,7 +122,7 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
123
122
 
124
123
  async def _calculate_raw(
125
124
  self,
126
- raw_diff: list[tuple[ResourceConfig, list[RawEntity]]],
125
+ raw_diff: list[tuple[ResourceConfig, list[RAW_ITEM]]],
127
126
  parse_all: bool = False,
128
127
  ) -> list[EntitySelectorDiff]:
129
128
  return await asyncio.gather(
@@ -139,40 +138,23 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
139
138
  results: list[dict[Any, Any]],
140
139
  user_agent_type: UserAgentType,
141
140
  parse_all: bool = False,
142
- ) -> list[Entity]:
141
+ ) -> EntitySelectorDiff:
143
142
  objects_diff = await self._calculate_raw([(resource, results)], parse_all)
143
+ await self.entities_state_applier.upsert(
144
+ objects_diff[0].passed, user_agent_type
145
+ )
144
146
 
145
- entities_after: list[Entity] = objects_diff[0]["passed"]
146
- await self.entities_state_applier.upsert(entities_after, user_agent_type)
147
-
148
- # If an entity didn't pass the JQ selector, we want to delete it if it exists in Port
149
- for entity_to_delete in objects_diff[0]["failed"]:
150
- is_owner = (
151
- await ocean.port_client.does_integration_has_ownership_over_entity(
152
- entity_to_delete, user_agent_type
153
- )
154
- )
155
- if not is_owner:
156
- logger.info(
157
- f"Skipping deletion of entity {entity_to_delete.identifier}, "
158
- f"Couldn't find an entity that's related to the current integration."
159
- )
160
- continue
161
- await self.entities_state_applier.delete(
162
- objects_diff[0]["failed"], user_agent_type
163
- )
164
-
165
- return entities_after
147
+ return objects_diff[0]
166
148
 
167
149
  async def _unregister_resource_raw(
168
150
  self,
169
151
  resource: ResourceConfig,
170
- results: list[RawEntity],
152
+ results: list[RAW_ITEM],
171
153
  user_agent_type: UserAgentType,
172
154
  ) -> list[Entity]:
173
155
  objects_diff = await self._calculate_raw([(resource, results)])
174
156
 
175
- entities_after: list[Entity] = objects_diff[0]["passed"]
157
+ entities_after: list[Entity] = objects_diff[0].passed
176
158
  await self.entities_state_applier.delete(entities_after, user_agent_type)
177
159
  logger.info("Finished unregistering change")
178
160
  return entities_after
@@ -189,17 +171,21 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
189
171
  else:
190
172
  async_generators.append(result)
191
173
 
192
- entities = await self._register_resource_raw(
193
- resource_config, raw_results, user_agent_type
194
- )
174
+ entities = (
175
+ await self._register_resource_raw(
176
+ resource_config, raw_results, user_agent_type
177
+ )
178
+ ).passed
195
179
 
196
180
  for generator in async_generators:
197
181
  try:
198
182
  async for items in generator:
199
183
  entities.extend(
200
- await self._register_resource_raw(
201
- resource_config, items, user_agent_type
202
- )
184
+ (
185
+ await self._register_resource_raw(
186
+ resource_config, items, user_agent_type
187
+ )
188
+ ).passed
203
189
  )
204
190
  except* OceanAbortException as error:
205
191
  errors.append(error)
@@ -233,13 +219,47 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
233
219
  resource for resource in config.resources if resource.kind == kind
234
220
  ]
235
221
 
236
- return await asyncio.gather(
222
+ if not resource_mappings:
223
+ return []
224
+
225
+ diffs: list[EntitySelectorDiff] = await asyncio.gather(
237
226
  *(
238
227
  self._register_resource_raw(resource, results, user_agent_type, True)
239
228
  for resource in resource_mappings
240
229
  )
241
230
  )
242
231
 
232
+ registered_entities, entities_to_delete = zip_and_sum(
233
+ (entities_diff.passed, entities_diff.failed) for entities_diff in diffs
234
+ )
235
+
236
+ registered_entities_attributes = {
237
+ (entity.identifier, entity.blueprint) for entity in registered_entities
238
+ }
239
+
240
+ filtered_entities_to_delete: list[Entity] = (
241
+ await ocean.port_client.search_batch_entities(
242
+ user_agent_type,
243
+ [
244
+ entity
245
+ for entity in entities_to_delete
246
+ if (entity.identifier, entity.blueprint)
247
+ not in registered_entities_attributes
248
+ ],
249
+ )
250
+ )
251
+
252
+ if filtered_entities_to_delete:
253
+ logger.info(
254
+ f"Deleting {len(filtered_entities_to_delete)} entities that didn't pass any of the selectors"
255
+ )
256
+
257
+ await self.entities_state_applier.delete(
258
+ filtered_entities_to_delete, user_agent_type
259
+ )
260
+
261
+ return registered_entities
262
+
243
263
  async def unregister_raw(
244
264
  self,
245
265
  kind: str,
@@ -306,16 +326,13 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
306
326
  [(mapping, raw_desired_state["after"]) for mapping in resource_mappings]
307
327
  )
308
328
 
309
- entities_before_flatten = [
310
- item
311
- for sublist in [d["passed"] for d in entities_before]
312
- for item in sublist
313
- ]
314
- entities_after_flatten = [
315
- item
316
- for sublist in [d["passed"] for d in entities_after]
317
- for item in sublist
318
- ]
329
+ entities_before_flatten: list[Entity] = sum(
330
+ (entities_diff.passed for entities_diff in entities_before), []
331
+ )
332
+
333
+ entities_after_flatten: list[Entity] = sum(
334
+ (entities_diff.passed for entities_diff in entities_after), []
335
+ )
319
336
 
320
337
  await self.entities_state_applier.apply_diff(
321
338
  {"before": entities_before_flatten, "after": entities_after_flatten},
@@ -1,14 +1,21 @@
1
- from typing import TypedDict, Any, AsyncIterator, Callable, Awaitable
1
+ from typing import TypedDict, Any, AsyncIterator, Callable, Awaitable, NamedTuple
2
2
 
3
3
  from port_ocean.core.models import Entity
4
4
 
5
5
 
6
- RawEntity = dict[Any, Any]
6
+ RAW_ITEM = dict[Any, Any]
7
+ RAW_RESULT = list[RAW_ITEM]
8
+ ASYNC_GENERATOR_RESYNC_TYPE = AsyncIterator[RAW_RESULT]
9
+ RESYNC_RESULT = list[RAW_ITEM | ASYNC_GENERATOR_RESYNC_TYPE]
10
+
11
+ LISTENER_RESULT = Awaitable[RAW_RESULT] | ASYNC_GENERATOR_RESYNC_TYPE
12
+ RESYNC_EVENT_LISTENER = Callable[[str], LISTENER_RESULT]
13
+ START_EVENT_LISTENER = Callable[[], Awaitable[None]]
7
14
 
8
15
 
9
16
  class RawEntityDiff(TypedDict):
10
- before: list[RawEntity]
11
- after: list[RawEntity]
17
+ before: list[RAW_ITEM]
18
+ after: list[RAW_ITEM]
12
19
 
13
20
 
14
21
  class EntityDiff(TypedDict):
@@ -16,21 +23,11 @@ class EntityDiff(TypedDict):
16
23
  after: list[Entity]
17
24
 
18
25
 
19
- class EntitySelectorDiff(TypedDict):
26
+ class EntitySelectorDiff(NamedTuple):
20
27
  passed: list[Entity]
21
28
  failed: list[Entity]
22
29
 
23
30
 
24
- RAW_ITEM = dict[Any, Any]
25
- RAW_RESULT = list[RAW_ITEM]
26
- ASYNC_GENERATOR_RESYNC_TYPE = AsyncIterator[RAW_RESULT]
27
- RESYNC_RESULT = list[RAW_ITEM | ASYNC_GENERATOR_RESYNC_TYPE]
28
-
29
- LISTENER_RESULT = Awaitable[RAW_RESULT] | ASYNC_GENERATOR_RESYNC_TYPE
30
- RESYNC_EVENT_LISTENER = Callable[[str], LISTENER_RESULT]
31
- START_EVENT_LISTENER = Callable[[], Awaitable[None]]
32
-
33
-
34
31
  class IntegrationEventsCallbacks(TypedDict):
35
32
  start: list[START_EVENT_LISTENER]
36
33
  resync: dict[str | None, list[RESYNC_EVENT_LISTENER]]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.5.13
3
+ Version: 0.5.15
4
4
  Summary: Port Ocean is a CLI tool for managing your Port projects.
5
5
  Home-page: https://app.getport.io
6
6
  Keywords: ocean,port-ocean,port
@@ -25,7 +25,7 @@ Requires-Dist: aiostream (>=0.5.2,<0.6.0)
25
25
  Requires-Dist: click (>=8.1.3,<9.0.0) ; extra == "cli"
26
26
  Requires-Dist: confluent-kafka (>=2.1.1,<3.0.0)
27
27
  Requires-Dist: cookiecutter (>=2.1.1,<3.0.0) ; extra == "cli"
28
- Requires-Dist: fastapi (>=0.100,<0.110)
28
+ Requires-Dist: fastapi (>=0.100,<0.111)
29
29
  Requires-Dist: httpx (>=0.24.1,<0.28.0)
30
30
  Requires-Dist: jinja2-time (>=0.2.0,<0.3.0) ; extra == "cli"
31
31
  Requires-Dist: loguru (>=0.7.0,<0.8.0)
@@ -37,7 +37,7 @@ Requires-Dist: pyyaml (>=6.0,<7.0)
37
37
  Requires-Dist: rich (>=13.4.1,<14.0.0) ; extra == "cli"
38
38
  Requires-Dist: six (>=1.16.0,<2.0.0)
39
39
  Requires-Dist: tomli (>=2.0.1,<3.0.0)
40
- Requires-Dist: urllib3 (>=1.26.16,<2.0.0)
40
+ Requires-Dist: urllib3 (>=1.26.16,<3.0.0)
41
41
  Requires-Dist: uvicorn (>=0.22,<0.30)
42
42
  Requires-Dist: werkzeug (>=2.3.4,<4.0.0)
43
43
  Project-URL: Repository, https://github.com/port-labs/Port-Ocean
@@ -23,7 +23,7 @@ port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/resources/.g
23
23
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/.port/spec.yaml,sha256=Yq06gYoC6jFWES5mxlvJGFTGXbfD7E9R8j_PDbVIM3M,497
24
24
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/CHANGELOG.md,sha256=nzAmB0Bjnd2eZo79OjrlyVOdpTBHTmTxvO7c2C8Q-VQ,292
25
25
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Dockerfile,sha256=Hh1dBnL959V2n28pmqFpXSrNvSMQjX6fDCUos8ITiu0,326
26
- port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Makefile,sha256=HdDAoHiL0unYoMubrfngUA9GrweJny8jdTDqYFcNnTY,1773
26
+ port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/Makefile,sha256=kTa72qEp8pi-joOH6zl8oeIgjEHSCF628p2074yHHNA,1779
27
27
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/README.md,sha256=5VZmgDRW9gO4d8UuzkujslOIDfIDBiAGL2Hd74HK770,468
28
28
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/changelog/.gitignore,sha256=kCpRPdl3S_jqYYZaOrc0-xa6-l3KqVjNRXc6jCkd_-Q,12
29
29
  port_ocean/cli/cookiecutter/{{cookiecutter.integration_slug}}/config.yaml,sha256=zKaBQNCbWEE3MFxDHaMn9NIeBGVPiZUV6cBjH35f2kw,906
@@ -40,7 +40,7 @@ port_ocean/clients/port/authentication.py,sha256=t3z6h4vld-Tzkpth15sstaMJg0rccX-
40
40
  port_ocean/clients/port/client.py,sha256=3GYCM0ZkX3pB6sNoOb-7_6dm0Jr5_vqhflD9iltf_As,2640
41
41
  port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  port_ocean/clients/port/mixins/blueprints.py,sha256=BiqkhvDFdkySWgL1NHI-LAQ9ieZWazZAojPo9E8d7U4,4575
43
- port_ocean/clients/port/mixins/entities.py,sha256=6AgQocxwaXGYs9IHfaquz5M-4REj5a53-l0lsYJ7N7A,7199
43
+ port_ocean/clients/port/mixins/entities.py,sha256=Lg5Sa6jQuhDTQKLURVavqXlBQt4-XPUUigB9JqQ1X0k,7364
44
44
  port_ocean/clients/port/mixins/integrations.py,sha256=GJRweeibfKhukOkWB07pb09G6pp06FOiXPJU-pOV7M8,5155
45
45
  port_ocean/clients/port/mixins/migrations.py,sha256=A6896oJF6WbFL2WroyTkMzr12yhVyWqGoq9dtLNSKBY,1457
46
46
  port_ocean/clients/port/retry_transport.py,sha256=PtIZOAZ6V-ncpVysRUsPOgt8Sf01QLnTKB5YeKBxkJk,1861
@@ -77,8 +77,8 @@ port_ocean/core/handlers/entities_state_applier/port/applier.py,sha256=G6RyPgBAy
77
77
  port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py,sha256=1zncwCbE-Gej0xaWKlzZgoXxOBe9bgs_YxlZ8QW3NdI,1751
78
78
  port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py,sha256=82BvU8t5w9uhsxX8hbnwuRPuWhW3cMeuT_5sVIkip1I,1550
79
79
  port_ocean/core/handlers/entity_processor/__init__.py,sha256=FvFCunFg44wNQoqlybem9MthOs7p1Wawac87uSXz9U8,156
80
- port_ocean/core/handlers/entity_processor/base.py,sha256=G5pkZX9yUQzU_FhgILu8h86A-OQ3yQzp3Up6n1CC7ew,1955
81
- port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=hRryLQ21SwJRtR_it7Uryt04MaDeepdUiLv0ssLimzg,5607
80
+ port_ocean/core/handlers/entity_processor/base.py,sha256=4JVCAAohEKtl8FdlnuyIxJ1afSXk3o2-e_m4LSy7vmw,1952
81
+ port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=R5PwTcsJYRVU-lwoYZa1Af8kserp2BXIpyzVtRdEqA8,5617
82
82
  port_ocean/core/handlers/port_app_config/__init__.py,sha256=8AAT5OthiVM7KCcM34iEgEeXtn2pRMrT4Dze5r1Ixbk,134
83
83
  port_ocean/core/handlers/port_app_config/api.py,sha256=6VbKPwFzsWG0IYsVD81hxSmfqtHUFqrfUuj1DBX5g4w,853
84
84
  port_ocean/core/handlers/port_app_config/base.py,sha256=nnMZ4jH6a-4Of9Cn-apMsU0CgNLD9avd5q0gRmc7nZ8,1495
@@ -89,10 +89,10 @@ port_ocean/core/integrations/mixins/__init__.py,sha256=FA1FEKMM6P-L2_m7Q4L20mFa4
89
89
  port_ocean/core/integrations/mixins/events.py,sha256=Ddfx2L4FpghV38waF8OfVeOV0bHBxNIgjU-q5ffillI,2341
90
90
  port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
91
91
  port_ocean/core/integrations/mixins/sync.py,sha256=TKqRytxXONVhuCo3CB3rDvWNbITnZz33TYTDs3SWWVk,3880
92
- port_ocean/core/integrations/mixins/sync_raw.py,sha256=ryl4u3MCQo-qYmu2Pt9fHMnLL0RavmjFNKJzEHakQmk,14656
92
+ port_ocean/core/integrations/mixins/sync_raw.py,sha256=kWZ44L2M2G6B0n3oOqF1Ko9Rct06CXnTnWY25Q2eh3c,15049
93
93
  port_ocean/core/integrations/mixins/utils.py,sha256=7y1rGETZIjOQadyIjFJXIHKkQFKx_SwiP-TrAIsyyLY,2303
94
94
  port_ocean/core/models.py,sha256=bDO_I4Yd33TEZIh2QSV8UwXQIuwE7IgrINkYDHI0dkc,714
95
- port_ocean/core/ocean_types.py,sha256=df0Yu0Ft2nKmBj-1xbiuJsV3JDGOtGEBBQNJLJE0fuM,897
95
+ port_ocean/core/ocean_types.py,sha256=ltnn22eRuDMFW02kIgmIAu6S06-i9jJV2NJ-MZcwwj0,879
96
96
  port_ocean/core/utils.py,sha256=B040Wokk28g9tQj_06qk_uvm85RIXc6XGXysZV6gtQw,1957
97
97
  port_ocean/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
98
  port_ocean/exceptions/api.py,sha256=TLmTMqn4uHGaHgZK8PMIJ0TVJlPB4iP7xl9rx7GtCyY,426
@@ -122,8 +122,8 @@ port_ocean/utils/misc.py,sha256=2XmO8W0SgPjV0rd9HZvrHhoMlHprIwmMFsINxlAmgyw,1723
122
122
  port_ocean/utils/repeat.py,sha256=0EFWM9d8lLXAhZmAyczY20LAnijw6UbIECf5lpGbOas,3231
123
123
  port_ocean/utils/signal.py,sha256=Fab0049Cjs69TPTQgvEvilaVZKACQr6tGkRdySjNCi8,1515
124
124
  port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
125
- port_ocean-0.5.13.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
126
- port_ocean-0.5.13.dist-info/METADATA,sha256=Jxvee3Xw_3SafxSc98Gq0KB556-BWMBkuh06buCnjuk,6554
127
- port_ocean-0.5.13.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
128
- port_ocean-0.5.13.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
129
- port_ocean-0.5.13.dist-info/RECORD,,
125
+ port_ocean-0.5.15.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
126
+ port_ocean-0.5.15.dist-info/METADATA,sha256=TeyhvegjRCiZJ1nEhYDSRdXdLCuClIslKwLiAr1wbqw,6554
127
+ port_ocean-0.5.15.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
128
+ port_ocean-0.5.15.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
129
+ port_ocean-0.5.15.dist-info/RECORD,,