aridity 91__tar.gz → 93__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aridity
3
- Version: 91
3
+ Version: 93
4
4
  Summary: DRY config and template system, easily extensible with Python
5
5
  Home-page: https://pypi.org/project/aridity/
6
6
  Author: foyono
@@ -191,6 +191,17 @@ def plusequals(prefix, suffix, scope)
191
191
 
192
192
  Assign expression to prefix plus an opaque key, i.e. add to list.
193
193
 
194
+ <a id="aridity.directives.commaequals"></a>
195
+
196
+ ###### commaequals
197
+
198
+ ```python
199
+ @prime
200
+ def commaequals(prefix, suffix, scope)
201
+ ```
202
+
203
+ Split expression on whitespace and make a list out of the parts.
204
+
194
205
  <a id="aridity.functions"></a>
195
206
 
196
207
  ### aridity.functions
@@ -474,7 +485,7 @@ class AbstractScope(Resolvable)
474
485
  ###### resolved
475
486
 
476
487
  ```python
477
- def resolved(*path, **kwargs)
488
+ def resolved(*path)
478
489
  ```
479
490
 
480
491
  Follow the given path to get an expression, evaluate it (resolving any paths it requires, recursively), and return the resulting model object.
@@ -181,6 +181,17 @@ def plusequals(prefix, suffix, scope)
181
181
 
182
182
  Assign expression to prefix plus an opaque key, i.e. add to list.
183
183
 
184
+ <a id="aridity.directives.commaequals"></a>
185
+
186
+ ###### commaequals
187
+
188
+ ```python
189
+ @prime
190
+ def commaequals(prefix, suffix, scope)
191
+ ```
192
+
193
+ Split expression on whitespace and make a list out of the parts.
194
+
184
195
  <a id="aridity.functions"></a>
185
196
 
186
197
  ### aridity.functions
@@ -464,7 +475,7 @@ class AbstractScope(Resolvable)
464
475
  ###### resolved
465
476
 
466
477
  ```python
467
- def resolved(*path, **kwargs)
478
+ def resolved(*path)
468
479
  ```
469
480
 
470
481
  Follow the given path to get an expression, evaluate it (resolving any paths it requires, recursively), and return the resulting model object.
@@ -1,7 +1,8 @@
1
1
  'Print given config (with optional path in config) as shell snippet.'
2
+ from .functions import OpaqueKey
2
3
  from .model import Boolean, Entry, Locator, Number, Text
3
4
  from .scope import Scope
4
- import os, sys
5
+ import os, shlex, sys
5
6
 
6
7
  def _configpath(configname):
7
8
  if os.sep in configname:
@@ -12,17 +13,21 @@ def _configpath(configname):
12
13
  return path
13
14
  raise Exception(f"Not found: {configname}")
14
15
 
16
+ def _bashforeval(scope):
17
+ return ''.join(f"{name}={obj.tobash()}\n" for name, obj in scope.resolveditems())
18
+
15
19
  def _scopetobash(self, toplevel = False):
16
20
  if toplevel:
17
- return ''.join(f"{name}={obj.resolve(self).tobash()}\n" for name, obj in self.resolvables.items())
18
- if self.islist:
19
- return f"({' '.join(x.resolve(self).tobash() for _, x in self.resolvables.items())})"
20
- return Text(self.tobash(True)).tobash()
21
+ return _bashforeval(self)
22
+ d = dict(self.resolveditems())
23
+ if all(map(OpaqueKey.isopaque, d)):
24
+ return f"({' '.join(x.tobash() for x in d.values())})"
25
+ return shlex.quote(_bashforeval(self))
21
26
 
22
27
  Scope.tobash = _scopetobash
23
28
  Boolean.tobash = lambda self, toplevel: 'true' if self.booleanvalue else 'false'
24
29
  Number.tobash = lambda self: str(self.numbervalue)
25
- Text.tobash = lambda self: "'{}'".format(self.textvalue.replace("'", r"'\''"))
30
+ Text.tobash = lambda self: shlex.quote(self.textvalue)
26
31
 
27
32
  def main():
28
33
  scope = Scope()
