-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathREADME
550 lines (395 loc) · 16.2 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
******************************************************************
Cogs - Toolkit for developing command-line utilities in Python
******************************************************************
Overview
========
Cogs is a toolkit for developing command-line utilities in Python. It
handles common operations such as parsing command-line parameters,
dispatching commands and loading configuration files. It is targeted to
developers, sysadmins, testers, or anyone who needs to script their
routine tasks.
Cogs is a free software licensed under MIT license. Cogs is written by
Kirill Simonov from Prometheus Research, LLC.
Getting Started
===============
You can install Cogs using `PIP package manager`_::
# pip install Cogs
.. _PIP package manager: http://www.pip-installer.org/
This operation installs a command-line utility ``cogs`` and a Python
package of the same name. The ``cogs`` utility is a dispatcher which
which lets you select and execute your scripts (called *tasks*). Let us
show how to do it with a simple example.
In the current directory, create a file ``cogs.local.py`` with the
following content::
from cogs import task
import os
@task
def Hello(name=None):
"""greet someone (if not specified, the current user)"""
if name is None:
name = os.getlogin()
print "Hello, %s!" % name.capitalize()
Now run::
$ cogs hello world
Hello, World!
$ cogs hello
Hello, Xi!
$ cogs help hello
HELLO - greet someone (if not specified, the current user)
Usage: cogs hello [<name>]
Cogs converts function ``Hello()`` into a command-line script with
parameters inferred from the function signature, so then when you
execute a command ``cogs hello world``, you invoke a function
``Hello('world')``.
Loading Extensions
==================
In this section, we describe how Cogs finds and loads extensions.
Cogs loads extensions from two places:
* ``cogs.local.py`` or ``cogs.local/__init__.py`` module in the current
directory;
* all Python packages under ``cogs.extensions`` entry point.
The easiest way to add an extension is to create a ``cogs.local.py``
module and add your tasks there. If you want to split the module into
multiple files, create a ``cogs.local`` subdirectory with an
``__init__.py`` module. ``cogs.local.py`` or ``cogs.local/__init__.py``
are executed by Cogs on startup. The modules must be owned by the same
user who runs the ``cogs`` script, or by ``root``; otherwise they are
ignored.
If you need to package and distribute your Cogs extensions, using
``cogs.local`` may be inconvenient. In this case, you may package your
Cogs extensions as a regular Python distribution.
Suppose we want to pack the ``hello`` task as a separate package.
Create a directory tree with the following structure::
Cogs-Hello/
src/
cogs/
__init__.py
hello.py
setup.py
The file ``cogs/hello.py`` contains the definition of the ``hello`` task
and has the same content as ``cogs.local.py`` in our previous example.
The file ``cogs/__init__.py`` contains just one line::
__import__('pkg_resources').declare_namespace(__name__)
The file ``setup.py`` contains the meta-data of the package and may
look like this::
from setuptools import setup
setup(
name='Cogs-Hello',
version='0.1',
description="""A Cogs task to greet somebody""",
packages=['cogs'],
namespace_packages=['cogs'],
package_dir={'': 'src'},
install_requires=['Cogs'],
entry_points={ 'cogs.extensions': ['Hello = cogs.hello'] },
)
Note the parameter ``entry_points`` in ``setup()`` invocation; it adds
an entry point ``cogs.extensions`` named ``Hello`` that refers to module
``cogs.hello``. On startup, Cogs finds and loads all packages defined
for the entry point ``cogs.extensions``.
Defining Tasks
==============
A task can be created from a function or a class by augmenting it with
the ``task`` decorator::
from cogs import task, argument
from cogs.log import log, fail
@task
def Factorial(n):
"""calculate n!
This task calculates the value of the factorial of the given
positive number `n`. Factorial of n, also known as n!, is
defined by the formula:
n! = 1*2*...*(n-1)*n
"""
try:
n = int(n)
except ValueError:
raise fail("n must be an integer")
if n < 1:
raise fail("n must be positive")
f = 1
for k in range(2, n+1):
f *= k
log("{}! = `{}`", n, f)
@task
class Fibonacci:
"""calculate the n-th Fibonacci number
The n-th Fibonacci number `F_n` is defined by:
F_0 = 0
F_1 = 1
F_n = F_{n-1}+F_{n-2} (n>1)
"""
n = argument(int)
def __init__(self, n):
if n < 0:
raise ValueError("n must be non-negative")
self.n = n
def __call__(self):
p, q = 0, 1
for k in range(self.n):
p, q = p+q, p
log("F_{} = `{}`", self.n, p)
You can now execute the tasks by running::
$ cogs factorial 10
10! = 3628800
$ cogs fibonacci 10
F_10 = 55
Cogs uses the name of the function or the class as the task identifier.
The name is normalized: it is converted to lower case and has all
underscore characters converted to the dash symbol.
If the task is derived from a function, the task arguments are inferred
from the function signature. Cogs executes such task by calling the
function with the parsed command-line parameters.
If the task is derived from a class, the task arguments and options must
be defined using ``argument()`` and ``option()`` descriptors. To
execute a task, Cogs creates an instance of the class passing the task
parameters as the constructor arguments. Then Cogs invokes the
``__call__`` method of the instance. Thus the call of::
$ cogs factorial 10
is translated to::
Factorial('10')
and the call of::
$ cogs fibonacci 10
is translated to::
t = Fibonacci(10)
t()
The docstring of the function or the class becomes the task
description::
$ cogs help factorial
FACTORIAL - calculate n!
Usage: cogs factorial <n>
This task calculates the value of the factorial of the given
positive number n. Factorial of n, also known as n!, is
defined by the formula:
n! = 1*2*...*(n-1)*n
$ cogs help fibonacci
Usage: cogs fibonacci <n>
The n-th Fibonacci number F_n is defined by:
F_0 = 0
F_1 = 1
F_n = F_{n-1}+F_{n-2} (n>1)
A task derived from a function cannot have options. To add an option to
a task derived from a class, use the ``option()`` descriptor. For
example::
from cogs import task, argument, option
import sys, os
@task
class Write_Hello:
name = argument(default=None)
output = option(key='o', default=None)
def __init__(self, name, output):
if name is None:
name = os.getlogin()
self.name = name
if output is None:
self.file = sys.stdout
else:
self.file = open(output, 'w')
def __call__(self):
self.file.write("Hello, %s!\n" % self.name.capitalize())
You can execute this task with option ``--output`` or ``-o`` to redirect
the output to a file::
$ cogs write-hello world -o hello.txt
Configuration and Environment
=============================
Cogs allows you to define custom configuration parameters. For example::
from cogs import env, task, setting
import os
@setting
def Default_Name(name=None):
"""the name to use for greetings (if not set: login name)"""
if name is None or name == '':
name = os.getlogin()
if not isinstance(name, str):
raise ValueError("a string value is expected")
env.add(default_name=name)
@task
def Hello_With_Configuration(name=None):
if name is None:
name = env.default_name
print "Hello, %s!" % name.capitalize()
Now you could specify the name as a configuration parameter
``default-name``. One way to do it is to use global option
``--default-name``::
$ cogs --default-name=world hello-with-configuration
You could also pass a configuration parameter using an environment
variable::
$ COGS_DEFAULT_NAME=world cogs hello-with-configuration
Alternatively, you can put parameters to a configuration file. In the
current directory, create a file ``cogs.conf`` with the following
content::
default-name: world
Now run::
$ cogs hello-with-configuration
Cogs reads configuration from the following locations:
* ``/etc/cogs.conf``
* ``$PREFIX/etc/cogs.conf``
* ``$HOME/.cogs/cogs.conf``
* ``./cogs.conf``
* program environment
* command-line parameters
If you'd like to specify the usage of a configuration file that is in a
different location than the standard locations listed above, you can use
the global option ``--config`` as follows::
$ cogs --config=alternate-cogs.conf hello-with-configuration
To create a new configuration parameter, wrap a function named after the
parameter with the ``@setting`` decorator. The function must accept
zero or one argument: the function is called without arguments if the
parameter is not specified explicitly, and is called with the value of
the parameter is it was set using one of the methods described above.
Cogs does not impose any rules on what to do with the parameter value,
but we recommend to store the value in the global ``env`` variable. The
call of ``env.add(default_name=name)`` adds a new parameter
``default_name`` which could then be accessed as ``env.default_name``.
API Reference
=============
``cogs.core``
-------------
Classes and functions defined in ``cogs.core`` are also importable from
the ``cogs`` package.
``@task``
The ``@task`` decorator converts the wrapped function or class into
a task. Task properties are inferred from the wrapped object as
follows:
*name*
Generated from the function or the class name. The name is
converted to lower case and all underscores are replaced with
dashes.
*documentation*
Generated from the docstring. The first line of the docstring
produces a one-line *hint* string, the rest of the docstring
produces a multi-line *help* string.
*arguments*
When the task is inferred from a function, the arguments are
generated from the function signature. Each function parameter
becomes a task argument, those which have default values are
optional.
If the task is inferred from a class, the arguments must be
specified using the ``argument()`` descriptor.
*options*
A task inferred from a function has no options. A task inferred
from a class may have options specified using the ``option()``
descriptor.
When a task is executed, the wrapped object is invoked according
to the following rules:
* If the task is inferred from a function, parsed command-line
parameters are passed as the function arguments.
* If the task is inferred from a class, command-line parameters
are passed to the class constructor, then the ``__call__``
method is called on the instance.
``@setting``
The ``@setting`` decorator converts the wrapped function to a
configuration parameter, which properties are inferred from the
function attributes.
The setting name is generated from the function name. The name is
converted to lower case and has all underscores replaced with
dashes.
The setting documentation is generated from the function docstring.
The function must be able to accept zero and one parameter. The
function is called at startup with no parameters if the setting is
not explicitly set by the user; otherwise it is called with the
value of the setting. The function is responsible for storing the
value in the ``env`` object.
``argument(check, default, plural=False)``
Describes a task argument.
``check``
A function which is called to check and/or transform the
argument value. The function must return the transformed value
or raise ``ValueError`` exception on error.
``default``
The default value to be used if the argument is optional and not
specified. If this parameter is not set, the argument is
mandatory.
``plural``
If set, the argument consumes all the remaining command-line
parameters. Must be the last argument specified.
``option(key, check, default, plural=False, value_name=None, hint=None)``
Describes a task option.
``key``
A one-character shorthand.
``check``
A function called to check and transform the value of the
option. The function must return the transformed value or raise
``ValueError`` exception on error.
``default``
The default value used when the option is not specified. If
this parameter is not set, the option does not accept a value.
Such an option is treated is a toggle and takes a value ``True``
if set and ``False`` if not set.
``plural``
If set, indicates that the option could be specified more than
once.
``value_name``
The preferred name for the option value; used for the task
description.
``hint``
A one-line description of the option; used for the task
description.
``env``
A global object that keeps values of configuration parameters and
other properties.
``env.add(**keywords)``
Add new parameters.
``env.set(**keywords)``
Set values for existing parameters.
``env.push(**keywords)``
Save the current state and set new values for existing
parameters.
``env.pop()``
Restore a previously saved state of parameters and values.
``env(**keywords)``
A context manager for ``with`` statement. On entering, saves
the current state and sets new parameter values. On exiting,
restores the saved state.
``cogs.log``
------------
Printing utilities.
``log(msg="", *args, **kwds)``
Print the message to the standard output.
If extra positional or keyword arguments are given, they are
formatted with ``format()`` function using ``msg`` as the template.
``log()`` supports output coloring: a substring of the form::
`...`
or::
:fmt:`...`
is colorized when displayed on a color terminal. The supported
formats are: *default* (white), ``debug`` (dark grey),
``warning`` (red), ``success`` (green).
``debug(msg, *args, **kwds)``
Print the message when the ``env.debug`` parameter is set. We
recommend to accompany any permanent change to the filesystem or
other system state with a respective ``debug()`` call.
Add command-line parameter ``--debug`` or set environment variable
``COGS_DEBUG=1`` to see debug output.
``warn(msg, *args, **kwds)``
Display a warning. ``warn()`` should be used for reporting error
conditions which do not prevent the script from continuing the job.
``fail(msg, *args, **kwds)``
Display an error message and return an exception object. It should
be used in the following manner::
raise fail("no more beer in the refrigerator")
``cogs.fs``
-----------
File and system utilities.
``cp(src, dst)``
Copy a file or a directory tree.
``mv(src, dst)``
Move a file or a directory tree.
``rm(path)``
Remove a file.
``mktree(path)``
Create all directories in the path.
``rmtree(path)``
Remove a directory tree.
``exe(cmd, cd=None, environ=None)``
Replace the current process with the given shell command.
If ``cd`` is given, changes the directory to the specified
path before executing the command.
If ``environ`` is given, adds the given parameters to the
environment before executing the command.
``sh(cmd, data=None, cd=None, environ=None)``
Execute a shell command with the given input and working directory.
``pipe(cmd, data=None, cd=None, environ=None)``
Execute a shell command with the given input and working directory;
return the command output.
.. vim: set spell spelllang=en textwidth=72: