vecorel-cli 0.2.2__tar.gz → 0.2.3__tar.gz
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.
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/PKG-INFO +1 -1
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/pyproject.toml +1 -1
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/vecorel/schemas.py +107 -13
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli.egg-info/PKG-INFO +1 -1
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/LICENSE +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/README.md +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/setup.cfg +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_convert.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_converters.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_create_geojson.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_create_geoparquet.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_create_stac.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_describe.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_encoding_auto.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_encoding_geojson.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_encoding_geoparquet.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_improve.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_jsonschema.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_merge.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_ops.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_rename_extension.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_validate.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/tests/test_validate_schema.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/__init__.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/basecommand.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/cli/__init__.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/cli/logger.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/cli/options.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/cli/path_url.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/cli/setup.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/cli/util.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/const.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/conversion/__init__.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/conversion/admin.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/conversion/base.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/convert.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/converters.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/create_geojson.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/create_geoparquet.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/create_jsonschema.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/create_stac.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/datasets/__init__.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/datasets/template.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/describe.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/encoding/__init__.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/encoding/auto.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/encoding/base.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/encoding/geojson.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/encoding/geoparquet.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/improve.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/jsonschema/__init__.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/jsonschema/template.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/merge.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/parquet/__init__.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/parquet/geopandas.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/parquet/types.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/registry.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/rename_extension.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/validate.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/validate_schema.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/validation/__init__.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/validation/base.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/validation/data.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/validation/geojson.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/validation/geoparquet.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/vecorel/__init__.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/vecorel/collection.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/vecorel/extensions.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/vecorel/ops.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/vecorel/typing.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/vecorel/util.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli/vecorel/version.py +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli.egg-info/SOURCES.txt +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli.egg-info/dependency_links.txt +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli.egg-info/entry_points.txt +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli.egg-info/requires.txt +0 -0
- {vecorel_cli-0.2.2 → vecorel_cli-0.2.3}/vecorel_cli.egg-info/top_level.txt +0 -0
|
@@ -95,13 +95,105 @@ class VecorelSchema(dict):
|
|
|
95
95
|
if self.get_sdl_version() != other.get_sdl_version():
|
|
96
96
|
raise ValueError("Schemas have different SDL versions, can't merge.")
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
self.
|
|
100
|
-
|
|
98
|
+
# Merge required properties
|
|
99
|
+
self["required"] = list(set(self["required"]) | set(other.get("required", [])))
|
|
100
|
+
|
|
101
|
+
# Merge collection details
|
|
102
|
+
other_collection = other.get("collection", {}).items()
|
|
103
|
+
for key, value in other_collection:
|
|
104
|
+
if key not in self["collection"]:
|
|
105
|
+
self["collection"][key] = value
|
|
106
|
+
else:
|
|
107
|
+
self_value = self["collection"][key]
|
|
108
|
+
if self_value != value:
|
|
109
|
+
raise ValueError(
|
|
110
|
+
f"Schema has conflicts in 'collection': Property '{key}' has values '{self_value}' and '{value}'."
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
pass # exists as is, no action needed
|
|
114
|
+
|
|
115
|
+
# Merge actual schemas for properties
|
|
116
|
+
self["properties"] = self._merge_properties(
|
|
117
|
+
self["properties"], other.get("properties", {}), "properties"
|
|
118
|
+
)
|
|
101
119
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
120
|
+
def _merge_properties(self, a: dict, b: dict, path: str = "") -> dict:
|
|
121
|
+
all_properties = set(a.keys()) | set(b.keys())
|
|
122
|
+
a = a.copy()
|
|
123
|
+
for key in all_properties:
|
|
124
|
+
if key in a:
|
|
125
|
+
if key in b:
|
|
126
|
+
# merge schemas
|
|
127
|
+
a[key] = self._merge_json_schema(a[key], b[key], f"{path}.{key}")
|
|
128
|
+
else:
|
|
129
|
+
pass # doesn't exist in other schema, keep as is
|
|
130
|
+
else:
|
|
131
|
+
# doesn't exist in this schema, just add it from the other schema
|
|
132
|
+
a[key] = b[key]
|
|
133
|
+
return a
|
|
134
|
+
|
|
135
|
+
def _merge_json_schema(self, a: dict, b: dict, path: str = "") -> dict:
|
|
136
|
+
"""Merge two JSON schemas"""
|
|
137
|
+
if not isinstance(a, dict) or not isinstance(b, dict):
|
|
138
|
+
raise ValueError(
|
|
139
|
+
f"Conflict in '{path}': Cannot merge types '{type(a)}' and '{type(b)}'."
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
a = a.copy()
|
|
143
|
+
all_keys = set(a.keys()) | set(b.keys())
|
|
144
|
+
for key in all_keys:
|
|
145
|
+
if key == "additionalProperties":
|
|
146
|
+
# We need to handle additionalProperties separately as it has a default value
|
|
147
|
+
ap1 = a.get("additionalProperties", False)
|
|
148
|
+
ap2 = b.get("additionalProperties", False)
|
|
149
|
+
if isinstance(ap1, dict) and isinstance(ap2, dict):
|
|
150
|
+
a[key] = self._merge_json_schema(ap1, ap2, f"{path}.additionalProperties")
|
|
151
|
+
elif a[key] is False or b[key] is False:
|
|
152
|
+
a[key] = False
|
|
153
|
+
elif a[key] is True and isinstance(b[key], dict):
|
|
154
|
+
a[key] = b[key]
|
|
155
|
+
else:
|
|
156
|
+
pass # a is good as is, no action needed
|
|
157
|
+
elif key not in b:
|
|
158
|
+
continue
|
|
159
|
+
elif key not in a:
|
|
160
|
+
a[key] = b[key]
|
|
161
|
+
else:
|
|
162
|
+
p = f"{path}.{key}"
|
|
163
|
+
match key:
|
|
164
|
+
case "$schema":
|
|
165
|
+
pass # ignore for now. todo: Properly check versions
|
|
166
|
+
case "required":
|
|
167
|
+
a[key] = list(set(a[key]) | set(b[key]))
|
|
168
|
+
case ("enum", "geometryTypes"):
|
|
169
|
+
a[key] = list(set(a[key]) & set(b[key]))
|
|
170
|
+
case ("items", "contains"):
|
|
171
|
+
a[key] = self._merge_json_schema(a[key], b[key], p)
|
|
172
|
+
case ("properties", "patternProperties"):
|
|
173
|
+
a[key] = self._merge_properties(a[key], b[key], p)
|
|
174
|
+
case ("minLength", "minimum", "exclusiveMinimum", "minItems", "minProperties"):
|
|
175
|
+
a[key] = max(a[key], b[key])
|
|
176
|
+
case ("maxLength", "maximum", "exclusiveMaximum", "maxItems", "maxProperties"):
|
|
177
|
+
a[key] = min(a[key], b[key])
|
|
178
|
+
case ("deprecated", "uniqueItems"):
|
|
179
|
+
if b[key]:
|
|
180
|
+
a[key] = True
|
|
181
|
+
case _: # description, type, format, pattern, default
|
|
182
|
+
if a[key] == b[key]:
|
|
183
|
+
a[key] = b[key]
|
|
184
|
+
else:
|
|
185
|
+
raise ValueError(f"Conflict in '{p}': '{a[key]}' != '{b[key]}'")
|
|
186
|
+
|
|
187
|
+
if "minimum" in a and "exclusiveMinimum" in a:
|
|
188
|
+
raise ValueError(
|
|
189
|
+
f"Conflict in '{path}': 'minimum' and 'exclusiveMinimum' cannot coexist"
|
|
190
|
+
)
|
|
191
|
+
if "maximum" in a and "exclusiveMaximum" in a:
|
|
192
|
+
raise ValueError(
|
|
193
|
+
f"Conflict in '{path}': 'maximum' and 'exclusiveMaximum' cannot coexist"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
return a
|
|
105
197
|
|
|
106
198
|
def _check_conflicts(self, other: "VecorelSchema", where: str):
|
|
107
199
|
"""Check if there are conflicts between two sets of properties"""
|
|
@@ -109,13 +201,15 @@ class VecorelSchema(dict):
|
|
|
109
201
|
b = other.get(where)
|
|
110
202
|
if a is None or b is None:
|
|
111
203
|
return
|
|
112
|
-
if isinstance(a, dict):
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
204
|
+
if isinstance(a, dict) and isinstance(b, dict):
|
|
205
|
+
k1 = a.keys()
|
|
206
|
+
k2 = b.keys()
|
|
207
|
+
intersection = set(k1).intersection(set(k2))
|
|
208
|
+
for key in intersection:
|
|
209
|
+
if a[key] != b[key]:
|
|
210
|
+
raise ValueError(
|
|
211
|
+
f"Schema has conflicts: {', '.join(intersection)} in '{where}', e.g. for '{key}' values are '{a[key]}' and '{b[key]}'."
|
|
212
|
+
)
|
|
119
213
|
|
|
120
214
|
def pick(self, property_names: list[str], rename: dict[str, str] = {}) -> "VecorelSchema":
|
|
121
215
|
"""Pick and rename schemas for specific properties"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|