port-ocean 0.25.5__py3-none-any.whl → 0.26.2__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.
@@ -40,6 +40,7 @@ RUN apt-get update \
40
40
  openssl \
41
41
  curl \
42
42
  acl \
43
+ sudo \
43
44
  && apt-get clean
44
45
 
45
46
  LABEL INTEGRATION_VERSION=${INTEGRATION_VERSION}
@@ -68,6 +69,11 @@ RUN chmod a+x /app/init.sh
68
69
  RUN ln -s /app/.venv/bin/ocean /usr/bin/ocean
69
70
  # Add ocean user to ssl certs group
70
71
  RUN setfacl -m u:ocean:rwX /etc/ssl/certs
72
+
73
+ # Allow ocean user to run update-ca-certificates without password (secure, limited sudo)
74
+ RUN echo "ocean ALL=(root) NOPASSWD: /usr/sbin/update-ca-certificates" >> /etc/sudoers.d/ocean-certs \
75
+ && chmod 440 /etc/sudoers.d/ocean-certs
76
+
71
77
  USER ocean
72
78
  # Run the application
73
79
  CMD ["bash", "/app/init.sh"]
@@ -1,5 +1,5 @@
1
1
  if test -e /usr/local/share/ca-certificates/cert.crt; then
2
- update-ca-certificates
2
+ sudo update-ca-certificates
3
3
  fi
4
4
 
5
5
  ocean sail
@@ -483,38 +483,56 @@ class EntityClientMixin:
483
483
  query: dict[Any, Any] | None = None,
484
484
  parameters_to_include: list[str] | None = None,
485
485
  ) -> list[Entity]:
