aridity 83__tar.gz → 85__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aridity
3
- Version: 83
3
+ Version: 85
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
@@ -247,16 +247,6 @@ Process the given template to stdout using config from stdin.
247
247
 
248
248
  ### aridity.config
249
249
 
250
- <a id="aridity.config.ForeignScopeException"></a>
251
-
252
- #### ForeignScopeException Objects
253
-
254
- ```python
255
- class ForeignScopeException(Exception)
256
- ```
257
-
258
- The operation required a scope at precisely the given path.
259
-
260
250
  <a id="aridity.config.ConfigCtrl"></a>
261
251
 
262
252
  #### ConfigCtrl Objects
@@ -558,7 +548,7 @@ If given 3 args, the first two are variable names for scope key and scope respec
558
548
  ###### join
559
549
 
560
550
  ```python
561
- def join(scope, resolvables, *args)
551
+ def join(scope, partsresolvable, sepresolvable=None)
562
552
  ```
563
553
 
564
554
  Concatenate the given list, using optional separator. Frequently used with `map`.
@@ -780,7 +770,7 @@ Get the regular object associated with `parabject` or raise UnknownParabjectExce
780
770
  #### Parabject Objects
781
771
 
782
772
  ```python
783
- class Parabject(object)
773
+ class Parabject()
784
774
  ```
785
775
 
786
776
  Subclasses typically implement `__getattr__` for dynamic behaviour on attribute access.
@@ -237,16 +237,6 @@ Process the given template to stdout using config from stdin.
237
237
 
238
238
  ### aridity.config
239
239
 
240
- <a id="aridity.config.ForeignScopeException"></a>
241
-
242
- #### ForeignScopeException Objects
243
-
244
- ```python
245
- class ForeignScopeException(Exception)
246
- ```
247
-
248
- The operation required a scope at precisely the given path.
249
-
250
240
  <a id="aridity.config.ConfigCtrl"></a>
251
241
 
252
242
  #### ConfigCtrl Objects
@@ -548,7 +538,7 @@ If given 3 args, the first two are variable names for scope key and scope respec
548
538
  ###### join
549
539
 
550
540
  ```python
551
- def join(scope, resolvables, *args)
541
+ def join(scope, partsresolvable, sepresolvable=None)
552
542
  ```
553
543
 
554
544
  Concatenate the given list, using optional separator. Frequently used with `map`.
@@ -770,7 +760,7 @@ Get the regular object associated with `parabject` or raise UnknownParabjectExce
770
760
  #### Parabject Objects
771
761
 
772
762
  ```python
773
- class Parabject(object)
763
+ class Parabject()
774
764
  ```
775
765
 
776
766
  Subclasses typically implement `__getattr__` for dynamic behaviour on attribute access.
@@ -1,12 +1,11 @@
1
1
  from .functions import OpaqueKey
2
- from .model import Entry, Function, Locator, Number, Resource, Scalar, Stream, Text, wrap
2
+ from .model import Entry, Locator, Resource, Stream, Text, wrap
3
3
  from .repl import Repl
4
4
  from .scope import Scope
5
5
  from .search import resolvedscopeornone
6
6
  from .util import dotpy, NoSuchPathException, qualname, selectentrypoints, solo
7
- from functools import partial
8
- from itertools import chain
9
7
  from parabject import Parabject, register
8
+ from pathlib import Path
10
9
  import errno, logging, os, sys
11
10
 
12
11
  log = logging.getLogger(__name__)
@@ -29,19 +28,22 @@ def _processmainfunction(mainfunction):
29
28
  appname, = (ep.name for ep in selectentrypoints('console_scripts') if ep.module == module and ep.attr == attr)
30
29
  return module, appname
31
30
 
32
- class ForeignScopeException(Exception):
33
- 'The operation required a scope at precisely the given path.'
34
-
35
31
  def _wrappathorstream(pathorstream):
36
32
  return (Stream if getattr(pathorstream, 'readable', lambda: False)() else Locator)(pathorstream)
37
33
 
38
34
  class ConfigCtrl:
39
35
  'High level scope API.'
40
36
 
37
+ settingsopenable = Locator(Path.home() / '.settings.arid')
38
+
41
39
  @classmethod
42
40
  def _of(cls, *args, **kwargs):
43
41
  return cls(*args, **kwargs)
44
42
 
43
+ @property
44
+ def node(self):
45
+ return register(self, Config)
46
+
45
47
  @property
46
48
  def r(self):
47
49
  'Get config object for reading, i.e. missing scopes will error.'
@@ -53,7 +55,6 @@ class ConfigCtrl:
53
55
  return register(self, WConfig)
54
56
 