@@ -74,8 +74,16 @@ def colonequals(prefix, suffix, scope):
74
74
  def plusequals(prefix, suffix, scope):
75
75
  'Assign expression to prefix plus an opaque key, i.e. add to list.'
76
76
  from .functions import OpaqueKey
77
- phrase = suffix.tophrase()
78
- scope[prefix.topath(scope) + (OpaqueKey(),)] = phrase
77
+ scope[prefix.topath(scope) + (OpaqueKey(),)] = suffix.tophrase()
78
+
79
+ @prime
80
+ def commaequals(prefix, suffix, scope):
81
+ 'Split expression on whitespace and make a list out of the parts.'
82
+ from .functions import OpaqueKey
83
+ basepath = prefix.topath(scope)
84
+ scope.getorcreatesubscope(basepath)
85
+ for r in suffix.words():
86
+ scope[basepath + (OpaqueKey(),)] = r
79
87
 
80
88
  @prime
81
89
  def _cat(prefix, suffix, scope):
@@ -92,4 +100,5 @@ def coredirectives():
92
100
  yield '=', equals
93
101
  yield ':=', colonequals
94
102
  yield '+=', plusequals
103
+ yield ',=', commaequals
95
104
  yield '<', _cat
@@ -103,7 +103,7 @@ def map_(scope, objsresolvable, *args):
103
103
  kname, vname, resolvable = args
104
104
  kname = kname.resolve(scope).textvalue
105
105
  vname = vname.resolve(scope).textvalue
106
- result = Scope(islist = True) # XXX: Really no parent?
106
+ result = Scope() # XXX: Really no parent?
107
107
  for k, v in objs.resolveditems():
108
108
  result.resolvables.put(k, resolvable.resolve(context(k, v)))
109
109
  return result
@@ -116,7 +116,7 @@ def map1(scope, contextresolvable, resultresolvable):
116
116
 
117
117
  def _flat(scope, listsresolvable):
118
118
  from .scope import Scope
119
- s = Scope(islist = True) # XXX: Really no parent?
119
+ s = Scope() # XXX: Really no parent?
120
120
  for lk, l in listsresolvable.resolve(scope).resolvables.items():
121
121
  for ok, obj in l.resolvables.items():
122
122
  s.resolvables.put((lk, ok), obj)
@@ -129,16 +129,13 @@ def join(scope, partsresolvable, sepresolvable = None):
129
129
  'Concatenate the given list, using optional separator. Frequently used with `map`.'
130
130
  return Join(scope, partsresolvable).execute(sepresolvable)
131
131
 
132
- def _aslist(scope, *resolvables):
133
- return scope.resolved(*(r.resolve(scope).textvalue for r in resolvables), **{'aslist': True})
134
-
135
132
  def str_(scope, resolvable):
136
133
  'Coerce to string.'
137
134
  return resolvable.resolve(scope).totext()
138
135
 
139
136
  def list_(scope, *resolvables):
140
137
  'Create a list.'
141
- v = scope.createchild(islist = True)
138
+ v = scope.createchild()
142
139
  for r in resolvables:
143
140
  v[OpaqueKey(),] = r
144
141
  return v
@@ -250,8 +247,8 @@ class Join:
250
247
  break
251
248
  return resobj
252
249
 
253
- def getimpl(scope, *resolvables, **kwargs):
254
- return scope.resolved(*(r.resolve(scope).textvalue for r in resolvables), **kwargs)
250
+ def getimpl(scope, *resolvables):
251
+ return scope.resolved(*(r.resolve(scope).textvalue for r in resolvables))
255
252
 
256
253
  def corefunctions():
257
254
  yield 'gpg', gpg
@@ -272,7 +269,6 @@ def corefunctions():
272
269
  yield 'flat', _flat
273
270
  yield 'label', _label
274
271
  yield 'join', join
275
- yield ',', _aslist # XXX: Really comma?
276
272
  yield 'str', str_
277
273
  yield 'list', list_
278
274
  yield 'fork', _fork
@@ -37,8 +37,8 @@ class Resolvable(Struct):
37
37
 
38
38
  class Resolved(Resolvable):
39
39
 
