aridity 92__tar.gz → 94__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-92 → aridity-94}/PKG-INFO +18 -7
- {aridity-92 → aridity-94}/README.md +17 -6
- {aridity-92 → aridity-94}/aridity/__init__.py +2 -2
- {aridity-92 → aridity-94}/aridity/arid_config.py +13 -8
- {aridity-92 → aridity-94}/aridity/config.py +4 -8
- {aridity-92 → aridity-94}/aridity/directives.py +12 -3
- {aridity-92 → aridity-94}/aridity/functions.py +7 -11
- {aridity-92 → aridity-94}/aridity/model.py +12 -45
- {aridity-92 → aridity-94}/aridity/resolve.py +10 -15
- {aridity-92 → aridity-94}/aridity/scope.py +45 -48
- {aridity-92 → aridity-94}/aridity/search.py +4 -7
- {aridity-92 → aridity-94}/aridity/util.py +8 -48
- {aridity-92 → aridity-94}/aridity.egg-info/PKG-INFO +18 -7
- {aridity-92 → aridity-94}/setup.py +1 -1
- {aridity-92 → aridity-94}/aridity/grammar.py +0 -0
- {aridity-92 → aridity-94}/aridity/keyring.py +0 -0
- {aridity-92 → aridity-94}/aridity/processtemplate.py +0 -0
- {aridity-92 → aridity-94}/aridity/repl.py +0 -0
- {aridity-92 → aridity-94}/aridity/stacks.py +0 -0
- {aridity-92 → aridity-94}/aridity.egg-info/SOURCES.txt +0 -0
- {aridity-92 → aridity-94}/aridity.egg-info/dependency_links.txt +0 -0
- {aridity-92 → aridity-94}/aridity.egg-info/entry_points.txt +0 -0
- {aridity-92 → aridity-94}/aridity.egg-info/requires.txt +0 -0
- {aridity-92 → aridity-94}/aridity.egg-info/top_level.txt +0 -0
- {aridity-92 → aridity-94}/parabject.py +0 -0
- {aridity-92 → aridity-94}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: aridity
|
|
3
|
-
Version:
|
|
3
|
+
Version: 94
|
|
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
|
|
@@ -35,7 +35,7 @@ Process the given template to stdout using config from stdin.
|
|
|
35
35
|
#### ConfigCtrl Objects
|
|
36
36
|
|
|
37
37
|
```python
|
|
38
|
-
class ConfigCtrl()
|
|
38
|
+
class ConfigCtrl(Forkable)
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
High level scope API.
|
|
@@ -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
|
|
@@ -461,20 +472,20 @@ Attempt to wrap the given value in a model object of the most specific type.
|
|
|
461
472
|
|
|
462
473
|
### aridity.scope
|
|
463
474
|
|
|
464
|
-
<a id="aridity.scope.
|
|
475
|
+
<a id="aridity.scope.Scope"></a>
|
|
465
476
|
|
|
466
|
-
####
|
|
477
|
+
#### Scope Objects
|
|
467
478
|
|
|
468
479
|
```python
|
|
469
|
-
class
|
|
480
|
+
class Scope(Resolvable)
|
|
470
481
|
```
|
|
471
482
|
|
|
472
|
-
<a id="aridity.scope.
|
|
483
|
+
<a id="aridity.scope.Scope.resolved"></a>
|
|
473
484
|
|
|
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.
|
|
@@ -25,7 +25,7 @@ Process the given template to stdout using config from stdin.
|
|
|
25
25
|
#### ConfigCtrl Objects
|
|
26
26
|
|
|
27
27
|
```python
|
|
28
|
-
class ConfigCtrl()
|
|
28
|
+
class ConfigCtrl(Forkable)
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
High level scope API.
|
|
@@ -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
|
|
@@ -451,20 +462,20 @@ Attempt to wrap the given value in a model object of the most specific type.
|
|
|
451
462
|
|
|
452
463
|
### aridity.scope
|
|
453
464
|
|
|
454
|
-
<a id="aridity.scope.
|
|
465
|
+
<a id="aridity.scope.Scope"></a>
|
|
455
466
|
|
|
456
|
-
####
|
|
467
|
+
#### Scope Objects
|
|
457
468
|
|
|
458
469
|
```python
|
|
459
|
-
class
|
|
470
|
+
class Scope(Resolvable)
|
|
460
471
|
```
|
|
461
472
|
|
|
462
|
-
<a id="aridity.scope.
|
|
473
|
+
<a id="aridity.scope.Scope.resolved"></a>
|
|
463
474
|
|
|
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,6 +1,6 @@
|
|
|
1
1
|
'Interactive REPL.'
|
|
2
2
|
from .repl import CommandReader
|
|
3
|
-
from .scope import
|
|
3
|
+
from .scope import StaticScope
|
|
4
4
|
from .util import NoSuchPathException
|
|
5
5
|
from traceback import print_exc
|
|
6
6
|
import sys
|
|
@@ -8,7 +8,7 @@ import sys
|
|
|
8
8
|
assert NoSuchPathException
|
|
9
9
|
|
|
10
10
|
def main():
|
|
11
|
-
scope =
|
|
11
|
+
scope = StaticScope.createchild()
|
|
12
12
|
for command in CommandReader(sys.stdin):
|
|
13
13
|
try:
|
|
14
14
|
scope.execute(command)
|
|
@@ -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
|
-
from .scope import Scope
|
|
4
|
-
import os, sys
|
|
4
|
+
from .scope import Scope, StaticScope
|
|
5
|
+
import os, shlex, sys
|
|
5
6
|
|
|
6
7
|
def _configpath(configname):
|
|
7
8
|
if os.sep in configname:
|
|
@@ -12,20 +13,24 @@ 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
|
-
scope =
|
|
33
|
+
scope = StaticScope.createchild()
|
|
29
34
|
Locator(_configpath(sys.argv[1])).source(scope, Entry([]))
|
|
30
35
|
sys.stdout.write(scope.resolved(*sys.argv[2:]).tobash(True))
|
|
31
36
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from .functions import OpaqueKey
|
|
2
2
|
from .model import Entry, Locator, Resource, Stream, Text, wrap
|
|
3
|
-
from .scope import
|
|
3
|
+
from .scope import StaticScope
|
|
4
4
|
from .search import resolvedscopeornone
|
|
5
|
-
from .util import dotpy, NoSuchPathException, qualname, selectentrypoints, solo
|
|
5
|
+
from .util import dotpy, Forkable, NoSuchPathException, qualname, selectentrypoints, solo
|
|
6
6
|
from io import StringIO
|
|
7
7
|
from parabject import Parabject, register
|
|
8
8
|
from pathlib import Path
|
|
@@ -31,16 +31,12 @@ def _processmainfunction(mainfunction):
|
|
|
31
31
|
def _wrappathorstream(pathorstream):
|
|
32
32
|
return (Stream if getattr(pathorstream, 'readable', lambda: False)() else Locator)(pathorstream)
|
|
33
33
|
|
|
34
|
-
class ConfigCtrl:
|
|
34
|
+
class ConfigCtrl(Forkable):
|
|
35
35
|
'High level scope API.'
|
|
36
36
|
|
|
37
37
|
if 'HOME' in os.environ:
|
|
38
38
|
settingsopenable = Locator(Path.home() / '.settings.arid')
|
|
39
39
|
|
|
40
|
-
@classmethod
|
|
41
|
-
def _of(cls, *args, **kwargs):
|
|
42
|
-
return cls(*args, **kwargs)
|
|
43
|
-
|
|
44
40
|
@property
|
|
45
41
|
def node(self):
|
|
46
42
|
return register(self, Config)
|
|
@@ -56,7 +52,7 @@ class ConfigCtrl:
|
|
|
56
52
|
return register(self, WConfig)
|
|
57
53
|
|
|
58
54
|
def __init__(self, basescope = None, prefix = None):
|
|
59
|
-
self.basescope =
|
|
55
|
+
self.basescope = StaticScope.createchild() if basescope is None else basescope
|
|
60
56
|
self.prefix = [] if prefix is None else prefix
|
|
61
57
|
|
|
62
58
|
def loadappconfig(self, mainfunction, moduleresource, encoding = 'ascii', settingsoptional = False):
|
|
@@ -30,7 +30,7 @@ def source(prefix, suffix, scope):
|
|
|
30
30
|
# XXX: Use full algo to get phrasescope?
|
|
31
31
|
def rootscopes():
|
|
32
32
|
for s in chain.from_iterable(phrasescope.scopedepths()):
|
|
33
|
-
if [staticscope] == s.parents:
|
|
33
|
+
if [staticscope] == s.parents: # XXX: Slow?
|
|
34
34
|
yield s
|
|
35
35
|
staticscope = scope.staticscope()
|
|
36
36
|
phrasescope = 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([])
|
|
107
107
|
for k, v in objs.resolveditems():
|
|
108
108
|
result.resolvables.put(k, resolvable.resolve(context(k, v)))
|
|
109
109
|
return result
|
|
@@ -116,9 +116,9 @@ def map1(scope, contextresolvable, resultresolvable):
|
|
|
116
116
|
|
|
117
117
|
def _flat(scope, listsresolvable):
|
|
118
118
|
from .scope import Scope
|
|
119
|
-
s = Scope(
|
|
120
|
-
for lk, l in listsresolvable.resolve(scope).
|
|
121
|
-
for ok, obj in l.
|
|
119
|
+
s = Scope([])
|
|
120
|
+
for lk, l in listsresolvable.resolve(scope).resolveditems():
|
|
121
|
+
for ok, obj in l.resolveditems():
|
|
122
122
|
s.resolvables.put((lk, ok), obj)
|
|
123
123
|
return s
|
|
124
124
|
|
|
@@ -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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .util import dotpy
|
|
1
|
+
from .util import dotpy, Forkable
|
|
2
2
|
from contextlib import contextmanager
|
|
3
3
|
from importlib import import_module
|
|
4
4
|
from importlib.resources import files
|
|
@@ -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):
|
|
@@ -156,16 +154,12 @@ class Scalar(BaseScalar):
|
|
|
156
154
|
def __init__(self, scalar):
|
|
157
155
|
self.scalar = scalar
|
|
158
156
|
|
|
159
|
-
class Text(BaseScalar):
|
|
157
|
+
class Text(BaseScalar, Forkable):
|
|
160
158
|
|
|
161
159
|
@classmethod
|
|
162
160
|
def joinpa(cls, s, l, t):
|
|
163
161
|
return cls(''.join(t))
|
|
164
162
|
|
|
165
|
-
@classmethod
|
|
166
|
-
def _of(cls, textvalue):
|
|
167
|
-
return cls(textvalue)
|
|
168
|
-
|
|
169
163
|
@property
|
|
170
164
|
def scalar(self):
|
|
171
165
|
return self.textvalue
|
|
@@ -245,11 +239,7 @@ class Openable:
|
|
|
245
239
|
with self.pushopen(scope) as f:
|
|
246
240
|
return Stream(f).processtemplate(scope)
|
|
247
241
|
|
|
248
|
-
class Locator(Resolved, Openable):
|
|
249
|
-
|
|
250
|
-
@classmethod
|
|
251
|
-
def _of(cls, pathvalue):
|
|
252
|
-
return cls(pathvalue)
|
|
242
|
+
class Locator(Resolved, Openable, Forkable):
|
|
253
243
|
|
|
254
244
|
@property
|
|
255
245
|
def scalar(self):
|
|
@@ -267,11 +257,7 @@ class Locator(Resolved, Openable):
|
|
|
267
257
|
def modulenameornone(self):
|
|
268
258
|
pass
|
|
269
259
|
|
|
270
|
-
class Resource(Resolved, Openable):
|
|
271
|
-
|
|
272
|
-
@classmethod
|
|
273
|
-
def _of(cls, *args):
|
|
274
|
-
return cls(*args)
|
|
260
|
+
class Resource(Resolved, Openable, Forkable):
|
|
275
261
|
|
|
276
262
|
def __init__(self, package_or_requirement, resource_name, encoding = 'ascii'):
|
|
277
263
|
self.package_or_requirement = package_or_requirement
|
|
@@ -304,11 +290,7 @@ class Resource(Resolved, Openable):
|
|
|
304
290
|
if self.resource_name.endswith(dotpy):
|
|
305
291
|
return f"{self._packagename()}.{self.resource_name[:-len(dotpy)].replace('/', '.')}"
|
|
306
292
|
|
|
307
|
-
class Binary(BaseScalar):
|
|
308
|
-
|
|
309
|
-
@classmethod
|
|
310
|
-
def _of(cls, *args):
|
|
311
|
-
return cls(*args)
|
|
293
|
+
class Binary(BaseScalar, Forkable):
|
|
312
294
|
|
|
313
295
|
@property
|
|
314
296
|
def scalar(self):
|
|
@@ -375,9 +357,8 @@ class Call(Resolvable):
|
|
|
375
357
|
if not a.ignorable:
|
|
376
358
|
yield a
|
|
377
359
|
|
|
378
|
-
def resolve(self, scope
|
|
379
|
-
|
|
380
|
-
return List([result]) if aslist else result
|
|
360
|
+
def resolve(self, scope):
|
|
361
|
+
return self._functionvalue(scope)(scope.getresolvecontext(), *self._resolvables())
|
|
381
362
|
|
|
382
363
|
def resolvemulti(self, j, scope):
|
|
383
364
|
f = self._functionvalue(scope.getresolvecontext())
|
|
@@ -388,13 +369,6 @@ class Call(Resolvable):
|
|
|
388
369
|
for k, o in resolvable.resolve(scope).resolveditems():
|
|
389
370
|
yield (j, k), o
|
|
390
371
|
|
|
391
|
-
def List(objs):
|
|
392
|
-
from .scope import Scope
|
|
393
|
-
s = Scope(islist = True)
|
|
394
|
-
for obj in objs:
|
|
395
|
-
s.resolvables.put(object(), obj) # XXX: Not OpaqueKey?
|
|
396
|
-
return s
|
|
397
|
-
|
|
398
372
|
class Directive(Resolved):
|
|
399
373
|
|
|
400
374
|
@property
|
|
@@ -438,25 +412,18 @@ class Stream(Resolved):
|
|
|
438
412
|
with scope.staticscope().indent.push() as monitor:
|
|
439
413
|
return templateparser(monitor)(self.streamvalue.read()).resolve(scope)
|
|
440
414
|
|
|
441
|
-
class Entry(Struct):
|
|
415
|
+
class Entry(Struct, Forkable):
|
|
442
416
|
|
|
443
417
|
@classmethod
|
|
444
418
|
def pa(cls, s, l, t):
|
|
445
419
|
return cls(t.asList())
|
|
446
420
|
|
|
447
|
-
@classmethod
|
|
448
|
-
def _of(cls, *args):
|
|
449
|
-
return cls(*args)
|
|
450
|
-
|
|
451
421
|
def __init__(self, resolvables):
|
|
452
422
|
self.resolvables = resolvables
|
|
453
423
|
|
|
454
424
|
def size(self):
|
|
455
425
|
return sum(1 for r in self.resolvables if not r.ignorable)
|
|
456
426
|
|
|
457
|
-
def resolve(self):
|
|
458
|
-
return List([r.resolve(None) for r in self.resolvables])
|
|
459
|
-
|
|
460
427
|
def word(self, i):
|
|
461
428
|
word, = islice((r for r in self.resolvables if not r.ignorable), i, i + 1)
|
|
462
429
|
return word
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from .search import Query
|
|
2
|
-
from .util import CycleException, NoSuchPathException, TreeNoSuchPathException
|
|
3
|
-
from itertools import chain
|
|
2
|
+
from .util import CycleException, Forkable, NoSuchPathException, TreeNoSuchPathException
|
|
4
3
|
|
|
5
4
|
class BaseResolveContext:
|
|
6
5
|
|
|
@@ -16,8 +15,8 @@ class BaseResolveContext:
|
|
|
16
15
|
def resolvables(self):
|
|
17
16
|
return self.leafscope().resolvables
|
|
18
17
|
|
|
19
|
-
def createchild(self
|
|
20
|
-
return self.leafscope().createchild(
|
|
18
|
+
def createchild(self):
|
|
19
|
+
return self.leafscope().createchild()
|
|
21
20
|
|
|
22
21
|
def getresolvecontext(self):
|
|
23
22
|
return self
|
|
@@ -25,8 +24,8 @@ class BaseResolveContext:
|
|
|
25
24
|
def resolvableornone(self, key):
|
|
26
25
|
return self.leafscope().resolvableornone(key)
|
|
27
26
|
|
|
28
|
-
def resolved(self, *path
|
|
29
|
-
return self.resolvedimpl(path
|
|
27
|
+
def resolved(self, *path):
|
|
28
|
+
return self.resolvedimpl(path) if path else self.leafscope()
|
|
30
29
|
|
|
31
30
|
def staticscope(self):
|
|
32
31
|
return self.leafscope().staticscope()
|
|
@@ -39,15 +38,11 @@ class AnchorResolveContext(BaseResolveContext):
|
|
|
39
38
|
def leafscope(self):
|
|
40
39
|
return self.anchorscope
|
|
41
40
|
|
|
42
|
-
def resolvedimpl(self, path
|
|
41
|
+
def resolvedimpl(self, path):
|
|
43
42
|
hit = Query([], path).search(self.anchorscope)
|
|
44
|
-
return hit.resolvable.resolve(ResolveContext(self.anchorscope, path, [hit.address])
|
|
43
|
+
return hit.resolvable.resolve(ResolveContext(self.anchorscope, path, [hit.address]))
|
|
45
44
|
|
|
46
|
-
class ResolveContext(BaseResolveContext):
|
|
47
|
-
|
|
48
|
-
@classmethod
|
|
49
|
-
def _of(cls, *args):
|
|
50
|
-
return cls(*args)
|
|
45
|
+
class ResolveContext(BaseResolveContext, Forkable):
|
|
51
46
|
|
|
52
47
|
def __init__(self, anchorscope, exprpath, addresses):
|
|
53
48
|
self.anchorscope = anchorscope
|
|
@@ -58,11 +53,11 @@ class ResolveContext(BaseResolveContext):
|
|
|
58
53
|
def leafscope(self):
|
|
59
54
|
return Query([], self.scopepath).search(self.anchorscope).naiveresolve() if self.scopepath else self.anchorscope # XXX: Is naiveresolve correct here?
|
|
60
55
|
|
|
61
|
-
def resolvedimpl(self, path
|
|
56
|
+
def resolvedimpl(self, path):
|
|
62
57
|
try:
|
|
63
58
|
hit = Query(self.scopepath, path).search(self.anchorscope)
|
|
64
59
|
if hit.address in self.addresses: # XXX: Could it be valid to resolve the same address recursively with 2 different contexts?
|
|
65
60
|
raise CycleException(path)
|
|
66
|
-
return hit.resolvable.resolve(self._of(self.anchorscope,
|
|
61
|
+
return hit.resolvable.resolve(self._of(self.anchorscope, [*self.scopepath, *path], [*self.addresses, hit.address]))
|
|
67
62
|
except NoSuchPathException as e:
|
|
68
63
|
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):
|
|
@@ -69,7 +69,7 @@ class Resolvables:
|
|
|
69
69
|
yield k, v
|
|
70
70
|
|
|
71
71
|
# XXX: Isn't this Resolved rather than Resolvable?
|
|
72
|
-
class
|
|
72
|
+
class Scope(Resolvable):
|
|
73
73
|
|
|
74
74
|
nametypes = {str, type(None), OpaqueKey} # XXX: Is None still used by anything?
|
|
75
75
|
|
|
@@ -117,7 +117,7 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
|
|
|
117
117
|
def getresolvecontext(self):
|
|
118
118
|
return AnchorResolveContext(self)
|
|
119
119
|
|
|
120
|
-
def resolved(self, *path
|
|
120
|
+
def resolved(self, *path):
|
|
121
121
|
'Follow the given path to get an expression, evaluate it (resolving any paths it requires, recursively), and return the resulting model object.'
|
|
122
122
|
# TODO LATER: API to filter out results unsuitable for context.
|
|
123
123
|
for s in chain.from_iterable(self.scopedepths()):
|
|
@@ -127,7 +127,7 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
|
|
|
127
127
|
break
|
|
128
128
|
except AttributeError:
|
|
129
129
|
pass
|
|
130
|
-
return g(self.getresolvecontext(), *map(Text, path)
|
|
130
|
+
return g(self.getresolvecontext(), *map(Text, path))
|
|
131
131
|
|
|
132
132
|
def resolvableornone(self, key):
|
|
133
133
|
return self.resolvables.getornone(key)
|
|
@@ -141,9 +141,12 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
|
|
|
141
141
|
yield scopes
|
|
142
142
|
scopes = nextscopes
|
|
143
143
|
|
|
144
|
-
def unravel(self):
|
|
145
|
-
|
|
146
|
-
|
|
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())
|
|
147
150
|
|
|
148
151
|
def staticscope(self):
|
|
149
152
|
for s in chain.from_iterable(self.scopedepths()):
|
|
@@ -197,10 +200,40 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
|
|
|
197
200
|
for t in r.resolvemulti(k, self):
|
|
198
201
|
yield t
|
|
199
202
|
|
|
200
|
-
def createchild(self
|
|
201
|
-
return Scope([self]
|
|
203
|
+
def createchild(self):
|
|
204
|
+
return Scope([self])
|
|
205
|
+
|
|
206
|
+
def resolve(self, scope):
|
|
207
|
+
return self
|
|
208
|
+
|
|
209
|
+
def _slashfunction(scope, *resolvables):
|
|
210
|
+
path = None
|
|
211
|
+
for r in reversed(resolvables):
|
|
212
|
+
component = r.resolve(scope).textvalue
|
|
213
|
+
path = component if path is None else os.path.join(component, path)
|
|
214
|
+
if os.path.isabs(path):
|
|
215
|
+
break
|
|
216
|
+
return Text(os.path.join() if path is None else path)
|
|
217
|
+
|
|
218
|
+
class Slash(Text, Function):
|
|
219
|
+
'As text, the platform slash. As function, join args using that slash, starting with the last absolute path (or using all args if all relative).'
|
|
220
|
+
|
|
221
|
+
def __init__(self):
|
|
222
|
+
Text.__init__(self, os.sep)
|
|
223
|
+
Function.__init__(self, _slashfunction)
|
|
224
|
+
|
|
225
|
+
class Star(Function, Directive):
|
|
202
226
|
|
|
203
|
-
|
|
227
|
+
protokey = object()
|
|
228
|
+
|
|
229
|
+
def __init__(self):
|
|
230
|
+
Function.__init__(self, star)
|
|
231
|
+
Directive.__init__(self, prime(self._star))
|
|
232
|
+
|
|
233
|
+
def _star(self, prefix, suffix, scope):
|
|
234
|
+
scope.getorcreatesubscope(prefix.topath(scope) + (self.protokey,)).execute(suffix)
|
|
235
|
+
|
|
236
|
+
class StaticScope(Scope):
|
|
204
237
|
|
|
205
238
|
rootscopekey = OpaqueKey()
|
|
206
239
|
stacktypes = dict(here = SimpleStack, indent = IndentStack)
|
|
@@ -234,44 +267,8 @@ class StaticScope(AbstractScope):
|
|
|
234
267
|
setattr(threadlocals, name, stack)
|
|
235
268
|
return stack
|
|
236
269
|
|
|
237
|
-
class Slash(Text, Function):
|
|
238
|
-
'As text, the platform slash. As function, join args using that slash, starting with the last absolute path (or using all args if all relative).'
|
|
239
|
-
|
|
240
|
-
def __init__(self):
|
|
241
|
-
Text.__init__(self, os.sep)
|
|
242
|
-
Function.__init__(self, slashfunction)
|
|
243
|
-
|
|
244
|
-
def slashfunction(scope, *resolvables):
|
|
245
|
-
path = None
|
|
246
|
-
for r in reversed(resolvables):
|
|
247
|
-
component = r.resolve(scope).textvalue
|
|
248
|
-
path = component if path is None else os.path.join(component, path)
|
|
249
|
-
if os.path.isabs(path):
|
|
250
|
-
break
|
|
251
|
-
return Text(os.path.join() if path is None else path)
|
|
252
|
-
|
|
253
|
-
class Star(Function, Directive):
|
|
254
|
-
|
|
255
|
-
protokey = object()
|
|
256
|
-
|
|
257
|
-
def __init__(self):
|
|
258
|
-
Function.__init__(self, star)
|
|
259
|
-
Directive.__init__(self, prime(self._star))
|
|
260
|
-
|
|
261
|
-
def _star(self, prefix, suffix, scope):
|
|
262
|
-
scope.getorcreatesubscope(prefix.topath(scope) + (self.protokey,)).execute(suffix)
|
|
263
|
-
|
|
264
270
|
StaticScope = StaticScope()
|
|
265
271
|
|
|
266
|
-
class Scope(AbstractScope):
|
|
267
|
-
|
|
268
|
-
def __init__(self, parents = None, islist = False):
|
|
269
|
-
super().__init__([StaticScope] if parents is None else parents)
|
|
270
|
-
self.islist = islist
|
|
271
|
-
|
|
272
|
-
def resolve(self, scope):
|
|
273
|
-
return self
|
|
274
|
-
|
|
275
272
|
class ScalarScope(Scope):
|
|
276
273
|
|
|
277
274
|
def __init__(self, parents, scalarobj):
|
|
@@ -73,20 +73,17 @@ def _lt(*depthspair):
|
|
|
73
73
|
class Query:
|
|
74
74
|
|
|
75
75
|
def __init__(self, prefix, path):
|
|
76
|
-
assert path
|
|
77
76
|
self.prefixlen = len(prefix)
|
|
78
77
|
self.path = [*prefix, *path]
|
|
79
78
|
|
|
80
79
|
def _search(self, scope):
|
|
81
|
-
merges = [
|
|
80
|
+
merges = [[Hit([], None, scope)], *(Merge() for _ in self.path)]
|
|
82
81
|
size = len(self.path)
|
|
83
|
-
for _ in range(1, size):
|
|
84
|
-
merges.append(Merge())
|
|
85
82
|
for cursor, merge in enumerate(merges):
|
|
86
|
-
zerocount = size - cursor
|
|
83
|
+
zerocount = size - cursor
|
|
87
84
|
yield None, zerocount
|
|
88
85
|
for hit in merge:
|
|
89
|
-
for x in range(cursor
|
|
86
|
+
for x in range(cursor, size):
|
|
90
87
|
i = hit.iterornone(self.path[x], x < self.prefixlen)
|
|
91
88
|
if i is None:
|
|
92
89
|
break
|
|
@@ -94,7 +91,7 @@ class Query:
|
|
|
94
91
|
hit = i.next()
|
|
95
92
|
except StopIteration:
|
|
96
93
|
break
|
|
97
|
-
merges[x].add(i)
|
|
94
|
+
merges[1 + x].add(i)
|
|
98
95
|
else:
|
|
99
96
|
yield hit, zerocount
|
|
100
97
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from importlib.metadata import entry_points
|
|
2
|
-
import
|
|
2
|
+
import sys
|
|
3
3
|
|
|
4
4
|
dotpy = '.py'
|
|
5
5
|
inf = float('inf')
|
|
@@ -17,7 +17,7 @@ class TreeNoSuchPathException(NoSuchPathException):
|
|
|
17
17
|
|
|
18
18
|
def __str__(self):
|
|
19
19
|
path, causes = self.args # XXX: Also collect (and show) where in the tree the causes happened?
|
|
20
|
-
causestrtocount =
|
|
20
|
+
causestrtocount = {}
|
|
21
21
|
for causestr in map(str, causes):
|
|
22
22
|
try:
|
|
23
23
|
causestrtocount[causestr] += 1
|
|
@@ -35,52 +35,6 @@ class CycleException(UnparseNoSuchPathException): pass
|
|
|
35
35
|
|
|
36
36
|
class UnsupportedEntryException(Exception): pass
|
|
37
37
|
|
|
38
|
-
class OrderedDictWrapper:
|
|
39
|
-
|
|
40
|
-
def __init__(self, *args):
|
|
41
|
-
self.d = collections.OrderedDict(*args)
|
|
42
|
-
|
|
43
|
-
def __bool__(self):
|
|
44
|
-
return bool(self.d)
|
|
45
|
-
|
|
46
|
-
def __nonzero__(self):
|
|
47
|
-
return self.__bool__()
|
|
48
|
-
|
|
49
|
-
class OrderedDict(OrderedDictWrapper):
|
|
50
|
-
|
|
51
|
-
def __setitem__(self, k, v):
|
|
52
|
-
self.d[k] = v
|
|
53
|
-
|
|
54
|
-
def __getitem__(self, k):
|
|
55
|
-
return self.d[k]
|
|
56
|
-
|
|
57
|
-
def __delitem__(self, k):
|
|
58
|
-
del self.d[k]
|
|
59
|
-
|
|
60
|
-
def get(self, k, default = None):
|
|
61
|
-
return self.d.get(k, default)
|
|
62
|
-
|
|
63
|
-
def keys(self):
|
|
64
|
-
return self.d.keys()
|
|
65
|
-
|
|
66
|
-
def values(self):
|
|
67
|
-
return self.d.values()
|
|
68
|
-
|
|
69
|
-
def items(self):
|
|
70
|
-
return self.d.items()
|
|
71
|
-
|
|
72
|
-
def __iter__(self):
|
|
73
|
-
return iter(self.d.values())
|
|
74
|
-
|
|
75
|
-
def __eq__(self, that):
|
|
76
|
-
return self.d == that
|
|
77
|
-
|
|
78
|
-
def __repr__(self):
|
|
79
|
-
return repr(self.d)
|
|
80
|
-
|
|
81
|
-
def update(self, other):
|
|
82
|
-
return self.d.update(other)
|
|
83
|
-
|
|
84
38
|
def openresource(package_or_name, resource_name, encoding = 'ascii'):
|
|
85
39
|
'Like `pkg_resources.resource_stream` but text mode.'
|
|
86
40
|
from .model import Resource
|
|
@@ -112,3 +66,9 @@ def popattr(obj, name):
|
|
|
112
66
|
val = getattr(obj, name)
|
|
113
67
|
delattr(obj, name)
|
|
114
68
|
return val
|
|
69
|
+
|
|
70
|
+
class Forkable:
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def _of(cls, *args, **kwargs):
|
|
74
|
+
return cls(*args, **kwargs)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: aridity
|
|
3
|
-
Version:
|
|
3
|
+
Version: 94
|
|
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
|
|
@@ -35,7 +35,7 @@ Process the given template to stdout using config from stdin.
|
|
|
35
35
|
#### ConfigCtrl Objects
|
|
36
36
|
|
|
37
37
|
```python
|
|
38
|
-
class ConfigCtrl()
|
|
38
|
+
class ConfigCtrl(Forkable)
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
High level scope API.
|
|
@@ -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
|
|
@@ -461,20 +472,20 @@ Attempt to wrap the given value in a model object of the most specific type.
|
|
|
461
472
|
|
|
462
473
|
### aridity.scope
|
|
463
474
|
|
|
464
|
-
<a id="aridity.scope.
|
|
475
|
+
<a id="aridity.scope.Scope"></a>
|
|
465
476
|
|
|
466
|
-
####
|
|
477
|
+
#### Scope Objects
|
|
467
478
|
|
|
468
479
|
```python
|
|
469
|
-
class
|
|
480
|
+
class Scope(Resolvable)
|
|
470
481
|
```
|
|
471
482
|
|
|
472
|
-
<a id="aridity.scope.
|
|
483
|
+
<a id="aridity.scope.Scope.resolved"></a>
|
|
473
484
|
|
|
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 = '94',
|
|
53
53
|
description = 'DRY config and template system, easily extensible with Python',
|
|
54
54
|
url = 'https://pypi.org/project/aridity/',
|
|
55
55
|
author = 'foyono',
|
|
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
|