55
57
  def __init__(self, basescope = None, prefix = None):
56
- self.node = register(self, Config)
57
58
  self.basescope = Scope() if basescope is None else basescope
58
59
  self.prefix = [] if prefix is None else prefix
59
60
 
@@ -73,7 +74,7 @@ class ConfigCtrl:
73
74
  return appconfig
74
75
 
75
76
  def _loadappconfig(self, appname, resource):
76
- resource.source(self.basescope.getorcreatesubscope(self.prefix + [appname]), Entry([]))
77
+ resource.source(self.basescope.getorcreatesubscope([*self.prefix, appname]), Entry([]))
77
78
  return getattr(self.node, appname)
78
79
 
79
80
  def reapplysettings(self, mainfunction):
@@ -81,7 +82,7 @@ class ConfigCtrl:
81
82
  appname = mainfunction
82
83
  else:
83
84
  _, appname = _processmainfunction(mainfunction)
84
- s = self.scope(True).duplicate()
85
+ s = self.scope.duplicate()
85
86
  s.label = Text(appname)
86
87
  p = solo(s.parents)
87
88
  p[appname,] = s
@@ -89,101 +90,72 @@ class ConfigCtrl:
89
90
  parent.loadsettings()
90
91
  return getattr(parent.node, appname)
91
92
 
92
- def printf(self, template, *args):
93
- with Repl(self.basescope) as repl:
94
- repl.printf(''.join(chain(("%s " for _ in self.prefix), [template])), *chain(self.prefix, args))
95
-
96
93
  def load(self, pathorstream):
97
94
  'Execute config from the given path or stream.'
98
- s = self.scope(True)
99
- _wrappathorstream(pathorstream).source(s, Entry([]))
95
+ _wrappathorstream(pathorstream).source(self.scope, Entry([]))
100
96
 
101
97
  def loadsettings(self):
102
- self.load(os.path.join(os.path.expanduser('~'), '.settings.arid'))
103
-
104
- def repl(self):
105
- assert not self.prefix # XXX: Support prefix?
106
- return Repl(self.basescope)
98
+ self.settingsopenable.source(self.scope, Entry([]))
107
99
 
108
100
  def execute(self, text):
109
101
  'Execute given config text.'
110
- with self.repl() as repl:
102
+ with Repl(self.scope) as repl:
111
103
  for line in text.splitlines(True):
112
104
  repl(line)
113
105
 
114
- def put(self, *path, **kwargs):
115
- def pairs():
116
- for t, k in [
117
- [Function, 'function'],
118
- [Number, 'number'],
119
- [Scalar, 'scalar'],
120
- [Text, 'text'],
121
- [lambda x: x, 'resolvable']]:
122
- try:
123
- yield t, kwargs[k]
124
- except KeyError:
125
- pass
126
- # XXX: Support combination of types e.g. slash is both function and text?
127
- factory, = (partial(t, v) for t, v in pairs())
128
- self.basescope[tuple(self.prefix) + path] = factory()
129
-
130
- def scope(self, strict = False):
131
- if strict:
132
- s = resolvedscopeornone(self.basescope, self.prefix)
133
- if s is None:
134
- raise ForeignScopeException
135
- return s
136
- return self.basescope.resolved(*self.prefix) # TODO: Test what happens if it changes.
106
+ @property
107
+ def scope(self):
108
+ s = resolvedscopeornone(self.basescope, self.prefix)
109
+ assert s is not None
110
+ return s
137
111
 
138
112
  def __iter__(self): # TODO: Add API to get keys without resolving values.
139
113
  'Yield keys and values.'
140
- for k, o in self.scope().resolveditems():
114
+ for k, o in self.resolve().resolveditems():
141
115
  try:
142
116
  yield k, o.scalar
143
117
  except AttributeError:
144
- yield k, self._of(self.basescope, self.prefix + [k]).node
118
+ yield k, self.addname(k).node
145
119
 
146
120
  def processtemplate(self, frompathorstream, topathorstream):
147
121
  'Evaluate expression from path/stream and write result to path/stream.'
148
- s = self.scope()
149
- obj = _wrappathorstream(frompathorstream).processtemplate(s)
122
+ obj = _wrappathorstream(frompathorstream).processtemplate(self.resolve())
150
123
  if getattr(topathorstream, 'writable', lambda: False)():
151
- topathorstream.write(obj.cat() if hasattr(topathorstream, 'encoding') else obj.binaryvalue)
124
+ topathorstream.write(obj.textvalue if hasattr(topathorstream, 'encoding') else obj.binaryvalue)
152
125
  else:
153
126
  obj.writeout(topathorstream)
154
127
 
155
- def freectrl(self):
156
- return self._of(self.scope()) # XXX: Strict?
157
-
158
128
  def childctrl(self):
