idlib 0.0.1.dev19__tar.gz → 0.0.1.dev21__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.
Files changed (48) hide show
  1. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/PKG-INFO +12 -2
  2. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/__init__.py +1 -1
  3. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/from_oq.py +61 -12
  4. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/streams.py +4 -1
  5. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/orcid.py +3 -4
  6. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/rrid.py +45 -20
  7. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/PKG-INFO +12 -2
  8. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/test/test_identifiers.py +2 -0
  9. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/LICENSE +0 -0
  10. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/Pipfile +0 -0
  11. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/README.md +0 -0
  12. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/docs/identifiers.org +0 -0
  13. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/docs/stream-grammer.org +0 -0
  14. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/apis/__init__.py +0 -0
  15. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/apis/protocols_io.py +0 -0
  16. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/auth-config.py +0 -0
  17. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/cache.py +0 -0
  18. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/config.py +0 -0
  19. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/conventions/__init__.py +0 -0
  20. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/conventions/core.py +0 -0
  21. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/conventions/local.py +0 -0
  22. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/conventions/type.py +0 -0
  23. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/core.py +0 -0
  24. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/edit.py +0 -0
  25. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/exceptions.py +0 -0
  26. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/formats/__init__.py +0 -0
  27. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/formats/rdf.py +0 -0
  28. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/identifiers.py +0 -0
  29. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/__init__.py +0 -0
  30. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/ark.py +0 -0
  31. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/doi.py +0 -0
  32. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/handle.py +0 -0
  33. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/isni.py +0 -0
  34. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/pmid.py +0 -0
  35. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/ror.py +0 -0
  36. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/urn.py +0 -0
  37. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/web.py +0 -0
  38. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/utils.py +0 -0
  39. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/SOURCES.txt +0 -0
  40. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/dependency_links.txt +0 -0
  41. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/requires.txt +0 -0
  42. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/top_level.txt +0 -0
  43. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/setup.cfg +0 -0
  44. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/setup.py +0 -0
  45. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/test/__init__.py +0 -0
  46. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/test/test_cache.py +0 -0
  47. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/test/test_simple.py +0 -0
  48. {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/test/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: idlib
3
- Version: 0.0.1.dev19
3
+ Version: 0.0.1.dev21
4
4
  Summary: A library for working with identifiers of all kinds.
5
5
  Home-page: https://github.com/tgbugs/idlib
6
6
  Author: Tom Gillespie
@@ -22,11 +22,21 @@ Classifier: Operating System :: MacOS :: MacOS X
22
22
  Classifier: Operating System :: Microsoft :: Windows
23
23
  Requires-Python: >=3.6
24
24
  Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: orthauth[sxpr,yaml]>=0.0.13
27
+ Requires-Dist: requests
25
28
  Provides-Extra: test
29
+ Requires-Dist: pytest; extra == "test"
30
+ Requires-Dist: joblib>=1.1.0; extra == "test"
31
+ Requires-Dist: beautifulsoup4[html5lib]; extra == "test"
32
+ Requires-Dist: pyontutils>=0.1.28; extra == "test"
33
+ Requires-Dist: google-auth-oauthlib; extra == "test"
26
34
  Provides-Extra: org
35
+ Requires-Dist: beautifulsoup4[html5lib]; extra == "org"
27
36
  Provides-Extra: rdf
37
+ Requires-Dist: pyontutils>=0.1.28; extra == "rdf"
28
38
  Provides-Extra: oauth
29
- License-File: LICENSE
39
+ Requires-Dist: google-auth-oauthlib; extra == "oauth"
30
40
 
31
41
  # idlib
32
42
  [![PyPI version](https://badge.fury.io/py/idlib.svg)](https://pypi.org/project/idlib/)
@@ -55,4 +55,4 @@ def get_right_id(uri):
55
55
  return pi
56
56
 
57
57
 
58
- __version__ = '0.0.1.dev19'
58
+ __version__ = '0.0.1.dev21'
@@ -149,7 +149,9 @@ class PioId(oq.OntId, idlib.Identifier, idlib.Stream):
149
149
 
150
150
  @property
151
151
  def uri_human(self):
152
- return self.__class__(prefix='pio.view', suffix=self.slug)
152
+ pid = self.__class__(prefix='pio.view', suffix=self.slug)
153
+ pid._id_converted_from = self
154
+ return pid
153
155
 
154
156
  @property
155
157
  def uri_api(self):
@@ -157,15 +159,21 @@ class PioId(oq.OntId, idlib.Identifier, idlib.Stream):
157
159
  #prefix = 'pio.folders.api' # XXX prefix won't work here, requires a suffix /ids SIGH
158
160
  #else:
159
161
  #prefix = 'pio.api'
160
- return self.__class__(prefix='pio.api', suffix=self.slug)
162
+ pid = self.__class__(prefix='pio.api', suffix=self.slug)
163
+ pid._id_converted_from = self
164
+ return pid
161
165
 
162
166
  @property
163
167
  def uri_api1(self):
164
- return self.__class__(prefix='pio.api1', suffix=self.slug)
168
+ pid = self.__class__(prefix='pio.api1', suffix=self.slug)
169
+ pid._id_converted_from = self
170
+ return pid
165
171
 
166
172
  @property
167
173
  def uri_api3(self):
168
- return self.__class__(prefix='pio.api3', suffix=self.slug)
174
+ pid = self.__class__(prefix='pio.api3', suffix=self.slug)
175
+ pid._id_converted_from = self
176
+ return pid
169
177
 
170
178
  def normalize(self):
171
179
  return self
@@ -584,8 +592,11 @@ class Pio(formats.Rdf, idlib.Stream):
584
592
  if noh:
585
593
  # XXX noh assumes that the originating form was a uri_human use at own risk
586
594
  return self._get_direct(self.identifier.uri_api)
587
- else:
595
+ elif self.uri_human:
588
596
  return self.uri_human._get_direct(self.identifier.uri_api)
597
+ else:
598
+ msg = 'no beans'
599
+ raise NotImplementedError(msg)
589
600
 
590
601
  def data1(self, fail_ok=False, noh=False):
591
602
  # FIXME this depends on data4 for uri_human and cannot be used alone at the moment
@@ -595,11 +606,38 @@ class Pio(formats.Rdf, idlib.Stream):
595
606
  # because there is no oauth for it
596
607
  # get data from the api/v1 endpoint which is is needed for unmangled steps
597
608
  resp = self._data_direct(noh=noh)
609
+ # amazingly we get json even on fail ... 500 will not though
610
+ # I'm sure so some day this will break
598
611
  j = resp.json()
599
612
  if 'protocol' not in j: # FIXME SIGH
600
- if 'status_code' in j and j['status_code'] in (250, 205):
601
- message = j['text'] + ' ' + self.identifier.asStr()
602
- raise exc.NotAuthorizedError(message)
613
+ if 'status_code' in j:
614
+ # FIXME this is not the right way to do it
615
+ # we could be handed an id that was an api
616
+ # id that is clearly using the len 32 private
617
+ # id and we should probably just detect that
618
+ # instead of trying to chase conversion prov
619
+ # which can get broken for any number of reasons
620
+ id_was_private = False
621
+ _id = self.identifier
622
+ while True:
623
+ if _id.is_private():
624
+ id_was_private = True
625
+ break
626
+ elif not hasattr(_id, '_id_converted_from'):
627
+ break
628
+ else:
629
+ _id = _id._id_converted_from
630
+
631
+ if j['status_code'] in (250, 205):
632
+ message = j['status_text'] + ' ' + self.identifier.asStr()
633
+ raise exc.NotAuthorizedError(message)
634
+ elif j['status_code'] == 1 and id_was_private:
635
+ msg = (
636
+ f'{j["status_text"]} private id might have existed before but '
637
+ f'does not exist any more {self.identifier.asStr()}')
638
+ raise exc.IdDoesNotExistError(msg)
639
+ else:
640
+ raise NotImplementedError((resp.url, j))
603
641
 
604
642
  return None
605
643
 
@@ -767,10 +805,17 @@ class Pio(formats.Rdf, idlib.Stream):
767
805
  elif self.identifier.is_private():
768
806
  try:
769
807
  return self.data4(fail_ok=fail_ok)
770
- except:
771
- return self.data1(fail_ok=fail_ok)
808
+ except exc.IdDoesNotExistError as ene:
809
+ raise ene
810
+ except Exception as e0:
811
+ try:
812
+ return self.data1(fail_ok=fail_ok)
813
+ except Exception as e1:
814
+ raise exc.TransportError() from e1
772
815
  else:
773
816
  return self.data4(fail_ok=fail_ok)
817
+ except (AttributeError, NotImplementedError) as e:
818
+ raise e
774
819
  except Exception as e:
775
820
  wait_until = time() + self._wait_time
776
821
  self._data_in_error_sigh = wait_until, e
@@ -2174,7 +2219,9 @@ class PioUser(idlib.HelperNoData, idlib.Stream):
2174
2219
 
2175
2220
  @property
2176
2221
  def orcid(self):
2177
- orcid = self.metadata()['orcid']
2222
+ md = self.metadata()
2223
+ k = 'orcid'
2224
+ orcid = md[k]if k in md and md[k] else None
2178
2225
  if orcid is not None:
2179
2226
  return idlib.Orcid.fromIdInit(prefix='orcid', suffix=orcid)
2180
2227
 
@@ -2186,7 +2233,9 @@ class PioUser(idlib.HelperNoData, idlib.Stream):
2186
2233
 
2187
2234
  @property
2188
2235
  def bio(self):
2189
- return self.metadata()['bio']
2236
+ md = self.metadata()
2237
+ k = 'bio'
2238
+ return md[k]if k in md and md[k] else None
2190
2239
 
2191
2240
  description = bio
2192
2241
 
@@ -451,8 +451,10 @@ class HelpTestStreams:
451
451
  # a separate loop?
452
452
  cypher = self._hashlib.blake2b
453
453
  bads = []
454
+ ds = []
454
455
  for i in self.ids:
455
456
  d = self.stream(i)
457
+ ds.append(d)
456
458
  d.identifier
457
459
  if d.identifier and hasattr(d.identifier, 'prefix'):
458
460
  print(d.identifier)
@@ -547,10 +549,12 @@ class HelpTestStreams:
547
549
  assert i1.identifier is not i2
548
550
 
549
551
  def test_asDict(self):
552
+ ds = []
550
553
  for id in self.ids:
551
554
  s = self.stream(id)
552
555
  try:
553
556
  d = s.asDict()
557
+ ds.append(d)
554
558
  except self._requests.exceptions.ConnectionError as e:
555
559
  self._pytest.skip('Internet done goofed')
556
560
 
@@ -572,7 +576,6 @@ class StreamUri(Stream):
572
576
 
573
577
  _id_class = None # NOTE asssigned to idlib.Uri in idlib.__init__
574
578
 
575
-
576
579
  @property
577
580
  def identifier(self):
578
581
  return self._identifier
@@ -29,10 +29,9 @@ OrcidPrefixes({'orcid': 'https://orcid.org/',
29
29
  class OrcidId(oq.OntId, idlib.Identifier):
30
30
  _namespaces = OrcidPrefixes
31
31
  _local_conventions = _namespaces
32
- local_regex = ('^0000-000(1-[5-9]|2-[0-9]|3-[0-4])'
33
- '[0-9][0-9][0-9]-[0-9][0-9][0-9]([0-9]|X)$')
34
- canonical_regex = ('^https://orcid.org/0000-000(1-[5-9]|2-[0-9]|3-'
35
- '[0-4])[0-9][0-9][0-9]-[0-9][0-9][0-9]([0-9]|X)$')
32
+ _common = '000[09]-00[01][0-9]-[0-9]{4}-[0-9]{3}([0-9]|X)'
33
+ local_regex = (f'^{_common}$')
34
+ canonical_regex = (f'^https://orcid.org/{_common}$')
36
35
 
37
36
  class OrcidMalformedError(exc.IdlibError):
38
37
  """ WHAT HAVE YOU DONE!? """
@@ -57,7 +57,9 @@ class Rrid(formats.Rdf, idlib.HelperNoData, idlib.Stream):
57
57
  def id_bound_metadata(self): # FIXME bound_id_metadata bound_id_data
58
58
  metadata = self.metadata()
59
59
  # wouldn't it be nice if all the metadata schemas had a common field called 'identifier' ?
60
- id = metadata['rrid']['curie']
60
+ ids = set(m['rrid']['curie'] for m in metadata)
61
+ assert len(ids) == 1, ids
62
+ id = next(iter(ids))
61
63
  return self._id_class(id)
62
64
 
63
65
  identifier_bound_metadata = id_bound_metadata
@@ -78,9 +80,12 @@ class Rrid(formats.Rdf, idlib.HelperNoData, idlib.Stream):
78
80
  if metadata is not None:
79
81
  self._path_metadata = path
80
82
  self._progenitor_metadata_blob = metadata
81
- source = metadata['hits']['hits'][0]['_source']
83
+ self._total_hits = metadata['hits']['total']
84
+ source = [h['_source'] for h in metadata['hits']['hits']]
82
85
  return source
83
86
 
87
+ return []
88
+
84
89
  def _cooldown(self):
85
90
  self._COOLDOWN = True
86
91
  metadata, path = self._metadata(self.identifier)
@@ -109,12 +114,11 @@ class Rrid(formats.Rdf, idlib.HelperNoData, idlib.Stream):
109
114
  # the identity of the RRID record ...
110
115
  m = cypher()
111
116
  metadata = self.metadata()
112
- proper_citation = metadata['rrid']['properCitation']
117
+ proper_citations = sorted([m['rrid']['properCitation'] for m in metadata])
113
118
  m.update(self.identifier.checksum(cypher))
114
119
  m.update(self.id_bound_metadata.checksum(cypher))
115
- m.update(proper_citation.encode())
116
- for vuri in self.vendorUris:
117
- m.update(vuri.encode())
120
+ for proper_citation in proper_citations:
121
+ m.update(proper_citation.encode())
118
122
 
119
123
  return m.digest()
120
124
 
@@ -122,33 +126,54 @@ class Rrid(formats.Rdf, idlib.HelperNoData, idlib.Stream):
122
126
  def vendorUris(self):
123
127
  metadata = self.metadata()
124
128
  if 'vendors' in metadata: # FIXME SCR continues to be a bad citizen >_<
125
- return [v['uri'] for v in metadata['vendors']]
129
+ return [v['uri'] if 'uri' in v else None for v in metadata['vendors']]
126
130
  else:
127
131
  return []
128
132
 
129
133
  @property
130
134
  def name(self):
131
- m = self.metadata()
132
- if m is not None:
133
- return m['item']['name']
135
+ ms = self.metadata()
136
+ if ms:
137
+ names = set(m['item']['name'] for m in ms)
138
+ return sorted(names, key=len, reverse=True)[0]
134
139
 
135
140
  label = name
136
141
 
137
142
  @property
138
143
  def synonyms(self):
139
- m = self.metadata()['item']
140
- fs = 'label', 'synonyms', 'abbreviations'
141
- out = []
142
- for f in fs:
143
- if f in m:
144
- for v in m[f]:
145
- out.append(v)
146
-
147
- return out
144
+ ms = self.metadata()
145
+ out = set()
146
+ for m in ms:
147
+ it = m['item']
148
+ fs = 'label', 'name', 'nomenclature', 'synonyms', 'abbreviations'
149
+ for f in fs:
150
+ if f in it:
151
+ thing = it[f]
152
+ if isinstance(thing, list):
153
+ for v in thing:
154
+ if type(v) is dict:
155
+ if 'name' in v:
156
+ for sigh in v['name'].split('. '):
157
+ syn = sigh.strip()
158
+ if syn:
159
+ out.add(syn)
160
+ else:
161
+ breakpoint()
162
+ raise NotImplementedError(f'TODO {v}')
163
+ else:
164
+ out.add(v)
165
+ else:
166
+ out.add(thing)
167
+
168
+ return sorted(out)
148
169
 
149
170
  @property
150
171
  def description(self):
151
- return self.metadata()['item']['description']
172
+ ms = self.metadata()
173
+ if ms:
174
+ descs = set(m['item']['description'] for m in ms if 'description' in m['item'])
175
+ if descs:
176
+ return sorted(descs, key=len, reverse=True)[0]
152
177
 
153
178
  # alternate representations
154
179
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: idlib
3
- Version: 0.0.1.dev19
3
+ Version: 0.0.1.dev21
4
4
  Summary: A library for working with identifiers of all kinds.
5
5
  Home-page: https://github.com/tgbugs/idlib
6
6
  Author: Tom Gillespie
@@ -22,11 +22,21 @@ Classifier: Operating System :: MacOS :: MacOS X
22
22
  Classifier: Operating System :: Microsoft :: Windows
23
23
  Requires-Python: >=3.6
24
24
  Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: orthauth[sxpr,yaml]>=0.0.13
27
+ Requires-Dist: requests
25
28
  Provides-Extra: test
29
+ Requires-Dist: pytest; extra == "test"
30
+ Requires-Dist: joblib>=1.1.0; extra == "test"
31
+ Requires-Dist: beautifulsoup4[html5lib]; extra == "test"
32
+ Requires-Dist: pyontutils>=0.1.28; extra == "test"
33
+ Requires-Dist: google-auth-oauthlib; extra == "test"
26
34
  Provides-Extra: org
35
+ Requires-Dist: beautifulsoup4[html5lib]; extra == "org"
27
36
  Provides-Extra: rdf
37
+ Requires-Dist: pyontutils>=0.1.28; extra == "rdf"
28
38
  Provides-Extra: oauth
29
- License-File: LICENSE
39
+ Requires-Dist: google-auth-oauthlib; extra == "oauth"
30
40
 
31
41
  # idlib
32
42
  [![PyPI version](https://badge.fury.io/py/idlib.svg)](https://pypi.org/project/idlib/)
@@ -58,6 +58,7 @@ class TestOrcid(HelpTestStreams, unittest.TestCase):
58
58
  'https://orcid.org/0000-0002-1825-0097',
59
59
  'https://orcid.org/0000-0001-5109-3700',
60
60
  'https://orcid.org/0000-0002-1694-233X',
61
+ 'https://orcid.org/0009-0005-9822-0865',
61
62
  ]
62
63
  ids_bad = []
63
64
  for __i in ids: # LOL PYTHON class scope list comprehensions
@@ -175,6 +176,7 @@ class TestPio(HelpTestStreams, unittest.TestCase):
175
176
  'lol not an identifier',
176
177
  'https://www.protocols.io/view/18980', # deleted
177
178
  'https://www.protocols.io/api/v3/protocols/18980', # deleted
179
+ 'https://www.protocols.io/private/D327A68038D5B4ECB32964C22D904270', # deleted
178
180
  ]
179
181
 
180
182
  @skipif_no_net
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