idlib 0.0.1.dev21__tar.gz → 0.0.1.dev22__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 (49) hide show
  1. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/PKG-INFO +5 -4
  2. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/__init__.py +1 -1
  3. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/conventions/type.py +11 -9
  4. idlib-0.0.1.dev22/idlib/core.py +77 -0
  5. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/exceptions.py +9 -1
  6. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/streams.py +3 -2
  7. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/doi.py +2 -2
  8. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/orcid.py +50 -6
  9. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/ror.py +1 -0
  10. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib.egg-info/PKG-INFO +5 -4
  11. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib.egg-info/requires.txt +1 -1
  12. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/setup.py +4 -3
  13. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/test/test_identifiers.py +16 -1
  14. idlib-0.0.1.dev21/idlib/core.py +0 -33
  15. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/LICENSE +0 -0
  16. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/Pipfile +0 -0
  17. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/README.md +0 -0
  18. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/docs/identifiers.org +0 -0
  19. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/docs/stream-grammer.org +0 -0
  20. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/apis/__init__.py +0 -0
  21. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/apis/protocols_io.py +0 -0
  22. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/auth-config.py +0 -0
  23. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/cache.py +0 -0
  24. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/config.py +0 -0
  25. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/conventions/__init__.py +0 -0
  26. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/conventions/core.py +0 -0
  27. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/conventions/local.py +0 -0
  28. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/edit.py +0 -0
  29. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/formats/__init__.py +0 -0
  30. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/formats/rdf.py +0 -0
  31. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/from_oq.py +0 -0
  32. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/identifiers.py +0 -0
  33. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/__init__.py +0 -0
  34. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/ark.py +0 -0
  35. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/handle.py +0 -0
  36. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/isni.py +0 -0
  37. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/pmid.py +0 -0
  38. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/rrid.py +0 -0
  39. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/urn.py +0 -0
  40. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/systems/web.py +0 -0
  41. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib/utils.py +0 -0
  42. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib.egg-info/SOURCES.txt +0 -0
  43. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib.egg-info/dependency_links.txt +0 -0
  44. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/idlib.egg-info/top_level.txt +0 -0
  45. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/setup.cfg +0 -0
  46. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/test/__init__.py +0 -0
  47. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/test/test_cache.py +0 -0
  48. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/test/test_simple.py +0 -0
  49. {idlib-0.0.1.dev21 → idlib-0.0.1.dev22}/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.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,21 +9,22 @@ 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
29
  Provides-Extra: test
29
30
  Requires-Dist: pytest; extra == "test"
@@ -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'
@@ -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)
@@ -0,0 +1,77 @@
1
+ import requests
2
+ from . import exceptions as exc
3
+ from .utils import log
4
+
5
+
6
+ def resolution_chain(iri):
7
+ for head in resolution_chain_responses(iri):
8
+ yield head.url
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
+
32
+ def resolution_chain_responses(iri, raise_on_final=True):
33
+ #doi = doi # TODO
34
+ s = requests.Session()
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
+
42
+ while head.is_redirect and head.status_code < 400: # FIXME redirect loop issue
43
+ yield head.next
44
+ try:
45
+ head = s.send(head.next)
46
+ except requests.exceptions.SSLError as e:
47
+ raise exc.InbetweenError(msg) from e
48
+
49
+ if head.status_code < 400:
50
+ yield head
51
+ else:
52
+ head = try_get(s, head)
53
+ yield head
54
+
55
+ if not head.is_redirect:
56
+ break
57
+
58
+ if raise_on_final: # we still want the chain ... null pointer error comes later?
59
+ if head.status_code == 404:
60
+ head.raise_for_status() # probably a permissions issue
61
+ elif head.status_code >= 500:
62
+ msg = f'Remote in error due to {head.status_code} at {head.url}\n'
63
+ raise exc.RemoteError(msg)
64
+ elif head.status_code >= 400:
65
+ msg = f'Nothing found due to {head.status_code} at {head.url}\n'
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
@@ -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):
@@ -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
@@ -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:
@@ -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
@@ -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,21 +9,22 @@ 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
29
  Provides-Extra: test
29
30
  Requires-Dist: pytest; extra == "test"
