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.
- {aridity-91 → aridity-93}/PKG-INFO +13 -2
- {aridity-91 → aridity-93}/README.md +12 -1
- {aridity-91 → aridity-93}/aridity/arid_config.py +11 -6
- {aridity-91 → aridity-93}/aridity/directives.py +11 -2
- {aridity-91 → aridity-93}/aridity/functions.py +5 -9
- {aridity-91 → aridity-93}/aridity/model.py +7 -10
- aridity-93/aridity/resolve.py +68 -0
- {aridity-91 → aridity-93}/aridity/scope.py +15 -17
- {aridity-91 → aridity-93}/aridity/search.py +46 -52
- {aridity-91 → aridity-93}/aridity/util.py +3 -48
- {aridity-91 → aridity-93}/aridity.egg-info/PKG-INFO +13 -2
- {aridity-91 → aridity-93}/setup.py +1 -1
- aridity-91/aridity/resolve.py +0 -83
- {aridity-91 → aridity-93}/aridity/__init__.py +0 -0
- {aridity-91 → aridity-93}/aridity/config.py +0 -0
- {aridity-91 → aridity-93}/aridity/grammar.py +0 -0
- {aridity-91 → aridity-93}/aridity/keyring.py +0 -0
- {aridity-91 → aridity-93}/aridity/processtemplate.py +0 -0
- {aridity-91 → aridity-93}/aridity/repl.py +0 -0
- {aridity-91 → aridity-93}/aridity/stacks.py +0 -0
- {aridity-91 → aridity-93}/aridity.egg-info/SOURCES.txt +0 -0
- {aridity-91 → aridity-93}/aridity.egg-info/dependency_links.txt +0 -0
- {aridity-91 → aridity-93}/aridity.egg-info/entry_points.txt +0 -0
- {aridity-91 → aridity-93}/aridity.egg-info/requires.txt +0 -0
- {aridity-91 → aridity-93}/aridity.egg-info/top_level.txt +0 -0
- {aridity-91 → aridity-93}/parabject.py +0 -0
- {aridity-91 → aridity-93}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: aridity
|
|
3
|
-
Version:
|
|
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
|
|
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
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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:
|
|
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
|
-
|
|
78
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
254
|
-
return scope.resolved(*(r.resolve(scope).textvalue for r in resolvables)
|
|
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
|
|
41
|
-
return
|
|
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
|
|
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
|
|
379
|
-
|
|
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(
|
|
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,
|
|
7
|
+
from .util import NoSuchPathException, solo, UnsupportedEntryException
|
|
8
8
|
from itertools import chain
|
|
9
|
-
import
|
|
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 =
|
|
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
|
|
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)
|
|
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
|
-
|
|
150
|
-
|
|
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
|
|
205
|
-
return Scope([self]
|
|
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
|
|
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.
|
|
77
|
+
self.prefixlen = len(prefix)
|
|
78
|
+
self.path = [*prefix, *path]
|
|
91
79
|
|
|
92
|
-
def
|
|
93
|
-
|
|
80
|
+
def _search(self, scope):
|
|
81
|
+
merges = [Iterator([], scope, self.path[0], self.prefixlen)]
|
|
94
82
|
size = len(self.path)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
for
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
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 = '
|
|
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',
|
aridity-91/aridity/resolve.py
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|