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.
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aridity
3
- Version: 97
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, Text, wrap
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, solo
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, keyring
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, islice
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
- class Boundary(BaseSimpleValue):
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, brackets):
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._resolvables())
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._resolvables())
358
+ yield j, f(scope, *self.args)
368
359
  else:
369
- resolvable, = self._resolvables() # XXX: Support many?
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 templateparser
402
+ from .grammar import Parser
413
403
  with scope.staticscope().indent.push() as monitor:
414
- return templateparser(monitor)(self.streamvalue.read()).resolve(scope)
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 commandparser
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 = Suffix(commandparser(''.join([*self.stack, line])))
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: 97
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 = '97',
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', 'pyparsing>=3.0.2'],
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(),
@@ -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))
@@ -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
@@ -1,2 +0,0 @@
1
- foyndation>=14
2
- pyparsing>=3.0.2
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