idlib 0.0.1.dev21__py2.py3-none-any.whl → 0.0.1.dev22__py2.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.
idlib/__init__.py CHANGED
@@ -55,4 +55,4 @@ def get_right_id(uri):
55
55
  return pi
56
56
 
57
57
 
58
- __version__ = '0.0.1.dev21'
58
+ __version__ = '0.0.1.dev22'
idlib/conventions/type.py CHANGED
@@ -13,19 +13,21 @@ class ConvTypeBytesHeader(ConventionsType):
13
13
  self.stop = stop
14
14
  self.sentinel = sentinel
15
15
 
16
- def _findElement(self, element, chunk):
17
- m = re.search(element, chunk)
16
+ def _findElement(self, element, chunk, prev):
17
+ to_search = prev + chunk
18
+ m = re.search(element, to_search)
18
19
  if m is not None:
20
+ lp = len(prev)
19
21
  # FIXME icky api
20
- return m.start(), m.end()
22
+ return m.start() - lp, m.end() -lp
21
23
  else:
22
24
  return None, None
23
25
 
24
- def findStart(self, chunk):
25
- return self._findElement(self.start, chunk)
26
+ def findStart(self, chunk, prev):
27
+ return self._findElement(self.start, chunk, prev)
26
28
 
27
- def findStop(self, chunk):
28
- return self._findElement(self.stop, chunk)
29
+ def findStop(self, chunk, prev):
30
+ return self._findElement(self.stop, chunk, prev)
29
31
 
30
- def findSentinel(self, chunk):
31
- return self._findElement(self.sentinel, chunk)
32
+ def findSentinel(self, chunk, prev):
33
+ return self._findElement(self.sentinel, chunk, prev)
idlib/core.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import requests
2
2
  from . import exceptions as exc
3
+ from .utils import log
3
4
 
4
5
 
5
6
  def resolution_chain(iri):
@@ -7,11 +8,37 @@ def resolution_chain(iri):
7
8
  yield head.url
8
9
 
9
10
 
11
+ def try_get(s, head):
12
+ # see whether the server has a bad/broken support for head requests
13
+ # sometimes they return e.g. 400 instead of 405, and really they should
14
+ # just work because they work correctly with get request ...
15
+ head = s.get(head.url, stream=True)
16
+ if head.ok:
17
+ log.info(f'bad HEAD implementation {head.url}')
18
+ else:
19
+ content = head.content
20
+ if content:
21
+ if head.headers['Content-Type'] == 'application/json':
22
+ try:
23
+ j = head.json()
24
+ log.error(f'{head.url} error json {j}')
25
+ except Exception as e:
26
+ log.error(f'{head.url} error text {head.text}')
27
+
28
+ head.close()
29
+ return head
30
+
31
+
10
32
  def resolution_chain_responses(iri, raise_on_final=True):
11
33
  #doi = doi # TODO
12
34
  s = requests.Session()
13
- head = requests.head(iri)
14
- yield head
35
+ head = s.head(iri, allow_redirects=False)
36
+ if head.status_code < 400:
37
+ yield head
38
+ else:
39
+ head = try_get(s, head)
40
+ yield head
41
+
15
42
  while head.is_redirect and head.status_code < 400: # FIXME redirect loop issue
16
43
  yield head.next
17
44
  try:
@@ -19,7 +46,12 @@ def resolution_chain_responses(iri, raise_on_final=True):
19
46
  except requests.exceptions.SSLError as e:
20
47
  raise exc.InbetweenError(msg) from e
21
48
 
22
- yield head
49
+ if head.status_code < 400:
50
+ yield head
51
+ else:
52
+ head = try_get(s, head)
53
+ yield head
54
+
23
55
  if not head.is_redirect:
24
56
  break
25
57
 
@@ -27,7 +59,19 @@ def resolution_chain_responses(iri, raise_on_final=True):
27
59
  if head.status_code == 404:
28
60
  head.raise_for_status() # probably a permissions issue
29
61
  elif head.status_code >= 500:
30
- raise exc.RemoteError()
62
+ msg = f'Remote in error due to {head.status_code} at {head.url}\n'
63
+ raise exc.RemoteError(msg)
31
64
  elif head.status_code >= 400:
32
65
  msg = f'Nothing found due to {head.status_code} at {head.url}\n'
33
66
  raise exc.ResolutionError(msg)
