josh-gmail-backup 0.104
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.
- data/bin/gmail-backup +6 -0
- data/bin/gmail-backup-gui +6 -0
- data/gmail-backup-gui.pyo +0 -0
- data/gmail-backup-gui.sh +2 -0
- data/gmail-backup.gemspec +36 -0
- data/gmail-backup.pyo +0 -0
- data/gmail-backup.sh +2 -0
- data/gmb.gif +0 -0
- data/gmb.ico +0 -0
- data/gmb.pyo +0 -0
- data/svc/LICENSE.TXT +12 -0
- data/svc/__init__.py +0 -0
- data/svc/egg.py +166 -0
- data/svc/map.py +123 -0
- data/svc/osutils.py +67 -0
- data/svc/registry.py +171 -0
- data/svc/retrans.py +225 -0
- data/svc/scripting/__init__.py +1461 -0
- data/svc/scripting/conversions.py +157 -0
- data/svc/scripting/externals.py +580 -0
- data/svc/scripting/extractors.py +361 -0
- data/svc/scripting/help.py +173 -0
- data/svc/template.py +76 -0
- data/svc/utils.py +261 -0
- metadata +77 -0
@@ -0,0 +1,1461 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2008 Jan Svec and Filip Jurcicek
|
3
|
+
#
|
4
|
+
# YOU USE THIS TOOL ON YOUR OWN RISK!
|
5
|
+
#
|
6
|
+
# email: info@gmail-backup.com
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# Disclaimer of Warranty
|
10
|
+
# ----------------------
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, licensor provides
|
13
|
+
# this tool (and each contributor provides its contributions) on an "AS IS"
|
14
|
+
# BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
15
|
+
# implied, including, without limitation, any warranties or conditions of
|
16
|
+
# TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR
|
17
|
+
# PURPOSE. You are solely responsible for determining the appropriateness of
|
18
|
+
# using this work and assume any risks associated with your exercise of
|
19
|
+
# permissions under this license.
|
20
|
+
|
21
|
+
"""Framework pro parametrizaci skriptů
|
22
|
+
|
23
|
+
Úvod
|
24
|
+
====
|
25
|
+
|
26
|
+
Rychlé prototypování v jazyce Python umožňuje vyvinutí samotného skriptu nebo
|
27
|
+
aplikace během několika hodin. Má-li však skript (aplikace) být široce
|
28
|
+
použitelnou, je třeba zpracovat kvalitní uživatelské rozhraní. Toto může být
|
29
|
+
zpracováno jednak pomocí příkazové řádky, konfiguračních souborů nebo
|
30
|
+
grafického uživatelského rozhraní.
|
31
|
+
|
32
|
+
Každý z těchto přístupů umožňuje (resp. vyžaduje) zadávání parametrů skriptu
|
33
|
+
pomocí řetězců. Zpracování a konverze těchto řetězců je úkolem aplikace.
|
34
|
+
Poněvadž jde o část aplikační logiky, jež se neustále opakuje, je možné ji
|
35
|
+
vyčlenit do samostatného frameworku.
|
36
|
+
|
37
|
+
Požadavky kladené na tento framework jsou především následující:
|
38
|
+
|
39
|
+
- Použití řetězců jako vstupních parametrů.
|
40
|
+
- Možnost specifikace požadavků na parametry (vícenásobný, vyžadovaný,
|
41
|
+
návratový ...).
|
42
|
+
- Práce s více zdroji parametrů zároveň (příkazová řádka, konfigurační
|
43
|
+
soubory, proměnné prostředí, GUI).
|
44
|
+
- Možnost spojit parametry z více zdrojů v jeden parametr.
|
45
|
+
|
46
|
+
Návrh
|
47
|
+
=====
|
48
|
+
|
49
|
+
Reprezentací parametrizovatelného skriptu ve frameworku `svc.scripting` je
|
50
|
+
instance třídy `ParametrizedObject`. Volby předané skriptu jsou funkčnímu
|
51
|
+
objektu předány jako hodnoty funkčních argumentů. To umožňuje používat kódovací
|
52
|
+
styl, kdy je nejprve napsána určitá funkce sloužící jako hlavní funkce skriptu,
|
53
|
+
odladěna pomocí testovacích funkcí a následně "zparametrizována" pomocí
|
54
|
+
frameworku ``svc.scripting``.
|
55
|
+
|
56
|
+
.. image:: ../uml1.png
|
57
|
+
|
58
|
+
Instance třídy ``OptionManager`` zodpovídá za správu všech parametrů (voleb,
|
59
|
+
options) funkčního objektu. Umožňuje získávání jmen voleb, jejich konverzních
|
60
|
+
funkcí a požadavků na tyto volby (specifiers, např. `Multiple` - vícenásobná
|
61
|
+
volba nebo `Required` - požadovaná volba). Obsahuje též metody pro kontrolování
|
62
|
+
(`OptionManager.validate`) a konverzi (`OptionStack.popObjects`) seznamu voleb
|
63
|
+
ve tvaru řetězců do tvaru objektů.
|
64
|
+
|
65
|
+
Při volání parametrizovaného objektu jsou provedeny následující operace
|
66
|
+
zůčastněných objektů:
|
67
|
+
|
68
|
+
.. image:: ../uml2.png
|
69
|
+
|
70
|
+
V současné době je ve frameworku implementována jediná třída s rozhraním
|
71
|
+
``ExternalAdapter`` - třída `Script`. Pro napsání vlastního skriptu je třeba
|
72
|
+
odvodit potomka této třídy a v něm předefinovat následující metody a atributy:
|
73
|
+
|
74
|
+
- ``main`` - hlavní metoda skriptu, která je po provedení konstruktoru
|
75
|
+
převedena na instanci třídy `ParametrizedObject`.
|
76
|
+
- ``options`` - atribut typu ``dict`` specifikující popis jednotlivých voleb
|
77
|
+
skriptu. Více viz třída `OptionManager`.
|
78
|
+
- ``shortOpts``, ``posOpts`` - atributy popisující chování extraktoru
|
79
|
+
`extractors.CmdlineExtractor` sloužícího pro získávání voleb z příkazové řádky.
|
80
|
+
- ``envPrefix`` - atribut sloužící pro vytvoření extraktoru
|
81
|
+
`extractors.EnvironExtractor`.
|
82
|
+
- ``pyFiles``, ``pyFilesGlobals`` - atributy pro konstruktor extraktoru
|
83
|
+
`extractors.PyFileExtractor`.
|
84
|
+
- ``writeReturnValue`` - metoda sloužící pro uložení návratové hodnoty
|
85
|
+
skriptu.
|
86
|
+
|
87
|
+
Pokud odděděná třída nepředefinuje atributy specifikující chování extraktorů,
|
88
|
+
použijí se výchozí hodnoty. Konstruktor třídy ``Script`` vytvoří na základě
|
89
|
+
metody `Script.main` a atributu `Script.options` instanci třídy
|
90
|
+
``ParametrizedObject``, přičemž nastaví adapter této instance (pomocí
|
91
|
+
`ParametrizedObject.setAdapter`) na instanci třídy ``Script``.
|
92
|
+
|
93
|
+
.. image:: ../uml3.png
|
94
|
+
|
95
|
+
Třída ``Script`` používá pro získání hodnota jednotlivých voleb z vnějšího
|
96
|
+
prostředí tzv. extraktory - třídy implementující rozhraní `Extractor`.
|
97
|
+
Extraktory jsou vytvářeny metodou `Script.createExtractors`.
|
98
|
+
|
99
|
+
Rozhraní ``Extractor`` umožňuje nastavení a získání zdroje extraktoru (metody
|
100
|
+
`Extractor.getSource` a `Extractor.setSource`), získání jména zdroje extraktoru
|
101
|
+
(`Extractor.getSourceName`), získání seznamu voleb pomocí extraktoru
|
102
|
+
(`Extractor.extract`). Extraktory rovněž obdrží odkaz na instanci třídy
|
103
|
+
``OptionManager`` (`Extractor.setManager`). V případě, že objekt ``Script``
|
104
|
+
požaduje použití implicitního zdroje, použije ``Extractor.setSource(None)``.
|
105
|
+
|
106
|
+
Jméno zdroje extraktoru slouží pro jejich centralizovanou správu. Tu zajišťují
|
107
|
+
metody `Script.getSources` a `Script.setSources`. Centrální nastavení zdrojů
|
108
|
+
používá asociativní pole, přičemž jeho klíče odpovídají jménům zdrojů
|
109
|
+
extraktorů podle `Extractor.getSourceName` a hodnoty jsou zdroje následně
|
110
|
+
nastavené metodou `Extractor.setSource`.
|
111
|
+
|
112
|
+
Posloupnost operací vykonaných při vytvoření nové instance třídy `Script`:
|
113
|
+
|
114
|
+
.. image:: ../uml4.png
|
115
|
+
|
116
|
+
Při volání metody `Script.run` dojde k nastavení zdrojů pomocí
|
117
|
+
`Script.setSources` a následně k zavolání instance třídy `ParametrizedObject`.
|
118
|
+
Následuje obvyklý scénář spolupráce instance ``ParametrizedObject`` s rozhraním
|
119
|
+
``ExternalAdapter``:
|
120
|
+
|
121
|
+
.. image:: ../uml5.png
|
122
|
+
|
123
|
+
Seznamy voleb ve formě řetězců navrácených metodami `Extractor.extract` jsou
|
124
|
+
zřetězeny do jediného seznamu a předány metodě
|
125
|
+
`OptionStack.popObjects`. Je-li této metodě předán seznam, v němž jsou
|
126
|
+
dvě volby specifikovány v různých zdrojích, pak volba definovaná později
|
127
|
+
přepíše původní hodnotu. Je-li však použit specifikátor `JoinSources`, jsou
|
128
|
+
tyto hodnoty řazeny za sebou do jediného seznamu. Metoda
|
129
|
+
`OptionManager.validate` kontroluje chyby při zadávání voleb, například zadání
|
130
|
+
jednoduché volby vícekrát v jednom zdroji, vynechání povinné volby, zadání
|
131
|
+
neznámé volby apod.
|
132
|
+
|
133
|
+
Příklad
|
134
|
+
=======
|
135
|
+
|
136
|
+
Mějme následující funkci pro kopírování souborů::
|
137
|
+
|
138
|
+
def cp(source, destination, verbose=False):
|
139
|
+
if len(source) == 0:
|
140
|
+
raise ValueError("You must specify source files")
|
141
|
+
if os.path.isdir(destination):
|
142
|
+
for fn in source:
|
143
|
+
fn_file = os.path.basename(fn)
|
144
|
+
if verbose:
|
145
|
+
print fn
|
146
|
+
fr = file(fn)
|
147
|
+
fw = file(os.path.join(destination, fn_file), 'w')
|
148
|
+
try:
|
149
|
+
fw.write(fr.read())
|
150
|
+
finally:
|
151
|
+
fr.close()
|
152
|
+
fw.close()
|
153
|
+
else:
|
154
|
+
if len(source) > 1:
|
155
|
+
raise ValueError("Destination must be directory")
|
156
|
+
if verbose:
|
157
|
+
print fn
|
158
|
+
fr = file(source[0])
|
159
|
+
fw = file(destination, 'w')
|
160
|
+
try:
|
161
|
+
fw.write(fr.read())
|
162
|
+
finally:
|
163
|
+
fr.close()
|
164
|
+
fw.close()
|
165
|
+
|
166
|
+
Nyní tuto funkci použijeme pro vytvoření modulu - skriptu, jež bude obdobou
|
167
|
+
UNIXového příkazu ``cp`` (následující kód uložme do souboru ``cp.py``)::
|
168
|
+
|
169
|
+
#!/usr/bin/env python2.4
|
170
|
+
import os
|
171
|
+
from svc.scripting import *
|
172
|
+
|
173
|
+
def cp(source, destination, verbose=False):
|
174
|
+
... # See above
|
175
|
+
|
176
|
+
class CP(Script):
|
177
|
+
debug = False
|
178
|
+
debugMain = False
|
179
|
+
|
180
|
+
main = staticmethod(cp)
|
181
|
+
options = {
|
182
|
+
'source': (Required, Multiple, String),
|
183
|
+
'destination': (Required, String),
|
184
|
+
'verbose': Flag,
|
185
|
+
}
|
186
|
+
|
187
|
+
shortOpts = {'v': 'verbose'}
|
188
|
+
posOpts = ['source', Ellipsis, 'destination']
|
189
|
+
|
190
|
+
if __name__ == '__main__':
|
191
|
+
script = CP()
|
192
|
+
script.run()
|
193
|
+
|
194
|
+
Vytvořili jsme obálkovou třídu ``CP`` odděděnou od třídy `Script`. Dále jsme
|
195
|
+
vypnuli její ladicí možnosti, tj. při výjimkách se bude vypisovat pouze
|
196
|
+
jednořádkové hlášení o chybě. Jako hlavní metodu ``main`` jsme přiřazením
|
197
|
+
určili funkci ``cp``. Pro použití jako metoda musí být funkce ``cp`` převedena
|
198
|
+
na statickou metodu.
|
199
|
+
|
200
|
+
Dále jsme určili typ jednotlivých voleb. Volba ``source`` reprezentující jména
|
201
|
+
zdrojových souborů je povinná a může být specifikována vícekrát. Její typ je
|
202
|
+
řetězec (``String``). Obdobně jméno cílového souboru, popř. adresáře, musí být
|
203
|
+
určeno a je řetězcového typu. Nakonec volba ``verbose`` zapínající tisk
|
204
|
+
ladicích informací je typu příznak (``Flag``).
|
205
|
+
|
206
|
+
Následují dva atributy určující chování třídy `extractors.CmdlineExtractor`.
|
207
|
+
Prvním je slovník ``shortOpts``, jde o výčet krátkých argumentů příkazového
|
208
|
+
řádku. V našem případě tedy můžeme na příkazovém řádku zapsat ``-v`` namísto
|
209
|
+
``--verbose`` se stejným efektem. Druhý atribut ``posOpts`` je seznam mapující
|
210
|
+
jména pozičních argumentů na jména voleb. Hodnota `Ellipsis` v tomto případě
|
211
|
+
znamená *maximální množství voleb* a může být použita pouze jednou. Volba
|
212
|
+
``source`` tedy z příkazové řádky obdrží poziční argumenty (nikoli krátké nebo
|
213
|
+
dlouhé argumenty) příkazové řádky 1 až (N-1), zatímco volba ``destination``
|
214
|
+
obdrží poslední N-tý argument.
|
215
|
+
|
216
|
+
Poslední tři řádky zajistí vytvoření a spuštění instance třídy ``CP``, pokud je
|
217
|
+
modul spuštěn jako hlavní modul jazyka Python. Uložíme-li zdrojový kód do
|
218
|
+
souboru ``cp.py``, můžeme ho používat následujícím způsobem ($ značí výzvu
|
219
|
+
příkazového řádku)::
|
220
|
+
|
221
|
+
$ ./cp.py
|
222
|
+
Script CP: Option 'destination' is not specified
|
223
|
+
$ ./cp.py xyz.txt
|
224
|
+
Script CP: Option 'source' is not specified
|
225
|
+
$ ./cp.py abc.txt xyz.txt
|
226
|
+
$ ./cp.py -v abc.txt xyz.txt
|
227
|
+
abc.txt
|
228
|
+
$ ./cp.py --verbose abc.txt opq.txt xyz.txt dir
|
229
|
+
abc.txt
|
230
|
+
opq.txt
|
231
|
+
xyz.txt
|
232
|
+
$ ./cp.py dir xyz.txt
|
233
|
+
Script CP: IOError: [Errno 21] Is a directory
|
234
|
+
|
235
|
+
:Variables:
|
236
|
+
- `Required` - specifikátor *povinné* volby
|
237
|
+
- `Multiple` - specifikátor *vícenásobné* volby
|
238
|
+
- `JoinSources` - specifikátor *vícenásobné* volby, přičemž je možné
|
239
|
+
spojovat volby definované ve více zdrojích
|
240
|
+
- `EnvVar` - volba s tímto specifikátorem může být specifikována proměnnou
|
241
|
+
prostředí
|
242
|
+
- `FullParam` - je-li použito jako specifikátor, pak jméno volby je určeno
|
243
|
+
jeho plnou cestou s tečkami '.' nahrazenými podtržítkem '_', jinak je
|
244
|
+
jako jméno volby použit poslední element cesty (tj. jméno za poslední
|
245
|
+
tečkou).
|
246
|
+
- `Prior` - konverze voleb s tímto specifikátorem je provedena před
|
247
|
+
konverzí ostatních
|
248
|
+
"""
|
249
|
+
import sys
|
250
|
+
import os
|
251
|
+
import logging
|
252
|
+
import inspect
|
253
|
+
|
254
|
+
from svc.egg import PythonEgg
|
255
|
+
from svc.utils import sym, issequence, isstr, seqIntoDict
|
256
|
+
from svc.scripting.conversions import *
|
257
|
+
from svc.scripting.help import HelpManager
|
258
|
+
|
259
|
+
__docformat__ = 'restructuredtext cs'
|
260
|
+
|
261
|
+
Required = sym('Required')
|
262
|
+
Multiple = sym('Multiple')
|
263
|
+
JoinSources = sym('JoinSources')
|
264
|
+
FullParam = sym('FullParam')
|
265
|
+
EnvVar = sym('EnvVar')
|
266
|
+
Prior = sym('Prior')
|
267
|
+
|
268
|
+
# TODO: Other specifiers
|
269
|
+
# GlobOption = sym('GlobOption')
|
270
|
+
# FlatList = sym('FlatList')
|
271
|
+
|
272
|
+
OptionAlias = sym('OptionAlias')
|
273
|
+
|
274
|
+
class ParametrizedObject(PythonEgg):
|
275
|
+
"""Třída reprezentující parametrizovaný objekt
|
276
|
+
|
277
|
+
:Ivariables:
|
278
|
+
- `_state` - odkaz na objekt `OptionStack` obsahující stav konverze
|
279
|
+
objektů. Existuje po dobu od zavolání `createState` do zavolání
|
280
|
+
`destroyState`. Po tuto dobu k němu lze přistupovat pomocí metody
|
281
|
+
`getState`.
|
282
|
+
"""
|
283
|
+
def createState(self):
|
284
|
+
raise TypeError("Abstract method ParametrizedObject.createState()")
|
285
|
+
|
286
|
+
def getState(self):
|
287
|
+
return self._state
|
288
|
+
|
289
|
+
def setState(self, state):
|
290
|
+
self._state = state
|
291
|
+
|
292
|
+
def destroyState(self):
|
293
|
+
del self._state
|
294
|
+
|
295
|
+
def premain(self):
|
296
|
+
self.state.enableAll()
|
297
|
+
self.state.disable(self.state.manager.paramsChildren('__premain__'))
|
298
|
+
return False
|
299
|
+
|
300
|
+
def main(self):
|
301
|
+
raise TypeError("Abstract method ParametrizedObject.main()")
|
302
|
+
|
303
|
+
def run(self):
|
304
|
+
"""Zavolá parametrizovaný objekt
|
305
|
+
|
306
|
+
Nejprve jsou pomocí `ExternalAdapter.extractOptions` získány volby ve
|
307
|
+
formě řetězců, následně jsou metodou `OptionStack.popObjects`
|
308
|
+
převedeny na objekty. Následně dojde k rozdělení na *přímé* a
|
309
|
+
*návratové* volby. Poté je zavolána hlavní funkce s přímými volbami a
|
310
|
+
nakonec je její návratová hodnota uložena pomocí
|
311
|
+
`ExternalAdapter.writeReturnValue`.
|
312
|
+
|
313
|
+
Při vzniklých výjimkách jsou zavolány příslušné handlery chyb.
|
314
|
+
"""
|
315
|
+
|
316
|
+
self.createState()
|
317
|
+
self.state.disableAll()
|
318
|
+
self.state.enable(self.manager.paramsChildren('__premain__'))
|
319
|
+
|
320
|
+
retval = True
|
321
|
+
while retval:
|
322
|
+
try:
|
323
|
+
objects = self.state.popObjects()
|
324
|
+
except OptionError:
|
325
|
+
self._validationError(sys.exc_info()[1])
|
326
|
+
except:
|
327
|
+
self._conversionError(sys.exc_info()[1])
|
328
|
+
|
329
|
+
premain_opts = objects.get('__premain__', {})
|
330
|
+
try:
|
331
|
+
retval = self.premain(**premain_opts)
|
332
|
+
except SystemExit:
|
333
|
+
raise
|
334
|
+
except:
|
335
|
+
self._mainError(sys.exc_info()[1])
|
336
|
+
|
337
|
+
|
338
|
+
try:
|
339
|
+
objects = self.state.getObjects()
|
340
|
+
except OptionError:
|
341
|
+
self._validationError(sys.exc_info()[1])
|
342
|
+
except SystemExit:
|
343
|
+
raise
|
344
|
+
except:
|
345
|
+
self._conversionError(sys.exc_info()[1])
|
346
|
+
|
347
|
+
try:
|
348
|
+
retval = self.main(**objects)
|
349
|
+
except:
|
350
|
+
self._mainError(sys.exc_info()[1])
|
351
|
+
|
352
|
+
self.destroyState()
|
353
|
+
|
354
|
+
return retval
|
355
|
+
|
356
|
+
def _conversionError(self, e):
|
357
|
+
raise
|
358
|
+
|
359
|
+
def _validationError(self, e):
|
360
|
+
raise
|
361
|
+
|
362
|
+
def _mainError(self, e):
|
363
|
+
raise
|
364
|
+
|
365
|
+
class OptionError(Exception):
|
366
|
+
def __init__(self, msg, option=''):
|
367
|
+
Exception.__init__(self, msg, option)
|
368
|
+
self.option = option
|
369
|
+
self.msg = msg
|
370
|
+
|
371
|
+
def __str__(self):
|
372
|
+
return self.msg
|
373
|
+
|
374
|
+
class OptionManager(PythonEgg):
|
375
|
+
"""Třída pro správu, kontrolu a konverzi voleb
|
376
|
+
|
377
|
+
Vstupním údajem pro vytvoření instance je tzv. *specifikace voleb*. Jde o
|
378
|
+
slovník, jehož klíče jsou názvy voleb a hodnoty určují jejich konkrétní
|
379
|
+
vlastnosti. Hodnoty jsou tuple obsahující *specifikátory*, *konverzní
|
380
|
+
funkci* a její *dodatečné argumenty*.
|
381
|
+
|
382
|
+
Příklad:
|
383
|
+
========
|
384
|
+
|
385
|
+
::
|
386
|
+
|
387
|
+
specification = {
|
388
|
+
'option1' : Integer,
|
389
|
+
'option2' : (Required, Integer),
|
390
|
+
'option3' : (Required, ListOf, Integer),
|
391
|
+
}
|
392
|
+
|
393
|
+
V uvedeném příkladě je volba ``option1`` číslo. Její konverzní funkce je
|
394
|
+
funkce `Integer`. Pokud volbu určuje pouze konverzní funkce, lze ji uvést
|
395
|
+
jako samostatnou hodnotu a ne jak tuple.
|
396
|
+
|
397
|
+
Volba ``option2`` je opět celé číslo. Podle specifikátoru před konverzní
|
398
|
+
funkci jde však o volby povinnou (`Required`).
|
399
|
+
|
400
|
+
Konečně volba ``option3`` je seznam celých čísel. Konverzní funkce `ListOf`
|
401
|
+
bude volána ve tvaru::
|
402
|
+
|
403
|
+
ListOf(option_value, Integer)
|
404
|
+
|
405
|
+
kde ``option_value`` je hodnota volby typu řetězec. Konverzní funkce
|
406
|
+
`ListOf` rozdělí řetězec podle znaků čárka a na výslednou posloupnost
|
407
|
+
řetězců aplikuje konverzní funkci `Integer`, jež jí byla předána jakou
|
408
|
+
druhý argument.
|
409
|
+
|
410
|
+
Získání jednotlivých položek ze specifikace volby probíhá následovně:
|
411
|
+
|
412
|
+
1. Specifikace volby musí obsahovat alespoň jeden objekt, jež je
|
413
|
+
funkčním objektem - *konverzní funkcí* (viz metoda `conversion`).
|
414
|
+
2. Všechny objekty před konverzní funkcí jsou *specifikátory* (viz
|
415
|
+
`Required`, `Multiple` atd., též metoda `specifiers`).
|
416
|
+
3. Všechny objekty za konverzní funkcí jsou *dodatečné argumenty*
|
417
|
+
konverzní funkce a jsou jí předány za hodnotou konvertovaného
|
418
|
+
řetězce (viz metoda `conversion`).
|
419
|
+
|
420
|
+
:Ivariables:
|
421
|
+
- `_specification` - specifikace voleb
|
422
|
+
- `_rawspec` - undocumented
|
423
|
+
"""
|
424
|
+
|
425
|
+
def __init__(self, specification, docs={}):
|
426
|
+
self._aliases = set()
|
427
|
+
self._optionAliases = {}
|
428
|
+
self.specification = specification
|
429
|
+
self.helpForOptions = docs
|
430
|
+
|
431
|
+
def getSpecification(self):
|
432
|
+
"""Vrátí aktuální specifikaci voleb
|
433
|
+
"""
|
434
|
+
return self._specification
|
435
|
+
|
436
|
+
def setSpecification(self, specification):
|
437
|
+
"""Nastaví novou specifikaci voleb
|
438
|
+
"""
|
439
|
+
self.validateSpecification(specification)
|
440
|
+
self._specification = specification
|
441
|
+
rawspec = self._rawspec = {}
|
442
|
+
param2option = self._paramToOptionMap = {}
|
443
|
+
option2param = self._optionToParamMap = {}
|
444
|
+
|
445
|
+
aliases = set()
|
446
|
+
|
447
|
+
for key, value in specification.iteritems():
|
448
|
+
if value == OptionAlias:
|
449
|
+
aliases.add(key)
|
450
|
+
continue
|
451
|
+
|
452
|
+
if not issequence(value):
|
453
|
+
value = [value]
|
454
|
+
specifiers = []
|
455
|
+
conversion = None
|
456
|
+
args = []
|
457
|
+
for i in value:
|
458
|
+
if callable(i) and not conversion:
|
459
|
+
conversion = i
|
460
|
+
elif not conversion:
|
461
|
+
specifiers.append(i)
|
462
|
+
else:
|
463
|
+
args.append(i)
|
464
|
+
specifiers = frozenset(specifiers)
|
465
|
+
|
466
|
+
if FullParam not in specifiers:
|
467
|
+
# Get option name from parameter name
|
468
|
+
new_key = self._splitOptionName(key)
|
469
|
+
else:
|
470
|
+
# Convert dotted parameter into underscored parameter
|
471
|
+
new_key = key.replace('.', '_')
|
472
|
+
|
473
|
+
if new_key in option2param:
|
474
|
+
raise ValueError("%r and %r both maps to %r" \
|
475
|
+
% (key, option2param[new_key], new_key))
|
476
|
+
option2param[new_key] = key
|
477
|
+
param2option[key] = new_key
|
478
|
+
rawspec[key] = (specifiers, conversion, args)
|
479
|
+
|
480
|
+
self.setAliases(aliases)
|
481
|
+
|
482
|
+
def getAliases(self):
|
483
|
+
return self._aliases
|
484
|
+
|
485
|
+
def setAliases(self, aliases):
|
486
|
+
del self.aliases
|
487
|
+
self._aliases = set(aliases)
|
488
|
+
self._optionAliases = {}
|
489
|
+
for param in self._aliases:
|
490
|
+
option = self._splitOptionName(param)
|
491
|
+
self._paramToOptionMap[param] = option
|
492
|
+
ref_param = self.optionToParam(option)
|
493
|
+
self._rawspec[param] = self._rawspec[ref_param]
|
494
|
+
if option not in self._optionAliases:
|
495
|
+
self._optionAliases[option] = set([ref_param])
|
496
|
+
self._optionAliases[option].add(param)
|
497
|
+
|
498
|
+
def delAliases(self):
|
499
|
+
# FIXME: Remove
|
500
|
+
for param in self._aliases:
|
501
|
+
del self._paramToOptionMap[param]
|
502
|
+
del self._rawspec[param]
|
503
|
+
self._aliases.clear()
|
504
|
+
self._optionAliases.clear()
|
505
|
+
|
506
|
+
def getHelpForOptions(self):
|
507
|
+
return self._helpForOptions
|
508
|
+
|
509
|
+
def setHelpForOptions(self, help):
|
510
|
+
unknown = set(help.keys()) - self.options()
|
511
|
+
if unknown:
|
512
|
+
raise ValueError("Unknown option: %r" % unknown.pop())
|
513
|
+
self._helpForOptions = help
|
514
|
+
|
515
|
+
def options(self):
|
516
|
+
"""Vrátí seznam jmen všech voleb
|
517
|
+
"""
|
518
|
+
return set(self.paramToOption(k) for k in self.params())
|
519
|
+
|
520
|
+
def optionsWithSpecifier(self, specifier):
|
521
|
+
"""Vrátí seznam jmen všech voleb SE specifikátorem `specifier`
|
522
|
+
"""
|
523
|
+
return set(self.paramToOption(k) for k in self.paramsWithSpecifier(specifier))
|
524
|
+
|
525
|
+
def optionsWithoutSpecifier(self, specifier):
|
526
|
+
"""Vrátí seznam jmen všech voleb BEZ specifikátoru `specifier`
|
527
|
+
"""
|
528
|
+
return set(self.paramToOption(k) for k in self.paramsWithoutSpecifier(specifier))
|
529
|
+
|
530
|
+
def params(self):
|
531
|
+
"""Vrátí seznam jmen všech parametrů
|
532
|
+
"""
|
533
|
+
return set(self._rawspec)
|
534
|
+
|
535
|
+
def paramsWithSpecifier(self, specifier):
|
536
|
+
"""Vrátí seznam jmen všech parametrů SE specifikátorem `specifier`
|
537
|
+
"""
|
538
|
+
return set(key for (key, (s, c, a)) in self._rawspec.iteritems()
|
539
|
+
if specifier in s)
|
540
|
+
|
541
|
+
def paramsWithoutSpecifier(self, specifier):
|
542
|
+
"""Vrátí seznam jmen všech parametrů BEZ specifikátoru `specifier`
|
543
|
+
"""
|
544
|
+
return set(key for (key, (s, c, a)) in self._rawspec.iteritems()
|
545
|
+
if specifier not in s)
|
546
|
+
|
547
|
+
def paramsAbove(self, level):
|
548
|
+
"""Vrátí množinu jmen parametrů na úrovni nejvýše `level`
|
549
|
+
"""
|
550
|
+
return set(p for p in self.params() if len(self._splitParam(p)) <= level)
|
551
|
+
|
552
|
+
def paramsBelow(self, level):
|
553
|
+
"""Vrátí množinu jmen parametrů na úrovni pod `level`
|
554
|
+
"""
|
555
|
+
return set(p for p in self.params() if len(self._splitParam(p)) > level)
|
556
|
+
|
557
|
+
def paramsChildren(self, param):
|
558
|
+
"""Vrátí množinu parametrů, jež jsou dětmi parametru `param`
|
559
|
+
"""
|
560
|
+
param_t = self._splitParam(param)
|
561
|
+
m = len(param_t)
|
562
|
+
return set(p for p in self.params() if self._splitParam(p)[:m] == param_t)
|
563
|
+
|
564
|
+
def specifiers(self, paramName):
|
565
|
+
"""Vrátí všechny specifkátory parametru `paramName`
|
566
|
+
"""
|
567
|
+
return self._rawspec[paramName][0]
|
568
|
+
|
569
|
+
def conversion(self, paramName):
|
570
|
+
"""Vrátí dvojici (konverzní_funkce, dodatečné_argumenty) pro parametr `paramName`
|
571
|
+
"""
|
572
|
+
return self._rawspec[paramName][1:]
|
573
|
+
|
574
|
+
def validateSpecification(self, specification):
|
575
|
+
"""Provede kontrolu specifikace (nepoužito)
|
576
|
+
"""
|
577
|
+
|
578
|
+
def paramToOption(self, param):
|
579
|
+
try:
|
580
|
+
return self._paramToOptionMap[param]
|
581
|
+
except KeyError:
|
582
|
+
raise OptionError("Unknown param %r" % param, param)
|
583
|
+
|
584
|
+
def optionToParam(self, option):
|
585
|
+
try:
|
586
|
+
return self._optionToParamMap[option]
|
587
|
+
except KeyError:
|
588
|
+
raise OptionError("Unknown option %r" % option, option)
|
589
|
+
|
590
|
+
def optionToAliases(self, option):
|
591
|
+
aliases = self._optionAliases.get(option, None)
|
592
|
+
if aliases is not None:
|
593
|
+
return aliases
|
594
|
+
else:
|
595
|
+
return set([self.optionToParam(option)])
|
596
|
+
|
597
|
+
def _splitParam(self, param):
|
598
|
+
return param.split('.')
|
599
|
+
|
600
|
+
def _splitOptionName(self, param):
|
601
|
+
return self._splitParam(param)[-1]
|
602
|
+
|
603
|
+
class OptionStack(PythonEgg, list):
|
604
|
+
"""Třída pro uchovávání aktuální stavu OptionManageru
|
605
|
+
"""
|
606
|
+
def __init__(self, manager):
|
607
|
+
self._enabledParams = set()
|
608
|
+
self.setManager(manager)
|
609
|
+
super(OptionStack, self).__init__()
|
610
|
+
|
611
|
+
def getManager(self):
|
612
|
+
return self._manager
|
613
|
+
|
614
|
+
def setManager(self, m):
|
615
|
+
self._manager = m
|
616
|
+
self.clear()
|
617
|
+
|
618
|
+
def clear(self):
|
619
|
+
"""Znovupovolí všechny parametry a vymaže stav
|
620
|
+
"""
|
621
|
+
self.enableAll()
|
622
|
+
del self[:]
|
623
|
+
|
624
|
+
def _checkParams(self, params):
|
625
|
+
"""Provede kontrolu `params` a vrátí ho jako množinu
|
626
|
+
|
627
|
+
Kontroluje neexistující parametry.
|
628
|
+
"""
|
629
|
+
params = set(params)
|
630
|
+
bad_params = params - self.manager.params()
|
631
|
+
if bad_params:
|
632
|
+
bad_param = bad_params.pop()
|
633
|
+
raise OptionError("Unknown parameter in set: %r" % bad_param, bad_param)
|
634
|
+
return params
|
635
|
+
|
636
|
+
def enable(self, params):
|
637
|
+
"""Povolí pouze parametry `params`
|
638
|
+
"""
|
639
|
+
self._enabledParams |= self._checkParams(params)
|
640
|
+
|
641
|
+
def enableAll(self):
|
642
|
+
"""Povolí všechny parametry
|
643
|
+
"""
|
644
|
+
self._enabledParams = self.manager.params()
|
645
|
+
|
646
|
+
def enableExcept(self, params):
|
647
|
+
"""Povolí všechny parametry kromě `params`
|
648
|
+
"""
|
649
|
+
self._enabledParams = self.manager.params() - self._checkParams(params)
|
650
|
+
|
651
|
+
def disable(self, params):
|
652
|
+
"""Zakáže pouze parametry `params`
|
653
|
+
"""
|
654
|
+
self._enabledParams -= self._checkParams(params)
|
655
|
+
|
656
|
+
def disableAll(self):
|
657
|
+
"""Zakáže všechny parametry
|
658
|
+
"""
|
659
|
+
self._enabledParams.clear()
|
660
|
+
|
661
|
+
def disableExcept(self, params):
|
662
|
+
"""Zakáže všechny parametry kromě `params`
|
663
|
+
"""
|
664
|
+
self._enabledParams = self._checkParams(params)
|
665
|
+
|
666
|
+
def getEnabled(self):
|
667
|
+
"""Vrátí množinu všech povolených parametrů
|
668
|
+
"""
|
669
|
+
return set(self._enabledParams)
|
670
|
+
|
671
|
+
def getDisabled(self):
|
672
|
+
"""Vrátí množinu všech zakázaných parametrů
|
673
|
+
"""
|
674
|
+
return self.manager.params() - self._enabledParams
|
675
|
+
|
676
|
+
def popObjects(self):
|
677
|
+
return self.getObjects(_pop=True)
|
678
|
+
|
679
|
+
def getObjects(self, _pop=False):
|
680
|
+
"""Převede zásobník voleb na slovník
|
681
|
+
|
682
|
+
Ze seznamu voleb ve formě řetězců vytvoří slovník, jehož klíče jsou
|
683
|
+
rovny jménům voleb a hodnoty objektům vzniklým po konverzi řetězců.
|
684
|
+
|
685
|
+
Seznam voleb je tvořen uspořádanými čteřicemi ve tvaru::
|
686
|
+
|
687
|
+
(name, value, source_name, description)
|
688
|
+
|
689
|
+
kde ``name`` je jméno volby, ``value`` její řetězcová hodnota,
|
690
|
+
``source_name`` je jméno zdroje, ze kterého pochází a ``description``
|
691
|
+
je libovolný textový popis volby, který se použije při výpisu chyby.
|
692
|
+
|
693
|
+
Je možné, aby ``value`` byl i libovolný objekt, pak se nebude provádět
|
694
|
+
konverzní funkce a použije se rovnou hodnota tohoto objektu.
|
695
|
+
|
696
|
+
Před vlastní konverzí je zavolána metoda `validate` pro kontrolu
|
697
|
+
správnosti seznamu voleb.
|
698
|
+
"""
|
699
|
+
self.validate()
|
700
|
+
objects = _OptionTree(self.manager)
|
701
|
+
not_processed = []
|
702
|
+
yet_processed = set()
|
703
|
+
|
704
|
+
def isntPrior(item):
|
705
|
+
return Prior not in self.manager.specifiers(self.manager.optionToParam(item[0]))
|
706
|
+
|
707
|
+
for item in sorted(self, key = isntPrior):
|
708
|
+
opt_name, opt_val, source, desc = item
|
709
|
+
# Create param name from the short option name
|
710
|
+
par_name = self.manager.optionToParam(opt_name)
|
711
|
+
targets = self.manager.optionToAliases(opt_name)
|
712
|
+
|
713
|
+
if not (targets & self.enabled):
|
714
|
+
# If parameter is disabled, parameter value will be stored for
|
715
|
+
# future processing
|
716
|
+
not_processed.append(item)
|
717
|
+
# Skip disabled parameters
|
718
|
+
continue
|
719
|
+
|
720
|
+
opt_val = self.convertParameter(par_name, opt_val)
|
721
|
+
|
722
|
+
for par_name in targets & self.enabled:
|
723
|
+
objects.storeValue(par_name, opt_val, source)
|
724
|
+
|
725
|
+
# Store parameter into an already processed set
|
726
|
+
yet_processed.add(par_name)
|
727
|
+
|
728
|
+
if _pop:
|
729
|
+
# Store not processed items in OptionStack for future processing
|
730
|
+
self[:] = not_processed
|
731
|
+
# Disable already processed parameters
|
732
|
+
self.disable(yet_processed)
|
733
|
+
|
734
|
+
return objects.nested()
|
735
|
+
|
736
|
+
def convertParameter(self, par_name, str_value):
|
737
|
+
if isstr(str_value):
|
738
|
+
# If opt_val is string, call conversion function
|
739
|
+
conv_func, conv_args = self.manager.conversion(par_name)
|
740
|
+
try:
|
741
|
+
return conv_func(str_value, *conv_args)
|
742
|
+
except:
|
743
|
+
e = sys.exc_info()[1]
|
744
|
+
e.optionName = par_name
|
745
|
+
raise
|
746
|
+
else:
|
747
|
+
return str_value
|
748
|
+
|
749
|
+
def validate(self):
|
750
|
+
"""Zkontroluje zásobník voleb
|
751
|
+
|
752
|
+
Hledá tři typy chyb:
|
753
|
+
|
754
|
+
1. Volby bez specifikace (chybně zapsané volby, překlepy)
|
755
|
+
2. Vícekrát určené jednoduché volby (vícenásobné použití parametru
|
756
|
+
na příkazové řádce apod.)
|
757
|
+
3. Chybějící povinné volby (tj. se specifikátorem `Required`)
|
758
|
+
|
759
|
+
:Raises ValueError:
|
760
|
+
Při porušení libovolného pravidla 1. - 3.
|
761
|
+
"""
|
762
|
+
opt_counts = dict((key, 0) for key in self.manager.params())
|
763
|
+
par_specs = dict((key, self.manager.specifiers(key)) for key in self.manager.params())
|
764
|
+
opt_lastsource = dict((key, None) for key in self.manager.options())
|
765
|
+
for opt_name, par_str_val, source_name, desc in self:
|
766
|
+
# If option has no enabled alias, skip this option
|
767
|
+
aliases = self.manager.optionToAliases(opt_name)
|
768
|
+
if not aliases & self.enabled:
|
769
|
+
continue
|
770
|
+
|
771
|
+
par_name = self.manager.optionToParam(opt_name)
|
772
|
+
|
773
|
+
if opt_lastsource[opt_name] != source_name:
|
774
|
+
opt_counts[opt_name] = 0
|
775
|
+
opt_counts[opt_name] += 1
|
776
|
+
opt_count = opt_counts[opt_name]
|
777
|
+
par_spec = par_specs[par_name]
|
778
|
+
if opt_count > 1 and not (Multiple in par_spec or JoinSources in par_spec):
|
779
|
+
raise OptionError("Option %r specified multiple times (source: %s, %s)" \
|
780
|
+
% (opt_name, source_name, desc), opt_name)
|
781
|
+
|
782
|
+
opt_lastsource[opt_name] = source_name
|
783
|
+
|
784
|
+
for par_name in self.manager.paramsWithSpecifier(Required) & self.enabled:
|
785
|
+
opt_name = self.manager.paramToOption(par_name)
|
786
|
+
if opt_name not in opt_counts or opt_counts[opt_name] == 0:
|
787
|
+
raise OptionError("Option %r is not specified" % opt_name, opt_name)
|
788
|
+
|
789
|
+
return True
|
790
|
+
|
791
|
+
def addObjects(self, dict, source='dict', subsource=None):
|
792
|
+
"""Přidá na zásobník voleb objekty ze slovníku `dict`
|
793
|
+
|
794
|
+
Rozšíří zásobník voleb o prvky slovníku. Jeho klíče jsou jména voleb
|
795
|
+
(options) a hodnoty odpovídají hodnotě volby. Je-li volba vícenásobná
|
796
|
+
(tj. specifikátory Multiple nebo JoinSources), považuje se odpovídající
|
797
|
+
hodnota ve slovníku za sekvenční kontejner jenž je procházen a jeho
|
798
|
+
prvky jsou postupně přidávány na zásobník.
|
799
|
+
|
800
|
+
:Parameters:
|
801
|
+
- `dict` - slovník, jehož hodnoty rozšíří zásobník
|
802
|
+
- `source` - použitá hodnota zdroje, defaultně 'dict'
|
803
|
+
- `subsource` - použitá hodnota podzdroje, defaultně `None`
|
804
|
+
"""
|
805
|
+
multi_options = self.manager.optionsWithSpecifier(Multiple)
|
806
|
+
multi_options |= self.manager.optionsWithSpecifier(JoinSources)
|
807
|
+
|
808
|
+
tmp = []
|
809
|
+
for opt_name, value in dict.iteritems():
|
810
|
+
if opt_name in multi_options:
|
811
|
+
if not issequence(value):
|
812
|
+
value = [value]
|
813
|
+
for subvalue in value:
|
814
|
+
tmp.append( (opt_name, subvalue, source, subsource) )
|
815
|
+
else:
|
816
|
+
tmp.append( (opt_name, value, source, subsource) )
|
817
|
+
self.extend(tmp)
|
818
|
+
|
819
|
+
class _OptionTree(dict):
|
820
|
+
pathsep = '.'
|
821
|
+
|
822
|
+
def __init__(self, manager):
|
823
|
+
super(_OptionTree, self).__init__()
|
824
|
+
self._manager = manager
|
825
|
+
self._optionSources = {}
|
826
|
+
|
827
|
+
def nested(self, path=None):
|
828
|
+
if path is not None:
|
829
|
+
req_path = path.split(self.pathsep)
|
830
|
+
req_path_len = len(req_path)
|
831
|
+
else:
|
832
|
+
req_path = []
|
833
|
+
req_path_len = 0
|
834
|
+
ret = {}
|
835
|
+
|
836
|
+
def getParentDict(path):
|
837
|
+
old_path = []
|
838
|
+
parent = ret
|
839
|
+
while path:
|
840
|
+
parent = parent.setdefault(path[0], {})
|
841
|
+
old_path.append(path[0])
|
842
|
+
if not isinstance(parent, dict):
|
843
|
+
return None
|
844
|
+
path = path[1:]
|
845
|
+
return parent
|
846
|
+
|
847
|
+
for key, value in self.iteritems():
|
848
|
+
path = key.split(self.pathsep)
|
849
|
+
if path[:req_path_len] != req_path:
|
850
|
+
continue
|
851
|
+
else:
|
852
|
+
path = path[req_path_len:]
|
853
|
+
|
854
|
+
where_key = path[-1]
|
855
|
+
path = path[:-1]
|
856
|
+
where = getParentDict(path)
|
857
|
+
if where is None or where_key in where:
|
858
|
+
raise ValueError("Cannot branch tree (node %r)" % key)
|
859
|
+
where[where_key] = value
|
860
|
+
return ret
|
861
|
+
|
862
|
+
def storeValue(self, par_name, value, source):
|
863
|
+
specifiers = self._manager.specifiers(par_name)
|
864
|
+
|
865
|
+
# Watch the changes of sources and delete value from previous
|
866
|
+
# source
|
867
|
+
if self._optionSources.get(par_name, None) != source:
|
868
|
+
if par_name in self \
|
869
|
+
and JoinSources not in specifiers:
|
870
|
+
del self[par_name]
|
871
|
+
self._optionSources[par_name] = source
|
872
|
+
|
873
|
+
if JoinSources in specifiers or Multiple in specifiers:
|
874
|
+
# Value can have multiple values, so append it to the list of
|
875
|
+
# values
|
876
|
+
if par_name not in self:
|
877
|
+
self[par_name] = []
|
878
|
+
lst = self[par_name]
|
879
|
+
lst.append(value)
|
880
|
+
else:
|
881
|
+
# Single value, store it in option tree
|
882
|
+
self[par_name] = value
|
883
|
+
|
884
|
+
class Extractor(PythonEgg):
|
885
|
+
"""Rozhraní extraktoru sloužící třídě `Script`
|
886
|
+
|
887
|
+
Umožňuje třídě `Script` implementující rozhraní `ExternalAdapter` používat
|
888
|
+
více zdrojů voleb. Každý zdroj voleb je reprezentován jednou třídou
|
889
|
+
implementující rozhraní `Extractor`.
|
890
|
+
"""
|
891
|
+
def getSource(self):
|
892
|
+
"""Vrátí používaný zdroj voleb
|
893
|
+
|
894
|
+
Není-li nastaven, vrátí ``None``.
|
895
|
+
|
896
|
+
:See:
|
897
|
+
`Script.getSources`
|
898
|
+
"""
|
899
|
+
|
900
|
+
def setSource(self, source):
|
901
|
+
"""Nastaví používaný zdroj voleb.
|
902
|
+
|
903
|
+
Pro použití implicitního zdroje, použijte ``source = None``.
|
904
|
+
|
905
|
+
:See:
|
906
|
+
`Script.setSources`
|
907
|
+
"""
|
908
|
+
|
909
|
+
def getSourceName(self):
|
910
|
+
"""Vrátí jméno zdroje
|
911
|
+
|
912
|
+
Jménem zdroje je každý `Extractor` reprezentován v rámci objektu
|
913
|
+
`Script`. Umožňuje hromadné nastavování používaných zdrojů.
|
914
|
+
|
915
|
+
:See:
|
916
|
+
`Script.setSources`
|
917
|
+
"""
|
918
|
+
|
919
|
+
def extract(self, state):
|
920
|
+
"""Naplní seznam voleb
|
921
|
+
|
922
|
+
:See:
|
923
|
+
`OptionStack.popObjects`
|
924
|
+
"""
|
925
|
+
|
926
|
+
def setManager(self, manager):
|
927
|
+
"""Umožní extraktoru získat odkaz na instanci `OptionManager`
|
928
|
+
"""
|
929
|
+
|
930
|
+
def getHelpForOptions(self):
|
931
|
+
"""Vrátí slovník s nápovědou pro parametry od tohoto extractoru
|
932
|
+
"""
|
933
|
+
|
934
|
+
def getHelpForExtractor(self):
|
935
|
+
"""Vrátí řetězec - nápovědu tohoto extractoru
|
936
|
+
"""
|
937
|
+
|
938
|
+
|
939
|
+
class SimpleScript(ParametrizedObject):
|
940
|
+
"""Třída reprezentující skript v jazyce Python
|
941
|
+
|
942
|
+
Pro napsání vlastního skriptu je třeba odvodit potomka této třídy a v něm
|
943
|
+
předefinovat minimálně následující atributy a metody: `main` a `options`.
|
944
|
+
|
945
|
+
Chybí-li některý parametr určující chování extraktoru, je nahrazen
|
946
|
+
odpovídající prázdnou hodnotou.
|
947
|
+
|
948
|
+
:Ivariables:
|
949
|
+
- `main` - hlavní metoda skriptu. Více viz `ParametrizedObject`.
|
950
|
+
- `options` - atribut typu ``dict`` specifikující popis jednotlivých
|
951
|
+
voleb skriptu. Více viz třída `OptionManager`.
|
952
|
+
- `shortOpts` -atributy popisující chování extraktoru
|
953
|
+
`extractors.CmdlineExtractor`. Atribut `shortOpts` je slovník, jehož
|
954
|
+
klíče jsou *jednopísmené zkrácené volby*, hodnoty pak jméno
|
955
|
+
odpovídající volby v nezkráceném tvaru.
|
956
|
+
- `posOpts` - atributy popisující chování extraktoru
|
957
|
+
`extractors.CmdlineExtractor`. `posOpts` pak popisuje *poziční*
|
958
|
+
volby předané na příkazové řádce. Jde o seznam jmen jednotlivých
|
959
|
+
voleb.
|
960
|
+
- `envPrefix` - atribut sloužící pro vytvoření extraktoru
|
961
|
+
`extractors.EnvironExtractor`. Seznam určující prefixy, jež lze
|
962
|
+
připojit před jméno volby. Takto se získá jméno, jež se hledá mezi
|
963
|
+
proměnnými prostředí.
|
964
|
+
- `pyFiles` - atributy pro konstruktor extraktoru
|
965
|
+
`extractors.PyFileExtractor`. Seznam jmen konfiguračních souborů. Ve
|
966
|
+
jménech jsou znaky tildy (``~``) nahrazeny domácím adresářem
|
967
|
+
uživatele.
|
968
|
+
- `pyFilesGlobals` - atributy pro konstruktor extraktoru
|
969
|
+
`extractors.PyFileExtractor`. Slovník definující globální prostor
|
970
|
+
jmen pro spouštění skriptů `pyFiles`.
|
971
|
+
- `debug` - je-li ``True``, nebudou použity handlery pro obsluhu chyb a
|
972
|
+
výjimky budou vypisovány interpretrem v nezkráceném tvaru.
|
973
|
+
- `debugMain` - je-li ``True``, nebude použit handler pro obsluhu chyb
|
974
|
+
v hlavní funkci a výjimky budou vypisovány interpretrem v nezkráceném
|
975
|
+
tvaru.
|
976
|
+
- `_extractors` - seznam používaných extraktorů
|
977
|
+
- `_manager` - odkaz na používaný `OptionManager`
|
978
|
+
"""
|
979
|
+
debug = False
|
980
|
+
debugMain = False
|
981
|
+
|
982
|
+
options = {}
|
983
|
+
optionsDoc = {}
|
984
|
+
|
985
|
+
shortOpts = {}
|
986
|
+
posOpts = []
|
987
|
+
envPrefix = None
|
988
|
+
pyFiles = []
|
989
|
+
pyFilesGlobals = None
|
990
|
+
|
991
|
+
def __init__(self, sources={}):
|
992
|
+
super(SimpleScript, self).__init__()
|
993
|
+
|
994
|
+
self.createExtractors(**self.extractorsArgs)
|
995
|
+
self.setSources(sources)
|
996
|
+
|
997
|
+
self.manager = OptionManager(**self.managerArgs)
|
998
|
+
|
999
|
+
def getExtractorsArgs(self):
|
1000
|
+
ex_args = {}
|
1001
|
+
if hasattr(self, 'short_opts'):
|
1002
|
+
raise AttributeError("Please, use shortOpts instead of short_opts")
|
1003
|
+
if hasattr(self, 'pos_opts'):
|
1004
|
+
raise AttributeError("Please, use posOpts instead of pos_opts")
|
1005
|
+
if hasattr(self, 'env_prefix'):
|
1006
|
+
raise AttributeError("Please, use envPrefix instead of env_prefix")
|
1007
|
+
if hasattr(self, 'pyfiles'):
|
1008
|
+
raise AttributeError("Please, use pyFiles instead of pyfiles")
|
1009
|
+
if hasattr(self, 'pyfiles_globals'):
|
1010
|
+
raise AttributeError("Please, use pyFilesGlobals instead of pyfiles_globals")
|
1011
|
+
ex_args['short_opts'] = self.shortOpts.copy()
|
1012
|
+
ex_args['pos_opts'] = self.posOpts[:]
|
1013
|
+
ex_args['env_prefix'] = self.envPrefix
|
1014
|
+
ex_args['pyfiles'] = self.pyFiles
|
1015
|
+
ex_args['pyfiles_globals'] = self.pyFilesGlobals
|
1016
|
+
return ex_args
|
1017
|
+
|
1018
|
+
def getManagerArgs(self):
|
1019
|
+
oargs = {}
|
1020
|
+
oargs['specification'] = self.options.copy()
|
1021
|
+
oargs['docs'] = self.optionsDoc.copy()
|
1022
|
+
return oargs
|
1023
|
+
|
1024
|
+
def _extractionError(self, e):
|
1025
|
+
"""Handler pro výpis chyb při získávání voleb
|
1026
|
+
"""
|
1027
|
+
if self.debug: raise
|
1028
|
+
extractorName = ''
|
1029
|
+
if hasattr(e, 'extractorName'):
|
1030
|
+
extractorName = "%s: " % e.extractorName
|
1031
|
+
print >> sys.stderr, 'Script %s: %s%s' % \
|
1032
|
+
(self.__class__.__name__, extractorName, str(e))
|
1033
|
+
sys.exit(-1)
|
1034
|
+
|
1035
|
+
def _conversionError(self, e):
|
1036
|
+
"""Handler pro výpis chyb při konverzi hodnot voleb na objekty
|
1037
|
+
"""
|
1038
|
+
if self.debug: raise
|
1039
|
+
optionName = ''
|
1040
|
+
if hasattr(e, 'optionName'):
|
1041
|
+
optionName = " '%s'" % e.optionName
|
1042
|
+
print >> sys.stderr, 'Script %s: Bad option%s: %s: %s' % \
|
1043
|
+
(self.__class__.__name__, optionName, e.__class__.__name__, str(e))
|
1044
|
+
sys.exit(-1)
|
1045
|
+
|
1046
|
+
def _mainError(self, e):
|
1047
|
+
"""Handler pro výpis chyb v hlavní funkci skriptu
|
1048
|
+
"""
|
1049
|
+
if self.debugMain: raise
|
1050
|
+
if not isinstance(e, OptionError):
|
1051
|
+
print >> sys.stderr, 'Script %s: %s: %s' % \
|
1052
|
+
(self.__class__.__name__, e.__class__.__name__, str(e))
|
1053
|
+
else:
|
1054
|
+
# TODO: Move into self._extractionError()
|
1055
|
+
print >> sys.stderr, 'Script %s: %s' % \
|
1056
|
+
(self.__class__.__name__, str(e))
|
1057
|
+
sys.exit(-1)
|
1058
|
+
|
1059
|
+
def run(self, sources=None):
|
1060
|
+
"""Vykoná skript, umožňuje nastavit zdroje
|
1061
|
+
|
1062
|
+
Pokud je `sources` různé od ``None``, provede se nejprve uchování
|
1063
|
+
starých zdrojů, následně se nastaví zdroje na `sources`, vykoná se
|
1064
|
+
hlavní funkce `main` a následně se obnoví zdroje na původní hodnotu.
|
1065
|
+
|
1066
|
+
:See:
|
1067
|
+
`getSources`, `setSources`
|
1068
|
+
"""
|
1069
|
+
if sources is not None:
|
1070
|
+
old_sources = self.getSources()
|
1071
|
+
self.setSources(sources)
|
1072
|
+
else:
|
1073
|
+
old_sources = None
|
1074
|
+
|
1075
|
+
retval = super(SimpleScript, self).run()
|
1076
|
+
|
1077
|
+
if old_sources is not None:
|
1078
|
+
self.setSources(old_sources)
|
1079
|
+
|
1080
|
+
return retval
|
1081
|
+
|
1082
|
+
def createState(self):
|
1083
|
+
self.state = OptionStack(self.manager)
|
1084
|
+
self.state.disableAll()
|
1085
|
+
|
1086
|
+
self.extractOptions(self.state)
|
1087
|
+
|
1088
|
+
def getExtractors(self):
|
1089
|
+
"""Vrátí seznam extraktorů
|
1090
|
+
"""
|
1091
|
+
return self._extractors
|
1092
|
+
|
1093
|
+
def createExtractors(self, short_opts, pos_opts, env_prefix, pyfiles, pyfiles_globals):
|
1094
|
+
"""Vytvoří extraktory
|
1095
|
+
|
1096
|
+
Implicitně vytváří následující extraktory (pomocí argumentů v závorce):
|
1097
|
+
|
1098
|
+
1. `extractors.EnvironExtractor` (`env_prefix`) - extraktor pro
|
1099
|
+
získávání voleb z proměnných prostředí.
|
1100
|
+
2. `extractors.PyFileExtractor` (`pyfiles_globals`, `pyfiles`) -
|
1101
|
+
extraktor pro získávání voleb z konfiguračních souborů v jazyce
|
1102
|
+
Python.
|
1103
|
+
3. `extractors.CmdlineExtractor` (`short_opts`, `pos_opts`) -
|
1104
|
+
extraktor pro získávání voleb z argumentů předávaných na příkazové
|
1105
|
+
řádce.
|
1106
|
+
|
1107
|
+
Extraktory jsou uchovány v atributy `_extractors` a při volání metody
|
1108
|
+
`extractOptions` jsou použity v uvedeném pořadí.
|
1109
|
+
"""
|
1110
|
+
from svc.scripting.extractors import CmdlineExtractor, EnvironExtractor, PyFileExtractor
|
1111
|
+
env = self._extractor_env = EnvironExtractor(env_prefix)
|
1112
|
+
pyfiles = self._extractor_pyfiles = PyFileExtractor(pyfiles_globals, pyfiles)
|
1113
|
+
argv = self._extractor_argv = CmdlineExtractor(short_opts, pos_opts)
|
1114
|
+
self._extractors = [env, pyfiles, argv]
|
1115
|
+
|
1116
|
+
def getSources(self):
|
1117
|
+
"""Vrátí nastavené zdroje
|
1118
|
+
|
1119
|
+
Zdroje extraktorů s implicitní hodnotou (tj. `Extractor.getSource`
|
1120
|
+
vrátí ``None``) nejsou ve výsledku zahrnuty.
|
1121
|
+
"""
|
1122
|
+
ret = {}
|
1123
|
+
for e in self.getExtractors():
|
1124
|
+
name = e.getSourceName()
|
1125
|
+
source = e.getSource()
|
1126
|
+
if source is not None:
|
1127
|
+
ret[name] = source
|
1128
|
+
return ret
|
1129
|
+
|
1130
|
+
def setSources(self, sources):
|
1131
|
+
"""Nastaví zdroje používané extraktory
|
1132
|
+
|
1133
|
+
Nejprve jsou zdroje všech extraktorů nastaveny na implicitní hodotu
|
1134
|
+
(tj. ``None``) a poté jsou brány páry ``jméno:hodnota`` slovníku
|
1135
|
+
`sources` a extraktoru jehož `Extractor.getSourceName` vrátí ``jméno``
|
1136
|
+
je nastaven zdroj ``hodnota``.
|
1137
|
+
"""
|
1138
|
+
extractors = {}
|
1139
|
+
for e in self.getExtractors():
|
1140
|
+
name = e.getSourceName()
|
1141
|
+
extractors[name] = e
|
1142
|
+
e.setSource(None)
|
1143
|
+
for name, source in sources.iteritems():
|
1144
|
+
e = extractors[name]
|
1145
|
+
e.setSource(source)
|
1146
|
+
|
1147
|
+
def getManager(self):
|
1148
|
+
"""Vrátí používaní `OptionManager`
|
1149
|
+
|
1150
|
+
:See:
|
1151
|
+
`_manager`
|
1152
|
+
"""
|
1153
|
+
return self._manager
|
1154
|
+
|
1155
|
+
def setManager(self, manager):
|
1156
|
+
"""Nastaví používaný `OptionManager`
|
1157
|
+
|
1158
|
+
Tento `manager` ja rovněž nastaven všem extraktorům.
|
1159
|
+
|
1160
|
+
:See:
|
1161
|
+
`_manager`, `Extractor.setManager`
|
1162
|
+
"""
|
1163
|
+
self._manager = manager
|
1164
|
+
for e in self.getExtractors():
|
1165
|
+
e.setManager(manager)
|
1166
|
+
|
1167
|
+
def extractOptions(self, state):
|
1168
|
+
"""Vrátí seznam voleb ve formě řetězců
|
1169
|
+
|
1170
|
+
Prochází jednotlivé extraktory, volá jejich metody `Extractor.extract`
|
1171
|
+
a vrátí sjednocení jejich návratových hodnot.
|
1172
|
+
|
1173
|
+
:See:
|
1174
|
+
`OptionStack.popObjects`
|
1175
|
+
"""
|
1176
|
+
for e in self.getExtractors():
|
1177
|
+
try:
|
1178
|
+
e.extract(state)
|
1179
|
+
except:
|
1180
|
+
exc = sys.exc_info()[1]
|
1181
|
+
exc.extractorName = e.getSourceName()
|
1182
|
+
raise
|
1183
|
+
|
1184
|
+
def getScriptFile(self):
|
1185
|
+
"""Vrátí soubor, v němž je skript definován
|
1186
|
+
"""
|
1187
|
+
#return inspect.getmodule(self).__file__
|
1188
|
+
return sys.argv[0]
|
1189
|
+
|
1190
|
+
def getHelpForFunc(self):
|
1191
|
+
"""Vrátí řetězec s nápovědou pro asociovanou funkci
|
1192
|
+
"""
|
1193
|
+
return self.main.__doc__
|
1194
|
+
|
1195
|
+
class Script(SimpleScript):
|
1196
|
+
_SCREEN_WIDTH = 80
|
1197
|
+
|
1198
|
+
def __init__(self, sources={}):
|
1199
|
+
super(Script, self).__init__(sources)
|
1200
|
+
self.createLogger()
|
1201
|
+
|
1202
|
+
def getExtractorsArgs(self):
|
1203
|
+
ex_args = super(Script, self).getExtractorsArgs()
|
1204
|
+
ex_args['short_opts'].update({
|
1205
|
+
'v': 'verbose',
|
1206
|
+
'h': 'help',
|
1207
|
+
})
|
1208
|
+
return ex_args
|
1209
|
+
|
1210
|
+
def getManagerArgs(self):
|
1211
|
+
m_args = super(Script, self).getManagerArgs()
|
1212
|
+
m_args['specification'].update({
|
1213
|
+
'__premain__.pyfile': (Multiple, String),
|
1214
|
+
'__premain__.logging.verbose_level': String,
|
1215
|
+
'__premain__.logging.verbose': (Multiple, Bool),
|
1216
|
+
'__premain__.help': Flag,
|
1217
|
+
'__premain__.debug': Flag,
|
1218
|
+
})
|
1219
|
+
m_args['docs'].update({
|
1220
|
+
'pyfile': "Additional configuration file to include",
|
1221
|
+
'verbose_level': "Precise verbose level (DEBUG, INFO, WARNING, ERROR)",
|
1222
|
+
'verbose': "Verbose output",
|
1223
|
+
'help': 'Prints help message',
|
1224
|
+
'debug': 'Turn on printing of tracebacks',
|
1225
|
+
})
|
1226
|
+
return m_args
|
1227
|
+
|
1228
|
+
def premain(self, help=False, pyfile=[], logging={}, debug=False, **kwargs):
|
1229
|
+
# Setup Script logging system
|
1230
|
+
self.setupLogger(**logging)
|
1231
|
+
|
1232
|
+
if help:
|
1233
|
+
self.printHelp()
|
1234
|
+
sys.exit()
|
1235
|
+
|
1236
|
+
if debug:
|
1237
|
+
self.debug = self.debugMain = True
|
1238
|
+
|
1239
|
+
# Add extra PyFiles to the PyFileExtractor instance and extend current
|
1240
|
+
# state with extracted values
|
1241
|
+
orig_source = self._extractor_pyfiles.source
|
1242
|
+
if orig_source is None:
|
1243
|
+
orig_source = []
|
1244
|
+
self._extractor_pyfiles.source = orig_source + pyfile
|
1245
|
+
self._extractor_pyfiles.extract(self.state)
|
1246
|
+
|
1247
|
+
return super(Script, self).premain(**kwargs)
|
1248
|
+
|
1249
|
+
def createLogger(self):
|
1250
|
+
self.logger = logging.getLogger(self.__class__.__name__)
|
1251
|
+
self.logger.addHandler(logging.StreamHandler(sys.stderr))
|
1252
|
+
|
1253
|
+
def setupLogger(self, verbose_level=logging.WARNING, verbose=[]):
|
1254
|
+
try:
|
1255
|
+
verbose_level = int(verbose_level)
|
1256
|
+
except ValueError:
|
1257
|
+
verbose_level = verbose_level.upper()
|
1258
|
+
try:
|
1259
|
+
verbose_level = logging._levelNames[verbose_level]
|
1260
|
+
except KeyError:
|
1261
|
+
raise ValueError("Unknown verbose_level: %r" % verbose_level)
|
1262
|
+
for i in verbose:
|
1263
|
+
if i: verbose_level -= 10
|
1264
|
+
verbose_level = max(verbose_level, 1)
|
1265
|
+
self.logger.setLevel(verbose_level)
|
1266
|
+
|
1267
|
+
def _getMainOptions(self):
|
1268
|
+
script_params = self.manager.params()
|
1269
|
+
script_params -= self.manager.paramsChildren('__premain__')
|
1270
|
+
script_options = [self.manager.paramToOption(p) for p in script_params]
|
1271
|
+
return script_options
|
1272
|
+
|
1273
|
+
def _getPremainOptions(self):
|
1274
|
+
other_params = self.manager.paramsChildren('__premain__')
|
1275
|
+
other_options = [self.manager.paramToOption(p) for p in other_params]
|
1276
|
+
return other_options
|
1277
|
+
|
1278
|
+
def printHelp(self):
|
1279
|
+
m = HelpManager(self.manager, self.extractors, screenWidth=self._SCREEN_WIDTH)
|
1280
|
+
|
1281
|
+
script_file = os.path.basename(self.scriptFile)
|
1282
|
+
|
1283
|
+
m.printUsage(script_file, self.posOpts, self.__class__)
|
1284
|
+
|
1285
|
+
script_options = self._getMainOptions()
|
1286
|
+
m.printHelpDictOptionsHdr(script_options, 'Options', newline=True)
|
1287
|
+
|
1288
|
+
other_options = self._getPremainOptions()
|
1289
|
+
if other_options:
|
1290
|
+
m.printHelpDictOptionsHdr(other_options, 'Other options')
|
1291
|
+
|
1292
|
+
class ExScript(Script):
|
1293
|
+
"""Třída pro výkoné skripty v jazyce Python
|
1294
|
+
|
1295
|
+
Automaticky umožňuje práci s logováním a více příkazy.
|
1296
|
+
|
1297
|
+
:Ivariables:
|
1298
|
+
- `defaultCommand` - výchozí příkaz, pokud nejsou specifikovány jiné
|
1299
|
+
příkazy
|
1300
|
+
"""
|
1301
|
+
CommandParam = (Multiple, sym('_CommandParam'), String)
|
1302
|
+
defaultCommand = None
|
1303
|
+
|
1304
|
+
def __init__(self, *args, **kwargs):
|
1305
|
+
self._cmdPosOpts = {}
|
1306
|
+
super(ExScript, self).__init__(*args, **kwargs)
|
1307
|
+
|
1308
|
+
def createExtractors(self, **kwargs):
|
1309
|
+
from svc.scripting.extractors import CmdPosOptsExtractor
|
1310
|
+
super(ExScript, self).createExtractors(**kwargs)
|
1311
|
+
cmdPos = self._extractor_cmdPos = CmdPosOptsExtractor(self)
|
1312
|
+
self._extractors.append(cmdPos)
|
1313
|
+
|
1314
|
+
def setCmdPosOpts(self, cmdPosOpts):
|
1315
|
+
self._cmdPosOpts = cmdPosOpts.copy()
|
1316
|
+
|
1317
|
+
def getCmdPosOpts(self):
|
1318
|
+
return self._cmdPosOpts
|
1319
|
+
|
1320
|
+
def _modifyPosOpts(self, pos_opts):
|
1321
|
+
has_cpo = False
|
1322
|
+
ret = []
|
1323
|
+
for i in pos_opts:
|
1324
|
+
if isinstance(i, dict):
|
1325
|
+
if has_cpo:
|
1326
|
+
raise ValueError('You can use only one command specific positional option part')
|
1327
|
+
has_cpo = True
|
1328
|
+
self.cmdPosOpts = i
|
1329
|
+
ret.append('_command_pos_opts')
|
1330
|
+
ret.append(Ellipsis)
|
1331
|
+
elif i is Ellipsis and has_cpo:
|
1332
|
+
raise ValueError("You can't use Ellipsis together with command specific positional options")
|
1333
|
+
else:
|
1334
|
+
ret.append(i)
|
1335
|
+
return ret
|
1336
|
+
|
1337
|
+
def getExtractorsArgs(self):
|
1338
|
+
args = super(ExScript, self).getExtractorsArgs()
|
1339
|
+
args['pos_opts'] = self._modifyPosOpts(args['pos_opts'])
|
1340
|
+
return args
|
1341
|
+
|
1342
|
+
def getManagerArgs(self):
|
1343
|
+
m_args = super(ExScript, self).getManagerArgs()
|
1344
|
+
m_args['specification'].update({
|
1345
|
+
'__premain__._command_pos_opts': (Multiple, String),
|
1346
|
+
})
|
1347
|
+
return m_args
|
1348
|
+
|
1349
|
+
def getCommandOption(self):
|
1350
|
+
command_options = self.manager.paramsWithSpecifier('_CommandParam')
|
1351
|
+
if len(command_options) != 1:
|
1352
|
+
raise ValueError("There must be exactly one ExScript.CommandParam option")
|
1353
|
+
return command_options.pop()
|
1354
|
+
|
1355
|
+
def getCommandValue(self):
|
1356
|
+
commandOption = self.commandOption
|
1357
|
+
enabled = self.state.enabled
|
1358
|
+
try:
|
1359
|
+
self.state.disableAll()
|
1360
|
+
self.state.enable([commandOption])
|
1361
|
+
try:
|
1362
|
+
return self.state.getObjects()[commandOption]
|
1363
|
+
except KeyError:
|
1364
|
+
# TODO: Change type of exception
|
1365
|
+
raise ValueError('Command not specified')
|
1366
|
+
finally:
|
1367
|
+
self.state.disableExcept(enabled)
|
1368
|
+
|
1369
|
+
def premain(self, _command_pos_opts=[], **kwargs):
|
1370
|
+
cont = super(ExScript, self).premain(**kwargs)
|
1371
|
+
# Enable all parameters, which will be passed to main() method ...
|
1372
|
+
self.state.enableAll()
|
1373
|
+
# ... and disable parameters, which belongs to any command
|
1374
|
+
for command in self.commands:
|
1375
|
+
params = self.state.manager.paramsChildren(command)
|
1376
|
+
self.state.disable(params)
|
1377
|
+
return cont
|
1378
|
+
|
1379
|
+
def main(self, **kwargs):
|
1380
|
+
commands = []
|
1381
|
+
if self.defaultCommand:
|
1382
|
+
commands.append(self.defaultCommand)
|
1383
|
+
# Get commands and invoke this commands via invokeCommand()
|
1384
|
+
command_parameters = self.manager.paramsWithSpecifier('_CommandParam')
|
1385
|
+
if command_parameters:
|
1386
|
+
new_commands = []
|
1387
|
+
for param in command_parameters:
|
1388
|
+
if param in kwargs:
|
1389
|
+
new_commands.extend(kwargs.pop(param))
|
1390
|
+
if new_commands:
|
1391
|
+
commands[:] = new_commands
|
1392
|
+
if kwargs:
|
1393
|
+
raise TypeError("Not supported main() parameters: %s" % ', '.join(kwargs.keys()))
|
1394
|
+
if commands:
|
1395
|
+
for c in commands:
|
1396
|
+
self.invokeCommand(c)
|
1397
|
+
|
1398
|
+
def _methodForCommand(self, command):
|
1399
|
+
method = getattr(self, command, None)
|
1400
|
+
if not self.isCommand(method):
|
1401
|
+
raise ValueError("Unknown command %r" % command)
|
1402
|
+
return method
|
1403
|
+
|
1404
|
+
def invokeCommand(self, command, **kwargs):
|
1405
|
+
method = self._methodForCommand(command)
|
1406
|
+
params = self.manager.paramsChildren(command)
|
1407
|
+
|
1408
|
+
self.state.enable(params)
|
1409
|
+
self.state.disable('%s.%s' % (command, arg) for arg in kwargs)
|
1410
|
+
params_values = self.state.getObjects()
|
1411
|
+
self.state.disable(params)
|
1412
|
+
|
1413
|
+
method_kwargs = params_values.get(command, {})
|
1414
|
+
method_kwargs.update(kwargs)
|
1415
|
+
|
1416
|
+
return method(**method_kwargs)
|
1417
|
+
|
1418
|
+
@staticmethod
|
1419
|
+
def command(func):
|
1420
|
+
func._ExScript_command = True
|
1421
|
+
return func
|
1422
|
+
|
1423
|
+
@staticmethod
|
1424
|
+
def isCommand(func):
|
1425
|
+
return getattr(func, '_ExScript_command', False)
|
1426
|
+
|
1427
|
+
def getCommands(self):
|
1428
|
+
ret = set()
|
1429
|
+
for class_ in self.__class__.__mro__:
|
1430
|
+
for name, func in vars(class_).iteritems():
|
1431
|
+
if self.isCommand(func):
|
1432
|
+
ret.add(name)
|
1433
|
+
return ret
|
1434
|
+
|
1435
|
+
def _getMainOptions(self):
|
1436
|
+
script_params = self.manager.params()
|
1437
|
+
script_params -= self.manager.paramsChildren('__premain__')
|
1438
|
+
for command in self.commands:
|
1439
|
+
script_params -= self.manager.paramsChildren(command)
|
1440
|
+
script_options = [self.manager.paramToOption(p) for p in script_params]
|
1441
|
+
return script_options
|
1442
|
+
|
1443
|
+
def printHelp(self):
|
1444
|
+
m = HelpManager(self.manager, self.extractors, screenWidth=self._SCREEN_WIDTH)
|
1445
|
+
|
1446
|
+
script_file = os.path.basename(self.scriptFile)
|
1447
|
+
m.printUsage(script_file, self.posOpts, self.__class__)
|
1448
|
+
|
1449
|
+
script_options = self._getMainOptions()
|
1450
|
+
m.printHelpDictOptionsHdr(script_options, 'Options', newline=True)
|
1451
|
+
|
1452
|
+
if self.commands:
|
1453
|
+
m.printHeader('Commands')
|
1454
|
+
for command in sorted(self.commands):
|
1455
|
+
method = self._methodForCommand(command)
|
1456
|
+
m.printHelpForCommand(command, method)
|
1457
|
+
|
1458
|
+
other_options = [o for o in self._getPremainOptions() if not o.startswith('_')]
|
1459
|
+
if other_options:
|
1460
|
+
m.printHelpDictOptionsHdr(other_options, 'Other options')
|
1461
|
+
|