port-ocean 0.29.10__py3-none-any.whl → 0.30.1__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.
- port_ocean/clients/port/mixins/entities.py +14 -0
- port_ocean/core/handlers/actions/execution_manager.py +0 -8
- port_ocean/core/handlers/entity_processor/jq_entity_processor.py +31 -9
- port_ocean/core/models.py +0 -1
- port_ocean/tests/core/handlers/actions/test_execution_manager.py +0 -4
- port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py +29 -6
- {port_ocean-0.29.10.dist-info → port_ocean-0.30.1.dist-info}/METADATA +1 -1
- {port_ocean-0.29.10.dist-info → port_ocean-0.30.1.dist-info}/RECORD +11 -11
- {port_ocean-0.29.10.dist-info → port_ocean-0.30.1.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.29.10.dist-info → port_ocean-0.30.1.dist-info}/WHEEL +0 -0
- {port_ocean-0.29.10.dist-info → port_ocean-0.30.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
|
+
from collections import Counter
|
|
3
4
|
from typing import Any, Literal
|
|
4
5
|
from urllib.parse import quote_plus
|
|
5
6
|
|
|
@@ -355,6 +356,19 @@ class EntityClientMixin:
|
|
|
355
356
|
entities_results: list[tuple[bool, Entity]] = []
|
|
356
357
|
blueprint = entities[0].blueprint
|
|
357
358
|
|
|
359
|
+
identifier_counts = Counter((e.blueprint, e.identifier) for e in entities)
|
|
360
|
+
duplicate_count = sum(
|
|
361
|
+
count - 1 for count in identifier_counts.values() if count > 1
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
if duplicate_count:
|
|
365
|
+
duplicate_examples = [
|
|
366
|
+
key for key, cnt in identifier_counts.items() if cnt > 1
|
|
367
|
+
][:5]
|
|
368
|
+
logger.warning(
|
|
369
|
+
f"Detected {duplicate_count} duplicate entities (by blueprint and identifier) that may not be ingested because an identical identifier existed. Examples: {duplicate_examples}"
|
|
370
|
+
)
|
|
371
|
+
|
|
358
372
|
bulk_size = self.calculate_entities_batch_size(entities)
|
|
359
373
|
bulks = [
|
|
360
374
|
entities[i : i + bulk_size] for i in range(0, len(entities), bulk_size)
|
|
@@ -13,7 +13,6 @@ from port_ocean.core.handlers.webhook.processor_manager import (
|
|
|
13
13
|
LiveEventsProcessorManager,
|
|
14
14
|
)
|
|
15
15
|
from port_ocean.context.ocean import ocean
|
|
16
|
-
from port_ocean.core.models import IntegrationFeatureFlag
|
|
17
16
|
from port_ocean.exceptions.execution_manager import (
|
|
18
17
|
DuplicateActionExecutorError,
|
|
19
18
|
PartitionKeyNotFoundError,
|
|
@@ -132,13 +131,6 @@ class ExecutionManager:
|
|
|
132
131
|
"""
|
|
133
132
|
Start polling and processing action runs for all registered actions.
|
|
134
133
|
"""
|
|
135
|
-
flags = await ocean.port_client.get_organization_feature_flags()
|
|
136
|
-
if IntegrationFeatureFlag.OCEAN_ACTIONS_PROCESSING_ENABLED not in flags:
|
|
137
|
-
logger.warning(
|
|
138
|
-
"Actions processing is not allowed for your organization, skipping actions processing"
|
|
139
|
-
)
|
|
140
|
-
return
|
|
141
|
-
|
|
142
134
|
if not await ocean.port_client.auth.is_machine_user():
|
|
143
135
|
logger.warning(
|
|
144
136
|
"Actions processing is allowed only for machine users, skipping actions processing"
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import re
|
|
2
4
|
from asyncio import Task
|
|
3
5
|
from dataclasses import dataclass, field
|
|
4
6
|
from functools import lru_cache
|
|
5
|
-
import json
|
|
6
7
|
from typing import Any, Optional
|
|
8
|
+
|
|
7
9
|
import jq # type: ignore
|
|
8
10
|
from loguru import logger
|
|
11
|
+
|
|
9
12
|
from port_ocean.context.ocean import ocean
|
|
10
13
|
from port_ocean.core.handlers.entity_processor.base import BaseEntityProcessor
|
|
14
|
+
from port_ocean.core.handlers.entity_processor.jq_input_evaluator import (
|
|
15
|
+
InputClassifyingResult,
|
|
16
|
+
can_expression_run_with_no_input,
|
|
17
|
+
classify_input,
|
|
18
|
+
)
|
|
11
19
|
from port_ocean.core.handlers.port_app_config.models import ResourceConfig
|
|
12
20
|
from port_ocean.core.models import Entity
|
|
13
21
|
from port_ocean.core.ocean_types import (
|
|
14
22
|
RAW_ITEM,
|
|
15
|
-
EntitySelectorDiff,
|
|
16
23
|
CalculationResult,
|
|
24
|
+
EntitySelectorDiff,
|
|
17
25
|
)
|
|
18
26
|
from port_ocean.core.utils.utils import (
|
|
19
27
|
gather_and_split_errors_from_results,
|
|
@@ -21,11 +29,6 @@ from port_ocean.core.utils.utils import (
|
|
|
21
29
|
)
|
|
22
30
|
from port_ocean.exceptions.core import EntityProcessorException
|
|
23
31
|
from port_ocean.utils.queue_utils import process_in_queue
|
|
24
|
-
from port_ocean.core.handlers.entity_processor.jq_input_evaluator import (
|
|
25
|
-
InputClassifyingResult,
|
|
26
|
-
classify_input,
|
|
27
|
-
can_expression_run_with_no_input,
|
|
28
|
-
)
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
class ExampleStates:
|
|
@@ -92,8 +95,29 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
92
95
|
searching for data in dictionaries, and transforming data based on object mappings.
|
|
93
96
|
"""
|
|
94
97
|
|
|
98
|
+
@staticmethod
|
|
99
|
+
def _format_filter(filter: str) -> str:
|
|
100
|
+
"""
|
|
101
|
+
Convert single quotes to double quotes in JQ expressions.
|
|
102
|
+
Only replaces single quotes that are opening or closing string delimiters,
|
|
103
|
+
not single quotes that are part of string content.
|
|
104
|
+
"""
|
|
105
|
+
# Escape single quotes only if they are opening or closing a string
|
|
106
|
+
# Pattern matches:
|
|
107
|
+
# - Single quote at start of string or after whitespace (opening quote)
|
|
108
|
+
# - Single quote before whitespace or end of string (closing quote)
|
|
109
|
+
# Uses negative lookahead/lookbehind to avoid replacing quotes inside strings
|
|
110
|
+
# \1 and \2 will be empty for the alternative that didn't match, so \1"\2 works for both cases
|
|
111
|
+
# This matches the TypeScript pattern: /(^|\s)'(?!\s|")|(?<!\s|")'(\s|$)/g
|
|
112
|
+
formatted_filter = re.sub(
|
|
113
|
+
r'(^|\s)\'(?!\s|")|(?<!\s|")\'(\s|$)', r'\1"\2', filter
|
|
114
|
+
)
|
|
115
|
+
return formatted_filter
|
|
116
|
+
|
|
95
117
|
@lru_cache
|
|
96
118
|
def _compile(self, pattern: str) -> Any:
|
|
119
|
+
# Convert single quotes to double quotes for JQ compatibility
|
|
120
|
+
pattern = self._format_filter(pattern)
|
|
97
121
|
if not ocean.config.allow_environment_variables_jq_access:
|
|
98
122
|
pattern = "def env: {}; {} as $ENV | " + pattern
|
|
99
123
|
return jq.compile(pattern)
|
|
@@ -119,7 +143,6 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
119
143
|
missing_required_fields: bool,
|
|
120
144
|
entity_mapping_fault_counter: int,
|
|
121
145
|
) -> None:
|
|
122
|
-
|
|
123
146
|
if len(entity_misconfigurations) > 0:
|
|
124
147
|
logger.info(
|
|
125
148
|
f"Unable to find valid data for: {entity_misconfigurations} (null, missing, or misconfigured)"
|
|
@@ -444,7 +467,6 @@ class JQEntityProcessor(BaseEntityProcessor):
|
|
|
444
467
|
key: str,
|
|
445
468
|
value: dict[str, Any],
|
|
446
469
|
) -> None:
|
|
447
|
-
|
|
448
470
|
if key in ["properties", "relations"]:
|
|
449
471
|
mapping_dicts: dict[InputClassifyingResult, dict[str, Any]] = {
|
|
450
472
|
InputClassifyingResult.SINGLE: {},
|
port_ocean/core/models.py
CHANGED
|
@@ -134,7 +134,6 @@ class EntityPortDiff:
|
|
|
134
134
|
class IntegrationFeatureFlag(StrEnum):
|
|
135
135
|
USE_PROVISIONED_DEFAULTS = "USE_PROVISIONED_DEFAULTS"
|
|
136
136
|
LAKEHOUSE_ELIGIBLE = "LAKEHOUSE_ELIGIBLE"
|
|
137
|
-
OCEAN_ACTIONS_PROCESSING_ENABLED = "OCEAN_ACTIONS_PROCESSING_ENABLED"
|
|
138
137
|
|
|
139
138
|
|
|
140
139
|
class RunStatus(StrEnum):
|
|
@@ -25,7 +25,6 @@ from port_ocean.core.handlers.webhook.processor_manager import (
|
|
|
25
25
|
from port_ocean.core.models import (
|
|
26
26
|
ActionRun,
|
|
27
27
|
IntegrationActionInvocationPayload,
|
|
28
|
-
IntegrationFeatureFlag,
|
|
29
28
|
RunStatus,
|
|
30
29
|
)
|
|
31
30
|
from port_ocean.exceptions.execution_manager import (
|
|
@@ -62,9 +61,6 @@ def mock_port_client() -> MagicMock:
|
|
|
62
61
|
mock_port_client.get_run_by_external_id = AsyncMock()
|
|
63
62
|
mock_port_client.patch_run = AsyncMock()
|
|
64
63
|
mock_port_client.post_run_log = AsyncMock()
|
|
65
|
-
mock_port_client.get_organization_feature_flags = AsyncMock(
|
|
66
|
-
return_value=[IntegrationFeatureFlag.OCEAN_ACTIONS_PROCESSING_ENABLED]
|
|
67
|
-
)
|
|
68
64
|
mock_port_client.auth = AsyncMock(spec=PortAuthentication)
|
|
69
65
|
mock_port_client.auth.is_machine_user = AsyncMock(return_value=True)
|
|
70
66
|
return mock_port_client
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from typing import cast, Any
|
|
2
|
-
from unittest.mock import AsyncMock, Mock
|
|
3
|
-
from loguru import logger
|
|
4
|
-
import pytest
|
|
5
1
|
from io import StringIO
|
|
2
|
+
from typing import Any, cast
|
|
3
|
+
from unittest.mock import AsyncMock, Mock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from loguru import logger
|
|
6
7
|
|
|
7
8
|
from port_ocean.context.ocean import PortOceanContext
|
|
8
9
|
from port_ocean.core.handlers.entity_processor.jq_entity_processor import (
|
|
@@ -10,12 +11,10 @@ from port_ocean.core.handlers.entity_processor.jq_entity_processor import (
|
|
|
10
11
|
)
|
|
11
12
|
from port_ocean.core.ocean_types import CalculationResult
|
|
12
13
|
from port_ocean.exceptions.core import EntityProcessorException
|
|
13
|
-
from unittest.mock import patch
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@pytest.mark.asyncio
|
|
17
17
|
class TestJQEntityProcessor:
|
|
18
|
-
|
|
19
18
|
@pytest.fixture
|
|
20
19
|
def mocked_processor(self, monkeypatch: Any) -> JQEntityProcessor:
|
|
21
20
|
mock_context = AsyncMock()
|
|
@@ -33,6 +32,30 @@ class TestJQEntityProcessor:
|
|
|
33
32
|
result = await mocked_processor._search(data, pattern)
|
|
34
33
|
assert result == "bar"
|
|
35
34
|
|
|
35
|
+
async def test_search_with_single_quotes(
|
|
36
|
+
self, mocked_processor: JQEntityProcessor
|
|
37
|
+
) -> None:
|
|
38
|
+
data = {"repository": "ocean", "organization": "port"}
|
|
39
|
+
pattern = ".organization + '/' + .repository"
|
|
40
|
+
result = await mocked_processor._search(data, pattern)
|
|
41
|
+
assert result == "port/ocean"
|
|
42
|
+
|
|
43
|
+
async def test_search_with_single_quotes_in_the_end(
|
|
44
|
+
self, mocked_processor: JQEntityProcessor
|
|
45
|
+
) -> None:
|
|
46
|
+
data = {"organization": "port"}
|
|
47
|
+
pattern = ".organization + '/'"
|
|
48
|
+
result = await mocked_processor._search(data, pattern)
|
|
49
|
+
assert result == "port/"
|
|
50
|
+
|
|
51
|
+
async def test_search_with_single_quotes_in_the_start(
|
|
52
|
+
self, mocked_processor: JQEntityProcessor
|
|
53
|
+
) -> None:
|
|
54
|
+
data = {"organization": "port"}
|
|
55
|
+
pattern = "'/' + .organization"
|
|
56
|
+
result = await mocked_processor._search(data, pattern)
|
|
57
|
+
assert result == "/port"
|
|
58
|
+
|
|
36
59
|
async def test_search_as_bool(self, mocked_processor: JQEntityProcessor) -> None:
|
|
37
60
|
data = {"foo": True}
|
|
38
61
|
pattern = ".foo"
|
|
@@ -61,7 +61,7 @@ port_ocean/clients/port/client.py,sha256=LHR6zKgCCCyhe3aPWH0kRFYS02BN-lIDZMPQbaz
|
|
|
61
61
|
port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
62
|
port_ocean/clients/port/mixins/actions.py,sha256=XkmK1C1zH-u8hy04No-SzsVh5iH6csKkYsEtyBzb84E,3479
|
|
63
63
|
port_ocean/clients/port/mixins/blueprints.py,sha256=iAKwguhDpUL-YLd7GRNjS-monVgOG8UyKJFOengO_zM,4291
|
|
64
|
-
port_ocean/clients/port/mixins/entities.py,sha256=
|
|
64
|
+
port_ocean/clients/port/mixins/entities.py,sha256=n2Cwc944TADpQaLboZUJi7P7ibAa7mDCxvOAVKWJIRI,25900
|
|
65
65
|
port_ocean/clients/port/mixins/integrations.py,sha256=rzmfv3BfsBXX21VZrhZLsH5B5spvVBo6xIiXKxOwNvg,12236
|
|
66
66
|
port_ocean/clients/port/mixins/migrations.py,sha256=vdL_A_NNUogvzujyaRLIoZEu5vmKDY2BxTjoGP94YzI,1467
|
|
67
67
|
port_ocean/clients/port/mixins/organization.py,sha256=A2cP5V49KnjoAXxjmnm_XGth4ftPSU0qURNfnyUyS_Y,1041
|
|
@@ -96,7 +96,7 @@ port_ocean/core/event_listener/webhooks_only.py,sha256=No4nNR7fb4ZtivzCFWzpYq4cg
|
|
|
96
96
|
port_ocean/core/handlers/__init__.py,sha256=d7ShmS90gLRzGKJA6oNy2Zs_dF2yjkmYZInRhBnO9Rw,572
|
|
97
97
|
port_ocean/core/handlers/actions/__init__.py,sha256=GNNfYb7C5cw5wPNVSFSmPENbvMZ1nXGhILWz04oasc4,223
|
|
98
98
|
port_ocean/core/handlers/actions/abstract_executor.py,sha256=4elvJuImKFO36D6fNJIgMho-bdPow-MBpMXSqtBvu2w,6189
|
|
99
|
-
port_ocean/core/handlers/actions/execution_manager.py,sha256=
|
|
99
|
+
port_ocean/core/handlers/actions/execution_manager.py,sha256=kr2yOnStobCEj5em6eWo6KrAXqLYyinR90zXw5hvwUk,19005
|
|
100
100
|
port_ocean/core/handlers/base.py,sha256=cTarblazu8yh8xz2FpB-dzDKuXxtoi143XJgPbV_DcM,157
|
|
101
101
|
port_ocean/core/handlers/entities_state_applier/__init__.py,sha256=kgLZDCeCEzi4r-0nzW9k78haOZNf6PX7mJOUr34A4c8,173
|
|
102
102
|
port_ocean/core/handlers/entities_state_applier/base.py,sha256=5wHL0icfFAYRPqk8iV_wN49GdJ3aRUtO8tumSxBi4Wo,2268
|
|
@@ -106,7 +106,7 @@ port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py,sha
|
|
|
106
106
|
port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py,sha256=lyv6xKzhYfd6TioUgR3AVRSJqj7JpAaj1LxxU2xAqeo,1720
|
|
107
107
|
port_ocean/core/handlers/entity_processor/__init__.py,sha256=FvFCunFg44wNQoqlybem9MthOs7p1Wawac87uSXz9U8,156
|
|
108
108
|
port_ocean/core/handlers/entity_processor/base.py,sha256=PsnpNRqjHth9xwOvDRe7gKu8cjnVV0XGmTIHGvOelX0,1867
|
|
109
|
-
port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=
|
|
109
|
+
port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=_ldJ71UIfZuNcLOgV5FYKllFohrtK13rDT9dI78ekPc,33279
|
|
110
110
|
port_ocean/core/handlers/entity_processor/jq_input_evaluator.py,sha256=R88wf69RVtBl8t5m2IKGTmgt4JEQSbct_AmHI_tUOjg,5350
|
|
111
111
|
port_ocean/core/handlers/port_app_config/__init__.py,sha256=8AAT5OthiVM7KCcM34iEgEeXtn2pRMrT4Dze5r1Ixbk,134
|
|
112
112
|
port_ocean/core/handlers/port_app_config/api.py,sha256=r_Th66NEw38IpRdnXZcRvI8ACfvxW_A6V62WLwjWXlQ,1044
|
|
@@ -131,7 +131,7 @@ port_ocean/core/integrations/mixins/live_events.py,sha256=zM24dhNc7uHx9XYZ6toVhD
|
|
|
131
131
|
port_ocean/core/integrations/mixins/sync.py,sha256=Vm_898pLKBwfVewtwouDWsXoxcOLicnAy6pzyqqk6U8,4053
|
|
132
132
|
port_ocean/core/integrations/mixins/sync_raw.py,sha256=kcL7flnQ25E3KKyo6L3aL9wSzgBtoWYzgQjS4uRbDOs,42612
|
|
133
133
|
port_ocean/core/integrations/mixins/utils.py,sha256=JegPuIQGBXMnywHBIX30i7gYz0gY7_bW_Jx5LUuQM9c,13718
|
|
134
|
-
port_ocean/core/models.py,sha256=
|
|
134
|
+
port_ocean/core/models.py,sha256=8ZNEmM3Nq0VSB3fYJVgEdJVjJmaGjMnng-bm-ZBbTNg,3695
|
|
135
135
|
port_ocean/core/ocean_types.py,sha256=bkLlTd8XfJK6_JDl0eXUHfE_NygqgiInSMwJ4YJH01Q,1399
|
|
136
136
|
port_ocean/core/utils/entity_topological_sorter.py,sha256=MDUjM6OuDy4Xj68o-7InNN0w1jqjxeDfeY8U02vySNI,3081
|
|
137
137
|
port_ocean/core/utils/utils.py,sha256=6ySxua6JgVxcjESPL5MScdkpaUj5XR9srorGHHb0B2o,7157
|
|
@@ -176,9 +176,9 @@ port_ocean/tests/conftest.py,sha256=JXASSS0IY0nnR6bxBflhzxS25kf4iNaABmThyZ0mZt8,
|
|
|
176
176
|
port_ocean/tests/core/conftest.py,sha256=0Oql7R1iTbjPyNdUoO6M21IKknLwnCIgDRz2JQ7nf0w,7748
|
|
177
177
|
port_ocean/tests/core/defaults/test_common.py,sha256=sR7RqB3ZYV6Xn6NIg-c8k5K6JcGsYZ2SCe_PYX5vLYM,5560
|
|
178
178
|
port_ocean/tests/core/event_listener/test_kafka.py,sha256=RN_JOCy4aRDUNvyQocO6WFvUMH2XeAZy-PIWHOYnD9M,2888
|
|
179
|
-
port_ocean/tests/core/handlers/actions/test_execution_manager.py,sha256=
|
|
179
|
+
port_ocean/tests/core/handlers/actions/test_execution_manager.py,sha256=5bOlcQ9qcXF_leoSFtw3FzTa7C1awUPo6TSss5e_76w,30753
|
|
180
180
|
port_ocean/tests/core/handlers/entities_state_applier/test_applier.py,sha256=7XWgwUB9uVYRov4VbIz1A-7n2YLbHTTYT-4rKJxjB0A,10711
|
|
181
|
-
port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256
|
|
181
|
+
port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=gyyWbkQop50NsPaHGq1K1wlUD_SYYdnquQfOTqTbnGc,59403
|
|
182
182
|
port_ocean/tests/core/handlers/entity_processor/test_jq_input_evaluator.py,sha256=xNlDK8d8YQOplgjZGSBq4rZYZx_atg2R5YyQnH0qfI4,42151
|
|
183
183
|
port_ocean/tests/core/handlers/mixins/test_live_events.py,sha256=Sbv9IZAGQoZDhf27xDjMMVYxUSie9mHltDtxLSqckmM,12548
|
|
184
184
|
port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=-Jd2rUG63fZM8LuyKtCp1tt4WEqO2m5woESjs1c91sU,44428
|
|
@@ -219,8 +219,8 @@ port_ocean/utils/repeat.py,sha256=U2OeCkHPWXmRTVoPV-VcJRlQhcYqPWI5NfmPlb1JIbc,32
|
|
|
219
219
|
port_ocean/utils/signal.py,sha256=J1sI-e_32VHP_VUa5bskLMFoJjJOAk5isrnewKDikUI,2125
|
|
220
220
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
|
221
221
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
|
222
|
-
port_ocean-0.
|
|
223
|
-
port_ocean-0.
|
|
224
|
-
port_ocean-0.
|
|
225
|
-
port_ocean-0.
|
|
226
|
-
port_ocean-0.
|
|
222
|
+
port_ocean-0.30.1.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
223
|
+
port_ocean-0.30.1.dist-info/METADATA,sha256=yi01OJ2_QzuHHCv43m_K8j1JdM9cCwBbCMkBEG4ncfw,7054
|
|
224
|
+
port_ocean-0.30.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
225
|
+
port_ocean-0.30.1.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
226
|
+
port_ocean-0.30.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|