67
+
68
+ if head.status_code >= 400:
69
+ # XXX this seems like the "right" thing to do, but it will
70
+ # probably break a bunch of stuff, but at least this way it
71
+ # will be visible now, obviously yeilding None as the final
72
+ # element in the reference chain conflates all the possible
73
+ # reasons so raising is a better solution and is the default
74
+ # for a reason but at least this way the user knows that the
75
+ # final element of the chain dereference to nothing instead of
76
+ # assuming that the last element succeeded
77
+ yield None
idlib/exceptions.py CHANGED
@@ -36,7 +36,7 @@ class CouldNotReachReferentError(CouldNotReachError):
36
36
 
37
37
  Note that in a system that is not based on redirects
38
38
  this is an error that could be returned by the index
39
- but it would appear as an referent unknown error. """
39
+ but it would appear as a referent unknown error. """
40
40
 
41
41
  # but its supertype is ReferentUnknown or ReferentUnreachable
42
42
 
@@ -59,6 +59,14 @@ class ResolutionError(IdlibError):
59
59
  # we should split into NetworkError/TimeoutError
60
60
  # and then have RemoteError, because Resolution error is
61
61
  # ambiguous, it is the superclass for all of them I think
62
+ # XXX as used, this is ClientError, and for all but 404
63
+ # and 410 the response means we can't actually say whether
64
+ # it is a true null pointer, the naming is thus very bad
65
+ # because as used this is not a network error, not a timeout
66
+ # error, it is the server saying i do not/cannot understand
67
+ # that is MessageReceivedButError which happens before we
68
+ # know if the id exists and probably actually covers most
69
+ # of the IdExistsButError cases
62
70
 
63
71
 
64
72
  class MalformedIdentifierError(IdlibError):
idlib/streams.py CHANGED
@@ -606,7 +606,7 @@ class StreamUri(Stream):
606
606
  if not self.identifier_actionable in dc:
607
607
  try:
608
608
  dc[self.identifier_actionable] = tuple(
609
- StringProgenitor(resp.url, progenitor=resp)
609
+ resp if resp is None else StringProgenitor(resp.url, progenitor=resp)
610
610
  for resp in
611
611
  self._resolution_chain_responses(self.identifier_actionable,
612
612
  raise_on_final=False))
@@ -668,7 +668,8 @@ class StreamUri(Stream):
668
668
  @cache_result
669
669
  def headers(self):
670
670
  uri = self.dereference()
671
- return uri.progenitor().headers
671
+ if uri is not None:
672
+ return uri.progenitor().headers
672
673
 
673
674
  def data(self, mimetype_accept=None):
674
675
  # FIXME TODO should the data associated with the doi
idlib/systems/doi.py CHANGED
@@ -26,8 +26,8 @@ class DoiId(oq.OntId, idlib.Identifier, idlib.Stream): # also _technically_ a s
26
26
  _namespaces = _DoiPrefixes
27
27
  _local_conventions = _namespaces
28
28
  _id_class = str # eventually we have to drop down to something
29
- local_regex = '^10\.[0-9]{4,9}[-._;()/:a-zA-Z0-9]+$'
30
- canonical_regex = '^https:\/\/doi.org/10\.[0-9]{4,9}[-._;()/:a-zA-Z0-9]+$'
29
+ local_regex = '^10\\.[0-9]{4,9}[-._;()/:a-zA-Z0-9]+$'
30
+ canonical_regex = '^https://doi.org/10\\.[0-9]{4,9}[-._;()/:a-zA-Z0-9]+$'
31
31
 
32
32
  def __new__(cls, doi_in_various_states_of_mangling=None, iri=None):
33
33
  if doi_in_various_states_of_mangling is None and iri is not None:
idlib/systems/orcid.py CHANGED
@@ -1,3 +1,4 @@
1
+ import random
1
2
  from pathlib import Path
2
3
  import ontquery as oq # temporary implementation detail
3
4
  import idlib
@@ -13,6 +14,22 @@ from idlib.config import auth
13
14
  # but I need more information ...
14
15
 
15
16
 
17
+ def genorcid():
18
+ """ generate a random orcid-like id suffix """
19
+ _09 = '09'
20
+ _01 = ''.join([str(_) for _ in range(2)])
21
+ _0_9 = ''.join([str(_) for _ in range(10)])
22
+ base = (
23
+ '000' + random.choice(_09) + '-' +
24
+ '00' + random.choice(_01) + random.choice(_0_9) + '-' +
25
+ ''.join(random.choice(_0_9) for _ in range(4))
26
+ + '-' +
27
+ ''.join(random.choice(_0_9) for _ in range(3)))
28
+ cs = OrcidId._digit_checksum(base.replace('-', ''))
29
+ fd = 'X' if cs == 10 else str(cs)
30
+ return base + fd
31
+
32
+
16
33
  class OrcidPrefixes(conv.QnameAsLocalHelper, oq.OntCuries):
