ogc-na 0.3.43__py3-none-any.whl → 0.3.45__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.

Potentially problematic release.


This version of ogc-na might be problematic. Click here for more details.

ogc/na/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.3.43'
16
- __version_tuple__ = version_tuple = (0, 3, 43)
15
+ __version__ = version = '0.3.45'
16
+ __version_tuple__ = version_tuple = (0, 3, 45)
ogc/na/annotate_schema.py CHANGED
@@ -122,7 +122,6 @@ import json
122
122
  import logging
123
123
  import re
124
124
  import sys
125
- from collections import deque
126
125
  from operator import attrgetter
127
126
  from pathlib import Path
128
127
  from typing import Any, AnyStr, Callable, Sequence, Iterable
@@ -131,7 +130,8 @@ from urllib.parse import urlparse, urljoin
131
130
  import jsonschema
132
131
  import requests_cache
133
132
 
134
- from ogc.na.util import is_url, load_yaml, LRUCache, dump_yaml, merge_contexts, merge_dicts, dict_contains
133
+ from ogc.na.util import is_url, load_yaml, LRUCache, dump_yaml, \
134
+ merge_contexts, merge_dicts, dict_contains, JSON_LD_KEYWORDS
135
135
 
136
136
  logger = logging.getLogger(__name__)
137
137
 