40
- def resolve(self, scope, aslist = False):
41
- return List([self]) if aslist else self
40
+ def resolve(self, scope):
41
+ return self
42
42
 
43
43
  nullmonitor = lambda text: None
44
44
 
@@ -80,9 +80,7 @@ class Concat(Resolvable): # TODO: Ban in path components.
80
80
  self.parts = parts
81
81
  self.monitor = monitor
82
82
 
83
- def resolve(self, scope, aslist = False):
84
- if aslist:
85
- return List([part.resolve(scope) for part in self.parts if not part.ignorable])
83
+ def resolve(self, scope):
86
84
  result = Indeterminate
87
85
  negbuffer = NegBuffer()
88
86
  for part in self.parts:
@@ -111,7 +109,7 @@ class BaseSimpleValue(Resolved):
111
109
  value, = t
112
110
  return cls(value)
113
111
 
114
- def unravel(self):
112
+ def unravel(self, listmode = None):
115
113
  return self.scalar
116
114
 
117
115
  class Blank(BaseSimpleValue):
@@ -375,9 +373,8 @@ class Call(Resolvable):
375
373
  if not a.ignorable:
376
374
  yield a
377
375
 
378
- def resolve(self, scope, aslist = False):
379
- result = self._functionvalue(scope)(scope.getresolvecontext(), *self._resolvables())
380
- return List([result]) if aslist else result
376
+ def resolve(self, scope):
377
+ return self._functionvalue(scope)(scope.getresolvecontext(), *self._resolvables())
381
378
 
382
379
  def resolvemulti(self, j, scope):
383
380
  f = self._functionvalue(scope.getresolvecontext())
@@ -390,7 +387,7 @@ class Call(Resolvable):
390
387
 
391
388
  def List(objs):
392
389
  from .scope import Scope
393
- s = Scope(islist = True)
390
+ s = Scope()
394
391
  for obj in objs:
395
392
  s.resolvables.put(object(), obj) # XXX: Not OpaqueKey?
396
393
  return s
@@ -0,0 +1,68 @@
1
+ from .search import Query
2
+ from .util import CycleException, NoSuchPathException, TreeNoSuchPathException
3
+ from itertools import chain
4
+
5
+ class BaseResolveContext:
6
+
7
+ @property
8
+ def label(self):
9
+ return self.leafscope().label
10
+
11
+ @property
12
+ def parents(self):
13
+ return self.leafscope().parents
14
+
15
+ @property
16
+ def resolvables(self):
17
+ return self.leafscope().resolvables
18
+
19
+ def createchild(self):
20
+ return self.leafscope().createchild()
21
+
22
+ def getresolvecontext(self):
23
+ return self
24
+
25
+ def resolvableornone(self, key):
26
+ return self.leafscope().resolvableornone(key)
27
+
28
+ def resolved(self, *path):
29
+ return self.resolvedimpl(path) if path else self.leafscope()
30
+
31
+ def staticscope(self):
32
+ return self.leafscope().staticscope()
33
+
34
+ class AnchorResolveContext(BaseResolveContext):
35
+
36
+ def __init__(self, anchorscope):
37
+ self.anchorscope = anchorscope
38
+
39
+ def leafscope(self):
40
+ return self.anchorscope
41
+
42
+ def resolvedimpl(self, path):
43
+ hit = Query([], path).search(self.anchorscope)
44
+ return hit.resolvable.resolve(ResolveContext(self.anchorscope, path, [hit.address]))
45
+
46
+ class ResolveContext(BaseResolveContext):
47
+
48
+ @classmethod
49
+ def _of(cls, *args):
50
+ return cls(*args)
51
+
52
+ def __init__(self, anchorscope, exprpath, addresses):
53
+ self.anchorscope = anchorscope
54
+ self.scopepath = exprpath[:-1]
55
+ self.exprpath = exprpath
56
+ self.addresses = addresses
57
+
58
+ def leafscope(self):
59
+ return Query([], self.scopepath).search(self.anchorscope).naiveresolve() if self.scopepath else self.anchorscope # XXX: Is naiveresolve correct here?
60
+
61
+ def resolvedimpl(self, path):
62
+ try:
63
+ hit = Query(self.scopepath, path).search(self.anchorscope)
64
+ if hit.address in self.addresses: # XXX: Could it be valid to resolve the same address recursively with 2 different contexts?
65
+ raise CycleException(path)
66
+ return hit.resolvable.resolve(self._of(self.anchorscope, list(chain(self.scopepath, path)), self.addresses + [hit.address]))
67
+ except NoSuchPathException as e:
68
+ raise TreeNoSuchPathException(self.exprpath, [e])
@@ -4,9 +4,9 @@ from .model import Directive, Function, Hole, Resolvable, Scalar, star, Stream,
4
4
  from .resolve import AnchorResolveContext
