aridity 97__tar.gz → 99__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-97 → aridity-99}/PKG-INFO +1 -2
- {aridity-97 → aridity-99}/aridity/config.py +2 -15
- {aridity-97 → aridity-99}/aridity/functions.py +2 -3
- aridity-99/aridity/grammar.py +211 -0
- aridity-99/aridity/keyring.py +24 -0
- {aridity-97 → aridity-99}/aridity/model.py +24 -29
- {aridity-97 → aridity-99}/aridity/repl.py +3 -17
- {aridity-97 → aridity-99}/aridity/scope.py +2 -5
- {aridity-97 → aridity-99}/aridity.egg-info/PKG-INFO +1 -2
- aridity-99/aridity.egg-info/requires.txt +1 -0
- {aridity-97 → aridity-99}/setup.py +2 -2
- aridity-97/aridity/grammar.py +0 -114
- aridity-97/aridity/keyring.py +0 -51
- aridity-97/aridity.egg-info/requires.txt +0 -2
- {aridity-97 → aridity-99}/README.md +0 -0
- {aridity-97 → aridity-99}/aridity/__init__.py +0 -0
- {aridity-97 → aridity-99}/aridity/arid_config.py +0 -0
- {aridity-97 → aridity-99}/aridity/directives.py +0 -0
- {aridity-97 → aridity-99}/aridity/processtemplate.py +0 -0
- {aridity-97 → aridity-99}/aridity/resolve.py +0 -0
- {aridity-97 → aridity-99}/aridity/search.py +0 -0
- {aridity-97 → aridity-99}/aridity/stacks.py +0 -0
- {aridity-97 → aridity-99}/aridity/util.py +0 -0
- {aridity-97 → aridity-99}/aridity.egg-info/SOURCES.txt +0 -0
- {aridity-97 → aridity-99}/aridity.egg-info/dependency_links.txt +0 -0
- {aridity-97 → aridity-99}/aridity.egg-info/entry_points.txt +0 -0
- {aridity-97 → aridity-99}/aridity.egg-info/top_level.txt +0 -0
- {aridity-97 → aridity-99}/setup.cfg +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: aridity
|
|
3
|
-
Version:
|
|
3
|
+
Version: 99
|
|
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
|
|
7
7
|
Author-email: shrovis@foyono.com
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: foyndation>=14
|
|
10
|
-
Requires-Dist: pyparsing>=3.0.2
|
|
11
10
|
|
|
12
11
|
# aridity
|
|
13
12
|
DRY config and template system, easily extensible with Python.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from .functions import OpaqueKey
|
|
2
|
-
from .model import Entry, Locator, Resource, Stream,
|
|
2
|
+
from .model import Entry, Locator, Resource, Stream, wrap
|
|
3
3
|
from .scope import StaticScope
|
|
4
4
|
from .search import resolvedscopeornone
|
|
5
5
|
from .util import NoSuchPathException, selectentrypoints
|
|
6
|
-
from foyndation import dotpy, Forkable, rmsuffix
|
|
6
|
+
from foyndation import dotpy, Forkable, rmsuffix
|
|
7
7
|
from io import StringIO
|
|
8
8
|
from parabject import Parabject, register
|
|
9
9
|
from pathlib import Path
|
|
@@ -74,19 +74,6 @@ class ConfigCtrl(Forkable):
|
|
|
74
74
|
resource.source(self.basescope.getorcreatesubscope([*self.prefix, appname]), Entry([]))
|
|
75
75
|
return getattr(self.node, appname)
|
|
76
76
|
|
|
77
|
-
def reapplysettings(self, mainfunction):
|
|
78
|
-
if hasattr(mainfunction, 'encode'):
|
|
79
|
-
appname = mainfunction
|
|
80
|
-
else:
|
|
81
|
-
_, appname = _processmainfunction(mainfunction)
|
|
82
|
-
s = self.scope.duplicate()
|
|
83
|
-
s.label = Text(appname)
|
|
84
|
-
p = solo(s.parents)
|
|
85
|
-
p[appname,] = s
|
|
86
|
-
parent = self._of(p)
|
|
87
|
-
parent.loadsettings()
|
|
88
|
-
return getattr(parent.node, appname)
|
|
89
|
-
|
|
90
77
|
def load(self, pathorstream):
|
|
91
78
|
'Execute config from the given path or stream.'
|
|
92
79
|
_wrappathorstream(pathorstream).source(self.scope, Entry([]))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .keyring import gpg
|
|
1
|
+
from .keyring import gpg
|
|
2
2
|
from .model import Boolean, Hole, Indeterminate, Number, Resource, Text, wrap
|
|
3
3
|
from .util import NoSuchPathException
|
|
4
4
|
from foyndation import dotpy
|
|
@@ -184,7 +184,7 @@ def processtemplate(scope, resolvable):
|
|
|
184
184
|
def _lower(scope, resolvable):
|
|
185
185
|
return Text(resolvable.resolve(scope).textvalue.lower())
|
|
186
186
|
|
|
187
|
-
def pyref(scope, moduleresolvable, qualnameresolvable):
|
|
187
|
+
def pyref(scope, moduleresolvable, qualnameresolvable): # FIXME: Allow load from __init__.py in current dir.
|
|
188
188
|
'Python object in given module with given qualified name. Module may be relative to current resource, in which case assignment with `:=` is normally necessary. Typically used to import functions.'
|
|
189
189
|
def moduleobj():
|
|
190
190
|
moduleref = moduleresolvable.resolve(scope).textvalue
|
|
@@ -253,7 +253,6 @@ def getimpl(scope, *resolvables):
|
|
|
253
253
|
|
|
254
254
|
def corefunctions():
|
|
255
255
|
yield 'gpg', gpg
|
|
256
|
-
yield 'keyring', keyring
|
|
257
256
|
yield 'screenstr', screenstr
|
|
258
257
|
yield 'scstr', scstr
|
|
259
258
|
yield 'hclstr', hclstr
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from .model import Blank, Boolean, Boundary, Call, Concat, Entry, Indeterminate, nullmonitor, Number, Suffix, Text
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
class ParseException(Exception): pass
|
|
6
|
+
|
|
7
|
+
booleans = {t: Boolean(b).augment(textvalue = t) for b in map(bool, range(2)) for t in [str(b).lower()]}
|
|
8
|
+
brackets = {x: y for pair in ['()', '[]'] for x, y in [pair, reversed(pair)]}
|
|
9
|
+
dollar = re.compile('[$]')
|
|
10
|
+
dollarorbracket = re.compile(r'[$()[\]]')
|
|
11
|
+
dollarorbracketorws = re.compile(r'[$()[\]\s]')
|
|
12
|
+
dollaroropenorws = re.compile(r'([$([])|\s')
|
|
13
|
+
eolornotws = re.compile(r'(\r\n|[\r\n])|\S')
|
|
14
|
+
notws = re.compile(r'\S')
|
|
15
|
+
numbermatch = re.compile('-?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)').fullmatch
|
|
16
|
+
|
|
17
|
+
def _scalar(text):
|
|
18
|
+
try:
|
|
19
|
+
return booleans[text]
|
|
20
|
+
except KeyError:
|
|
21
|
+
m = numbermatch(text)
|
|
22
|
+
if m is None:
|
|
23
|
+
return Text(text)
|
|
24
|
+
if '.' in text:
|
|
25
|
+
val = Decimal(text)
|
|
26
|
+
else:
|
|
27
|
+
val = int(text)
|
|
28
|
+
if not val and '-' == text[0]:
|
|
29
|
+
val = Decimal(text) # Preserve sign.
|
|
30
|
+
return Number(val).augment(textvalue = text)
|
|
31
|
+
|
|
32
|
+
class Parser:
|
|
33
|
+
|
|
34
|
+
def __init__(self, monitor):
|
|
35
|
+
self.monitor = monitor
|
|
36
|
+
|
|
37
|
+
def _join(self, parts):
|
|
38
|
+
n = len(parts)
|
|
39
|
+
if 0 == n:
|
|
40
|
+
return Indeterminate
|
|
41
|
+
if 1 == n:
|
|
42
|
+
return parts[0]
|
|
43
|
+
return Concat(parts, self.monitor)
|
|
44
|
+
|
|
45
|
+
def _templateparts(self, text):
|
|
46
|
+
eye = 0
|
|
47
|
+
parts = []
|
|
48
|
+
while eye < len(text):
|
|
49
|
+
m = dollar.search(text, eye)
|
|
50
|
+
if m is None:
|
|
51
|
+
parts.append(Text(text[eye:]))
|
|
52
|
+
break
|
|
53
|
+
fence = m.start()
|
|
54
|
+
if eye < fence:
|
|
55
|
+
parts.append(Text(text[eye:fence]))
|
|
56
|
+
call, eye = self._readcall(text, fence)
|
|
57
|
+
parts.append(call)
|
|
58
|
+
return parts
|
|
59
|
+
|
|
60
|
+
def readtemplate(self, text):
|
|
61
|
+
return self._join(self._templateparts(text))
|
|
62
|
+
|
|
63
|
+
def readparts(self, text):
|
|
64
|
+
eye = 0
|
|
65
|
+
parts = []
|
|
66
|
+
while eye < len(text):
|
|
67
|
+
m = eolornotws.search(text, eye)
|
|
68
|
+
if m is None:
|
|
69
|
+
parts.append(Blank(text[eye:]))
|
|
70
|
+
break
|
|
71
|
+
fence = m.start()
|
|
72
|
+
if eye < fence:
|
|
73
|
+
parts.append(Blank(text[eye:fence]))
|
|
74
|
+
if m.group(1) is None:
|
|
75
|
+
chunk, eye = self._readchunk(text, fence, None)
|
|
76
|
+
parts.append(chunk)
|
|
77
|
+
else:
|
|
78
|
+
parts.append(Boundary)
|
|
79
|
+
eye = m.end()
|
|
80
|
+
return parts
|
|
81
|
+
|
|
82
|
+
def readsuffix(self, text):
|
|
83
|
+
return Suffix(Entry(self.readparts(text)))
|
|
84
|
+
|
|
85
|
+
def _readcall(self, text, eye):
|
|
86
|
+
eye += 1
|
|
87
|
+
names = []
|
|
88
|
+
while True:
|
|
89
|
+
m = dollaroropenorws.search(text, eye)
|
|
90
|
+
openchar = m.group(1)
|
|
91
|
+
if openchar is None:
|
|
92
|
+
raise ParseException
|
|
93
|
+
names.append(text[eye:m.start()])
|
|
94
|
+
eye = m.end()
|
|
95
|
+
if '$' != openchar:
|
|
96
|
+
break
|
|
97
|
+
k = len(names) - 1
|
|
98
|
+
if '.' == names[k]:
|
|
99
|
+
args, eye = self._readspan(text, eye, False, openchar)
|
|
100
|
+
call = Text('') if not args else Concat(args, self.monitor)
|
|
101
|
+
elif "'" == names[k]:
|
|
102
|
+
args, eye = self._readspan(text, eye, True, openchar)
|
|
103
|
+
call = Text('') if not args else args[0] if 1 == len(args) else Concat(args, self.monitor)
|
|
104
|
+
else:
|
|
105
|
+
args, eye = self._readargs(text, eye, openchar)
|
|
106
|
+
call = Call(names[k], args)
|
|
107
|
+
while k:
|
|
108
|
+
k -= 1
|
|
109
|
+
if '.' != names[k]:
|
|
110
|
+
call = Call(names[k], [call])
|
|
111
|
+
return call, eye + 1
|
|
112
|
+
|
|
113
|
+
def _readargs(self, text, eye, openchar):
|
|
114
|
+
closechar = brackets[openchar]
|
|
115
|
+
args = []
|
|
116
|
+
while True:
|
|
117
|
+
m = notws.search(text, eye)
|
|
118
|
+
c = m.group()
|
|
119
|
+
eye = m.start()
|
|
120
|
+
if closechar == c:
|
|
121
|
+
break
|
|
122
|
+
chunk, eye = self._readchunk(text, eye, openchar)
|
|
123
|
+
args.append(chunk)
|
|
124
|
+
return args, eye
|
|
125
|
+
|
|
126
|
+
def _readspan(self, text, eye, literal, openchar):
|
|
127
|
+
closechar = brackets[openchar]
|
|
128
|
+
mark = eye
|
|
129
|
+
parts = []
|
|
130
|
+
depth = 0
|
|
131
|
+
while True:
|
|
132
|
+
m = dollarorbracket.search(text, eye)
|
|
133
|
+
if m is None:
|
|
134
|
+
raise ParseException
|
|
135
|
+
c = m.group()
|
|
136
|
+
if '$' == c:
|
|
137
|
+
if literal:
|
|
138
|
+
eye = m.end()
|
|
139
|
+
else:
|
|
140
|
+
fence = m.start()
|
|
141
|
+
if mark < fence:
|
|
142
|
+
parts.append(Text(text[mark:fence]))
|
|
143
|
+
call, eye = self._readcall(text, fence)
|
|
144
|
+
parts.append(call)
|
|
145
|
+
mark = eye
|
|
146
|
+
continue
|
|
147
|
+
if closechar == c:
|
|
148
|
+
if depth:
|
|
149
|
+
depth -= 1
|
|
150
|
+
eye = m.end()
|
|
151
|
+
continue
|
|
152
|
+
fence = m.start()
|
|
153
|
+
if mark < fence:
|
|
154
|
+
parts.append(Text(text[mark:fence]))
|
|
155
|
+
mark = eye = fence
|
|
156
|
+
break
|
|
157
|
+
if openchar == c:
|
|
158
|
+
depth += 1
|
|
159
|
+
eye = m.end()
|
|
160
|
+
continue
|
|
161
|
+
eye = m.end()
|
|
162
|
+
return parts, eye
|
|
163
|
+
|
|
164
|
+
def _readchunk(self, text, eye, openchar):
|
|
165
|
+
closechar = brackets.get(openchar)
|
|
166
|
+
mark = eye
|
|
167
|
+
parts = []
|
|
168
|
+
depth = 0
|
|
169
|
+
while True:
|
|
170
|
+
if eye == len(text):
|
|
171
|
+
if mark < eye:
|
|
172
|
+
parts.append(_scalar(text[mark:eye]))
|
|
173
|
+
break
|
|
174
|
+
m = dollarorbracketorws.search(text, eye)
|
|
175
|
+
if m is None:
|
|
176
|
+
parts.append(_scalar(text[mark:]))
|
|
177
|
+
mark = eye = len(text)
|
|
178
|
+
break
|
|
179
|
+
c = m.group()
|
|
180
|
+
if '$' == c:
|
|
181
|
+
fence = m.start()
|
|
182
|
+
if mark < fence:
|
|
183
|
+
parts.append(Text(text[mark:fence]))
|
|
184
|
+
call, eye = self._readcall(text, fence)
|
|
185
|
+
parts.append(call)
|
|
186
|
+
mark = eye
|
|
187
|
+
continue
|
|
188
|
+
if closechar == c:
|
|
189
|
+
if depth:
|
|
190
|
+
depth -= 1
|
|
191
|
+
eye = m.end()
|
|
192
|
+
continue
|
|
193
|
+
fence = m.start()
|
|
194
|
+
if mark < fence:
|
|
195
|
+
parts.append(_scalar(text[mark:fence]))
|
|
196
|
+
mark = eye = fence
|
|
197
|
+
break
|
|
198
|
+
if openchar == c:
|
|
199
|
+
depth += 1
|
|
200
|
+
eye = m.end()
|
|
201
|
+
continue
|
|
202
|
+
if c not in brackets:
|
|
203
|
+
fence = m.start()
|
|
204
|
+
if mark < fence:
|
|
205
|
+
parts.append(_scalar(text[mark:fence]))
|
|
206
|
+
mark = eye = fence
|
|
207
|
+
break
|
|
208
|
+
eye = m.end()
|
|
209
|
+
return self._join(parts), eye
|
|
210
|
+
|
|
211
|
+
staticparser = Parser(nullmonitor)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from .model import Scalar
|
|
2
|
+
from base64 import b64decode
|
|
3
|
+
from subprocess import CalledProcessError, check_output
|
|
4
|
+
from tempfile import NamedTemporaryFile
|
|
5
|
+
|
|
6
|
+
class Password(str):
|
|
7
|
+
|
|
8
|
+
def __enter__(self):
|
|
9
|
+
return self
|
|
10
|
+
|
|
11
|
+
def __exit__(self, *exc_info):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
class DecryptionFailedException(Exception): pass
|
|
15
|
+
|
|
16
|
+
def gpg(scope, resolvable):
|
|
17
|
+
'Use gpg to decrypt the given base64-encoded blob.'
|
|
18
|
+
with NamedTemporaryFile() as f:
|
|
19
|
+
f.write(b64decode(resolvable.resolve(scope).textvalue))
|
|
20
|
+
f.flush()
|
|
21
|
+
try:
|
|
22
|
+
return Scalar(Password(check_output(['gpg', '-d', f.name]).decode('ascii')))
|
|
23
|
+
except CalledProcessError:
|
|
24
|
+
raise DecryptionFailedException
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from .util import Burial
|
|
2
2
|
from contextlib import contextmanager
|
|
3
|
-
from foyndation import dotpy, Forkable, rmsuffix
|
|
3
|
+
from foyndation import dotpy, Forkable, rmsuffix, singleton, solo
|
|
4
4
|
from importlib import import_module
|
|
5
5
|
from importlib.resources import files
|
|
6
6
|
from io import TextIOWrapper
|
|
7
|
-
from itertools import chain
|
|
7
|
+
from itertools import chain
|
|
8
8
|
from parabject import dereference, UnknownParabjectException
|
|
9
9
|
from pathlib import PurePath
|
|
10
10
|
import numbers, os, re
|
|
@@ -124,14 +124,12 @@ class Blank(BaseSimpleValue):
|
|
|
124
124
|
def plus(self, that):
|
|
125
125
|
return Text(self.textvalue + that.textvalue)
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
@singleton
|
|
128
|
+
class Boundary:
|
|
128
129
|
|
|
129
130
|
ignorable = True
|
|
130
131
|
boundary = True
|
|
131
132
|
|
|
132
|
-
def __init__(self, scalar):
|
|
133
|
-
self.scalar = scalar
|
|
134
|
-
|
|
135
133
|
class BaseScalar(BaseSimpleValue):
|
|
136
134
|
|
|
137
135
|
ignorable = False
|
|
@@ -197,6 +195,7 @@ class Text(BaseScalar, Forkable):
|
|
|
197
195
|
def plus(self, that):
|
|
198
196
|
return self._of(self.textvalue + that.textvalue)
|
|
199
197
|
|
|
198
|
+
@singleton
|
|
200
199
|
class Indeterminate(BaseSimpleValue): # XXX: Base class needed?
|
|
201
200
|
|
|
202
201
|
binaryvalue = b''
|
|
@@ -208,8 +207,6 @@ class Indeterminate(BaseSimpleValue): # XXX: Base class needed?
|
|
|
208
207
|
def totext(self):
|
|
209
208
|
return Text('')
|
|
210
209
|
|
|
211
|
-
Indeterminate = Indeterminate()
|
|
212
|
-
|
|
213
210
|
class ResidualHoleException(Exception): pass
|
|
214
211
|
|
|
215
212
|
class Hole(BaseScalar):
|
|
@@ -345,29 +342,22 @@ class Call(Resolvable):
|
|
|
345
342
|
|
|
346
343
|
ignorable = False
|
|
347
344
|
|
|
348
|
-
def __init__(self, name, args
|
|
345
|
+
def __init__(self, name, args):
|
|
349
346
|
self.name = name
|
|
350
347
|
self.args = args
|
|
351
|
-
self.brackets = brackets
|
|
352
348
|
|
|
353
349
|
def _functionvalue(self, scope):
|
|
354
350
|
return scope.resolved(self.name).functionvalue
|
|
355
351
|
|
|
356
|
-
def _resolvables(self):
|
|
357
|
-
for a in self.args:
|
|
358
|
-
if not a.ignorable:
|
|
359
|
-
yield a
|
|
360
|
-
|
|
361
352
|
def resolve(self, scope):
|
|
362
|
-
return self._functionvalue(scope)(scope.getresolvecontext(), *self.
|
|
353
|
+
return self._functionvalue(scope)(scope.getresolvecontext(), *self.args)
|
|
363
354
|
|
|
364
355
|
def resolvemulti(self, j, scope):
|
|
365
356
|
f = self._functionvalue(scope.getresolvecontext())
|
|
366
357
|
if star != f:
|
|
367
|
-
yield j, f(scope, *self.
|
|
358
|
+
yield j, f(scope, *self.args)
|
|
368
359
|
else:
|
|
369
|
-
|
|
370
|
-
for k, o in resolvable.resolve(scope).resolveditems():
|
|
360
|
+
for k, o in solo(self.args).resolve(scope).resolveditems(): # XXX: Support multiple args?
|
|
371
361
|
yield (j, k), o
|
|
372
362
|
|
|
373
363
|
class Directive(Resolved):
|
|
@@ -409,26 +399,18 @@ class Stream(Resolved):
|
|
|
409
399
|
CommandReader(self.streamvalue, prefix).pipeto(scope)
|
|
410
400
|
|
|
411
401
|
def processtemplate(self, scope):
|
|
412
|
-
from .grammar import
|
|
402
|
+
from .grammar import Parser
|
|
413
403
|
with scope.staticscope().indent.push() as monitor:
|
|
414
|
-
return
|
|
404
|
+
return Parser(monitor).readtemplate(self.streamvalue.read()).resolve(scope)
|
|
415
405
|
|
|
416
406
|
class Entry(Struct, Forkable):
|
|
417
407
|
|
|
418
|
-
@classmethod
|
|
419
|
-
def pa(cls, s, l, t):
|
|
420
|
-
return cls(t.asList())
|
|
421
|
-
|
|
422
408
|
def __init__(self, resolvables):
|
|
423
409
|
self.resolvables = resolvables
|
|
424
410
|
|
|
425
411
|
def size(self):
|
|
426
412
|
return sum(1 for r in self.resolvables if not r.ignorable)
|
|
427
413
|
|
|
428
|
-
def word(self, i):
|
|
429
|
-
word, = islice((r for r in self.resolvables if not r.ignorable), i, i + 1)
|
|
430
|
-
return word
|
|
431
|
-
|
|
432
414
|
def words(self):
|
|
433
415
|
return [r for r in self.resolvables if not r.ignorable]
|
|
434
416
|
|
|
@@ -467,6 +449,19 @@ class Entry(Struct, Forkable):
|
|
|
467
449
|
def plus(self, that):
|
|
468
450
|
return self._of([*self.resolvables, *that.resolvables])
|
|
469
451
|
|
|
452
|
+
class MalformedEntryException(Exception): pass
|
|
453
|
+
|
|
454
|
+
class Suffix:
|
|
455
|
+
|
|
456
|
+
def __init__(self, entry):
|
|
457
|
+
self.indent = entry.indent()
|
|
458
|
+
self.entry = entry
|
|
459
|
+
|
|
460
|
+
def closing(self, contextindent):
|
|
461
|
+
if any(x != y for x, y in zip(self.indent, contextindent)):
|
|
462
|
+
raise MalformedEntryException(self.entry)
|
|
463
|
+
return len(self.indent) <= len(contextindent)
|
|
464
|
+
|
|
470
465
|
def wrap(value):
|
|
471
466
|
'Attempt to wrap the given value in a model object of the most specific type.'
|
|
472
467
|
try:
|
|
@@ -1,24 +1,10 @@
|
|
|
1
|
-
from .grammar import
|
|
2
|
-
from .model import Entry
|
|
3
|
-
from pyparsing import ParseException
|
|
1
|
+
from .grammar import ParseException, staticparser
|
|
2
|
+
from .model import Entry, Suffix
|
|
4
3
|
|
|
5
4
|
class DanglingStackException(Exception): pass
|
|
6
5
|
|
|
7
6
|
class NoSuchIndentException(Exception): pass
|
|
8
7
|
|
|
9
|
-
class MalformedEntryException(Exception): pass
|
|
10
|
-
|
|
11
|
-
class Suffix:
|
|
12
|
-
|
|
13
|
-
def __init__(self, entry):
|
|
14
|
-
self.indent = entry.indent()
|
|
15
|
-
self.entry = entry
|
|
16
|
-
|
|
17
|
-
def closing(self, contextindent):
|
|
18
|
-
if any(x != y for x, y in zip(self.indent, contextindent)):
|
|
19
|
-
raise MalformedEntryException(self.entry)
|
|
20
|
-
return len(self.indent) <= len(contextindent)
|
|
21
|
-
|
|
22
8
|
class Command:
|
|
23
9
|
|
|
24
10
|
def __init__(self, entry):
|
|
@@ -40,7 +26,7 @@ class CommandReader:
|
|
|
40
26
|
|
|
41
27
|
def _readcommands(self, line):
|
|
42
28
|
try:
|
|
43
|
-
suffix =
|
|
29
|
+
suffix = staticparser.readsuffix(''.join([*self.stack, line]))
|
|
44
30
|
del self.stack[:]
|
|
45
31
|
except ParseException:
|
|
46
32
|
self.stack.append(line)
|
|
@@ -5,7 +5,7 @@ from .resolve import AnchorResolveContext
|
|
|
5
5
|
from .search import Query
|
|
6
6
|
from .stacks import IndentStack, SimpleStack, ThreadLocalResolvable
|
|
7
7
|
from .util import NoSuchPathException, UnsupportedEntryException
|
|
8
|
-
from foyndation import solo
|
|
8
|
+
from foyndation import singleton, solo
|
|
9
9
|
from itertools import chain
|
|
10
10
|
import os, sys, threading
|
|
11
11
|
|
|
@@ -234,6 +234,7 @@ class Star(Function, Directive):
|
|
|
234
234
|
def _star(self, prefix, suffix, scope):
|
|
235
235
|
scope.getorcreatesubscope(prefix.topath(scope) + (self.protokey,)).execute(suffix)
|
|
236
236
|
|
|
237
|
+
@singleton
|
|
237
238
|
class StaticScope(Scope):
|
|
238
239
|
|
|
239
240
|
rootscopekey = OpaqueKey()
|
|
@@ -245,8 +246,6 @@ class StaticScope(Scope):
|
|
|
245
246
|
self[word,] = Directive(d)
|
|
246
247
|
for name, f in corefunctions():
|
|
247
248
|
self[name,] = Function(f)
|
|
248
|
-
self['keyring_cron',] = Scalar(False)
|
|
249
|
-
self['keyring_force',] = Scalar(False)
|
|
250
249
|
self['~',] = Text(os.path.expanduser('~'))
|
|
251
250
|
self['LF',] = Text('\n')
|
|
252
251
|
self['EOL',] = Text(os.linesep)
|
|
@@ -268,8 +267,6 @@ class StaticScope(Scope):
|
|
|
268
267
|
setattr(threadlocals, name, stack)
|
|
269
268
|
return stack
|
|
270
269
|
|
|
271
|
-
StaticScope = StaticScope()
|
|
272
|
-
|
|
273
270
|
class ScalarScope(Scope):
|
|
274
271
|
|
|
275
272
|
def __init__(self, parents, scalarobj):
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: aridity
|
|
3
|
-
Version:
|
|
3
|
+
Version: 99
|
|
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
|
|
7
7
|
Author-email: shrovis@foyono.com
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: foyndation>=14
|
|
10
|
-
Requires-Dist: pyparsing>=3.0.2
|
|
11
10
|
|
|
12
11
|
# aridity
|
|
13
12
|
DRY config and template system, easily extensible with Python.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
foyndation>=14
|
|
@@ -49,13 +49,13 @@ class SourceInfo:
|
|
|
49
49
|
sourceinfo = SourceInfo('.')
|
|
50
50
|
setup(
|
|
51
51
|
name = 'aridity',
|
|
52
|
-
version = '
|
|
52
|
+
version = '99',
|
|
53
53
|
description = 'DRY config and template system, easily extensible with Python',
|
|
54
54
|
url = 'https://pypi.org/project/aridity/',
|
|
55
55
|
author = 'foyono',
|
|
56
56
|
author_email = 'shrovis@foyono.com',
|
|
57
57
|
py_modules = [],
|
|
58
|
-
install_requires = ['foyndation>=14'
|
|
58
|
+
install_requires = ['foyndation>=14'],
|
|
59
59
|
package_data = {'': ['*.pxd', '*.pyx', '*.pyxbld', '*.arid', '*.aridt']},
|
|
60
60
|
entry_points = {'console_scripts': ['aridity=aridity.__init__:main', 'arid-config=aridity.arid_config:main', 'processtemplate=aridity.processtemplate:main']},
|
|
61
61
|
**sourceinfo.setup_kwargs(),
|
aridity-97/aridity/grammar.py
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
from .model import Blank, Boolean, Boundary, Call, Concat, Entry, nullmonitor, Number, Text
|
|
2
|
-
from decimal import Decimal
|
|
3
|
-
from functools import partial, reduce
|
|
4
|
-
from pyparsing import Forward, Literal, MatchFirst, NoMatch, OneOrMore, Optional, Regex, Suppress, ZeroOrMore
|
|
5
|
-
import operator, re
|
|
6
|
-
|
|
7
|
-
class AnyScalar:
|
|
8
|
-
|
|
9
|
-
numbermatch = re.compile('-?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)').fullmatch
|
|
10
|
-
booleans = {t: Boolean(b).augment(textvalue = t) for b in map(bool, range(2)) for t in [str(b).lower()]}
|
|
11
|
-
|
|
12
|
-
@classmethod
|
|
13
|
-
def pa(cls, s, l, t):
|
|
14
|
-
text, = t
|
|
15
|
-
try:
|
|
16
|
-
return cls.booleans[text]
|
|
17
|
-
except KeyError:
|
|
18
|
-
m = cls.numbermatch(text)
|
|
19
|
-
if m is None:
|
|
20
|
-
return Text(text)
|
|
21
|
-
if '.' in text:
|
|
22
|
-
val = Decimal(text)
|
|
23
|
-
else:
|
|
24
|
-
val = int(text)
|
|
25
|
-
if not val and '-' == text[0]:
|
|
26
|
-
val = Decimal(text) # Preserve sign.
|
|
27
|
-
return Number(val).augment(textvalue = text)
|
|
28
|
-
|
|
29
|
-
def _gettext(notchars, pa):
|
|
30
|
-
return Regex(fr"[^$\s{re.escape(notchars)}]+").leaveWhitespace().setParseAction(pa)
|
|
31
|
-
|
|
32
|
-
def _getarg(callchain, scalarpa, boundarychars):
|
|
33
|
-
gettext = partial(_gettext, boundarychars)
|
|
34
|
-
opttext = Optional(gettext(Text.pa))
|
|
35
|
-
return (OneOrMore(opttext + callchain) + opttext | gettext(scalarpa)).setParseAction(Concat.smartpa)
|
|
36
|
-
|
|
37
|
-
def _bracketed(callchain, blankpa, scalarpa, o, c):
|
|
38
|
-
gettext = partial(_gettext, o + c)
|
|
39
|
-
bracketed = Forward()
|
|
40
|
-
chainorbrackets = callchain | (Literal(o).setParseAction(Text.pa) + bracketed + Literal(c).setParseAction(Text.pa)).leaveWhitespace()
|
|
41
|
-
opttext = Optional(gettext(Text.pa))
|
|
42
|
-
concat = OneOrMore(opttext + chainorbrackets) + opttext
|
|
43
|
-
optblank = _getoptblank(blankpa, '')
|
|
44
|
-
bracketed << ZeroOrMore(optblank + (concat | gettext(scalarpa)).setParseAction(Concat.smartpa)) + optblank
|
|
45
|
-
return bracketed
|
|
46
|
-
|
|
47
|
-
def _literalbracketed(o, c):
|
|
48
|
-
bracketed = Forward()
|
|
49
|
-
brackets = (Literal(o) + bracketed + Literal(c)).leaveWhitespace()
|
|
50
|
-
opttext = Regex(f"[^{re.escape(o + c)}]*").leaveWhitespace()
|
|
51
|
-
bracketed << ZeroOrMore(opttext + brackets) + opttext
|
|
52
|
-
return bracketed
|
|
53
|
-
|
|
54
|
-
def _getoptblank(pa, boundarychars):
|
|
55
|
-
return Optional(Regex(fr"[^\S{re.escape(boundarychars)}]+").leaveWhitespace().setParseAction(pa))
|
|
56
|
-
|
|
57
|
-
class Parser:
|
|
58
|
-
|
|
59
|
-
def __init__(self, g, singleton = True):
|
|
60
|
-
self.g = g.parseWithTabs()
|
|
61
|
-
self.singleton = singleton
|
|
62
|
-
|
|
63
|
-
def __call__(self, text):
|
|
64
|
-
result = self.g.parseString(text, parseAll = True).asList()
|
|
65
|
-
if self.singleton:
|
|
66
|
-
result, = result
|
|
67
|
-
return result
|
|
68
|
-
|
|
69
|
-
def _principalcallpa(s, l, t):
|
|
70
|
-
return Call(t[0], t[2:-1], t[1] + t[-1])
|
|
71
|
-
|
|
72
|
-
def _additionalcallpa(s, l, t):
|
|
73
|
-
return Call(t[0], t[1:], ['', ''])
|
|
74
|
-
|
|
75
|
-
class GFactory:
|
|
76
|
-
|
|
77
|
-
bracketpairs = '()', '[]'
|
|
78
|
-
identifier = Regex(fr"[^\s${''.join(re.escape(o) for o, _ in bracketpairs)}]*")
|
|
79
|
-
|
|
80
|
-
def __init__(self, scalarpa = AnyScalar.pa, boundarychars = '\r\n', ormorecls = OneOrMore, monitor = nullmonitor):
|
|
81
|
-
self.scalarpa = scalarpa
|
|
82
|
-
self.boundarychars = boundarychars
|
|
83
|
-
self.ormorecls = ormorecls
|
|
84
|
-
self.monitor = monitor
|
|
85
|
-
|
|
86
|
-
def templatepa(self, s, l, t):
|
|
87
|
-
return Concat(t, self.monitor)
|
|
88
|
-
|
|
89
|
-
def _bracketspa(self, s, l, t):
|
|
90
|
-
return Concat(t[1:-1] or [Text('')], self.monitor)
|
|
91
|
-
|
|
92
|
-
def create(self, pa):
|
|
93
|
-
def itercalls():
|
|
94
|
-
def getbrackets(blankpa, scalarpa):
|
|
95
|
-
return Literal(o) + _bracketed(callchain, blankpa, scalarpa, o, c) + Literal(c)
|
|
96
|
-
for o, c in self.bracketpairs:
|
|
97
|
-
yield (Suppress(Regex("[$](?:lit|')")) + Suppress(o) + _literalbracketed(o, c) + Suppress(c)).setParseAction(Text.joinpa)
|
|
98
|
-
yield (Suppress(Regex('[$](?:pass|[.])')) + getbrackets(Text.pa, Text.pa)).setParseAction(self._bracketspa)
|
|
99
|
-
yield (Suppress('$') + self.identifier + getbrackets(Blank.pa, AnyScalar.pa)).setParseAction(_principalcallpa)
|
|
100
|
-
yield (Suppress('$') + self.identifier + callchain).setParseAction(_additionalcallpa)
|
|
101
|
-
optblank = _getoptblank(Blank.pa, self.boundarychars)
|
|
102
|
-
callchain = Forward()
|
|
103
|
-
callchain << MatchFirst(itercalls()).leaveWhitespace()
|
|
104
|
-
return reduce(operator.add, [
|
|
105
|
-
self.ormorecls(optblank + _getarg(callchain, self.scalarpa, self.boundarychars)),
|
|
106
|
-
optblank,
|
|
107
|
-
Optional(Regex(f"[{re.escape(self.boundarychars)}]+").leaveWhitespace().setParseAction(Boundary.pa) if self.boundarychars else NoMatch()),
|
|
108
|
-
]).setParseAction(pa)
|
|
109
|
-
|
|
110
|
-
commandparser = Parser(GFactory(ormorecls = ZeroOrMore).create(Entry.pa))
|
|
111
|
-
|
|
112
|
-
def templateparser(monitor):
|
|
113
|
-
gfactory = GFactory(scalarpa = Text.pa, boundarychars = '', monitor = monitor)
|
|
114
|
-
return Parser(gfactory.create(gfactory.templatepa) | Regex('^$').setParseAction(Text.pa))
|
aridity-97/aridity/keyring.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from .model import Scalar
|
|
2
|
-
from .util import null_exc_info
|
|
3
|
-
from base64 import b64decode
|
|
4
|
-
from functools import partial
|
|
5
|
-
from getpass import getpass
|
|
6
|
-
from subprocess import CalledProcessError, check_output
|
|
7
|
-
from tempfile import NamedTemporaryFile
|
|
8
|
-
from threading import Semaphore
|
|
9
|
-
import logging, os
|
|
10
|
-
|
|
11
|
-
log = logging.getLogger(__name__)
|
|
12
|
-
passwordbase = str
|
|
13
|
-
setenvonce = Semaphore()
|
|
14
|
-
|
|
15
|
-
class Password(passwordbase):
|
|
16
|
-
|
|
17
|
-
def __new__(cls, password, setter):
|
|
18
|
-
p = passwordbase.__new__(cls, password)
|
|
19
|
-
p.setter = setter
|
|
20
|
-
return p
|
|
21
|
-
|
|
22
|
-
def __enter__(self):
|
|
23
|
-
return self
|
|
24
|
-
|
|
25
|
-
def __exit__(self, *exc_info):
|
|
26
|
-
if self.setter is not None and null_exc_info == exc_info:
|
|
27
|
-
self.setter(self)
|
|
28
|
-
|
|
29
|
-
def keyring(scope, serviceres, usernameres):
|
|
30
|
-
if scope.resolved('keyring_cron').scalar and setenvonce.acquire(False):
|
|
31
|
-
key = 'DBUS_SESSION_BUS_ADDRESS'
|
|
32
|
-
value = f"unix:path=/run/user/{os.geteuid()}/bus"
|
|
33
|
-
log.debug("Set %s to: %s", key, value)
|
|
34
|
-
os.environ[key] = value
|
|
35
|
-
from keyring import get_password, set_password
|
|
36
|
-
service = serviceres.resolve(scope).textvalue
|
|
37
|
-
username = usernameres.resolve(scope).textvalue
|
|
38
|
-
password = None if scope.resolved('keyring_force').scalar else get_password(service, username)
|
|
39
|
-
return Scalar(Password(*[getpass(), partial(set_password, service, username)] if password is None else [password, None]))
|
|
40
|
-
|
|
41
|
-
class DecryptionFailedException(Exception): pass
|
|
42
|
-
|
|
43
|
-
def gpg(scope, resolvable):
|
|
44
|
-
'Use gpg to decrypt the given base64-encoded blob.'
|
|
45
|
-
with NamedTemporaryFile() as f:
|
|
46
|
-
f.write(b64decode(resolvable.resolve(scope).textvalue))
|
|
47
|
-
f.flush()
|
|
48
|
-
try:
|
|
49
|
-
return Scalar(Password(check_output(['gpg', '-d', f.name]).decode('ascii'), None))
|
|
50
|
-
except CalledProcessError:
|
|
51
|
-
raise DecryptionFailedException
|
|
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
|