@@ -146,6 +146,8 @@ ANNOTATION_IGNORE_EXPAND = [ANNOTATION_CONTEXT, ANNOTATION_EXTRA_TERMS, ANNOTATI
146
146
 
147
147
  CURIE_TERMS = '@id', '@type', '@index'
148
148
 
149
+ UNDEFINED = object()
150
+
149
151
  context_term_cache = LRUCache(maxsize=20)
150
152
  requests_session = requests_cache.CachedSession('ogc.na.annotate_schema', backend='memory', expire_after=180)
151
153
 
@@ -340,12 +342,14 @@ def resolve_context(ctx: Path | str | dict | list, expand_uris=True) -> Resolved
340
342
  prefixes = {}
341
343
 
342
344
  def expand_uri(curie, ctx_stack):
343
- if not expand_uris or not ctx_stack or not curie or curie[0] == '@':
345
+ if not expand_uris or not ctx_stack or not curie or curie in JSON_LD_KEYWORDS:
344
346
  return curie
345
347
  if ':' in curie:
346
348
  prefix, localpart = curie.split(':', 1)
347
349
  else:
348
350
  prefix, localpart = None, None
351
+
352
+ vocab = UNDEFINED
349
353
  for c in reversed(ctx_stack):
350
354
  if localpart:
351
355
  # prefix:localpart format
@@ -354,9 +358,11 @@ def resolve_context(ctx: Path | str | dict | list, expand_uris=True) -> Resolved
354
358
  prefix_uri = term_val if isinstance(term_val, str) else term_val.get('@id')
355
359
  prefixes[prefix] = prefix_uri
356
360
  return f"{prefix_uri}{localpart}"
357
- elif '@vocab' in c:
358
- # look for @vocab
359
- return f"{c['@vocab']}{curie}"
361
+ elif '@vocab' in c and vocab is UNDEFINED:
362
+ # look for @vocab unless it has been overridden (e.g. set to null) somewhere down the chain
363
+ vocab = c['@vocab']
364
+ if isinstance(vocab, str):
365
+ return f"{c['@vocab']}{curie}"
360
366
 
361
367
  return curie
362
368
 
@@ -388,7 +394,11 @@ def resolve_context(ctx: Path | str | dict | list, expand_uris=True) -> Resolved
388
394
  resolved_ctx = {}
389
395
  inner_prefixes = {}
390
396
  for ctx_entry in inner_ctx:
391
- resolved_entry = resolve_context(ctx_entry)
397
+ if isinstance(ctx_entry, dict):
398
+ # Array entries must be wrapped with @context
399
+ resolved_entry = resolve_context({'@context': ctx_entry})
400
+ else:
401
+ resolved_entry = resolve_context(ctx_entry)
392
402
  inner_prefixes.update(resolved_entry.prefixes)
393
403
  resolved = ResolvedContext(merge_dicts(resolved_entry.context, resolved_ctx), inner_prefixes)
394
404
  else:
@@ -481,26 +491,28 @@ class SchemaAnnotator:
481
491
  updated_refs: set[int] = set()
482
492
 
483
493
  def find_prop_context(prop, context_stack) -> dict | None:
494
+ vocab = UNDEFINED
484
495
  for ctx in reversed(context_stack):
485
- vocab = ctx.get('@vocab')
496
+ if vocab is UNDEFINED and '@vocab' in ctx:
497
+ vocab = ctx.get('@vocab')
486
498
  if prop in ctx:
487
499
  prop_ctx = ctx[prop]
488
500
  if isinstance(prop_ctx, str):
489
- if vocab and ':' not in prop_ctx and prop_ctx[0] != '@':
501
+ if vocab and ':' not in prop_ctx and prop_ctx not in JSON_LD_KEYWORDS:
490
502
  prop_ctx = f"{vocab}{prop_ctx}"
491
503
  return {'@id': prop_ctx}
492
504
  elif '@id' not in prop_ctx and not vocab:
493
505
  raise ValueError(f'Missing @id for property {prop} in context {json.dumps(ctx, indent=2)}')
494
506
  else:
495
- result = {k: v for k, v in prop_ctx.items() if k.startswith('@')}
507
+ result = {k: v for k, v in prop_ctx.items() if k in JSON_LD_KEYWORDS}
496
508
  if vocab:
497
509
  prop_id = result.get('@id')
498
510
  if not prop_id:
499
511
  result['@id'] = f"{vocab}{prop}"
500
- elif ':' not in prop_id and prop_id[0] != '@':
512
+ elif ':' not in prop_id and prop_id not in JSON_LD_KEYWORDS:
501
513
  result['@id'] = f"{vocab}{prop_id}"
502
514
  return result
503
- elif '@vocab' in ctx:
515
+ elif isinstance(vocab, str):
504
516
  return {'@id': f"{ctx['@vocab']}{prop}"}
505
517
 
506
518
  def process_properties(obj: dict, context_stack: list[dict[str, Any]],
@@ -514,7 +526,7 @@ class SchemaAnnotator:
514
526
 
515
527
  used_terms = set()
516
528
  for prop in list(properties.keys()):
517
- if prop[0] == '@':
529
+ if prop in JSON_LD_KEYWORDS:
518
530
  # skip JSON-LD keywords
519
531
  continue
520
532
  prop_value = properties[prop]
@@ -531,7 +543,7 @@ class SchemaAnnotator:
531
543
  used_terms.add(prop)
532
544
  prop_schema_ctx = {f"{ANNOTATION_PREFIX}{k[1:]}": v
533
545
  for k, v in prop_ctx.items()
534
- if k[0] == '@' and k != '@context'}
546
+ if k in JSON_LD_KEYWORDS and k != '@context'}
535
547
  prop_ctx_base = prop_ctx.get('@context', {}).get('@base')
536
548
  if prop_ctx_base:
537
549
  prop_schema_ctx[ANNOTATION_BASE] = prop_ctx_base
@@ -608,12 +620,14 @@ class SchemaAnnotator:
608
620
  if len(context_stack) == level and context_stack[-1]:
609
621
  extra_terms = {}
610
622
  for k, v in context_stack[-1].items():
611
- if k[0] != '@' and k not in prefixes and k not in used_terms:
623
+ if k not in JSON_LD_KEYWORDS and k not in prefixes and k not in used_terms:
612
624
  if isinstance(v, dict):
613
625
  if len(v) == 1 and '@id' in v:
614
626
  v = v['@id']
615
627
  else:
616
- v = {f"{ANNOTATION_PREFIX}{vk[1:]}": vv for vk, vv in v.items() if vk[0] == '@'}
628
+ v = {f"{ANNOTATION_PREFIX}{vk[1:]}": vv
629
+ for vk, vv in v.items()
630
+ if vk in JSON_LD_KEYWORDS}
617
631
  if isinstance(v, str) and v[-1] in ('#', '/', ':'):
618
632
  prefixes[k] = v
619
633
  else:
@@ -806,7 +820,7 @@ class ContextBuilder:
806
820
  if compact:
807
821
 
808
822
  def compact_uri(uri: str) -> str:
809
- if uri.startswith('@'):
823
+ if uri in JSON_LD_KEYWORDS:
810
824
  # JSON-LD keyword
811
825
  return uri
812
826
 
@@ -821,7 +835,7 @@ class ContextBuilder:
821
835
 
822
836
  def compact_branch(branch, context_stack=None) -> bool:
823
837
  child_context_stack = context_stack + [branch] if context_stack else [branch]
824
- terms = list(k for k in branch.keys() if k[0] != '@')
838
+ terms = list(k for k in branch.keys() if k not in JSON_LD_KEYWORDS)
825
839
 
826
840
  changed = False
827
841
  for term in terms:
@@ -853,14 +867,15 @@ class ContextBuilder:
853
867
 
854
868
  def compact_uris(branch, context_stack=None):
855
869
  child_context_stack = context_stack + [branch] if context_stack else [branch]
856
- terms = list(k for k in branch.keys() if k[0] != '@')
870
+ terms = list(k for k in branch.keys() if k not in JSON_LD_KEYWORDS)
857
871
  for term in terms:
858
872
  term_value = branch.get(term)
859
873
  if isinstance(term_value, str):
860
874
  branch[term] = compact_uri(term_value)
861
875
  elif isinstance(term_value, dict):
862
- if '@id' in term_value:
863
- term_value['@id'] = compact_uri(term_value['@id'])
876
+ for k in CURIE_TERMS:
877
+ if k in term_value:
878
+ term_value[k] = compact_uri(term_value[k])
864
879
  if len(term_value) == 1 and '@id' in term_value:
865
880
  branch[term] = term_value['@id']
866
881
  elif '@context' in term_value:
ogc/na/util.py CHANGED
@@ -28,6 +28,32 @@ try:
28
28
  except ImportError:
29
29
  from yaml import Loader as YamlLoader, SafeLoader as SafeYamlLoader, Dumper as YamlDumper
30
30
 
31
+ JSON_LD_KEYWORDS = {
32
+ '@base',
33
+ '@container',
34
+ '@context',
35
+ '@direction',
36
+ '@graph',
37
+ '@id',
38
+ '@import',
39
+ '@included',
40
+ '@index',
41
+ '@json',
42
+ '@language',
43
+ '@list',
44
+ '@nest',
45
+ '@none',
46
+ '@prefix',
47
+ '@propagate',
48
+ '@protected',
49
+ '@reverse',
50
+ '@set',
51
+ '@type',
52
+ '@value',
53
+ '@version',
54
+ '@vocab'
55
+ }
56
+
31
57
 
32
58
  class ContextMergeError(Exception):
33
59
  pass
@@ -294,7 +320,7 @@ def merge_contexts(a: dict, b: dict, fix_nest=True) -> dict[str, Any]:
294
320
  for term in list(a.keys()):
295
321
  va = a[term]
296
322
  vb = b.get(term)
297
- if term[0] != '@':
323
+ if term not in JSON_LD_KEYWORDS:
298
324
  if isinstance(va, str):
299
325
  va = {'@id': va}
300
326
  a[term] = va
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ogc_na
3
- Version: 0.3.43
3
+ Version: 0.3.45
4
4
  Summary: OGC Naming Authority tools
5
5
  Author-email: Rob Atkinson <ratkinson@ogc.org>, Piotr Zaborowski <pzaborowski@ogc.org>, Alejandro Villar <avillar@ogc.org>
6
6
  Project-URL: Homepage, https://github.com/opengeospatial/ogc-na-tools/
@@ -1,6 +1,6 @@
1
1
  ogc/na/__init__.py,sha256=uzcNiJ3uKFNJ1HBfKxIwgAy2HMUFsLAe5RkrUg8ncac,464
2
- ogc/na/_version.py,sha256=3SM8TQK0XTeTAO_UFFJd4QeOtXiE6t02ayy_xRZ5U98,413
3
- ogc/na/annotate_schema.py,sha256=zhg8sBSWcfr1JlGALomrKVhrmJQ8HMkIfLVf1yqQg9I,39592
2
+ ogc/na/_version.py,sha256=94Zx_mr9eAoDR-QvqK5Ld0uUkeJswI5m5cnKSahrcV8,413
3
+ ogc/na/annotate_schema.py,sha256=IdVc3my8FkxvJwwmIGFu5RcyvyOK-dQjwLWh-NFrm4g,40367
4
4
  ogc/na/domain_config.py,sha256=C6ao37dbXEKv_K7WcfzQgI3H1Hr3c4-3p24hgl2oH54,13896
5
5
  ogc/na/download.py,sha256=2afrLyl4WsAlxkCgXsl47fs9mNKfDmhVpeT2iwNSoq0,3354
6
6
  ogc/na/gsp.py,sha256=KGa2G9i8kPefYTHNPUDoXnNyF7Tiwt8K__Ew_Qa7eeg,6048
@@ -9,12 +9,12 @@ ogc/na/models.py,sha256=nGV8EALtXvmBtkUbu0FA4KOgwNUqQGWIDuMo7UGOKP8,652
9
9
  ogc/na/profile.py,sha256=T7nesbm7azF2ijF60UenJnQQKjIgJlnJ3pUbGT5nYgM,16511
10
10
  ogc/na/provenance.py,sha256=h7UtUb1wW_9MfEim0fjcqkHd0BYmpXqyE1ADzL9AH7I,5551
11
11
  ogc/na/update_vocabs.py,sha256=joLZxhpBJja5GvaBnt0aHQStKETOCIh5PotUxmJsOHs,18180
12
- ogc/na/util.py,sha256=ayMUT9HzNKynpSDTZ8epM8qSMTu4-_KlEL-Zlci2Pnk,11573
12
+ ogc/na/util.py,sha256=r-cwqxqVVH1oXWsWuobTvXFGUVCqwlalN97b85nUN4U,11951
13
13
  ogc/na/validation.py,sha256=5xjHH55NZKM8HtUk8XgVzm8W5ZlZY00u_qsWfXK_8dM,3732
14
14
  ogc/na/input_filters/__init__.py,sha256=AhE7n_yECwxFKwOM3Jc0ft96TtF5i_Z-fHrS4HYOjaE,1179
15
15
  ogc/na/input_filters/csv.py,sha256=nFfB1XQF_QApcGGzMqEvzD_b3pBtCtsfUECsZ9UGE6s,2616
16
16
  ogc/na/input_filters/xml.py,sha256=9qYjp_w5JLInFM48zB15IYH9eTafjp1Aqd_8kfuW3aA,2074
17
- ogc_na-0.3.43.dist-info/METADATA,sha256=tANOeQvrhO_4uhdtrPJs2zpyG7gjSA-3InVOt-5_mx0,3796
18
- ogc_na-0.3.43.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
19
- ogc_na-0.3.43.dist-info/top_level.txt,sha256=Kvy3KhzcIhNPT4_nZuJCmS946ptRr_MDyU4IIhZJhCY,4
20
- ogc_na-0.3.43.dist-info/RECORD,,
17
+ ogc_na-0.3.45.dist-info/METADATA,sha256=M64A21bqXChPrmV3fK6LfkXI9khRbLYtDTkC4xc2qv0,3796
18
+ ogc_na-0.3.45.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
19
+ ogc_na-0.3.45.dist-info/top_level.txt,sha256=Kvy3KhzcIhNPT4_nZuJCmS946ptRr_MDyU4IIhZJhCY,4
20
+ ogc_na-0.3.45.dist-info/RECORD,,