5
5
  from .search import Query
6
6
  from .stacks import IndentStack, SimpleStack, ThreadLocalResolvable
7
- from .util import NoSuchPathException, OrderedDict, solo, UnsupportedEntryException
7
+ from .util import NoSuchPathException, solo, UnsupportedEntryException
8
8
  from itertools import chain
9
- import collections, os, sys, threading
9
+ import os, sys, threading
10
10
 
11
11
  class NotAPathException(Exception): pass
12
12
 
@@ -45,7 +45,7 @@ class Resolvables:
45
45
  return {}
46
46
 
47
47
  def __init__(self, scope):
48
- self.d = collections.OrderedDict()
48
+ self.d = {}
49
49
  self.scope = scope
50
50
 
51
51
  def put(self, key, resolvable):
@@ -72,10 +72,6 @@ class Resolvables:
72
72
  class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be moved to Scope.
73
73
 
74
74
  nametypes = {str, type(None), OpaqueKey} # XXX: Is None still used by anything?
75
- try:
76
- nametypes.add(unicode)
77
- except NameError:
78
- pass
79
75
 
80
76
  def __init__(self, parents):
81
77
  self.resolvables = Resolvables(self)
@@ -121,7 +117,7 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
121
117
  def getresolvecontext(self):
122
118
  return AnchorResolveContext(self)
123
119
 
124
- def resolved(self, *path, **kwargs):
120
+ def resolved(self, *path):
125
121
  'Follow the given path to get an expression, evaluate it (resolving any paths it requires, recursively), and return the resulting model object.'
126
122
  # TODO LATER: API to filter out results unsuitable for context.
127
123
  for s in chain.from_iterable(self.scopedepths()):
@@ -131,7 +127,7 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
131
127
  break
132
128
  except AttributeError:
133
129
  pass
134
- return g(self.getresolvecontext(), *map(Text, path), **kwargs)
130
+ return g(self.getresolvecontext(), *map(Text, path))
135
131
 
136
132
  def resolvableornone(self, key):
137
133
  return self.resolvables.getornone(key)
@@ -145,9 +141,12 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
145
141
  yield scopes
146
142
  scopes = nextscopes
147
143
 
148
- def unravel(self):
149
- d = OrderedDict([k, o.unravel()] for k, o in self.resolveditems())
150
- return list(d) if self.islist or (d and all(OpaqueKey.isopaque(k) for k in d.keys())) else d
144
+ def unravel(self, listmode = 0):
145
+ def g():
146
+ args = [2] if 2 == listmode else []
147
+ for k, o in self.resolveditems():
148
+ yield k, o.unravel(*args)
149
+ return [v for _, v in g()] if listmode else dict(g())
151
150
 
152
151
  def staticscope(self):
153
152
  for s in chain.from_iterable(self.scopedepths()):
@@ -164,7 +163,7 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
164
163
  if not acceptdirectivename(word):
165
164
  continue
166
165
  try:
167
- resolvable = Query([word]).search(self).resolvable
166
+ resolvable = Query([], [word]).search(self).resolvable
168
167
  except NoSuchPathException:
169
168
  continue
170
169
  try:
@@ -201,8 +200,8 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
201
200
  for t in r.resolvemulti(k, self):
202
201
  yield t
203
202
 
204
- def createchild(self, **kwargs):
205
- return Scope([self], **kwargs)
203
+ def createchild(self):
204
+ return Scope([self])
206
205
 
207
206
  class StaticScope(AbstractScope):
