File: __init__.py

package info (click to toggle)
python-augeas 1.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 780 kB
  • sloc: python: 823; makefile: 26; sh: 2
file content (867 lines) | stat: -rw-r--r-- 32,315 bytes parent folder | download
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
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
"""
Augeas is a library for programmatically editing configuration files.
Augeas parses configuration files into a tree structure, which it exposes
through its public API. Changes made through the API are written back to
the initially read files.

The transformation works very hard to preserve comments and formatting
details. It is controlled by *lens* definitions that describe the file
format and the transformation into a tree.
"""

#
# Copyright (C) 2008 Nathaniel McCallum
# Copyright (C) 2008 Jeff Schroeder <jeffschroeder@computer.org>
# Copyright (C) 2009 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
#
# Author: Nathaniel McCallum <nathaniel@natemccallum.com>

from sys import version_info as _pyver

from _augeas import ffi, lib

__author__ = "Nathaniel McCallum <nathaniel@natemccallum.com>"
__credits__ = """Jeff Schroeder <jeffschroeder@computer.org>
Harald Hoyer <harald@redhat.com> - initial python bindings, packaging
Nils Philippsen <nils@redhat.com>
"""
PY3 = _pyver >= (3,)
AUGENC = 'utf8'

if PY3:
    string_types = str
else:
    string_types = basestring


def enc(st):
    if st:
        return st.encode(AUGENC)
    else:
        return b''


def dec(st):
    if st:
        return st.decode(AUGENC)
    else:
        return b''


class AugeasIOError(IOError):
    def __init__(self, ec, fullmessage, msg, minor, details, *args):
        self.message = fullmessage
        super(AugeasIOError, self).__init__(fullmessage, *args)
        self.error = ec
        self.msg = msg
        self.minor = minor
        self.details = details


class AugeasRuntimeError(RuntimeError):
    def __init__(self, ec, fullmessage, msg, minor, details, *args):
        self.message = fullmessage
        super(AugeasRuntimeError, self).__init__(fullmessage, *args)
        self.error = ec
        self.msg = msg
        self.minor = minor
        self.details = details


class AugeasValueError(ValueError):
    def __init__(self, ec, fullmessage, msg, minor, details, *args):
        self.message = fullmessage
        super(AugeasValueError, self).__init__(fullmessage, *args)
        self.error = ec
        self.msg = msg
        self.minor = minor
        self.details = details