159
- return self._of(self.scope(True).createchild())
129
+ return self._of(self.scope.createchild())
160
130
 
161
131
  def addname(self, name):
162
- return self._of(self.basescope, self.prefix + [name])
132
+ return self._of(self.basescope, [*self.prefix, name])
163
133
 
164
134
  def resolve(self):
165
135
  return self.basescope.resolved(*self.prefix)
166
136
 
137
+ def prefixrepr(self):
138
+ return '.'.join(w if w.isidentifier() else repr(w) for w in self.prefix)
139
+
167
140
  class Config(Parabject):
168
141
 
169
142
  def __getattr__(self, name):
170
- ctrl = -self
171
- path = ctrl.prefix + [name]
143
+ query = (-self).addname(name)
172
144
  try:
173
- obj = ctrl.basescope.resolved(*path) # TODO LATER: Guidance for how lazy non-scalars should be in this situation.
145
+ obj = query.resolve() # TODO LATER: Guidance for how lazy non-scalars should be in this situation.
174
146
  except NoSuchPathException:
175
- raise AttributeError(' '.join(path))
147
+ raise AttributeError(query.prefixrepr())
176
148
  try:
177
149
  return obj.scalar
178
150
  except AttributeError:
179
- return ctrl._of(ctrl.basescope, path).node
151
+ return query.node
180
152
 
181
153
  def __iter__(self):
182
154
  for _, o in -self:
183
155
  yield o
184
156
 
185
157
  def __setattr__(self, name, value):
186
- (-self).scope(True)[name,] = wrap(value)
158
+ (-self).scope[name,] = wrap(value)
187
159
 
188
160
  class RConfig(Parabject):
189
161
 
@@ -192,7 +164,7 @@ class RConfig(Parabject):
192
164
  try:
193
165
  obj = query.resolve()
194
166
  except NoSuchPathException:
195
- raise AttributeError
167
+ raise AttributeError(query.prefixrepr())
196
168
  try:
197
169
  return obj.scalar
198
170
  except AttributeError:
@@ -200,7 +172,7 @@ class RConfig(Parabject):
200
172
 
201
173
  def __iter__(self):
202
174
  'Yield values only. Iterate over `-self` for keys and values.'
203
- for _, o in (-self).scope().resolveditems(): # TODO: Investigate how iteration should work.
175
+ for _, o in (-self).resolve().resolveditems(): # TODO: Investigate how iteration should work.
204
176
  yield o.scalar
205
177
 
206
178
  class WConfig(Parabject):
@@ -36,7 +36,7 @@ class Redirect:
36
36
  class Write:
37
37
  name = '!write'
38
38
  def __call__(self, prefix, suffix, scope):
39
- scope.resolved('stdout').flush(suffix.tophrase().resolve(scope).cat())
39
+ scope.resolved('stdout').flush(suffix.tophrase().resolve(scope).textvalue)
40
40
 
41
41
  @_directive
42
42
  class Source:
@@ -94,4 +94,4 @@ class Cat:
94
94
  name = '<'
95
95
  def __call__(self, prefix, suffix, scope):
96
96
  scope = scope.getorcreatesubscope(prefix.topath(scope))
97
- scope.resolved('stdout').flush(suffix.tophrase().resolve(scope).openable(scope).processtemplate(scope).cat())
97
+ scope.resolved('stdout').flush(suffix.tophrase().resolve(scope).openable(scope).processtemplate(scope).textvalue)
@@ -1,4 +1,4 @@
1
- from .model import Boolean, Hole, Number, Resource, Text, wrap
1
+ from .model import Boolean, Hole, Indeterminate, Number, Resource, Text, wrap
2
2
  from .util import allfunctions, dotpy, NoSuchPathException, realname
3
3
  from importlib import import_module
4
4
  from itertools import chain
@@ -13,7 +13,7 @@ def _tomlquote(text):
13
13
  return ''.join(fr"\u{ord(c):04X}" for c in m.group())
14
14
  return f'"{tomlbasicbadchars.sub(repl, text)}"'
15
15
 
16
- class OpaqueKey(object):
16
+ class OpaqueKey:
17
17
 
18
18
  @classmethod
19
19
  def isopaque(cls, key):
@@ -25,19 +25,19 @@ class Functions:
25
25
 
26
26
  def screenstr(scope, resolvable):
27
27
  'GNU Screen string literal.'
28
- return Text('"{}"'.format(re.sub(r'[\\\n"]', r'\\\g<0>', resolvable.resolve(scope).cat())))
28
+ return Text('"{}"'.format(re.sub(r'[\\\n"]', r'\\\g<0>', resolvable.resolve(scope).textvalue)))
29
29
 