486
- default_query = {
487
- "combinator": "and",
488
- "rules": [
489
- {
490
- "property": "$datasource",
491
- "operator": "contains",
492
- "value": f"port-ocean/{self.auth.integration_type}/",
486
+ if query is None:
487
+ datasource_prefix = f"port-ocean/{self.auth.integration_type}/"
488
+ datasource_suffix = (
489
+ f"/{self.auth.integration_identifier}/{user_agent_type.value}"
490
+ )
491
+ logger.info(
492
+ f"Searching entities with datasource prefix: {datasource_prefix} and suffix: {datasource_suffix}"
493
+ )
494
+
495
+ response = await self.client.post(
496
+ f"{self.auth.api_url}/blueprints/entities/datasource-entities",
497
+ json={
498
+ "datasource_prefix": datasource_prefix,
499
+ "datasource_suffix": datasource_suffix,
493
500
  },
494
- {
495
- "property": "$datasource",
496
- "operator": "contains",
497
- "value": f"/{self.auth.integration_identifier}/{user_agent_type.value}",
501
+ headers=await self.auth.headers(user_agent_type),
502
+ extensions={"retryable": True},
503
+ )
504
+ else:
505
+ default_query = {
506
+ "combinator": "and",
507
+ "rules": [
508
+ {
509
+ "property": "$datasource",
510
+ "operator": "contains",
511
+ "value": f"port-ocean/{self.auth.integration_type}/",
512
+ },
513
+ {
514
+ "property": "$datasource",
515
+ "operator": "contains",
516
+ "value": f"/{self.auth.integration_identifier}/{user_agent_type.value}",
517
+ },
518
+ ],
519
+ }
520
+
521
+ if query.get("rules"):
522
+ query["rules"].extend(default_query["rules"])
523
+
524
+ logger.info(f"Searching entities with custom query: {query}")
525
+ response = await self.client.post(
526
+ f"{self.auth.api_url}/entities/search",
527
+ json=query,
528
+ headers=await self.auth.headers(user_agent_type),
529
+ params={
530
+ "exclude_calculated_properties": "true",
531
+ "include": parameters_to_include or ["blueprint", "identifier"],
498
532
  },
499
- ],
500
- }
533
+ extensions={"retryable": True},
534
+ )
501
535
 
502
- if query is None:
503
- query = default_query
504
- elif query.get("rules"):
505
- query["rules"].append(default_query)
506
-
507
- logger.info(f"Searching entities with query {query}")
508
- response = await self.client.post(
509
- f"{self.auth.api_url}/entities/search",
510
- json=query,
511
- headers=await self.auth.headers(user_agent_type),
512
- params={
513
- "exclude_calculated_properties": "true",
514
- "include": parameters_to_include or ["blueprint", "identifier"],
515
- },
516
- extensions={"retryable": True},
517
- )
518
536
  handle_port_status_code(response)
519
537
  return [Entity.parse_obj(result) for result in response.json()["entities"]]
520
538
 
@@ -1,9 +1,10 @@
1
1
  import asyncio
2
- from typing import Any, Dict, List, TYPE_CHECKING, Optional, TypedDict
2
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, TypedDict
3
3
  from urllib.parse import quote_plus
4
4
 
5
5
  import httpx
6
6
  from loguru import logger
7
+
7
8
  from port_ocean.clients.port.authentication import PortAuthentication
8
9
  from port_ocean.clients.port.utils import handle_port_status_code
9
10
  from port_ocean.exceptions.port_defaults import DefaultsProvisionFailed
@@ -125,7 +126,7 @@ class IntegrationClientMixin:
125
126
 
126
127
  while attempts < INTEGRATION_POLLING_RETRY_LIMIT:
127
128
  logger.info(
128
- f"Fetching created integration and validating config, attempt {attempts+1}/{INTEGRATION_POLLING_RETRY_LIMIT}",
129
+ f"Fetching created integration and validating config, attempt {attempts + 1}/{INTEGRATION_POLLING_RETRY_LIMIT}",
129
130
  attempt=attempts,
130
131
  )
131
132
  response = await self._get_current_integration()
@@ -288,3 +289,18 @@ class IntegrationClientMixin:
288
289
  response = await self._delete_current_integration()
289
290
  handle_port_status_code(response, should_raise, should_log)
290
291
  return response.json()
292
+
293
+ async def post_integration_raw_data(
294
+ self, raw_data: list[dict[Any, Any]], sync_id: str, kind: str
295
+ ) -> None:
296
+ logger.debug("starting POST raw data request", raw_data=raw_data)
297
+ headers = await self.auth.headers()
298
+ response = await self.client.post(
299
+ f"{self.auth.api_url}/lakehouse/integration/{self.integration_identifier}/sync/{sync_id}/kind/{kind}/items",
300
+ headers=headers,
301
+ json={
302
+ "items": raw_data,
303
+ },
304
+ )
305
+ handle_port_status_code(response, should_log=False)
306
+ logger.debug("Finished POST raw data request")
@@ -12,8 +12,8 @@ from port_ocean.core.event_listener import EventListenerSettingsType
12
12
  from port_ocean.core.models import (
13
13
  CachingStorageMode,
14
14
  CreatePortResourcesOrigin,
15
- Runtime,
16
15
  ProcessExecutionMode,
16
+ Runtime,
17
17
  )
18
18
  from port_ocean.utils.misc import get_integration_name, get_spec_file
19
19
 
@@ -108,6 +108,7 @@ class IntegrationConfiguration(BaseOceanSettings, extra=Extra.allow):
108
108
 
109
109
  upsert_entities_batch_max_length: int = 20
110
110
  upsert_entities_batch_max_size_in_bytes: int = 1024 * 1024
111
+ lakehouse_enabled: bool = False
111
112
 
112
113
  @validator("process_execution_mode")
113
114
  def validate_process_execution_mode(
@@ -363,9 +363,13 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
363
363
  results, errors = await self._get_resource_raw_results(resource_config)
364
364
  async_generators: list[ASYNC_GENERATOR_RESYNC_TYPE] = []
365
365
  raw_results: RAW_RESULT = []
366
+ lakehouse_data_enabled = await self._lakehouse_data_enabled()
367
+
366
368
  for result in results:
367
369
  if isinstance(result, dict):
368
370
  raw_results.append(result)
371
+ if lakehouse_data_enabled:
372
+ await ocean.port_client.post_integration_raw_data(result, event.id, resource_config.kind)
369
373
  else:
370
374
  async_generators.append(result)
371
375
 
@@ -397,6 +401,8 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
397
401
  for generator in async_generators:
398
402
  try:
399
403
  async for items in generator:
404
+ if lakehouse_data_enabled:
405
+ await ocean.port_client.post_integration_raw_data(items, event.id, resource_config.kind)
400
406
  number_of_raw_results += len(items)
401
407
  if send_raw_data_examples_amount > 0:
402
408
  send_raw_data_examples_amount = max(
@@ -462,6 +468,19 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
462
468
 
463
469
  return passed_entities, errors
464
470
 
471
+ async def _lakehouse_data_enabled(
472
+ self
473
+ ) -> bool:
474
+ """Check if lakehouse data is enabled.
475
+
476
+ Returns:
477
+ bool: True if lakehouse data is enabled, False otherwise
478
+ """
479
+ flags = await ocean.port_client.get_organization_feature_flags()
480
+ if "LAKEHOUSE_ELIGIBLE" in flags and ocean.config.lakehouse_enabled:
481
+ return True
482
+ return False
483
+
465
484
  async def register_raw(
466
485
  self,
467
486
  kind: str,
@@ -1,11 +1,11 @@
1
- from typing import Any, List, Generator
2
- from unittest.mock import MagicMock, patch, AsyncMock
1
+ from typing import Any, Generator, List
2
+ from unittest.mock import AsyncMock, MagicMock, patch
3
3
 
4
4
  import pytest
5
+ from httpx import ReadTimeout
5
6
 
6
7
  from port_ocean.clients.port.mixins.entities import EntityClientMixin
7
8
  from port_ocean.core.models import Entity
8
- from httpx import ReadTimeout
9
9
 
10
10
  # Mock the ocean context at module level
11
11
  pytestmark = pytest.mark.usefixtures("mock_ocean")
@@ -172,3 +172,44 @@ async def test_batch_upsert_entities_read_timeout_should_raise_true(
172
172
  await entity_client.upsert_entities_in_batches(
173
173
  entities=all_entities, request_options=MagicMock(), should_raise=True
174
174
  )
175
+
176
+
177
+ async def test_search_entities_uses_datasource_route_when_query_is_none(
178
+ entity_client: EntityClientMixin,
179
+ ) -> None:
180
+ """Test that search_entities uses datasource route when query is None"""
181
+ mock_response = MagicMock()
182
+ mock_response.json.return_value = {"entities": []}
183
+ mock_response.is_error = False
184
+ mock_response.status_code = 200
185
+ mock_response.headers = {}
186
+ entity_client.client.post = AsyncMock(return_value=mock_response) # type: ignore
187
+ entity_client.auth.headers = AsyncMock( # type: ignore
188
+ return_value={"Authorization": "Bearer test"}
189
+ )
190
+
191
+ entity_client.auth.integration_type = "test-integration"
192
+ entity_client.auth.integration_identifier = "test-identifier"
193
+ entity_client.auth.api_url = "https://api.getport.io/v1"
194
+
195
+ mock_user_agent_type = MagicMock()
196
+ mock_user_agent_type.value = "sync"
197
+
198
+ await entity_client.search_entities(
199
+ user_agent_type=mock_user_agent_type,
200
+ query=None,
201
+ )
202
+
203
+ entity_client.client.post.assert_called_once()
204
+ call_args = entity_client.client.post.call_args
205
+
206
+ assert (
207
+ call_args[0][0]
208
+ == "https://api.getport.io/v1/blueprints/entities/datasource-entities"
209
+ )
210
+
211
+ expected_json = {
212
+ "datasource_prefix": "port-ocean/test-integration/",
213
+ "datasource_suffix": "/test-identifier/sync",
214
+ }
215
+ assert call_args[1]["json"] == expected_json
@@ -1,10 +1,11 @@
1
1
  from contextlib import asynccontextmanager
2
2
  from typing import Any, AsyncGenerator
3
- from unittest.mock import MagicMock, AsyncMock, patch
3
+ from unittest.mock import AsyncMock, MagicMock, patch
4
4
 
5
5
  import pytest
6
6
  from httpx import Response
7
7
 
8
+ from port_ocean.cache.memory import InMemoryCacheProvider
8
9
  from port_ocean.clients.port.client import PortClient
9
10
  from port_ocean.config.settings import IntegrationSettings, MetricsSettings
10
11
  from port_ocean.context.event import EventContext
@@ -26,7 +27,6 @@ from port_ocean.core.handlers.port_app_config.models import (
26
27
  from port_ocean.core.models import Entity, ProcessExecutionMode
27
28
  from port_ocean.helpers.metric.metric import Metrics
28
29
  from port_ocean.ocean import Ocean
29
- from port_ocean.cache.memory import InMemoryCacheProvider
30
30
 
31
31
 
32
32
  @pytest.fixture
@@ -107,6 +107,7 @@ def mock_port_client(mock_http_client: MagicMock) -> PortClient:
107
107
  )
108
108
 
109
109
  mock_port_client.search_entities = AsyncMock(return_value=[]) # type: ignore
110
+ mock_port_client.get_organization_feature_flags = AsyncMock(return_value=[]) # type: ignore
110
111
  mock_port_client.client = mock_http_client
111
112
  return mock_port_client
112
113
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.25.5
3
+ Version: 0.26.2
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
@@ -1,4 +1,4 @@
1
- integrations/_infra/Dockerfile.Deb,sha256=mGbUtGmNbA2nUqDmhoy5ehU-UpBxc4hlB8yutXjOpgU,1998
1
+ integrations/_infra/Dockerfile.Deb,sha256=RW-BC8NaVmw3ASF7a3XNsjFN3ZN5gTVxbFrOmhBXq30,2251
2
2
  integrations/_infra/Dockerfile.alpine,sha256=7E4Sb-8supsCcseerHwTkuzjHZoYcaHIyxiBZ-wewo0,3482
3
3
  integrations/_infra/Dockerfile.base.builder,sha256=ESe1PKC6itp_AuXawbLI75k1Kruny6NTANaTinxOgVs,743
4
4
  integrations/_infra/Dockerfile.base.runner,sha256=uAcs2IsxrAAUHGXt_qULA5INr-HFguf5a5fCKiqEzbY,384
@@ -8,7 +8,7 @@ integrations/_infra/Makefile,sha256=YgLKvuF_Dw4IA7X98Nus6zIW_3cJ60M1QFGs3imj5c4,
8
8
  integrations/_infra/README.md,sha256=ZtJFSMCTU5zTeM8ddRuW1ZL1ga8z7Ic2F3mxmgOSjgo,1195
9
9
  integrations/_infra/entry_local.sh,sha256=Sn2TexTEpruH2ixIAGsk-fZV6Y7pT3jd2Pi9TxBeFuw,633
10
10
  integrations/_infra/grpcio.sh,sha256=m924poYznoRZ6Tt7Ct8Cs5AV_cmmOx598yIZ3z4DvZE,616
11
- integrations/_infra/init.sh,sha256=nN8lTrOhB286UfFvD6sJ9YJ-9asT9zVSddQB-RAb7Z4,99
11
+ integrations/_infra/init.sh,sha256=yyAall_O2PHRA1qS5_Pm-kGm7HKlASLFfzmyWGic6vg,104
12
12
  port_ocean/__init__.py,sha256=uMpjg5d_cXgnyCxA_LmICR8zqBmC6Fe9Ivu9hcvJ7EY,313
13
13
  port_ocean/bootstrap.py,sha256=CN1M5pVecZ7z_Vfu86Dk2HjFMiuiwt6E_SSOLFCYRMk,1321
14
14
  port_ocean/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -60,8 +60,8 @@ port_ocean/clients/port/authentication.py,sha256=r7r8Ag9WuwXy-CmgeOoj-PHbmJAQxhb
60
60
  port_ocean/clients/port/client.py,sha256=dv0mxIOde6J-wFi1FXXZkoNPVHrZzY7RSMhNkDD9xgA,3566
61
61
  port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
62
  port_ocean/clients/port/mixins/blueprints.py,sha256=aMCG4zePsMSMjMLiGrU37h5z5_ElfMzTcTvqvOI5wXY,4683
63
- port_ocean/clients/port/mixins/entities.py,sha256=_FKoSEUxc_fGcdoWm4ZAeUKUnnzkGPMDQEsxmpbj8Vo,23352
64
- port_ocean/clients/port/mixins/integrations.py,sha256=DdMLxSHL2zmlLecnkJk1lWJ2fxkY89pSSL-m7H0zBuI,11256
63
+ port_ocean/clients/port/mixins/entities.py,sha256=X2NqH00eK6TMJ3a3QEQRVQlKHzyj5l1FiPkIhonnbPg,24234
64
+ port_ocean/clients/port/mixins/integrations.py,sha256=9G1vo3n9pG1t6siUmPdYtxXbfhGXKhWAWwKHr8x7tU4,11891
65
65
  port_ocean/clients/port/mixins/migrations.py,sha256=vdL_A_NNUogvzujyaRLIoZEu5vmKDY2BxTjoGP94YzI,1467
66
66
  port_ocean/clients/port/mixins/organization.py,sha256=A2cP5V49KnjoAXxjmnm_XGth4ftPSU0qURNfnyUyS_Y,1041
67
67
  port_ocean/clients/port/retry_transport.py,sha256=PtIZOAZ6V-ncpVysRUsPOgt8Sf01QLnTKB5YeKBxkJk,1861
@@ -70,7 +70,7 @@ port_ocean/clients/port/utils.py,sha256=osFyAjw7Y5Qf2uVSqC7_RTCQfijiL1zS74JJM0go
70
70
  port_ocean/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
71
  port_ocean/config/base.py,sha256=x1gFbzujrxn7EJudRT81C6eN9WsYAb3vOHwcpcpX8Tc,6370
72
72
  port_ocean/config/dynamic.py,sha256=Lrk4JRGtR-0YKQ9DDGexX5NGFE7EJ6VoHya19YYhssM,2687
73
- port_ocean/config/settings.py,sha256=R4Ju15XKbwQEg2W7uUCxoj4_9gUS9uYUFQnX-FUNRDI,7156
73
+ port_ocean/config/settings.py,sha256=SaCophXTKj_MgJRw5vrIXaNds1NEfewt0jzPQ0ytM2o,7192
74
74
  port_ocean/consumers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
75
  port_ocean/consumers/kafka_consumer.py,sha256=N8KocjBi9aR0BOPG8hgKovg-ns_ggpEjrSxqSqF_BSo,4710
76
76
  port_ocean/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -122,7 +122,7 @@ port_ocean/core/integrations/mixins/events.py,sha256=2L7P3Jhp8XBqddh2_o9Cn4N261n
122
122
  port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
123
123
  port_ocean/core/integrations/mixins/live_events.py,sha256=zM24dhNc7uHx9XYZ6toVhDADPA90EnpOmZxgDegFZbA,4196
124
124
  port_ocean/core/integrations/mixins/sync.py,sha256=Vm_898pLKBwfVewtwouDWsXoxcOLicnAy6pzyqqk6U8,4053
125
- port_ocean/core/integrations/mixins/sync_raw.py,sha256=AzlOARxsKY1YYQ6xhjYiDD-vELkci0Vz7THw8hDVgAU,38522
125
+ port_ocean/core/integrations/mixins/sync_raw.py,sha256=6AHuroSoqIKvdx2BAKtILxfQFeYB_wikYaM4JaTeoGw,39315
126
126
  port_ocean/core/integrations/mixins/utils.py,sha256=N76dNu1Y6UEg0_pcSdF4RO6dQIZ8EBfX3xMelgWdMxM,3779
127
127
  port_ocean/core/models.py,sha256=DNbKpStMINI2lIekKprTqBevqkw_wFuFayN19w1aDfQ,2893
128
128
  port_ocean/core/ocean_types.py,sha256=bkLlTd8XfJK6_JDl0eXUHfE_NygqgiInSMwJ4YJH01Q,1399
@@ -159,12 +159,12 @@ port_ocean/tests/cache/test_memory_cache.py,sha256=xlwIOBU0RVLYYJU83l_aoZDzZ6QID
159
159
  port_ocean/tests/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
160
160
  port_ocean/tests/clients/oauth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
161
161
  port_ocean/tests/clients/oauth/test_oauth_client.py,sha256=2XVMQUalDpiD539Z7_dk5BK_ngXQzsTmb2lNBsfEm9c,3266
162
- port_ocean/tests/clients/port/mixins/test_entities.py,sha256=DeWbAQcaxT3LQQf_j9HA5nG7YgsQDvXmgK2aghlG9ug,6619
162
+ port_ocean/tests/clients/port/mixins/test_entities.py,sha256=_ZEQT7UMzg1gW8kH8oFjgRVcLwQb7dFac48Tw_vcCqk,8018
163
163
  port_ocean/tests/clients/port/mixins/test_integrations.py,sha256=vRt_EMsLozQC1LJNXxlvnHj3-FlOBGgAYxg5T0IAqtA,7621
164
164
  port_ocean/tests/clients/port/mixins/test_organization_mixin.py,sha256=zzKYz3h8dl4Z5A2QG_924m0y9U6XTth1XYOfwNrd_24,914
165
165
  port_ocean/tests/config/test_config.py,sha256=Rk4N-ldVSOfn1p23NzdVdfqUpPrqG2cMut4Sv-sAOrw,1023
166
166
  port_ocean/tests/conftest.py,sha256=JXASSS0IY0nnR6bxBflhzxS25kf4iNaABmThyZ0mZt8,101
167
- port_ocean/tests/core/conftest.py,sha256=7K_M1--wQ08VmiQRB0vo1nst2X00cwsuBS5UfERsnG8,7589
167
+ port_ocean/tests/core/conftest.py,sha256=5shShx81LRuQTBwS2sDIjNJO2LSD6cUNz46SgNYzjGY,7686
168
168
  port_ocean/tests/core/defaults/test_common.py,sha256=sR7RqB3ZYV6Xn6NIg-c8k5K6JcGsYZ2SCe_PYX5vLYM,5560
169
169
  port_ocean/tests/core/handlers/entities_state_applier/test_applier.py,sha256=_CZyViY9_gnxjY6ogWcDdmEDuejvpALogf9ESjVAwFY,10675
170
170
  port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=AyzLclJFKz8YzSi2hiFYVdXRzCFmqQwTxnwNFTo9roU,14091
@@ -203,8 +203,8 @@ port_ocean/utils/repeat.py,sha256=U2OeCkHPWXmRTVoPV-VcJRlQhcYqPWI5NfmPlb1JIbc,32
203
203
  port_ocean/utils/signal.py,sha256=mMVq-1Ab5YpNiqN4PkiyTGlV_G0wkUDMMjTZp5z3pb0,1514
204
204
  port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
205
205
  port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
206
- port_ocean-0.25.5.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
207
- port_ocean-0.25.5.dist-info/METADATA,sha256=z6eqWLmVOCtooAj5NDNhgK76ZNsAtBXWufhYAz2ee80,6887
208
- port_ocean-0.25.5.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
209
- port_ocean-0.25.5.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
210
- port_ocean-0.25.5.dist-info/RECORD,,
206
+ port_ocean-0.26.2.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
207
+ port_ocean-0.26.2.dist-info/METADATA,sha256=vKDMPtrSkiVpDBs1OdmO3RdRYqp88fLfKuQdXPL_no0,6887
208
+ port_ocean-0.26.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
209
+ port_ocean-0.26.2.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
210
+ port_ocean-0.26.2.dist-info/RECORD,,