class Augeas(object):
    """
    Class wrapper for the Augeas library.
    """
    # Augeas Flags
    NONE = 0
    #: Keep the original file with a :samp:`.augsave` extension
    SAVE_BACKUP = 1 << 0
    #: Save changes into a file with extension :samp:`.augnew`, and do not
    #: overwrite the original file. Takes precedence over :attr:`SAVE_BACKUP`
    SAVE_NEWFILE = 1 << 1
    #: Typecheck lenses; since it can be very expensive it is not done by
    #: default
    TYPE_CHECK = 1 << 2
    #: Do not use the builtin load path for modules
    NO_STDINC = 1 << 3
    #: Make save a no-op process, just record what would have changed
    SAVE_NOOP = 1 << 4
    #: Do not load the tree from :func:`~augeas.Augeas`
    NO_LOAD = 1 << 5
    NO_MODL_AUTOLOAD = 1 << 6
    #: Track the span in the input of nodes
    ENABLE_SPAN = 1 << 7

    # Augeas errors
    AUG_NOERROR = 0
    AUG_ENOMEM = 1
    AUG_EINTERNAL = 2
    AUG_EPATHX = 3
    AUG_ENOMATCH = 4
    AUG_EMMATCH = 5
    AUG_ESYNTAX = 6
    AUG_ENOLENS = 7
    AUG_EMXFM = 8
    AUG_ENOSPAN = 9
    AUG_EMVDESC = 10
    AUG_ECMDRUN = 11
    AUG_EBADARG = 12
    AUG_ELABEL = 13
    AUG_ECPDESC = 14

    def _optffistring(self, cffistr):
        if cffistr == ffi.NULL:
            return None
        else:
            return dec(ffi.string(cffistr))

    def _raise_error(self, errorclass, errmsg, *args):
        ec = lib.aug_error(self.__handle)
        if ec == Augeas.AUG_ENOMEM:
            raise MemoryError()
        msg = self._optffistring(lib.aug_error_message(self.__handle))
        fullmessage = (errmsg + ": " + msg) % args
        minor = self._optffistring(lib.aug_error_minor_message(self.__handle))
        if minor:
            fullmessage += ": " + minor
        details = self._optffistring(lib.aug_error_details(self.__handle))
        if details:
            fullmessage += ": " + details
        raise errorclass(ec, fullmessage, msg, minor, details)

    def __init__(self, root=None, loadpath=None, flags=NONE):
        """
        Initialize the library.

        :param root: the filesystem root. If `root` is :py:obj:`None`, use the
                     value of the environment variable :envvar:`AUGEAS_ROOT`.
                     If that doesn't exist either, use :samp:`/`.
        :type root: str or None

        :param loadpath: a colon-separated list of directories that modules
                         should be searched in. This is in addition to the
                         standard load path and the directories in
                         :envvar:`AUGEAS_LENS_LIB`.
        :type loadpath: str or None

        :param flags: a combination of values of :attr:`SAVE_BACKUP`,
                      :attr:`SAVE_NEWFILE`, :attr:`TYPE_CHECK`,
                      :attr:`NO_STDINC`, :attr:`SAVE_NOOP`, :attr:`NO_LOAD`,
                      :attr:`NO_MODL_AUTOLOAD`, and :attr:`ENABLE_SPAN`.
        :type flags: int or :attr:`NONE`
        """

        # Sanity checks
        if not isinstance(root, string_types) and root is not None:
            raise TypeError("root MUST be a string or None!")
        if not isinstance(loadpath, string_types) and loadpath is not None:
            raise TypeError("loadpath MUST be a string or None!")
        if not isinstance(flags, int):
            raise TypeError("flag MUST be a flag!")

        root = enc(root) if root else ffi.NULL
        loadpath = enc(loadpath) if loadpath else ffi.NULL

        # Create the Augeas object
        self.__handle = ffi.gc(lib.aug_init(root, loadpath, flags),
                               lambda x: self.close())
        if not self.__handle:
            raise RuntimeError("Unable to create Augeas object!")

    def get(self, path):
        """
        Lookup the value associated with `path`.
        It is an error if more than one node matches `path`.

        :returns: the value at the path specified
        :rtype: str
        """

        # Sanity checks
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Create the char * value
        value = ffi.new("char*[]", 1)

        # Call the function and pass value by reference (char **)
        ret = lib.aug_get(self.__handle, enc(path), value)
        if ret < 0:
            self._raise_error(AugeasValueError, "Augeas.get() failed")

        return self._optffistring(value[0])

    def label(self, path):
        """
        Lookup the label associated with `path`.
        It is an error if more than one node matches `path`.

        :returns: the label of the path specified
        :rtype: str
        """

        # Sanity checks
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Create the char * value
        label = ffi.new("char*[]", 1)

        # Call the function and pass value by reference (char **)
        ret = lib.aug_label(self.__handle, enc(path), label)
        if ret < 0:
            self._raise_error(AugeasValueError, "Augeas.label() failed")

        return self._optffistring(label[0])

    def set(self, path, value):
        """
        Set the value associated with `path` to `value`.
        Intermediate entries are created if they don't exist.
        It is an error if more than one node matches `path`.
        """

        # Sanity checks
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not isinstance(value, string_types) and value is not None:
            raise TypeError("value MUST be a string or None!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_set(self.__handle, enc(path), enc(value))
        if ret != 0:
            self._raise_error(AugeasValueError, "Augeas.set() failed")

    def setm(self, base, sub, value):
        """
        Set the value of multiple nodes in one operation.
        Find or create a node matching `sub` by interpreting `sub`
        as a path expression relative to each node matching `base`.
        `sub` may be :py:obj:`None`, in which case all the nodes matching
        `base` will be modified.
        """

        # Sanity checks
        if type(base) != str:
            raise TypeError("base MUST be a string!")
        if type(sub) != str and sub is not None:
            raise TypeError("sub MUST be a string or None!")
        if type(value) != str and value is not None:
            raise TypeError("value MUST be a string or None!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_setm(
            self.__handle, enc(base), enc(sub), enc(value))
        if ret < 0:
            self._raise_error(AugeasValueError, "Augeas.setm() failed")
        return ret

    def text_store(self, lens, node, path):
        """
        Use the value of node `node` as a string and transform it into a tree
        using the lens `lens` and store it in the tree at `path`, which will be
        overwritten. `path` and `node` are path expressions.
        """

        # Sanity checks
        if not isinstance(lens, string_types):
            raise TypeError("lens MUST be a string!")
        if not isinstance(node, string_types):
            raise TypeError("node MUST be a string!")
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_text_store(
            self.__handle, enc(lens), enc(node), enc(path))
        if ret != 0:
            self._raise_error(AugeasValueError, "Augeas.text_store() failed")
        return ret

    def text_retrieve(self, lens, node_in, path, node_out):
        """
        Transform the tree at `path` into a string using lens `lens` and store
        it in the node `node_out`, assuming the tree was initially generated
        using the value of node `node_in`. `path`, `node_in`, and `node_out`
        are path expressions.
        """

        # Sanity checks
        if not isinstance(lens, string_types):
            raise TypeError("lens MUST be a string!")
        if not isinstance(node_in, string_types):
            raise TypeError("node_in MUST be a string!")
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not isinstance(node_out, string_types):
            raise TypeError("node_out MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_text_retrieve(
            self.__handle, enc(lens), enc(node_in), enc(path), enc(node_out))
        if ret != 0:
            self._raise_error(AugeasValueError,
                              "Augeas.text_retrieve() failed")
        return ret

    def defvar(self, name, expr):
        """
        Define a variable `name` whose value is the result of
        evaluating `expr`. If a variable `name` already exists, its
        name will be replaced with the result of evaluating `expr`.

        If `expr` is :py:obj:`None`, the variable `name` will be removed if it
        is defined.

        Path variables can be used in path expressions later on by
        prefixing them with :samp:`$`.
        """

        # Sanity checks
        if type(name) != str:
            raise TypeError("name MUST be a string!")
        if type(expr) != str and expr is not None:
            raise TypeError("expr MUST be a string or None!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_defvar(self.__handle, enc(name), enc(expr))
        if ret < 0:
            self._raise_error(AugeasValueError, "Augeas.defvar() failed")
        return ret

    def defnode(self, name, expr, value):
        """
        Define a variable `name` whose value is the result of
        evaluating `expr`, which must not be :py:obj:`None` and evaluate to a
        nodeset. If a variable `name` already exists, its name will
        be replaced with the result of evaluating `expr`.

        If `expr` evaluates to an empty nodeset, a node is created,
        equivalent to calling ``set(expr, value)`` and `name` will be the
        nodeset containing that single node.
        """

        # Sanity checks
        if type(name) != str:
            raise TypeError("name MUST be a string!")
        if type(expr) != str:
            raise TypeError("expr MUST be a string!")
        if type(value) != str:
            raise TypeError("value MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_defnode(
            self.__handle, enc(name), enc(expr), enc(value), ffi.NULL)
        if ret < 0:
            self._raise_error(AugeasValueError, "Augeas.defnode() failed")
        return ret

    def move(self, src, dst):
        """
        Move the node `src` to `dst`. `src` must match exactly one node
        in the tree. `dst` must either match exactly one node in the
        tree, or may not exist yet. If `dst` exists already, it and all
        its descendants are deleted before moving `src` there. If `dst`
        does not exist yet, it and all its missing ancestors are created.
        """

        # Sanity checks
        if not isinstance(src, string_types):
            raise TypeError("src MUST be a string!")
        if not isinstance(dst, string_types):
            raise TypeError("dst MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_mv(self.__handle, enc(src), enc(dst))
        if ret != 0:
            self._raise_error(AugeasValueError, "Augeas.move() failed")

    def copy(self, src, dst):
        """
        Copy the node `src` to `dst`. `src` must match exactly one node
        in the tree. `dst` must either match exactly one node in the
        tree, or may not exist yet. If `dst` exists already, it and all
        its descendants are deleted before copying `src` there. If `dst`
        does not exist yet, it and all its missing ancestors are created.
        """

        # Sanity checks
        if not isinstance(src, string_types):
            raise TypeError("src MUST be a string!")
        if not isinstance(dst, string_types):
            raise TypeError("dst MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_cp(self.__handle, enc(src), enc(dst))
        if ret != 0:
            self._raise_error(AugeasValueError, "Augeas.copy() failed")

    def rename(self, src, dst):
        """
        Rename the label of all nodes matching `src` to `dst`.
        """

        # Sanity checks
        if not isinstance(src, string_types):
            raise TypeError("src MUST be a string!")
        if not isinstance(dst, string_types):
            raise TypeError("dst MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_rename(self.__handle, enc(src), enc(dst))
        if ret < 0:
            self._raise_error(AugeasValueError, "Augeas.rename() failed")
        return ret

    def insert(self, path, label, before=True):
        """
        Create a new sibling `label` for `path` by inserting into the tree
        just before `path` (if `before` is :py:obj:`True`) or just after `path`
        (if `before` is :py:obj:`False`).

        `path` must match exactly one existing node in the tree, and `label`
        must be a label, i.e. not contain a :samp:`/`, :samp:`*` or end with
        a bracketed index :samp:`[N]`.
        """

        # Sanity checks
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not isinstance(label, string_types):
            raise TypeError("label MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_insert(self.__handle, enc(path),
                             enc(label), before and 1 or 0)
        if ret != 0:
            self._raise_error(AugeasValueError, "Augeas.insert() failed")

    def remove(self, path):
        """
        Remove `path` and all its children. Returns the number of entries
        removed. All nodes that match `path`, and their descendants, are
        removed.
        """

        # Sanity checks
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        return lib.aug_rm(self.__handle, enc(path))

    def match(self, path):
        """
        Return the matches of the path expression `path`. The returned paths
        are sufficiently qualified to make sure that they match exactly one
        node in the current tree.

        Path expressions use a very simple subset of XPath: the path `path`
        consists of a number of segments, separated by :samp:`/`; each segment
        can either be a :samp:`*`, matching any tree node, or a string,
        optionally followed by an index in brackets, matching tree nodes
        labelled with exactly that string. If no index is specified, the
        expression matches all nodes with that label; the index can be a
        positive number N, which matches exactly the *N*-th node with that
        label (counting from 1), or the special expression :samp:`last()` which
        matches the last node with the given label. All matches are done in
        fixed positions in the tree, and nothing matches more than one path
        segment.
        """

        # Sanity checks
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        parray = ffi.new('char***')

        ret = lib.aug_match(self.__handle, enc(path), parray)
        if ret < 0:
            self._raise_error(AugeasRuntimeError,
                              "Augeas.match() failed: %s", path)

        # Loop through the string array
        array = parray[0]
        matches = []
        for i in range(ret):
            if array[i] != ffi.NULL:
                # Create a python string and append it to our matches list
                item = ffi.string(array[i])
                matches.append(dec(item))
                lib.free(array[i])
        lib.free(array)
        return matches

    def span(self, path):
        """
        Get the span according to input file of the node associated with
        `path`. If the node is associated with a file, a tuple of 7 elements is
        returned: ``(filename, label_start, label_end, value_start, value_end,
        span_start, span_end)``. If the node associated with `path` doesn't
        belong to a file or is doesn't exists, :py:obj:`ValueError` is raised.

        :rtype: tuple(str, int, int, int, int, int, int)
        """

        # Sanity checks
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # TODO: Rewrite this

        filename = ffi.new('char **')
        label_start = ffi.new('unsigned int *')
        label_end = ffi.new('unsigned int *')
        value_start = ffi.new('unsigned int *')
        value_end = ffi.new('unsigned int *')
        span_start = ffi.new('unsigned int *')
        span_end = ffi.new('unsigned int *')

        ret = lib.aug_span(self.__handle, enc(path), filename,
                           label_start, label_end,
                           value_start, value_end,
                           span_start, span_end)
        if (ret < 0):
            self._raise_error(AugeasValueError, "Augeas.span() failed")
        fname = self._optffistring(filename[0])
        return (fname, int(label_start[0]), int(label_end[0]),
                int(value_start[0]), int(value_end[0]),
                int(span_start[0]), int(span_end[0]))

    def save(self):
        """
        Write all pending changes to disk. Only files that had any changes
        made to them are written.

        If :attr:`SAVE_NEWFILE` is set in the creation `flags`, create changed
        files as new files with the extension :samp:`.augnew`, and leave the
        original file unmodified.

        Otherwise, if :attr:`SAVE_BACKUP` is set in the creation `flags`, move
        the original file to a new file with extension :samp:`.augsave`.

        If neither of these flags is set, overwrite the original file.
        """

        # Sanity checks
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Call the function
        ret = lib.aug_save(self.__handle)
        if ret != 0:
            self._raise_error(AugeasIOError, "Augeas.save() failed")

    def load(self):
        """
        Load files into the tree. Which files to load and what lenses to use
        on them is specified under :samp:`/augeas/load` in the tree; each entry
        :samp:`/augeas/load/NAME` specifies a 'transform', by having itself
        exactly one child 'lens' and any number of children labelled 'incl' and
        'excl'. The value of :samp:`NAME` has no meaning.

        The 'lens' grandchild of :samp:`/augeas/load` specifies which lens to
        use, and can either be the fully qualified name of a lens
        :samp:`Module.lens` or :samp:`@Module`. The latter form means that the
        lens from the transform marked for autoloading in MODULE should be
        used.

        The 'incl' and 'excl' grandchildren of :samp:`/augeas/load` indicate
        which files to transform. Their value are used as glob patterns. Any
        file that matches at least one 'incl' pattern and no 'excl' pattern is
        transformed. The order of 'incl' and 'excl' entries is irrelevant.

        When AUG_INIT is first called, it populates :samp:`/augeas/load` with
        the transforms marked for autoloading in all the modules it finds.

        Before loading any files, :func:`load` will remove everything
        underneath :samp:`/augeas/files` and :samp:`/files`, regardless of
        whether any entries have been modified or not.
        """

        # Sanity checks
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        ret = lib.aug_load(self.__handle)
        if ret != 0:
            self._raise_error(AugeasRuntimeError, "Augeas.load() failed")

    def load_file(self, filename):
        # Sanity checks
        if not isinstance(filename, string_types):
            raise TypeError("filename MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        ret = lib.aug_load_file(self.__handle, enc(filename))
        if ret != 0:
            self._raise_error(AugeasRuntimeError, "Augeas.load_file() failed")

    def source(self, path):
        # Sanity checks
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Create the char * value
        value = ffi.new("char*[]", 1)

        ret = lib.aug_source(self.__handle, enc(path), value)
        if ret != 0:
            self._raise_error(AugeasRuntimeError, "Augeas.source() failed")

        return self._optffistring(value[0])

    def srun(self, out, command):
        # Sanity checks
        if not hasattr(out, 'write'):
            raise TypeError("out MUST be a file!")
        if not isinstance(command, string_types):
            raise TypeError("path MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        ret = lib.aug_srun(self.__handle, out, enc(command))
        if ret < 0:
            self._raise_error(AugeasRuntimeError,
                              "Augeas.srun() failed (%d)", ret)

    def preview(self, path):
        # Sanity checks
        if not isinstance(path, string_types):
            raise TypeError("path MUST be a string!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        # Create the char * value
        out = ffi.new("char*[]", 1)

        ret = lib.aug_preview(self.__handle, enc(path), out)
        if ret < 0:
            self._raise_error(AugeasRuntimeError, "Augeas.preview() failed")
        return self._optffistring(out[0])

    def ns_attr(self, name, index):
        # Sanity checks
        if not isinstance(name, string_types):
            raise TypeError("name MUST be a string!")
        if not isinstance(index, int):
            raise TypeError("index MUST be an integer!")
        value = ffi.new("char*[]", 1)
        label = ffi.new("char*[]", 1)
        file_path  = ffi.new("char*[]", 1)

        ret = lib.aug_ns_attr(self.__handle, enc(name), index, value, label, file_path)
        if ret < 0:
            self._raise_error(AugeasRuntimeError, "Augeas.ns_attr() failed")

        return (self._optffistring(value[0]), self._optffistring(label[0]), self._optffistring(file_path[0]) )

    def ns_label(self, name, index):
        # Sanity checks
        if not isinstance(name, string_types):
            raise TypeError("name MUST be a string!")
        if not isinstance(index, int):
            raise TypeError("index MUST be an integer!")

        # Create the char * value
        label = ffi.new("char*[]", 1)
        labelindex = ffi.new("int*", 1)

        ret = lib.aug_ns_label(self.__handle, enc(name), index, label, labelindex)

        if ret < 0:
            self._raise_error(AugeasRuntimeError, "Augeas.ns_label() failed")

        return (self._optffistring(label[0]), labelindex[0] )

    def ns_value(self, name, index):
        # Sanity checks
        if not isinstance(name, string_types):
            raise TypeError("name MUST be a string!")
        if not isinstance(index, int):
            raise TypeError("index MUST be an integer!")

        # Create the char * value
        value = ffi.new("char*[]", 1)

        ret = lib.aug_ns_value(self.__handle, enc(name), index, value)
        if ret < 0:
            self._raise_error(AugeasRuntimeError, "Augeas.ns_value() failed")
        return self._optffistring(value[0])

    def ns_count(self, name):
        # Sanity checks
        if not isinstance(name, string_types):
            raise TypeError("name MUST be a string!")
        ret = lib.aug_ns_count(self.__handle, enc(name))
        if ret < 0:
            self._raise_error(AugeasRuntimeError, "Augeas.ns_count() failed")
        return ret


    def ns_path(self, name, index):
        # Sanity checks
        if not isinstance(name, string_types):
            raise TypeError("name MUST be a string!")
        if not isinstance(index, int):
            raise TypeError("index MUST be an integer!")

        # Create the char * value
        path = ffi.new("char*[]", 1)

        ret = lib.aug_ns_path(self.__handle, enc(name), index, path)
        if ret < 0:
            self._raise_error(AugeasRuntimeError, "Augeas.ns_path() failed")
        return self._optffistring(path[0])


    def clear_transforms(self):
        """
        Clear all transforms beneath :samp:`/augeas/load`. If :func:`load` is
        called right after this, there will be no files beneath :samp:`/files`.
        """
        self.remove("/augeas/load/*")

    def add_transform(self, lens, incl, name=None, excl=()):
        """
        Add a transform beneath :samp:`/augeas/load`.

        :param lens: the (file)name of the lens to use
        :type lens: str
        :param incl: one or more glob patterns for the files to transform
        :type incl: str or list(str)
        :param name: deprecated parameter
        :param excl: zero or more glob patterns of files to exclude from
                     transforming
        :type excl: str or list(str)
        """

        if name:
            import warnings
            warnings.warn("name is now deprecated in this function",
                          DeprecationWarning, stacklevel=2)
        if isinstance(incl, string_types):
            incl = [incl]
        if isinstance(excl, string_types):
            excl = [excl]

        for i in range(len(incl)):
            self.transform(lens, incl[i], False)
        for i in range(len(excl)):
            self.transform(lens, excl[i], True)

    def transform(self, lens, file, excl=False):
        """
        Add a transform for `file` using `lens`.

        `excl` specifies if this the file is to be included (:py:obj:`False`)
        or excluded (:py:obj:`True`) from the `lens`.
        The `lens` may be a module name or a full lens name.
        If a module name is given, then lns will be the lens assumed.
        """

        if not isinstance(lens, string_types):
            raise TypeError("lens MUST be a string!")
        if not isinstance(file, string_types):
            raise TypeError("file MUST be a string!")
        if not isinstance(excl, bool):
            raise TypeError("excl MUST be a boolean!")
        if not self.__handle:
            raise RuntimeError("The Augeas object has already been closed!")

        ret = lib.aug_transform(self.__handle, enc(lens), enc(file), excl)
        if ret != 0:
            self._raise_error(AugeasRuntimeError, "Augeas.transform() failed")

    def close(self):
        """
        Close this Augeas instance and free any storage associated with it.
        After this call, this Augeas instance is invalid and can not be used
        for any more operations.
        """

        # If we are already closed, return
        if not self.__handle or self.__handle == ffi.NULL:
            return

        # Call the function
        lib.aug_close(self.__handle)

        # Mark the object as closed
        self.__handle = None


# for backwards compatibility
# pylint: disable-msg=C0103
class augeas(Augeas):
    """
    Compat class, obsolete. Use class Augeas directly.

    :deprecated:
    """

    def __init__(self, *p, **k):
        import warnings
        warnings.warn("use Augeas instead of augeas", DeprecationWarning,
                      stacklevel=2)
        super(augeas, self).__init__(*p, **k)


__all__ = ['Augeas', 'augeas']