30
30
  def scstr(scope, resolvable):
31
31
  'SuperCollider string literal.'
32
- return Text('"{}"'.format(re.sub(r'[\\\n"]', r'\\\g<0>', resolvable.resolve(scope).cat())))
32
+ return Text('"{}"'.format(re.sub(r'[\\\n"]', r'\\\g<0>', resolvable.resolve(scope).textvalue)))
33
33
 
34
34
  def hclstr(scope, resolvable):
35
35
  'HashiCorp configuration language string literal.'
36
- return Text('"{}"'.format(re.sub(r'[\\\n"]', r'\\\g<0>', resolvable.resolve(scope).cat())))
36
+ return Text('"{}"'.format(re.sub(r'[\\\n"]', r'\\\g<0>', resolvable.resolve(scope).textvalue)))
37
37
 
38
38
  def groovystr(scope, resolvable):
39
39
  'Groovy string literal.'
40
- return Text("'{}'".format(re.sub(r"[\\\n']", r'\\\g<0>', resolvable.resolve(scope).cat())))
40
+ return Text("'{}'".format(re.sub(r"[\\\n']", r'\\\g<0>', resolvable.resolve(scope).textvalue)))
41
41
 
42
42
  def pystr(scope, resolvable):
43
43
  'Python literal.'
@@ -45,7 +45,7 @@ class Functions:
45
45
 
46
46
  def shstr(scope, resolvable):
47
47
  'Shell string literal.'
48
- return Text(shlex.quote(resolvable.resolve(scope).cat()))
48
+ return Text(shlex.quote(resolvable.resolve(scope).textvalue))
49
49
 
50
50
  def jsonquote(scope, resolvable):
51
51
  'JSON literal, also suitable for YAML.'
@@ -54,21 +54,21 @@ class Functions:
54
54
  def xmlattr(scope, resolvable):
55
55
  'XML attribute literal (including quotes).'
56
56
  from xml.sax.saxutils import quoteattr
57
- return Text(quoteattr(resolvable.resolve(scope).cat())) # TODO: Support booleans.
57
+ return Text(quoteattr(resolvable.resolve(scope).textvalue)) # TODO: Support booleans.
58
58
 
59
59
  def xmltext(scope, resolvable):
60
60
  'XML content, suggest assigning this to & with xmlattr assigned to " as is convention.'
61
61
  from xml.sax.saxutils import escape
62
- return Text(escape(resolvable.resolve(scope).cat(), xmlentities))
62
+ return Text(escape(resolvable.resolve(scope).textvalue, xmlentities))
63
63
 
64
64
  def tomlquote(scope, resolvable):
65
65
  'TOML string literal.'
66
- return Text(_tomlquote(resolvable.resolve(scope).cat()))
66
+ return Text(_tomlquote(resolvable.resolve(scope).textvalue))
67
67
 
68
68
  def urlquote(scope, resolvable):
69
69
  'Percent-encode all reserved characters.'
70
70
  from urllib.parse import quote
71
- return Text(quote(resolvable.resolve(scope).cat(), safe = ''))
71
+ return Text(quote(resolvable.resolve(scope).textvalue, safe = ''))
72
72
 
73
73
  def map(scope, objsresolvable, *args):