208
207
 
@@ -269,9 +268,8 @@ StaticScope = StaticScope()
269
268
 
270
269
  class Scope(AbstractScope):
271
270
 
272
- def __init__(self, parents = None, islist = False):
271
+ def __init__(self, parents = None):
273
272
  super().__init__([StaticScope] if parents is None else parents)
274
- self.islist = islist
275
273
 
276
274
  def resolve(self, scope):
277
275
  return self
@@ -1,4 +1,4 @@
1
- from .util import popattr, UnparseNoSuchPathException
1
+ from .util import inf, popattr, UnparseNoSuchPathException
2
2
  from collections import namedtuple
3
3
  from itertools import islice
4
4
  import heapq
@@ -18,12 +18,12 @@ class Address(namedtuple('BaseAddress', 'scope key')): pass
18
18
  class Hit(namedtuple('BaseHit', 'depths address resolvable')):
19
19
 
20
20
  def naiveresolve(self):
21
- return self.resolvable.resolve(self.address.scope) # XXX: Wise?
21
+ return self.resolvable if self.address is None else self.resolvable.resolve(self.address.scope) # XXX: Wise?
22
22
 
23
- def iterornone(self, word):
23
+ def iterornone(self, word, inprefix):
24
24
  contextscope = self.naiveresolve()
25
25
  if hasattr(contextscope, 'resolvableornone'):
26
- return Iterator(self.depths, contextscope, word)
26
+ return Iterator(self.depths, contextscope, word, inprefix)
27
27
 
28
28
  def shortcut(self, zerocount):
29
29
  return all(not d for d in islice(self.depths, len(self.depths) - zerocount, None))
@@ -35,13 +35,15 @@ class Iterable:
35
35
 
36
36
  class Iterator(Iterable):
37
37
 
38
- def __init__(self, depths, contextscope, word):
38
+ def __init__(self, depths, contextscope, word, inprefix):
39
39
  def g():
40
40
  for depth, scopes in enumerate(contextscope.scopedepths()):
41
41
  for scope in scopes:
42
42
  resolvable = scope.resolvableornone(word)
43
43
  if resolvable is not None:
44
44
  yield Hit(depths + [depth], Address(scope, word), resolvable)
45
+ if inprefix:
46
+ yield Hit(depths + [inf], None, contextscope)
45
47
  self.iterator = g()
46
48
 
47
49
  def next(self):
@@ -68,56 +70,48 @@ def _lt(*depthspair):
68
70
  if d1 > d2:
69
71
  break
70
72
 
71
- class Sump:
72
-
73
- besthit = None
74
-
75
- def add(self, iterable):
76
- pass
77
-
78
- def offer(self, hit):
79
- if self.besthit is None or _lt(hit.depths, self.besthit.depths):
80
- self.besthit = hit
81
-
82
- def __iter__(self):
83
- if self.besthit is not None:
84
- yield self.besthit
85
-
86
73
  class Query:
87
74
 
88
- def __init__(self, path):
75
+ def __init__(self, prefix, path):
89
76
  assert path
90
- self.path = path
77
+ self.prefixlen = len(prefix)
78
+ self.path = [*prefix, *path]
91
79
 
92
- def search(self, scope):
93
- root = Iterator([], scope, self.path[0])
80
+ def _search(self, scope):
81
+ merges = [Iterator([], scope, self.path[0], self.prefixlen)]
94
82
  size = len(self.path)
