1
1
from __future__ import annotations
2
2
3
3
from collections .abc import Iterable , Iterator , Sequence
4
- from typing import Any , Callable , ClassVar , Generic , Protocol , Tuple , cast
4
+ from typing import (
5
+ Any ,
6
+ Callable ,
7
+ ClassVar ,
8
+ Generic ,
9
+ Protocol ,
10
+ Tuple ,
11
+ TypeVar ,
12
+ cast ,
13
+ )
5
14
from urllib .parse import unquote , urldefrag , urljoin
6
15
7
16
from attrs import evolve , field
@@ -252,16 +261,8 @@ def __getitem__(self, uri: URI) -> Resource[D]:
252
261
"""
253
262
try :
254
263
return self ._resources [uri ]
255
- except LookupError :
256
- try :
257
- return self ._retrieve (uri )
258
- except (
259
- exceptions .CannotDetermineSpecification ,
260
- exceptions .NoSuchResource ,
261
- ):
262
- raise
263
- except Exception :
264
- raise exceptions .Unretrievable (ref = uri )
264
+ except KeyError :
265
+ raise exceptions .NoSuchResource (ref = uri )
265
266
266
267
def __iter__ (self ) -> Iterator [URI ]:
267
268
"""
@@ -288,6 +289,32 @@ def __repr__(self) -> str:
288
289
summary = f"{ pluralized } "
289
290
return f"<Registry ({ size } { summary } )>"
290
291
292
+ def get_or_retrieve (self , uri : URI ):
293
+ """
294
+ Get a resource from the registry, crawling or retrieving if necessary.
295
+ """
296
+ resource = self ._resources .get (uri )
297
+ if resource is not None :
298
+ return Retrieved (registry = self , value = resource )
299
+
300
+ registry = self .crawl ()
301
+ resource = registry ._resources .get (uri )
302
+ if resource is not None :
303
+ return Retrieved (registry = registry , value = resource )
304
+
305
+ try :
306
+ resource = registry ._retrieve (uri )
307
+ except (
308
+ exceptions .CannotDetermineSpecification ,
309
+ exceptions .NoSuchResource ,
310
+ ):
311
+ raise
312
+ except Exception :
313
+ raise exceptions .Unretrievable (ref = uri )
314
+ else :
315
+ registry = registry .with_resource (uri , resource )
316
+ return Retrieved (registry = registry , value = resource )
317
+
291
318
def remove (self , uri : URI ):
292
319
"""
293
320
Return a registry with the resource identified by a given URI removed.
@@ -308,7 +335,15 @@ def anchor(self, uri: URI, name: str):
308
335
"""
309
336
Retrieve the given anchor, which must already have been found.
310
337
"""
311
- return self ._anchors [uri , name ]
338
+ value = self ._anchors .get ((uri , name ))
339
+ if value is not None :
340
+ return Retrieved (value = value , registry = self )
341
+
342
+ registry = self .crawl ()
343
+ value = registry ._anchors .get ((uri , name ))
344
+ if value is not None :
345
+ return Retrieved (value = value , registry = registry )
346
+ raise exceptions .NoSuchAnchor (ref = uri , resource = self [uri ], anchor = name )
312
347
313
348
def contents (self , uri : URI ) -> D :
314
349
"""
@@ -424,10 +459,23 @@ def resolver_with_root(self, resource: Resource[D]) -> Resolver[D]:
424
459
)
425
460
426
461
462
+ T = TypeVar ("T" , AnchorType [Any ], Resource [Any ])
463
+
464
+
465
+ @frozen
466
+ class Retrieved (Generic [D , T ]):
467
+ """
468
+ A value retrieved from a `Registry`.
469
+ """
470
+
471
+ value : T
472
+ registry : Registry [D ]
473
+
474
+
427
475
@frozen
428
476
class Resolved (Generic [D ]):
429
477
"""
430
- A resolved reference .
478
+ A reference resolved to its contents by a `Resolver` .
431
479
"""
432
480
433
481
contents : D
@@ -486,44 +534,24 @@ def lookup(self, ref: URI) -> Resolved[D]:
486
534
uri , fragment = self ._base_uri , ref [1 :]
487
535
else :
488
536
uri , fragment = urldefrag (urljoin (self ._base_uri , ref ))
489
- registry = self ._registry
490
- resource = registry .get (uri )
491
- if resource is None :
492
- registry = registry .crawl ()
493
- try :
494
- resource = registry [uri ]
495
- except exceptions .NoSuchResource :
496
- raise exceptions .Unresolvable (ref = ref ) from None
497
- except exceptions .Unretrievable :
498
- raise exceptions .Unresolvable (ref = ref )
537
+ try :
538
+ retrieved = self ._registry .get_or_retrieve (uri )
539
+ except exceptions .NoSuchResource :
540
+ raise exceptions .Unresolvable (ref = ref ) from None
541
+ except exceptions .Unretrievable :
542
+ raise exceptions .Unresolvable (ref = ref )
499
543
500
544
if fragment .startswith ("/" ):
501
- return resource .pointer (
502
- pointer = fragment ,
503
- resolver = self ._evolve (registry = registry , base_uri = uri ),
504
- )
545
+ resolver = self ._evolve (registry = retrieved .registry , base_uri = uri )
546
+ return retrieved .value .pointer (pointer = fragment , resolver = resolver )
505
547
506
548
if fragment :
507
- try :
508
- anchor = registry .anchor (uri , fragment )
509
- except LookupError :
510
- registry = registry .crawl ()
511
- try :
512
- anchor = registry .anchor (uri , fragment )
513
- except LookupError :
514
- raise exceptions .NoSuchAnchor (
515
- ref = ref ,
516
- resource = resource ,
517
- anchor = fragment ,
518
- )
519
- return anchor .resolve (
520
- resolver = self ._evolve (registry = registry , base_uri = uri ),
521
- )
549
+ retrieved = retrieved .registry .anchor (uri , fragment )
550
+ resolver = self ._evolve (registry = retrieved .registry , base_uri = uri )
551
+ return retrieved .value .resolve (resolver = resolver )
522
552
523
- return Resolved (
524
- contents = resource .contents ,
525
- resolver = self ._evolve (registry = registry , base_uri = uri ),
526
- )
553
+ resolver = self ._evolve (registry = retrieved .registry , base_uri = uri )
554
+ return Resolved (contents = retrieved .value .contents , resolver = resolver )
527
555
528
556
def in_subresource (self , subresource : Resource [D ]) -> Resolver [D ]:
529
557
"""
0 commit comments