74
74
  '''If given 1 arg, evaluate it against every scope in `objsresolvable` and return that list.
@@ -96,7 +96,7 @@ class Functions:
96
96
  s[vname,] = v
97
97
  return s
98
98
  vname, resolvable = args
99
- vname = vname.resolve(scope).cat()
99
+ vname = vname.resolve(scope).textvalue
100
100
  else:
101
101
  def context(k, v):
102
102
  s = Scope(parents)
@@ -104,8 +104,8 @@ class Functions:
104
104
  s[vname,] = v
105
105
  return s
106
106
  kname, vname, resolvable = args
107
- kname = kname.resolve(scope).cat()
108
- vname = vname.resolve(scope).cat()
107
+ kname = kname.resolve(scope).textvalue
108
+ vname = vname.resolve(scope).textvalue
109
109
  result = Scope(islist = True) # XXX: Really no parent?
110
110
  for k, v in objs.resolveditems():
111
111
  result.resolvables.put(k, resolvable.resolve(context(k, v)))
@@ -122,19 +122,13 @@ class Functions:
122
122
  def label(scope):
123
123
  return scope.label
124
124
 
125
- def join(scope, resolvables, *args):
125
+ def join(scope, partsresolvable, sepresolvable = None):
126
126
  'Concatenate the given list, using optional separator. Frequently used with `map`.'
127
- if args:
128
- r, = args
129
- separator = r.resolve(scope).cat()
130
- else:
131
- separator = ''
132
- s = resolvables.resolve(scope)
133
- return Text(separator.join(o.cat() for _, o in s.resolveditems()))
127
+ return Join(scope, partsresolvable).execute(sepresolvable)
134
128
 
135
129
  @realname(',') # XXX: Oh yeah?
136
130
  def aslist(scope, *resolvables):
137
- return scope.resolved(*(r.resolve(scope).cat() for r in resolvables), **{'aslist': True})
131
+ return scope.resolved(*(r.resolve(scope).textvalue for r in resolvables), **{'aslist': True})
138
132
 
139
133
  def str(scope, resolvable):
140
134
  'Coerce to string.'
@@ -178,7 +172,7 @@ class Functions:
178
172
  @realname('./')
179
173
  def hereslash(scope, *resolvables):
180
174
  'Join the given path components with the directory of the current resource.'
181
- return scope.resolved('here').slash((r.resolve(scope).cat() for r in resolvables), False)
175
+ return scope.resolved('here').slash((r.resolve(scope).textvalue for r in resolvables), False)
182
176
 
183
177
  def readfile(scope, resolvable):
184
178
  'Include the content of the given path.'
@@ -190,12 +184,12 @@ class Functions:
190
184
  return resolvable.resolve(scope).openable(scope).processtemplate(scope)
191
185
 
192
186
  def lower(scope, resolvable):
193
- return Text(resolvable.resolve(scope).cat().lower())
187
+ return Text(resolvable.resolve(scope).textvalue.lower())
194
188
 
195
189
  def pyref(scope, moduleresolvable, qualnameresolvable):
196
190
  '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.'
197
191
  def moduleobj():
198
- moduleref = moduleresolvable.resolve(scope).cat()
192
+ moduleref = moduleresolvable.resolve(scope).textvalue
199
193
  leadingdots = len(zeroormoredots.match(moduleref).group())
200
194
  if not leadingdots:
201
195
  return import_module(moduleref)
@@ -212,23 +206,23 @@ class Functions:
212
206
  exec(f.read(), g)
213
207
  return M()
214
208
  pyobj = moduleobj()
215
- for name in qualnameresolvable.resolve(scope).cat().split('.'):
209
+ for name in qualnameresolvable.resolve(scope).textvalue.split('.'):
216
210
  pyobj = getattr(pyobj, name)
217
211
  return wrap(pyobj)
218
212
 
219
213
  def pyres(scope, packageresolvable, nameresolvable, encoding = Text('ascii')):
220
214
  'Python resource for inclusion with `.` directive.'
221
- return Resource(packageresolvable.resolve(scope).cat(), nameresolvable.resolve(scope).cat(), encoding.resolve(scope).cat())
215
+ return Resource(packageresolvable.resolve(scope).textvalue, nameresolvable.resolve(scope).textvalue, encoding.resolve(scope).textvalue)
222
216
 
223
217
  @realname('\N{NOT SIGN}')
224
218
  def not_(scope, resolvable):
225
219
  return Boolean(not resolvable.resolve(scope).truth())
226
220
 
227
221
  def getfrom(scope, scoperesolvable, *resolvables):
228
- return scoperesolvable.resolve(scope).resolved(*(r.resolve(scope).cat() for r in resolvables))
222
+ return scoperesolvable.resolve(scope).resolved(*(r.resolve(scope).textvalue for r in resolvables))
229
223
 
230
224
  def rmeol(scope, resolvable):
231
- text = resolvable.resolve(scope).cat()
225
+ text = resolvable.resolve(scope).textvalue
232
226
  if text.endswith('\r\n'):
233
227
  n = 2
234
228
  elif text.endswith(('\r', '\n')):
@@ -238,15 +232,40 @@ class Functions:
238
232
  return Text(text[:-n])
239
233
 
240
234
  def indentmorelines(scope, resolvable):
241
- indent = scope.resolved('indent').cat()
242
- lines = resolvable.resolve(scope).cat().splitlines(True)
235
+ indent = scope.resolved('indent').textvalue
236
+ lines = resolvable.resolve(scope).textvalue.splitlines(True)
243
237
  return Text(''.join(chain(lines[:1], (indent + line for line in lines[1:]))))
244
238
 
245
239
  def hole(scope, resolvable):
246
- return Hole(Text(''), resolvable.resolve(scope).cat())
240
+ return Hole(Text(''), resolvable.resolve(scope).textvalue)
241
+
242
+ class Join:
243
+
244
+ def __init__(self, scope, partsresolvable):
245
+ self.i = (o for _, o in partsresolvable.resolve(scope).resolveditems())
246
+ self.scope = scope
247
+
248
+ def _load(self):
249
+ try:
250
+ self.obj = next(self.i)
251
+ return True
252
+ except StopIteration:
253
+ pass
254
+
255
+ def execute(self, sepresolvable):
256
+ resobj = Indeterminate
257
+ if self._load():
258
+ resobj = resobj.plus(self.obj)
259
+ if self._load():
260
+ sepobj = Indeterminate if sepresolvable is None else sepresolvable.resolve(self.scope)
261
+ while True:
262
+ resobj = resobj.plus(sepobj).plus(self.obj)
263
+ if not self._load():
264
+ break
265
+ return resobj
247
266
 
248
267
  def getimpl(scope, *resolvables, **kwargs):
249
- return scope.resolved(*(r.resolve(scope).cat() for r in resolvables), **kwargs)
268
+ return scope.resolved(*(r.resolve(scope).textvalue for r in resolvables), **kwargs)
250
269
 
251
270
  def getfunctions():
252
271
  return allfunctions(Functions)
@@ -33,8 +33,8 @@ def keyring(scope, serviceres, usernameres):
33
33
  log.debug("Set %s to: %s", key, value)
34
34
  os.environ[key] = value
35
35
  from keyring import get_password, set_password
36
- service = serviceres.resolve(scope).cat()
37
- username = usernameres.resolve(scope).cat()
36
+ service = serviceres.resolve(scope).textvalue
37
+ username = usernameres.resolve(scope).textvalue
38
38
  password = None if scope.resolved('keyring_force').scalar else get_password(service, username)
39
39
  return Scalar(Password(*[getpass(), partial(set_password, service, username)] if password is None else [password, None]))
40
40
 
@@ -43,7 +43,7 @@ class DecryptionFailedException(Exception): pass
43
43
  def gpg(scope, resolvable):
44
44
  'Use gpg to decrypt the given base64-encoded blob.'
45
45
  with NamedTemporaryFile() as f:
46
- f.write(b64decode(resolvable.resolve(scope).cat()))
46
+ f.write(b64decode(resolvable.resolve(scope).textvalue))
47
47
  f.flush()
48
48
  try:
49
49
  return Scalar(Password(check_output(['gpg', '-d', f.name]).decode('ascii'), None))
@@ -7,7 +7,7 @@ from itertools import chain, islice
7
7
  from parabject import dereference, UnknownParabjectException
8
8
  import numbers, os
9
9
 
10
- class Struct(object):
10
+ class Struct:
11
11
 
12
12
  def __eq__(self, that):
13
13
  if type(self) != type(that):
@@ -52,7 +52,7 @@ class NegBuffer:
52
52
  def annihilate(self, obj):
53
53
  if not self.text:
54
54
  return obj
55
- text = obj.cat()
55
+ text = obj.textvalue
56
56
  if not text:
57
57
  return obj
58
58
  k = min(len(self.text), len(text))
@@ -63,7 +63,7 @@ class NegBuffer:
63
63
  def propagate(self, obj):
64
64
  return Hole(obj, self.text) if self.text else obj
65
65
 
66
- class Concat(Resolvable):
66
+ class Concat(Resolvable): # TODO: Ban in path components.
67
67
 
68
68
  ignorable = False
69
69
 
@@ -95,17 +95,14 @@ class Concat(Resolvable):
95
95
  obj = negbuffer.annihilate(obj)
96
96
  result = result.plus(obj)
97
97
  try:
98
- text = obj.cat()
99
- except CatNotSupportedException:
98
+ text = obj.textvalue
99
+ except AttributeError:
100
100
  pass
101
101
  else:
102
102
  self.monitor(text)
103
103
  negbuffer.insert(negtext)
104
104
  return negbuffer.propagate(result)
105
105
 
106
- # TODO: Always throw when concatenation within a path component is attempted.
107
- class CatNotSupportedException(Exception): pass
108
-
109
106
  class BaseSimpleValue(Resolved):
110
107
 
111
108
  @classmethod
@@ -113,35 +110,28 @@ class BaseSimpleValue(Resolved):
113
110
  value, = t
114
111
  return cls(value)
115
112
 
116
- def cat(self):
117
- raise CatNotSupportedException(self)
118
-
119
113
  def unravel(self):
120
114
  return self.scalar
121
115
 
122
- class SimpleValue(BaseSimpleValue):
123
-
124
- def __init__(self, scalar):
125
- self.scalar = scalar
126
-
127
- class Cat:
128
-
129
- def cat(self):
130
- return self.scalar
131
-
132
- class Blank(Cat, SimpleValue):
116
+ class Blank(BaseSimpleValue):
133
117
 
134
118
  ignorable = True
135
119
  boundary = False
136
120
 
121
+ def __init__(self, textvalue):
122
+ self.textvalue = textvalue
123
+
137
124
  def plus(self, that):
138
- return Text(self.scalar + that.cat())
125
+ return Text(self.textvalue + that.textvalue)
139
126
 
140
- class Boundary(SimpleValue):
127
+ class Boundary(BaseSimpleValue):
141
128
 
142
129
  ignorable = True
143
130
  boundary = True
144
131
 
132
+ def __init__(self, scalar):
133
+ self.scalar = scalar
134
+
145
135
  class BaseScalar(BaseSimpleValue):
146
136
 
147
137
  ignorable = False
@@ -154,12 +144,18 @@ class BaseScalar(BaseSimpleValue):
154
144
  setattr(self, k, v)
155
145
  return self
156
146
 
147
+ def __getattr__(self, name):
148
+ raise self.attrerror(name)
149
+
150
+ def attrerror(self, name):
151
+ return AttributeError(f"{self} does not have: {name}")
152
+
157
153
  class Scalar(BaseScalar):
158
154
 
159
155
  def __init__(self, scalar):
160
156
  self.scalar = scalar
161
157
 
162
- class Text(Cat, BaseScalar):
158
+ class Text(BaseScalar):
163
159
 
164
160
  @classmethod
165
161
  def joinpa(cls, s, l, t):
@@ -176,7 +172,7 @@ class Text(Cat, BaseScalar):
176
172
  @property
177
173
  def binaryvalue(self):
178
174
  if self.textvalue:
179
- raise AttributeError('binaryvalue')
175
+ raise self.attrerror('binaryvalue')
180
176
  return b''
181
177
 
182
178
  def __init__(self, textvalue):
@@ -203,12 +199,12 @@ class Text(Cat, BaseScalar):
203
199
  return Locator(s)
204
200
 
205
201
  def plus(self, that):
206
- return self._of(self.textvalue + that.cat())
202
+ return self._of(self.textvalue + that.textvalue)
207
203
 
208
- class Indeterminate:
204
+ class Indeterminate(BaseSimpleValue): # XXX: Base class needed?
209
205
 
210
- def cat(self):
211
- return ''
206
+ binaryvalue = b''
207
+ textvalue = ''
212
208
 
213
209
  def plus(self, that):
214
210
  return that
@@ -218,11 +214,13 @@ class Indeterminate:
218
214
 
219
215
  Indeterminate = Indeterminate()
220
216
 
217
+ class ResidualHoleException(Exception): pass
218
+
221
219
  class Hole(BaseScalar):
222
220
 
223
221
  @property
224
222
  def scalar(self):
225
- raise CatNotSupportedException
223
+ raise ResidualHoleException
226
224
 
227
225
  def __init__(self, prefix, holevalue):
228
226
  self.prefix = prefix
@@ -341,14 +339,8 @@ class Number(BaseScalar):
341
339
  text = str(self.numbervalue) # TODO: Test this.
342
340
  return Text(text)
343
341
 
344
- def cat(self):
345
- try:
346
- return self.textvalue
347
- except AttributeError:
348
- raise CatNotSupportedException
349
-
350
342
  def plus(self, that):
351
- return Text(self.textvalue + that.cat())
343
+ return Text(self.textvalue + that.textvalue)
352
344
 
353
345
  class Boolean(BaseScalar):
354
346
 
@@ -399,7 +391,7 @@ def List(objs):
399
391
  from .scope import Scope
400
392
  s = Scope(islist = True)
401
393
  for obj in objs:
402
- s.resolvables.put(object(), obj)
394
+ s.resolvables.put(object(), obj) # XXX: Not OpaqueKey?
403
395
  return s
404
396
 
405
397
  class Directive(Resolved):
@@ -472,7 +464,7 @@ class Entry(Struct):
472
464
  return [r for r in self.resolvables if not r.ignorable]
473
465
 
474
466
  def topath(self, scope):
475
- return tuple((None if self.wildcard == r else r.resolve(scope).totext().cat()) for r in self.resolvables if not r.ignorable)
467
+ return tuple((None if self.wildcard == r else r.resolve(scope).totext().textvalue) for r in self.resolvables if not r.ignorable)
476
468
 
477
469
  def subentry(self, i, j):
478
470
  v = list(self.resolvables)
@@ -501,7 +493,7 @@ class Entry(Struct):
501
493
  if not r.ignorable or r.boundary:
502
494
  break
503
495
  indent.append(r) # XXX: Can we simply grab its value?
504
- return Concat.unlesssingleton(indent).resolve(None).cat()
496
+ return Concat.unlesssingleton(indent).resolve(None).textvalue
505
497
 
506
498
  def wrap(value):
507
499
  'Attempt to wrap the given value in a model object of the most specific type.'
@@ -510,7 +502,7 @@ def wrap(value):
510
502
  except UnknownParabjectException:
511
503
  pass
512
504
  else:
513
- return ctrl.scope(True)
505
+ return ctrl.scope
514
506
  for b in map(bool, range(2)):
515
507
  if value is b:
516
508
  return Boolean(value)
@@ -1,7 +1,7 @@
1
1
  from . import directives
2
2
  from .directives import Precedence
3
3
  from .functions import getfunctions, getimpl, OpaqueKey
4
- from .model import CatNotSupportedException, Directive, Function, Hole, Resolvable, Scalar, star, Stream, Text
4
+ from .model import Directive, Function, Hole, Resolvable, Scalar, star, Stream, Text
5
5
  from .resolve import AnchorResolveContext
6
6
  from .search import Query
7
7
  from .stacks import IndentStack, SimpleStack, ThreadLocalResolvable
@@ -160,7 +160,7 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
160
160
  precedence = Precedence.void
161
161
  for i, wordobj in enumerate(entry.words()):
162
162
  try:
163
- word = wordobj.cat()
163
+ word = wordobj.textvalue
164
164
  if not word:
165
165
  continue
166
166
  initialcategory = _categoryornone(word[0])
@@ -172,7 +172,7 @@ class AbstractScope(Resolvable): # TODO LATER: Some methods should probably be m
172
172
  del directives[:]
173
173
  precedence = p
174
174
  directives.append((d, i))
175
- except (AttributeError, CatNotSupportedException, NoSuchPathException):
175
+ except (AttributeError, NoSuchPathException):
176
176
  pass
177
177
  if directives:
178
178
  d, i = directives[0] # XXX: Always use first?
@@ -216,7 +216,7 @@ class StaticScope(AbstractScope):
216
216
  def __init__(self):
217
217
  super(StaticScope, self).__init__(())
218
218
  for word, d in directives.lookup.items():
219
- self[word.cat(),] = Directive(d)
219
+ self[word.textvalue,] = Directive(d)
220
220
  for name, f in getfunctions():
221
221
  self[name,] = Function(f)
222
222
  self['',] = Function(getimpl) # TODO: Refer to the other one.
@@ -253,7 +253,7 @@ class Slash(Text, Function):
253
253
  def slashfunction(scope, *resolvables):
254
254
  path = None
255
255
  for r in reversed(resolvables):
256
- component = r.resolve(scope).cat()
256
+ component = r.resolve(scope).textvalue
257
257
  path = component if path is None else os.path.join(component, path)
258
258
  if os.path.isabs(path):
259
259
  break
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aridity
3
- Version: 83
3
+ Version: 85
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
@@ -247,16 +247,6 @@ Process the given template to stdout using config from stdin.
247
247
 
248
248
  ### aridity.config
249
249
 
250
- <a id="aridity.config.ForeignScopeException"></a>
251
-
252
- #### ForeignScopeException Objects
253
-
254
- ```python
255
- class ForeignScopeException(Exception)
256
- ```
257
-
258
- The operation required a scope at precisely the given path.
259
-
260
250
  <a id="aridity.config.ConfigCtrl"></a>
261
251
 
262
252
  #### ConfigCtrl Objects
@@ -558,7 +548,7 @@ If given 3 args, the first two are variable names for scope key and scope respec
558
548
  ###### join
559
549
 
560
550
  ```python
561
- def join(scope, resolvables, *args)
551
+ def join(scope, partsresolvable, sepresolvable=None)
562
552
  ```
563
553
 
564
554
  Concatenate the given list, using optional separator. Frequently used with `map`.
@@ -780,7 +770,7 @@ Get the regular object associated with `parabject` or raise UnknownParabjectExce
780
770
  #### Parabject Objects
781
771
 
782
772
  ```python
783
- class Parabject(object)
773
+ class Parabject()
784
774
  ```
785
775
 
786
776
  Subclasses typically implement `__getattr__` for dynamic behaviour on attribute access.
@@ -17,7 +17,7 @@ def dereference(parabject):
17
17
  except (KeyError, TypeError):
18
18
  raise UnknownParabjectException
19
19
 
20
- class Parabject(object):
20
+ class Parabject:
21
21
  'Subclasses typically implement `__getattr__` for dynamic behaviour on attribute access.'
22
22
 
23
23
  def __neg__(self):
@@ -49,7 +49,7 @@ class SourceInfo:
49
49
  sourceinfo = SourceInfo('.')
50
50
  setup(
51
51
  name = 'aridity',
52
- version = '83',
52
+ version = '85',
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