95
- if 1 == size:
96
- for hit in root:
97
- return hit
98
- else:
99
- merges = [root]
100
- for _ in range(size - 2):
101
- merges.append(Merge())
102
- sump = Sump()
103
- merges.append(sump)
104
- for cursor, merge in enumerate(merges):
105
- xs = range(cursor + 1, size)
106
- for hit in sump:
107
- if hit.shortcut(len(xs)):
108
- return hit
109
- for hit in merge:
110
- for x in xs:
111
- i = hit.iterornone(self.path[x])
112
- if i is None:
113
- break
114
- try:
115
- hit = i.next()
116
- except StopIteration:
117
- break
118
- merges[x].add(i)
119
- else:
120
- if hit.shortcut(len(xs)):
121
- return hit
122
- sump.offer(hit)
83
+ for _ in range(1, size):
84
+ merges.append(Merge())
85
+ for cursor, merge in enumerate(merges):
86
+ zerocount = size - cursor - 1
87
+ yield None, zerocount
88
+ for hit in merge:
89
+ for x in range(cursor + 1, size):
90
+ i = hit.iterornone(self.path[x], x < self.prefixlen)
91
+ if i is None:
92
+ break
93
+ try:
94
+ hit = i.next()
95
+ except StopIteration:
96
+ break
97
+ merges[x].add(i)
98
+ else:
99
+ yield hit, zerocount
100
+
101
+ def findall(self, scope):
102
+ for hit, _ in self._search(scope):
103
+ if hit is not None:
104
+ yield hit
105
+
106
+ def search(self, scope):
107
+ besthit = None
108
+ for hit, zc in self._search(scope):
109
+ if hit is None:
110
+ if besthit is not None and besthit.shortcut(zc):
111
+ return besthit
112
+ else:
113
+ if hit.shortcut(zc):
114
+ return hit
115
+ if besthit is None or _lt(hit.depths, besthit.depths):
116
+ besthit = hit
123
117
  raise UnparseNoSuchPathException(self.path)
@@ -1,7 +1,8 @@
1
1
  from importlib.metadata import entry_points
2
- import collections, sys
2
+ import sys
3
3
 
4
4
  dotpy = '.py'
5
+ inf = float('inf')
5
6
  null_exc_info = None, None, None
6
7
 
7
8
  class NoSuchPathException(Exception): pass
@@ -16,7 +17,7 @@ class TreeNoSuchPathException(NoSuchPathException):
16
17
 
17
18
  def __str__(self):
18
19
  path, causes = self.args # XXX: Also collect (and show) where in the tree the causes happened?
19
- causestrtocount = collections.OrderedDict()
20
+ causestrtocount = {}
20
21
  for causestr in map(str, causes):
21
22
  try:
22
23
  causestrtocount[causestr] += 1
@@ -34,52 +35,6 @@ class CycleException(UnparseNoSuchPathException): pass
34
35
 
35
36
  class UnsupportedEntryException(Exception): pass
36
37
 
37
- class OrderedDictWrapper:
38
-
39
- def __init__(self, *args):
40
- self.d = collections.OrderedDict(*args)
41
-
42
- def __bool__(self):
43
- return bool(self.d)
44
-
45
- def __nonzero__(self):
46
- return self.__bool__()
47
-
48
- class OrderedDict(OrderedDictWrapper):
49
-
50
- def __setitem__(self, k, v):
51
- self.d[k] = v
52
-
53
- def __getitem__(self, k):
54
- return self.d[k]
55
-
56
- def __delitem__(self, k):
57
- del self.d[k]
58
-
59
- def get(self, k, default = None):
60
- return self.d.get(k, default)
61
-
62
- def keys(self):
63
- return self.d.keys()
64
-
65
- def values(self):
66
- return self.d.values()
67
-
68
- def items(self):
69
- return self.d.items()
70
-
71
- def __iter__(self):
72
- return iter(self.d.values())
73
-
74
- def __eq__(self, that):
75
- return self.d == that
76
-
77
- def __repr__(self):
78
- return repr(self.d)
79
-
80
- def update(self, other):
81
- return self.d.update(other)
82
-
83
38
  def openresource(package_or_name, resource_name, encoding = 'ascii'):
84
39
  'Like `pkg_resources.resource_stream` but text mode.'
85
40
  from .model import Resource
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aridity
3
- Version: 91
3
+ Version: 93
4
4
  Summary: DRY config and template system, easily extensible with Python
5
5
  Home-page: https://pypi.org/project/aridity/
6
6
  Author: foyono
@@ -191,6 +191,17 @@ def plusequals(prefix, suffix, scope)
191
191
 
192
192
  Assign expression to prefix plus an opaque key, i.e. add to list.
193
193
 