17
34
  # set these manually since, sigh, factory patterns
18
35
  _dict = {}
@@ -68,6 +85,16 @@ class OrcidId(oq.OntId, idlib.Identifier):
68
85
 
69
86
  return self
70
87
 
88
+ @staticmethod
89
+ def _digit_checksum(digits):
90
+ total = 0
91
+ for digit_string in digits:
92
+ total = (total + int(digit_string)) * 2
93
+
94
+ remainder = total % 11
95
+ result = (12 - remainder) % 11
96
+ return result
97
+
71
98
  @property
72
99
  def checksumValid(self):
73
100
  """ see
@@ -77,12 +104,7 @@ class OrcidId(oq.OntId, idlib.Identifier):
77
104
  try:
78
105
  *digits, check_string = self.suffix.replace('-', '')
79
106
  check = 10 if check_string == 'X' else int(check_string)
80
- total = 0
81
- for digit_string in digits:
82
- total = (total + int(digit_string)) * 2
83
-
84
- remainder = total % 11
85
- result = (12 - remainder) % 11
107
+ result = self._digit_checksum(digits)
86
108
  return result == check
87
109
  except ValueError as e:
88
110
  raise self.OrcidChecksumError(self) from e
@@ -186,3 +208,25 @@ class Orcid(idlib.HelperNoData, idlib.Stream):
186
208
  return (self.identifier.iri
187
209
  if asType is None else
188
210
  asType(self.identifier.iri))
211
+
212
+
213
+ class OrcidSandboxPrefixes(conv.QnameAsLocalHelper, oq.OntCuries):
214
+ # set these manually since, sigh, factory patterns
215
+ _dict = {}
216
+ _n_to_p = {}
217
+ _strie = {}
218
+ _trie = {}
219
+
220
+
221
+ OrcidSandboxPrefixes({'orcid': 'https://sandbox.orcid.org/',
222
+ 'ORCID': 'https://sandbox.orcid.org/',
223
+ 'orcid.pub.3': 'https://pub.sandbox.orcid.org/v3.0/',})
224
+
225
+
226
+ class OrcidIdSandbox(OrcidId):
227
+ _namespaces = OrcidSandboxPrefixes
228
+ canonical_regex = (f'^https://sandbox.orcid.org/{OrcidId._common}$')
229
+
230
+
231
+ class OrcidSandbox(Orcid):
232
+ _id_class = OrcidIdSandbox
idlib/systems/ror.py CHANGED
@@ -167,6 +167,7 @@ class Ror(formats.Rdf, idlib.HelperNoData, idlib.Stream):
167
167
  'Healthcare': 'Institution',
168
168
  'Facility': 'CoreFacility',
169
169
  'Nonprofit': 'Nonprofit',
170
+ 'Funder': 'Funder', # FIXME funder is a role ...
170
171
  'Other': 'Institution',
171
172
  }
172
173
  @property
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: idlib
3
- Version: 0.0.1.dev21
3
+ Version: 0.0.1.dev22
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
@@ -9,34 +9,35 @@ License: MIT
9
9
  Keywords: python persistent identifiers
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Programming Language :: Python :: 3.6
13
12
  Classifier: Programming Language :: Python :: 3.7
14
13
  Classifier: Programming Language :: Python :: 3.8
15
14
  Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
18
19
  Classifier: Programming Language :: Python :: Implementation :: CPython
19
20
  Classifier: Programming Language :: Python :: Implementation :: PyPy
20
21
  Classifier: Operating System :: POSIX :: Linux
21
22
  Classifier: Operating System :: MacOS :: MacOS X
22
23
  Classifier: Operating System :: Microsoft :: Windows
23
- Requires-Python: >=3.6
24
+ Requires-Python: >=3.7
24
25
  Description-Content-Type: text/markdown
25
26
  License-File: LICENSE
26
- Requires-Dist: orthauth[sxpr,yaml] >=0.0.13
27
+ Requires-Dist: orthauth[sxpr,yaml]>=0.0.18
27
28
  Requires-Dist: requests
28
- Provides-Extra: oauth
29
- Requires-Dist: google-auth-oauthlib ; extra == 'oauth'
29
+ Provides-Extra: test
30
+ Requires-Dist: pytest; extra == "test"
31
+ Requires-Dist: joblib>=1.1.0; extra == "test"
32
+ Requires-Dist: beautifulsoup4[html5lib]; extra == "test"
33
+ Requires-Dist: pyontutils>=0.1.28; extra == "test"
34
+ Requires-Dist: google-auth-oauthlib; extra == "test"
30
35
  Provides-Extra: org
31
- Requires-Dist: beautifulsoup4[html5lib] ; extra == 'org'
36
+ Requires-Dist: beautifulsoup4[html5lib]; extra == "org"
32
37
  Provides-Extra: rdf
33
- Requires-Dist: pyontutils >=0.1.28 ; extra == 'rdf'
34
- Provides-Extra: test
35
- Requires-Dist: pytest ; extra == 'test'
36
- Requires-Dist: joblib >=1.1.0 ; extra == 'test'
37
- Requires-Dist: beautifulsoup4[html5lib] ; extra == 'test'
38
- Requires-Dist: pyontutils >=0.1.28 ; extra == 'test'
39
- Requires-Dist: google-auth-oauthlib ; extra == 'test'
38
+ Requires-Dist: pyontutils>=0.1.28; extra == "rdf"
39
+ Provides-Extra: oauth
40
+ Requires-Dist: google-auth-oauthlib; extra == "oauth"
40
41
 
41
42
  # idlib
42
43
  [![PyPI version](https://badge.fury.io/py/idlib.svg)](https://pypi.org/project/idlib/)
@@ -1,35 +1,35 @@
1
- idlib/__init__.py,sha256=gU4iaEA5x2uf_1tRUfaQ7d5y4KM-csnRBJyKa7if1UY,1696
1
+ idlib/__init__.py,sha256=FdowWsUq9bwbPqZPU2sTU1iWMxm1KetrH4kknNBlMmQ,1696
2
2
  idlib/auth-config.py,sha256=tYM2aYCYJFG5NB4Af1dSn6YxhafttOp7W7aSc-QbVV8,460
3
3
  idlib/cache.py,sha256=lR1I3iEYvBhNeqoptS-Sj7SpIHt1K65nYlBrTK-ROVQ,3875
4
4
  idlib/config.py,sha256=Gli2tZlbKxBos8sXDoDK1g9mngfDw1ZWpBt_7MDZH-g,76
5
- idlib/core.py,sha256=zLqHrSwtgbQll-04J2B5j8W69AXpWT_Ql5n0eBHp2pk,1055
5
+ idlib/core.py,sha256=DfJoPe_xqJJducri1ub7e_anJYuhJ37-ZxvZCuuOpaY,2704
6
6
  idlib/edit.py,sha256=s3yRtvNelQUA8fziu_igZeoFsW4xCK_kgfNVv3TS0iE,3635
7
- idlib/exceptions.py,sha256=4qUTUrGkCMu2jE0TO4_hUCP-7XDCrk6FE08Ff-c1N94,4242
7
+ idlib/exceptions.py,sha256=QC3Jq98iwezcqB7SmDr-UEOqdNCnvEXVkKB0eJLIwZs,4714
8
8
  idlib/from_oq.py,sha256=HlgepGbgSUbOt5R79ALRhgiLOEvU6TSu3evB8zq7N84,94170
9
9
  idlib/identifiers.py,sha256=s39bAS-fCQrlwAbMtaa7tjSnqKq4YPlfSbfJ6ju2o7I,3861
10
- idlib/streams.py,sha256=3L5JnSDzbodHET66Mua4zlfJEZbsXACIsWtVd-iDUZM,26381
10
+ idlib/streams.py,sha256=HGcs_S7bOiUYfiSjZNGLXo5bJI7yd0bYjc8BKkdqECI,26439
11
11
  idlib/utils.py,sha256=zItGz-lXovZytSENUZ9q7A7gLr5uK_9VMawokGSskbk,5127
12
12
  idlib/apis/__init__.py,sha256=q2-iSMUDPlYbjqGWa1mtbe2G95N7wbjWHGj30BUdVDM,88
13
13
  idlib/apis/protocols_io.py,sha256=3PGKBwe9P_DxhScIG-L3ZFtnEHxgHaccLhRGVPZuYHg,3956
14
14
  idlib/conventions/__init__.py,sha256=ug6W8eNHuq9JZy5bwrZ6Rjfu8r81Rrnm0nzApmvvMzw,204
15
15
  idlib/conventions/core.py,sha256=g0T1VoNpoC9afvUd0wp6Mu7tRxbPDFK59uwVXV-zTtA,940
16
16
  idlib/conventions/local.py,sha256=H1WnXTY06aLq0RB_SsTLVhMZiSP9GGvWuWm_zrFUNmQ,129
17
- idlib/conventions/type.py,sha256=2ATKqoWNaQrc4pirJ2-WucBTsJud7RC4g59Z62DSK7o,898
17
+ idlib/conventions/type.py,sha256=PM_BkxP5a7BTOSsb77kMmMRF2u5xMdlptIa4QYIaE6Y,1013
18
18
  idlib/formats/__init__.py,sha256=yS6l0mbDwa2xjyxhgVzlqIf0LPqJmuhNVg8rfKSsh7M,528
19
19
  idlib/formats/rdf.py,sha256=yIKHPaPn-SUuUMkR4VD7KffVgQD8oNaZNxcqDBilxtg,466
20
20
  idlib/systems/__init__.py,sha256=3lWSK2DyzYX0d61DJo-gGytc-HoiOlNJl5so4s7Grrg,203
21
21
  idlib/systems/ark.py,sha256=i9JqmuTWsJABrLHR0-_FX_PBbbpeFa7X4CO65QhyOD8,111
22
- idlib/systems/doi.py,sha256=GVL_uiLmU-Xa9odi7pGkbIniU7tTDesKO1pNbsYm2Fs,13100
22
+ idlib/systems/doi.py,sha256=PQAstSUocPpK9Ug7L-eKvJrQhn3jRU9WuKITGyZoL9g,13100
23
23
  idlib/systems/handle.py,sha256=Gg97IuysdAdCX9j6EhuNfIddb1GDq0CmwW1SmuLkpgM,95
24
24
  idlib/systems/isni.py,sha256=TgiSTlDcSBLRro3gg_YfQJQkfaJ3saw0Au1OMhE23wU,89
25
- idlib/systems/orcid.py,sha256=hharUZ5iixVDznHNpEh8BJadQV2dEpNH8h9TdKi0qZE,6634
25
+ idlib/systems/orcid.py,sha256=aTVu_Q6fnaQvqmd6jeXdst6yKRTO-BEUMs7_4fdQK5E,7886
26
26
  idlib/systems/pmid.py,sha256=Na-4_n7qmJ3VAbG0RUwnNTg7o4Z8OkuL_Hluq_A3IO0,181
27
- idlib/systems/ror.py,sha256=q_NyvVP-Xe9DrzQMFZAC2WeQU6QyxSa6RzrqQ0P7gUA,8067
27
+ idlib/systems/ror.py,sha256=xIS30mxKd0IcjwalBjnyUU7GKeeU5pxM3i9hSofG6u8,8129
28
28
  idlib/systems/rrid.py,sha256=XVHyHyiGC2OvbOSXmfU3_wswS6yUw65qZ3Pct0sjbfU,6886
29
29
  idlib/systems/urn.py,sha256=p222KjHgfF6y9srCm7kFWuWA9NK4lI-wEQUtSeuBqYk,113
30
30
  idlib/systems/web.py,sha256=vrSbQYWnxhe9pSgZcOqwLM3V6XQDLwv1QqWlhWWxbL0,1291
31
- idlib-0.0.1.dev21.dist-info/LICENSE,sha256=nEuJBsoHfpXGgf6ZyFGUhQJqxFWwhSYF-45pRAqTSb4,1080
32
- idlib-0.0.1.dev21.dist-info/METADATA,sha256=qWQNOrn8om22xx66S43Ao8rdjLjm_rUVKun5tG13yEk,2012
33
- idlib-0.0.1.dev21.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
34
- idlib-0.0.1.dev21.dist-info/top_level.txt,sha256=xIk87gJPbeCKih9BVMP8PlEFdpcsGplenz0VQhx5sP8,6
35
- idlib-0.0.1.dev21.dist-info/RECORD,,
31
+ idlib-0.0.1.dev22.dist-info/LICENSE,sha256=nEuJBsoHfpXGgf6ZyFGUhQJqxFWwhSYF-45pRAqTSb4,1080
32
+ idlib-0.0.1.dev22.dist-info/METADATA,sha256=kjQ8xltmifA_qoV2GmEQAFeO5P3HzWVMc0MjiINC38o,2052
33
+ idlib-0.0.1.dev22.dist-info/WHEEL,sha256=pxeNX5JdtCe58PUSYP9upmc7jdRPgvT0Gm9kb1SHlVw,109
34
+ idlib-0.0.1.dev22.dist-info/top_level.txt,sha256=xIk87gJPbeCKih9BVMP8PlEFdpcsGplenz0VQhx5sP8,6
35
+ idlib-0.0.1.dev22.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any