Skip to content

Commit 2ef05de

Browse files
authored
Merge pull request #220 from IdentityPython/ft-refactor-resources
refactor resources, adding a lot of typing
2 parents 196146f + 14c47fd commit 2ef05de

File tree

13 files changed

+314
-194
lines changed

13 files changed

+314
-194
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ lxml >=4.1.1
88
mako
99
pyXMLSecurity >=0.15
1010
pyconfig
11+
pydantic
1112
pyramid
1213
pyyaml >=3.10
1314
redis

src/pyff/api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import threading
33
from datetime import datetime, timedelta
44
from json import dumps
5-
from typing import Any, List, Mapping, Iterator
5+
from typing import Any, Iterable, List, Mapping
66

77
import pkg_resources
88
import pyramid.httpexceptions as exc
@@ -389,7 +389,7 @@ def resources_handler(request):
389389
:return: a JSON representation of the set of resources currently loaded by the server
390390
"""
391391

392-
def _infos(resources: Iterator[Resource]) -> List[Mapping[str, Any]]:
392+
def _infos(resources: Iterable[Resource]) -> List[Mapping[str, Any]]:
393393
return list(filter(lambda i: 'State' in i and i['State'] is not None, [_info(r) for r in resources]))
394394

395395
def _info(r: Resource) -> Mapping[str, Any]:

src/pyff/builtins.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .exceptions import MetadataException
2828
from .logs import get_log
2929
from .pipes import PipeException, PipelineCallback, Plumbing, pipe
30+
from .resource import ResourceOpts
3031
from .samlmd import (
3132
annotate_entity,
3233
discojson_t,
@@ -352,7 +353,7 @@ def _pipe(req, *opts):
352353

353354

354355
@pipe
355-
def when(req, condition, *values):
356+
def when(req: Plumbing.Request, condition: str, *values):
356357
"""
357358
Conditionally execute part of the pipeline.
358359
@@ -377,6 +378,8 @@ def when(req, condition, *values):
377378
followed. If 'bar' is present in the state with the value 'bill' then the other branch is followed.
378379
"""
379380
c = req.state.get(condition, None)
381+
if c is None:
382+
log.debug(f'Condition {repr(condition)} not present in state {req.state}')
380383
if c is not None and (not values or _any(values, c)):
381384
return Plumbing(pipeline=req.args, pid="%s.when" % req.plumbing.id).iprocess(req)
382385
return req.t
@@ -628,32 +631,37 @@ def load(req, *opts):
628631
)
629632

630633
url = r.pop(0)
631-
params = {"via": [], "cleanup": [], "verify": None, "as": url}
634+
635+
# Copy parent node opts as a starting point
636+
child_opts = req.md.rm.opts.copy(update={"via": [], "cleanup": [], "verify": None, "alias": url})
632637

633638
while len(r) > 0:
634639
elt = r.pop(0)
635640
if elt in ("as", "verify", "via", "cleanup"):
641+
# These elements have an argument
636642
if len(r) > 0:
637-
if elt in ("via", "cleanup"):
638-
params[elt].append(r.pop(0))
643+
value = r.pop(0)
644+
if elt == "as":
645+
child_opts.alias = value
646+
elif elt == "verify":
647+
child_opts.verify = value
648+
elif elt == "via":
649+
child_opts.via.append(PipelineCallback(value, req, store=req.md.store))
650+
elif elt == "cleanup":
651+
child_opts.cleanup.append(PipelineCallback(value, req, store=req.md.store))
639652
else:
640-
params[elt] = r.pop(0)
653+
raise ValueError(f'Unhandled resource option {elt}')
641654
else:
642655
raise PipeException(
643656
"Usage: load resource [as url] [[verify] verification] [via pipeline]* [cleanup pipeline]*"
644657
)
645658
else:
646-
params['verify'] = elt
647-
648-
if params['via'] is not None:
649-
params['via'] = [PipelineCallback(p, req, store=req.md.store) for p in params['via']]
650-
651-
if params['cleanup'] is not None:
652-
params['cleanup'] = [PipelineCallback(p, req, store=req.md.store) for p in params['cleanup']]
659+
child_opts.verify = elt
653660

654-
params.update(opts)
661+
# override anything in child_opts with what is in opts
662+
child_opts = child_opts.copy(update=opts)
655663

656-
req.md.rm.add_child(url, **params)
664+
req.md.rm.add_child(url, child_opts)
657665

658666
log.debug("Refreshing all resources")
659667
req.md.rm.reload(fail_on_error=bool(opts['fail_on_error']))

src/pyff/logs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def isEnabledFor(self, lvl):
5757
return self._log.isEnabledFor(lvl)
5858

5959

60-
def get_log(name):
60+
def get_log(name: str) -> PyFFLogger:
6161
return PyFFLogger(name)
6262

6363

src/pyff/parse.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import os
2-
from datetime import datetime
2+
from abc import ABC
3+
from collections import deque
4+
from typing import Any, AnyStr, Dict, List, Mapping, Optional, Union
35

46
from xmlsec.crypto import CertDict
57

68
from .constants import NS
79
from .logs import get_log
10+
from .resource import Resource, ResourceOpts
811
from .utils import find_matching_files, parse_xml, root, unicode_stream, utc_now
912

1013
__author__ = 'leifj'
@@ -22,10 +25,22 @@ def raise_wraped(self):
2225
raise self._wraped
2326

2427

25-
class PyffParser(object):
28+
class PyffParser(ABC):
2629
def to_json(self):
2730
return str(self)
2831

32+
def magic(self, content: str):
33+
"""Return True if this parser is applicable to this content"""
34+
raise NotImplementedError()
35+
36+
def parse(self, resource: Resource, content: str) -> Mapping[str, Any]:
37+
"""Initialise/update a resource based on this content, returning information about it
38+
TODO: Determine what 'parse' actually means
39+
40+
TODO: Return something more structured than an arbitrary mapping
41+
"""
42+
raise NotImplementedError()
43+
2944

3045
class NoParser(PyffParser):
3146
def __init__(self):
@@ -34,10 +49,10 @@ def __init__(self):
3449
def __str__(self):
3550
return "Not a supported type"
3651

37-
def magic(self, content):
52+
def magic(self, content: str) -> bool:
3853
return True
3954

40-
def parse(self, resource, content):
55+
def parse(self, resource: Resource, content: str) -> Mapping[str, Any]:
4156
raise ParserException("No matching parser found for %s" % resource.url)
4257

4358

@@ -48,17 +63,18 @@ def __init__(self, extensions):
4863
def __str__(self):
4964
return "Directory"
5065

51-
def magic(self, content):
66+
def magic(self, content: str) -> bool:
5267
return os.path.isdir(content)
5368

54-
def parse(self, resource, content):
55-
resource.children = []
69+
def parse(self, resource: Resource, content: str) -> Mapping[str, Any]:
70+
resource.children = deque()
5671
info = dict()
5772
info['Description'] = 'Directory'
5873
info['Expiration Time'] = 'never expires'
5974
n = 0
6075
for fn in find_matching_files(content, self.extensions):
61-
resource.add_child("file://" + fn)
76+
child_opts = resource.opts.copy(update={'alias': None})
77+
resource.add_child("file://" + fn, child_opts)
6278
n += 1
6379

6480
if n == 0:
@@ -78,10 +94,10 @@ def __init__(self):
7894
def __str__(self):
7995
return "XRD"
8096

81-
def magic(self, content):
97+
def magic(self, content: str) -> bool:
8298
return 'XRD' in content
8399

84-
def parse(self, resource, content):
100+
def parse(self, resource: Resource, content: str) -> Mapping[str, Any]:
85101
info = dict()
86102
info['Description'] = "XRD links"
87103
info['Expiration Time'] = 'never expires'
@@ -97,22 +113,24 @@ def parse(self, resource, content):
97113
if len(fingerprints) > 0:
98114
fp = fingerprints[0]
99115
log.debug("XRD: {} verified by {}".format(link_href, fp))
100-
resource.add_child(link_href, verify=fp)
116+
child_opts = resource.opts.copy(update={'alias': None})
117+
resource.add_child(link_href, child_opts)
101118
resource.last_seen = utc_now().replace(microsecond=0)
102119
resource.expire_time = None
103120
resource.never_expires = True
104121
return info
105122

106123

107-
_parsers = [XRDParser(), DirectoryParser(['xml']), NoParser()]
124+
_parsers: List[PyffParser] = [XRDParser(), DirectoryParser(['xml']), NoParser()]
108125

109126

110127
def add_parser(parser):
111128
_parsers.insert(0, parser)
112129

113130

114-
def parse_resource(resource, content):
131+
def parse_resource(resource: Resource, content: str) -> Optional[Mapping[str, Any]]:
115132
for parser in _parsers:
116133
if parser.magic(content):
117134
resource.last_parser = parser
118135
return parser.parse(resource, content)
136+
return None

0 commit comments

Comments
 (0)