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.
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/PKG-INFO +12 -2
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/__init__.py +1 -1
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/from_oq.py +61 -12
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/streams.py +4 -1
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/orcid.py +3 -4
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/rrid.py +45 -20
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/PKG-INFO +12 -2
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/test/test_identifiers.py +2 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/LICENSE +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/Pipfile +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/README.md +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/docs/identifiers.org +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/docs/stream-grammer.org +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/apis/__init__.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/apis/protocols_io.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/auth-config.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/cache.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/config.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/conventions/__init__.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/conventions/core.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/conventions/local.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/conventions/type.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/core.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/edit.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/exceptions.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/formats/__init__.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/formats/rdf.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/identifiers.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/__init__.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/ark.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/doi.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/handle.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/isni.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/pmid.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/ror.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/urn.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/systems/web.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib/utils.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/SOURCES.txt +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/dependency_links.txt +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/requires.txt +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/idlib.egg-info/top_level.txt +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/setup.cfg +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/setup.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/test/__init__.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/test/test_cache.py +0 -0
- {idlib-0.0.1.dev19 → idlib-0.0.1.dev21}/test/test_simple.py +0 -0
- {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.
|
|
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
|
-
|
|
39
|
+
Requires-Dist: google-auth-oauthlib; extra == "oauth"
|
|
30
40
|
|
|
31
41
|
# idlib
|
|
32
42
|
[](https://pypi.org/project/idlib/)
|
|
@@ -149,7 +149,9 @@ class PioId(oq.OntId, idlib.Identifier, idlib.Stream):
|
|
|
149
149
|
|
|
150
150
|
@property
|
|
151
151
|
def uri_human(self):
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
601
|
-
|
|
602
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
canonical_regex = ('^https://orcid.org/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
116
|
-
|
|
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
|
-
|
|
132
|
-
if
|
|
133
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
39
|
+
Requires-Dist: google-auth-oauthlib; extra == "oauth"
|
|
30
40
|
|
|
31
41
|
# idlib
|
|
32
42
|
[](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
|
|
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
|