cognite-neat 0.127.31__py3-none-any.whl → 0.128.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.
- cognite/neat/_config.py +53 -6
- cognite/neat/_session/_physical.py +8 -3
- cognite/neat/_session/_session.py +7 -14
- cognite/neat/_version.py +1 -1
- cognite/neat/v0/core/_utils/auth.py +10 -10
- cognite/neat/v1.py +2 -1
- {cognite_neat-0.127.31.dist-info → cognite_neat-0.128.1.dist-info}/METADATA +1 -1
- {cognite_neat-0.127.31.dist-info → cognite_neat-0.128.1.dist-info}/RECORD +10 -10
- {cognite_neat-0.127.31.dist-info → cognite_neat-0.128.1.dist-info}/WHEEL +0 -0
- {cognite_neat-0.127.31.dist-info → cognite_neat-0.128.1.dist-info}/licenses/LICENSE +0 -0
cognite/neat/_config.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import Any, Literal, TypeAlias
|
|
3
4
|
|
|
4
|
-
from pydantic import BaseModel, Field
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
5
6
|
|
|
7
|
+
from cognite.neat._exceptions import UserInputError
|
|
6
8
|
from cognite.neat._issues import ConsistencyError, ModelSyntaxError
|
|
9
|
+
from cognite.neat._utils.text import humanize_collection
|
|
7
10
|
from cognite.neat._utils.useful_types import ModusOperandi
|
|
8
11
|
|
|
9
12
|
if sys.version_info >= (3, 11):
|
|
@@ -11,8 +14,14 @@ if sys.version_info >= (3, 11):
|
|
|
11
14
|
else:
|
|
12
15
|
import tomli # type: ignore
|
|
13
16
|
|
|
17
|
+
PredefinedProfile: TypeAlias = Literal["legacy-additive", "legacy-rebuild", "deep-additive", "deep-rebuild"]
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
|
|
20
|
+
class ConfigModel(BaseModel):
|
|
21
|
+
model_config = ConfigDict(populate_by_name=True, validate_assignment=True)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ValidationConfig(ConfigModel):
|
|
16
25
|
"""Validation configuration."""
|
|
17
26
|
|
|
18
27
|
exclude: list[str] = Field(default_factory=list)
|
|
@@ -71,18 +80,45 @@ class ValidationConfig(BaseModel, populate_by_name=True):
|
|
|
71
80
|
return f"Excluded Rules: {', '.join(self.exclude)}"
|
|
72
81
|
|
|
73
82
|
|
|
74
|
-
class ModelingConfig(
|
|
83
|
+
class ModelingConfig(ConfigModel):
|
|
75
84
|
"""Modeling configuration."""
|
|
76
85
|
|
|
77
86
|
mode: ModusOperandi = "additive"
|
|
78
87
|
|
|
79
88
|
|
|
80
|
-
class
|
|
89
|
+
class AlphaFlagConfig(ConfigModel):
|
|
90
|
+
"""Alpha feature flags configuration."""
|
|
91
|
+
|
|
92
|
+
fix_validation_issues: bool = Field(
|
|
93
|
+
default=False,
|
|
94
|
+
description="If enabled, Neat will attempt to automatically fix certain validation issues in the data model.",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def __setattr__(self, key: str, value: Any) -> None:
|
|
98
|
+
"""Set attribute value or raise AttributeError."""
|
|
99
|
+
if key in self.model_fields:
|
|
100
|
+
super().__setattr__(key, value)
|
|
101
|
+
else:
|
|
102
|
+
available_flags = humanize_collection(type(self).model_fields.keys())
|
|
103
|
+
raise UserInputError(f"Alpha flag '{key}' not found. Available flags: {available_flags}.")
|
|
104
|
+
|
|
105
|
+
def _repr_html_(self) -> str:
|
|
106
|
+
"""HTML representation for Jupyter notebooks."""
|
|
107
|
+
lines = ["<b>Alpha Feature Flags:</b>"]
|
|
108
|
+
for field_name, field in type(self).model_fields.items():
|
|
109
|
+
value = getattr(self, field_name)
|
|
110
|
+
display = "Enabled" if value else "Disabled"
|
|
111
|
+
lines.append(f"<li><b>{field_name}</b>: {display} - {field.description}</li>")
|
|
112
|
+
return "<ul>" + "\n".join(lines) + "</ul>"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class NeatConfig(ConfigModel):
|
|
81
116
|
"""Configuration for a custom profile."""
|
|
82
117
|
|
|
83
118
|
profile: str
|
|
84
119
|
validation: ValidationConfig
|
|
85
120
|
modeling: ModelingConfig
|
|
121
|
+
alpha: AlphaFlagConfig = Field(default_factory=AlphaFlagConfig)
|
|
86
122
|
|
|
87
123
|
def __str__(self) -> str:
|
|
88
124
|
"""Human-readable configuration summary."""
|
|
@@ -93,8 +129,19 @@ class NeatConfig(BaseModel, populate_by_name=True):
|
|
|
93
129
|
]
|
|
94
130
|
return "\n".join(lines)
|
|
95
131
|
|
|
132
|
+
@classmethod
|
|
133
|
+
def create_predefined(cls, profile: PredefinedProfile = "legacy-additive") -> "NeatConfig":
|
|
134
|
+
"""Create NeatConfig from internal profiles."""
|
|
135
|
+
available_profiles = internal_profiles()
|
|
136
|
+
if profile not in available_profiles:
|
|
137
|
+
raise UserInputError(
|
|
138
|
+
f"Profile {profile!r} not found. Available predefined profiles: "
|
|
139
|
+
f"{humanize_collection(available_profiles.keys())}."
|
|
140
|
+
)
|
|
141
|
+
return available_profiles[profile]
|
|
142
|
+
|
|
96
143
|
|
|
97
|
-
def internal_profiles() -> dict[
|
|
144
|
+
def internal_profiles() -> dict[PredefinedProfile, NeatConfig]:
|
|
98
145
|
"""Get internal NeatConfig profile by name."""
|
|
99
146
|
return {
|
|
100
147
|
"legacy-additive": NeatConfig(
|
|
@@ -136,7 +183,7 @@ def internal_profiles() -> dict[str, NeatConfig]:
|
|
|
136
183
|
}
|
|
137
184
|
|
|
138
185
|
|
|
139
|
-
def
|
|
186
|
+
def get_neat_config_from_file(config_file_name: str, profile: str) -> NeatConfig:
|
|
140
187
|
"""Get NeatConfig from file or internal profiles.
|
|
141
188
|
|
|
142
189
|
Args:
|
|
@@ -68,7 +68,7 @@ class PhysicalDataModel:
|
|
|
68
68
|
|
|
69
69
|
@session_wrapper
|
|
70
70
|
class ReadPhysicalDataModel:
|
|
71
|
-
"""Read physical data model from various sources into NeatSession
|
|
71
|
+
"""Read physical data model from various sources into NeatSession store."""
|
|
72
72
|
|
|
73
73
|
def __init__(self, store: NeatStore, client: NeatClient, config: NeatConfig) -> None:
|
|
74
74
|
self._store = store
|
|
@@ -130,7 +130,12 @@ class ReadPhysicalDataModel:
|
|
|
130
130
|
return self._store.read_physical(reader, on_success)
|
|
131
131
|
|
|
132
132
|
def excel(self, io: Any) -> None:
|
|
133
|
-
"""Read physical data model from Excel file
|
|
133
|
+
"""Read physical data model from Excel file
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
io (Any): The file path or buffer to read from.
|
|
137
|
+
|
|
138
|
+
"""
|
|
134
139
|
|
|
135
140
|
path = NeatReader.create(io).materialize_path()
|
|
136
141
|
reader = DMSTableImporter.from_excel(path)
|
|
@@ -165,7 +170,7 @@ class ReadPhysicalDataModel:
|
|
|
165
170
|
|
|
166
171
|
@session_wrapper
|
|
167
172
|
class WritePhysicalDataModel:
|
|
168
|
-
"""Write physical data model to various sources from NeatSession
|
|
173
|
+
"""Write physical data model to various sources from NeatSession store."""
|
|
169
174
|
|
|
170
175
|
def __init__(self, store: NeatStore, client: NeatClient, config: NeatConfig) -> None:
|
|
171
176
|
self._store = store
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from typing import Literal
|
|
3
2
|
|
|
4
3
|
from cognite.client import ClientConfig, CogniteClient
|
|
5
4
|
|
|
6
5
|
from cognite.neat import _version
|
|
7
6
|
from cognite.neat._client import NeatClient
|
|
8
|
-
from cognite.neat._config import
|
|
7
|
+
from cognite.neat._config import NeatConfig, PredefinedProfile
|
|
9
8
|
from cognite.neat._state_machine import EmptyState, PhysicalState
|
|
10
9
|
from cognite.neat._store import NeatStore
|
|
11
10
|
from cognite.neat._utils.http_client import ParametersRequest, SuccessResponse
|
|
@@ -20,24 +19,18 @@ class NeatSession:
|
|
|
20
19
|
"""A session is an interface for neat operations."""
|
|
21
20
|
|
|
22
21
|
def __init__(
|
|
23
|
-
self,
|
|
24
|
-
client: CogniteClient | ClientConfig,
|
|
25
|
-
config: Literal["legacy-additive", "legacy-rebuild", "deep-additive", "deep-rebuild"] | None = None,
|
|
22
|
+
self, client: CogniteClient | ClientConfig, config: PredefinedProfile | NeatConfig = "legacy-additive"
|
|
26
23
|
) -> None:
|
|
27
24
|
"""Initialize a Neat session.
|
|
28
25
|
|
|
29
26
|
Args:
|
|
30
27
|
client (CogniteClient | ClientConfig): The Cognite client or client configuration to use for the session.
|
|
31
|
-
config (Literal["legacy-additive", "legacy-rebuild", "deep-additive", "deep-rebuild"] |
|
|
28
|
+
config (Literal["legacy-additive", "legacy-rebuild", "deep-additive", "deep-rebuild"] | NeatConfig):
|
|
32
29
|
The configuration profile to use for the session.
|
|
33
|
-
|
|
34
|
-
and apply only validations that were part of the legacy Neat version.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if config and config not in internal_profiles():
|
|
38
|
-
raise ValueError(f"Profile '{config}' not found among internal profiles.")
|
|
39
|
-
|
|
40
|
-
self._config = internal_profiles()[config or "legacy-additive"]
|
|
30
|
+
Defaults to "legacy-additive". This means Neat will perform additive modeling
|
|
31
|
+
and apply only validations that were part of the legacy Neat version.
|
|
32
|
+
"""
|
|
33
|
+
self._config = NeatConfig.create_predefined(config) if isinstance(config, str) else config
|
|
41
34
|
|
|
42
35
|
# Use configuration for physical data model
|
|
43
36
|
self._store = NeatStore()
|
cognite/neat/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.128.1"
|
|
2
2
|
__engine__ = "^2.0.4"
|
|
@@ -93,7 +93,7 @@ class EnvironmentVariables:
|
|
|
93
93
|
LOGIN_FLOW: _LOGIN_FLOW = "infer"
|
|
94
94
|
IDP_CLIENT_ID: str | None = None
|
|
95
95
|
IDP_CLIENT_SECRET: str | None = None
|
|
96
|
-
|
|
96
|
+
CDF_TOKEN: str | None = None
|
|
97
97
|
|
|
98
98
|
IDP_TENANT_ID: str | None = None
|
|
99
99
|
IDP_TOKEN_URL: str | None = None
|
|
@@ -103,7 +103,7 @@ class EnvironmentVariables:
|
|
|
103
103
|
IDP_SCOPES: str | None = None
|
|
104
104
|
IDP_AUTHORITY_URL: str | None = None
|
|
105
105
|
CDF_MAX_WORKERS: int | None = None
|
|
106
|
-
|
|
106
|
+
CDF_CLIENT_TIMEOUT: int | None = None
|
|
107
107
|
CDF_REDIRECT_PORT: int = 53_000
|
|
108
108
|
|
|
109
109
|
def __post_init__(self) -> None:
|
|
@@ -155,7 +155,7 @@ class EnvironmentVariables:
|
|
|
155
155
|
LOGIN_FLOW=os.environ.get("LOGIN_FLOW", "infer"), # type: ignore[arg-type]
|
|
156
156
|
IDP_CLIENT_ID=os.environ.get("IDP_CLIENT_ID"),
|
|
157
157
|
IDP_CLIENT_SECRET=os.environ.get("IDP_CLIENT_SECRET"),
|
|
158
|
-
|
|
158
|
+
CDF_TOKEN=os.environ.get("CDF_TOKEN"),
|
|
159
159
|
CDF_URL=os.environ.get("CDF_URL"),
|
|
160
160
|
IDP_TOKEN_URL=os.environ.get("IDP_TOKEN_URL"),
|
|
161
161
|
IDP_TENANT_ID=os.environ.get("IDP_TENANT_ID"),
|
|
@@ -163,7 +163,7 @@ class EnvironmentVariables:
|
|
|
163
163
|
IDP_SCOPES=os.environ.get("IDP_SCOPES"),
|
|
164
164
|
IDP_AUTHORITY_URL=os.environ.get("IDP_AUTHORITY_URL"),
|
|
165
165
|
CDF_MAX_WORKERS=int(os.environ["CDF_MAX_WORKERS"]) if "CDF_MAX_WORKERS" in os.environ else None,
|
|
166
|
-
|
|
166
|
+
CDF_CLIENT_TIMEOUT=int(os.environ["CDF_CLIENT_TIMEOUT"]) if "CDF_CLIENT_TIMEOUT" in os.environ else None,
|
|
167
167
|
CDF_REDIRECT_PORT=int(os.environ.get("CDF_REDIRECT_PORT", 53_000)),
|
|
168
168
|
)
|
|
169
169
|
|
|
@@ -179,7 +179,7 @@ class EnvironmentVariables:
|
|
|
179
179
|
IDP_CLIENT_ID="neat",
|
|
180
180
|
IDP_CLIENT_SECRET="secret",
|
|
181
181
|
IDP_SCOPES="project:read,project:write",
|
|
182
|
-
|
|
182
|
+
CDF_CLIENT_TIMEOUT=60,
|
|
183
183
|
CDF_MAX_WORKERS=3,
|
|
184
184
|
)
|
|
185
185
|
|
|
@@ -228,9 +228,9 @@ class EnvironmentVariables:
|
|
|
228
228
|
)
|
|
229
229
|
|
|
230
230
|
def get_token(self) -> Token:
|
|
231
|
-
if not self.
|
|
232
|
-
raise KeyError("
|
|
233
|
-
return Token(self.
|
|
231
|
+
if not self.CDF_TOKEN:
|
|
232
|
+
raise KeyError("CDF_TOKEN must be set in the environment", "CDF_TOKEN")
|
|
233
|
+
return Token(self.CDF_TOKEN)
|
|
234
234
|
|
|
235
235
|
def get_client(self) -> CogniteClient:
|
|
236
236
|
config = ClientConfig(
|
|
@@ -239,7 +239,7 @@ class EnvironmentVariables:
|
|
|
239
239
|
credentials=self.get_credentials(),
|
|
240
240
|
base_url=self.cdf_url,
|
|
241
241
|
max_workers=self.CDF_MAX_WORKERS,
|
|
242
|
-
timeout=self.
|
|
242
|
+
timeout=self.CDF_CLIENT_TIMEOUT,
|
|
243
243
|
)
|
|
244
244
|
return CogniteClient(config)
|
|
245
245
|
|
|
@@ -298,7 +298,7 @@ def _prompt_user() -> EnvironmentVariables:
|
|
|
298
298
|
variables.LOGIN_FLOW = login_flow # type: ignore[assignment]
|
|
299
299
|
if login_flow == "token":
|
|
300
300
|
token = Prompt.ask("Enter token")
|
|
301
|
-
variables.
|
|
301
|
+
variables.CDF_TOKEN = token
|
|
302
302
|
return variables
|
|
303
303
|
|
|
304
304
|
variables.IDP_CLIENT_ID = Prompt.ask("Enter IDP Client ID")
|
cognite/neat/v1.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cognite-neat
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.128.1
|
|
4
4
|
Summary: Knowledge graph transformation
|
|
5
5
|
Project-URL: Documentation, https://cognite-neat.readthedocs-hosted.com/
|
|
6
6
|
Project-URL: Homepage, https://cognite-neat.readthedocs-hosted.com/
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
cognite/neat/__init__.py,sha256=Lo4DbjDOwnhCYUoAgPp5RG1fDdF7OlnomalTe7n1ydw,211
|
|
2
|
-
cognite/neat/_config.py,sha256=
|
|
2
|
+
cognite/neat/_config.py,sha256=NXObeA-860LV40KlY4orsqjMGACa0jKRz2UE5L9kH6U,8401
|
|
3
3
|
cognite/neat/_exceptions.py,sha256=ox-5hXpee4UJlPE7HpuEHV2C96aLbLKo-BhPDoOAzhA,1650
|
|
4
4
|
cognite/neat/_issues.py,sha256=wH1mnkrpBsHUkQMGUHFLUIQWQlfJ_qMfdF7q0d9wNhY,1871
|
|
5
|
-
cognite/neat/_version.py,sha256=
|
|
5
|
+
cognite/neat/_version.py,sha256=8_r13miyHXqhT9zkzKnh5TPTyZ_MrFhvDyCdgRp7mMg,46
|
|
6
6
|
cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
cognite/neat/v1.py,sha256=
|
|
7
|
+
cognite/neat/v1.py,sha256=sLLxIO5MPsC0hsaex7HmMVVOzH1A8KTQBF2INrp_YCg,107
|
|
8
8
|
cognite/neat/_client/__init__.py,sha256=75Bh7eGhaN4sOt3ZcRzHl7pXaheu1z27kmTHeaI05vo,114
|
|
9
9
|
cognite/neat/_client/api.py,sha256=nbxCdWBXcTVM6MrQeT_VpB6ehfoI544JHPFq-ejQKCY,292
|
|
10
10
|
cognite/neat/_client/client.py,sha256=h0HELAHiBFxMNInkDu4AzbgfEIXqeM0BqqnMBmXjgi0,903
|
|
@@ -86,9 +86,9 @@ cognite/neat/_data_model/validation/dms/_views.py,sha256=M4egIa7UAMGtZlqzIxx6ZzL
|
|
|
86
86
|
cognite/neat/_session/__init__.py,sha256=owqW5Mml2DSZx1AvPvwNRTBngfhBNrQ6EH-7CKL7Jp0,61
|
|
87
87
|
cognite/neat/_session/_issues.py,sha256=E8UQeSJURg2dm4MF1pfD9dp-heSRT7pgQZgKlD1-FGs,2723
|
|
88
88
|
cognite/neat/_session/_opt.py,sha256=QcVK08JMmVzJpD0GKHelbljMOQi6CMD1w-maQOlbyZQ,1350
|
|
89
|
-
cognite/neat/_session/_physical.py,sha256=
|
|
89
|
+
cognite/neat/_session/_physical.py,sha256=ajNanS87THBESx_y1x6p6LomqOe51b197VKPaBPLZ30,11377
|
|
90
90
|
cognite/neat/_session/_result.py,sha256=po2X4s-Tioe0GQAGCfK862hKXNRX5YjJZsEzNcTC8nI,7879
|
|
91
|
-
cognite/neat/_session/_session.py,sha256=
|
|
91
|
+
cognite/neat/_session/_session.py,sha256=BNDrG5VdjviDGHiZD9HZcyuDCePdZAGHE-n0Yo8FW7Q,3238
|
|
92
92
|
cognite/neat/_session/_wrappers.py,sha256=9t_MnJ0Sw_v-f6oTIh8dtAT-3oEbqumGuND97aPCC3M,3581
|
|
93
93
|
cognite/neat/_session/_html/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
94
|
cognite/neat/_session/_html/_render.py,sha256=fD8iee4ql50CrHGH41SSh9Tw1lM0tHt-NF0OnnxHosg,1193
|
|
@@ -268,7 +268,7 @@ cognite/neat/v0/core/_store/_instance.py,sha256=zt7pgYwgMbnAwz7Xn-_AiMsXhbBdwXWB
|
|
|
268
268
|
cognite/neat/v0/core/_store/_provenance.py,sha256=Q96wkVXRovO_uTlNvwCAOl6pAoWItTgFq1F79L_FqBk,7335
|
|
269
269
|
cognite/neat/v0/core/_store/exceptions.py,sha256=dTaBSt7IV7XWtS3EsE8lBX1Dv3tfWX1nIEgGHkluy3s,1668
|
|
270
270
|
cognite/neat/v0/core/_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
271
|
-
cognite/neat/v0/core/_utils/auth.py,sha256=
|
|
271
|
+
cognite/neat/v0/core/_utils/auth.py,sha256=SHGujntO3_dGMzCXyF6R24ECtN8DZmmM_1cKigD6byk,14840
|
|
272
272
|
cognite/neat/v0/core/_utils/auxiliary.py,sha256=lflbBC_cxO3PyQGvnwIGDZZIOSGNNyNrbawaFe8tNys,7115
|
|
273
273
|
cognite/neat/v0/core/_utils/collection_.py,sha256=hYyvje47AAvY7sCmZy0NouX1OMVKLhJ6qAzwg0DIOhg,2382
|
|
274
274
|
cognite/neat/v0/core/_utils/graph_transformations_report.py,sha256=-V0LM9BI_bHT6PTTG9Qby4PGtFkYU3cxtF0cz8KoKek,1238
|
|
@@ -313,7 +313,7 @@ cognite/neat/v0/session/engine/__init__.py,sha256=D3MxUorEs6-NtgoICqtZ8PISQrjrr4
|
|
|
313
313
|
cognite/neat/v0/session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
|
|
314
314
|
cognite/neat/v0/session/engine/_interface.py,sha256=3W-cYr493c_mW3P5O6MKN1xEQg3cA7NHR_ev3zdF9Vk,533
|
|
315
315
|
cognite/neat/v0/session/engine/_load.py,sha256=u0x7vuQCRoNcPt25KJBJRn8sJabonYK4vtSZpiTdP4k,5201
|
|
316
|
-
cognite_neat-0.
|
|
317
|
-
cognite_neat-0.
|
|
318
|
-
cognite_neat-0.
|
|
319
|
-
cognite_neat-0.
|
|
316
|
+
cognite_neat-0.128.1.dist-info/METADATA,sha256=AfgJZmmNY7WbFk1_4pTwjfxNXsJiVvNW_eD29cQWlfE,9149
|
|
317
|
+
cognite_neat-0.128.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
318
|
+
cognite_neat-0.128.1.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
|
|
319
|
+
cognite_neat-0.128.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|