194
+ <a id="aridity.directives.commaequals"></a>
195
+
196
+ ###### commaequals
197
+
198
+ ```python
199
+ @prime
200
+ def commaequals(prefix, suffix, scope)
201
+ ```
202
+
203
+ Split expression on whitespace and make a list out of the parts.
204
+
194
205
  <a id="aridity.functions"></a>
195
206
 
196
207
  ### aridity.functions
@@ -474,7 +485,7 @@ class AbstractScope(Resolvable)
474
485
  ###### resolved
475
486
 
476
487
  ```python
477
- def resolved(*path, **kwargs)
488
+ def resolved(*path)
478
489
  ```
479
490
 
480
491
  Follow the given path to get an expression, evaluate it (resolving any paths it requires, recursively), and return the resulting model object.
@@ -49,7 +49,7 @@ class SourceInfo:
49
49
  sourceinfo = SourceInfo('.')
50
50
  setup(
51
51
  name = 'aridity',
52
- version = '91',
52
+ version = '93',
53
53
  description = 'DRY config and template system, easily extensible with Python',
54
54
  url = 'https://pypi.org/project/aridity/',
55
55
  author = 'foyono',
@@ -1,83 +0,0 @@
1
- from .search import Query
2
- from .util import CycleException, NoSuchPathException, TreeNoSuchPathException
3
- from itertools import chain
4
-
5
- def _slices(path):
6
- yield path
7
- limit = len(path) + 1
8
- for k in range(1, limit):
9
- for i in range(limit - k):
10
- yield path[:i] + path[i + k:]
11
-
12
- class BaseResolveContext:
13
-
14
- @property
15
- def label(self):
16
- return self.leafscope().label
17
-
18
- @property
19
- def parents(self):
20
- return self.leafscope().parents
21
-
22
- @property
23
- def resolvables(self):
24
- return self.leafscope().resolvables
25
-
26
- def createchild(self, **kwargs):
27
- return self.leafscope().createchild(**kwargs)
28
-
29
- def getresolvecontext(self):
30
- return self
31
-
32
- def resolvableornone(self, key):
33
- return self.leafscope().resolvableornone(key)
34
-
35
- def resolved(self, *path, **kwargs):
36
- return self.resolvedimpl(path, kwargs) if path else self.leafscope()
37
-
38
- def staticscope(self):
39
- return self.leafscope().staticscope()
40
-
41
- class AnchorResolveContext(BaseResolveContext):
42
-
43
- def __init__(self, anchorscope):
44
- self.anchorscope = anchorscope
45
-
46
- def leafscope(self):
47
- return self.anchorscope
48
-
49
- def resolvedimpl(self, path, kwargs):
50
- hit = Query(path).search(self.anchorscope)
51
- return hit.resolvable.resolve(ResolveContext(self.anchorscope, path, [hit.address]), **kwargs)
52
-
53
- class ResolveContext(BaseResolveContext):
54
-
55
- @classmethod
56
- def _of(cls, *args):
57
- return cls(*args)
58
-
59
- def __init__(self, anchorscope, exprpath, addresses):
60
- self.anchorscope = anchorscope
61
- self.scopepath = exprpath[:-1]
62
- self.exprpath = exprpath
63
- self.addresses = addresses
64
-
65
- def leafscope(self):
66
- return Query(self.scopepath).search(self.anchorscope).naiveresolve() if self.scopepath else self.anchorscope # XXX: Is naiveresolve correct here?
67
-
68
- def resolvedimpl(self, path, kwargs):
69
- errors = []
70
- for prefix in _slices(self.scopepath):
71
- try:
72
- hit = Query(list(chain(prefix, path))).search(self.anchorscope)
73
- if hit.address in self.addresses: # XXX: Could it be valid to resolve the same address recursively with 2 different contexts?
74
- raise CycleException(path)
75
- except NoSuchPathException as e:
76
- errors.append(e)
77
- continue
78
- try:
79
- return hit.resolvable.resolve(self._of(self.anchorscope, list(chain(self.scopepath, path)), self.addresses + [hit.address]), **kwargs)
80
- except NoSuchPathException as e:
81
- errors.append(e)
82
- break # XXX: Or continue?
83
- raise TreeNoSuchPathException(self.exprpath, errors)
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