@@ -1,4 +1,4 @@
1
- orthauth[sxpr,yaml]>=0.0.13
1
+ orthauth[sxpr,yaml]>=0.0.18
2
2
  requests
3
3
 
4
4
  [oauth]
@@ -37,12 +37,13 @@ setup(name='idlib',
37
37
  classifiers=[
38
38
  'Development Status :: 4 - Beta',
39
39
  'License :: OSI Approved :: MIT License',
40
- 'Programming Language :: Python :: 3.6',
41
40
  'Programming Language :: Python :: 3.7',
42
41
  'Programming Language :: Python :: 3.8',
43
42
  'Programming Language :: Python :: 3.9',
44
43
  'Programming Language :: Python :: 3.10',
45
44
  'Programming Language :: Python :: 3.11',
45
+ 'Programming Language :: Python :: 3.12',
46
+ 'Programming Language :: Python :: 3.13',
46
47
  'Programming Language :: Python :: Implementation :: CPython',
47
48
  'Programming Language :: Python :: Implementation :: PyPy',
48
49
  'Operating System :: POSIX :: Linux',
@@ -57,9 +58,9 @@ setup(name='idlib',
57
58
  'idlib.formats',
58
59
  'idlib.systems',
59
60
  ],
60
- python_requires='>=3.6',
61
+ python_requires='>=3.7',
61
62
  tests_require=tests_require,
62
- install_requires=['orthauth[yaml,sxpr]>=0.0.13', 'requests'],
63
+ install_requires=['orthauth[yaml,sxpr]>=0.0.18', 'requests'],
63
64
  extras_require={'test': tests_require,
64
65
  'org': org_require,
65
66
  'rdf': rdf_require,
@@ -73,12 +73,27 @@ class TestOrcid(HelpTestStreams, unittest.TestCase):
73
73
  def test_asType(self):
74
74
  import rdflib
75
75
  from idlib.formats import rdf as _bind_rdf
76
- o = idlib.Orcid(self.ids[0])
76
+ o = self.stream(self.ids[0])
77
77
  nt = o.asType(rdflib.URIRef)
78
78
  # still haven't decided if this is a good idea or not
79
79
  assert str(o) != str(nt), 'string representation of streams should not match id ???'
80
80
 
81
81
 
82
+ class TestOrcidSandbox(TestOrcid):
83
+ stream = idlib.systems.orcid.OrcidSandbox
84
+ ids = [
85
+ 'https://sandbox.orcid.org/0009-0007-0266-5208',
86
+ 'https://sandbox.orcid.org/0000-0002-9383-616X',
87
+ 'https://sandbox.orcid.org/0000-0002-9267-5456',
88
+ 'https://sandbox.orcid.org/0000-0002-0391-3177',
89
+ 'https://sandbox.orcid.org/0009-0002-1173-8142',
90
+ ]
91
+ ids_bad = []
92
+ for __i in ids: # LOL PYTHON class scope list comprehensions
93
+ ids_bad.append(__i.replace('https', 'http'))
94
+ del __i
95
+
96
+
82
97
  class TestRor(HelpTestStreams, unittest.TestCase):
83
98
  stream = idlib.Ror
84
99
  ids = [
@@ -1,33 +0,0 @@
1
- import requests
2
- from . import exceptions as exc
3
-
4
-
5
- def resolution_chain(iri):
6
- for head in resolution_chain_responses(iri):
7
- yield head.url
8
-
9
-
10
- def resolution_chain_responses(iri, raise_on_final=True):
11
- #doi = doi # TODO
12
- s = requests.Session()
13
- head = requests.head(iri)
14
- yield head
15
- while head.is_redirect and head.status_code < 400: # FIXME redirect loop issue
16
- yield head.next
17
- try:
18
- head = s.send(head.next)
19
- except requests.exceptions.SSLError as e:
20
- raise exc.InbetweenError(msg) from e
21
-
22
- yield head
23
- if not head.is_redirect:
24
- break
25
-
26
- if raise_on_final: # we still want the chain ... null pointer error comes later?
27
- if head.status_code == 404:
28
- head.raise_for_status() # probably a permissions issue
29
- elif head.status_code >= 500:
30
- raise exc.RemoteError()
31
- elif head.status_code >= 400:
32
- msg = f'Nothing found due to {head.status_code} at {head.url}\n'
33
- raise exc.ResolutionError(msg)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes