File: libdar_test.py

package info (click to toggle)
dar 2.8.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 12,024 kB
  • sloc: cpp: 86,219; sh: 6,978; ansic: 895; makefile: 489; python: 242; csh: 115; perl: 43; sed: 16
file content (482 lines) | stat: -rw-r--r-- 19,052 bytes parent folder | download | duplicates (4)
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
import libdar
import os, sys

# mandatory first action is to initialize libdar
# by calling libdar.get_version()

u = libdar.get_version()
print("using libdar version {}.{}.{}".format(u[0],u[1],u[2]))

# libdar.get_version() can be called at will

# now defining a very minimalist class to let libdar
# interact with the user directly. One could make use
# of graphical popup window here if a Graphical User Interface
# was used:
class myui(libdar.user_interaction):
    def __init__(self):
        libdar.user_interaction.__init__(self)
        # it is mandatory to initialize the parent
        # class: libdar.user_interaction

    def inherited_message(self, msg):
        print("LIBDAR MESSAGE:{0}".format(msg))
        # the "LIBDAR MESSAGE" is pure demonstration
        # to see when output comes through this
        # user_interaction child class

    def inherited_pause(self, msg):
        while True:
            res = input("LIBDAR QUESTION:{0} y/n ".format(msg))
            if res == "y":
                return True
            else:
                if res == "n":
                    return False
                else:
                    print("answer 'y' or 'n'")

    def inherited_get_string(self, msg, echo):
        return input(msg)
        # we should take care about the boolean value echo
        # and not show what user type when echo is False

    def inherited_get_secu_string(self, msg, echo):
        return input(msg)
        # we should take care about the boolean value echo
        # and not show what user type when echo is False

# exceptions from libdar (libdar::Egeneric, Erange, ...) in
# C++ side are all translated to libdar.darexc class introduced
# in the python binding. This class has the __str__() method to
# get the message string about the cause of the exception
# it get displayed naturally when you don't catch them from
# a python shell

# here is an example on how to handle libdar.darexc exceptions:

try:
    x = libdar.deci("not an integer")
except libdar.darexc as obj:
    print("libdar exception: {}".format(obj.__str__()))

# here follows some helper routines as illustration
# on how to manage some libdar data structures

# display libdar.statistics (not all fields are shown, see help(libdar.statistics)
def display_stats(stats):
    print("---- stats result ---")
    print("treated entries  = {}".format(stats.get_treated_str()))
    print("hard link entries = {}".format(stats.get_hard_links_str()))
    print("%skipped entries = {}".format(stats.get_skipped_str()))
    print("inode only entries  = {}".format(stats.get_inode_only_str()))
    print("ignored entries  = {}".format(stats.get_ignored_str()))
    print("too old entries  = {}".format(stats.get_tooold_str()))
    print("errored entries  = {}".format(stats.get_errored_str()))
    print("deleted entries  = {}".format(stats.get_deleted_str()))
    print("EA      entries  = {}".format(stats.get_ea_treated_str()))
    print("FSA     entries  = {}".format(stats.get_fsa_treated_str()))
    print("hard link entries = {}".format(stats.get_hard_links_str()))
    print("wasted byte amount = {}".format(stats.get_byte_amount_str()))
    print("total enries = {}".format(i2str(stats.total())))
    print("---------------------")

# displaying libdar.entree_stats there is a predefined method
# as used here that will rely on a libdar.user_interaction to
# display the contents. You may also access the different fields
# by hand. see help(libdar.entree_stats) for details
def display_entree_stats(stats, ui):
    print("--- archive content stats ---")
    stats.listing(ui)
    print("-----------------------------")

# for this later structure (libdar.entree_stats) you will probably
# want to play with libdar.infinit. I has quite all operation you
# can expect on integer (*,/,+,-,*=,+=,-=,/=,^=, >>=, <<=, %=,<,>,==,!=,...)
# the libdar.deci() class can be buil from a libdar.infinit or
# from a python string and provides two methods: human() and computer()
# that return a python string representing the number in base ten
# and computer() that returns a libdar.infinint

def f0():
    x = libdar.infinint("122")
    #
    dy = libdar.deci("28")
    y = dy.computer()
    # which is equivalent to y = libdar.infinint("28")

    z = x / y # integer division
    print("the integer division of {} by {} gives {}".format(libdar.deci(x).human(),
                                                             dy.human(),
                                                             libdar.deci(z).human()))

    # there is also the libdar.euclide(x, y) method that returns
    # the integer division and rest as a couple of their numerator and divisor
    # passed in argument:

    res = libdar.euclide(x, y)
    print("{} / {} = {} with a remaining of {}".format(libdar.deci(x).human(),
                                                    dy.human(),
                                                    libdar.deci(res[0]).human(),
                                                    libdar.deci(res[1]).human()))

# libdar.infinint to string:
def i2str(infinint):
    return libdar.deci(infinint).human()

# this is a example of routine that given an open libdar.archive
# will provide its listing content. This call is recursive but
# free to you to recurse or not upon user event (expanding a directory
# in the current display for example).
# Note that the method libdar.archive.get_children_in_table returns
# a list of object of type libdar.list_entry which has a long
# list of methods to provide a very much detailed information
# for a given entry in the archive. For more about it,
# see help(libdar.list_entry)
def list_dir(archive, chem = "", indent = ""):
    content = archive.get_children_in_table(chem, True)
    # contents is a list of libdar.list_entry objects

    for ent in content:
        ligne = indent
        if ent.is_eod():
            continue

        if ent.is_hard_linked():
            ligne += "*"
        else:
            ligne += " "

        if ent.is_dir():
            ligne += "d"
        else:
            if ent.is_file():
                ligne += "f"
            else:
                 if ent.is_symlink():
                     ligne += "l"
                 else:
                     if ent.is_char_device():
                         ligne += "c"
                     else:
                         if ent.is_block_device():
                             ligne += "b"
                         else:
                             if ent.is_unix_socket():
                                 ligne += "s"
                             else:
                                 if ent.is_named_pipe():
                                     ligne += "p"
                                 else:
                                     if ent.is_door_inode():
                                         ligne += "D"
                                     else:
                                         if ent.is_removed_entry():
                                             ligne += "Removed entry which was of of type {}".format(ent.get_removed_type())
                                         else:
                                             ligne += "WHAT THIS????"
                                             continue

        ligne += ent.get_perm() + " " + ent.get_name() + " "
        ligne += ent.get_uid(True) + "/" + ent.get_gid(True) + " "
        ligne += ent.get_last_modif()
        print(ligne)

        # now peparing the recursion when we meet a directory:
        if ent.is_dir():
            if chem != "":
                nchem = (libdar.path(chem) + ent.get_name()).display()
            else:
                nchem = ent.get_name()
            nindent = indent + "   "
            list_dir(archive, nchem, nindent)

# in the following we will provide several functions that
# either create, read, test, diff or extract an archive
# all will rely on the following global variables:

ui = myui()
sauv_path = libdar.path(".")
arch_name = "arch1"
ext = "dar"

# let's create an archive. the class
# libdar.archive_options_create has a default constructor
# that set the options to their default values, the clear()
# method can also be used to reset thm to default.
# then a bunch of method are provided to modify each of them
# according to your needs. See help(libdar.archive_options_create)
# and the API reference documentation for their nature and meaning

# the libdar.path can be set from a python string but has some
# method to pop, add a sub-directory easily. the libdar.path.display()
# method provides the representative string of the path
def f1():
    opt = libdar.archive_options_create()
    opt.set_info_details(True)
    opt.set_display_treated(True, False)
    opt.set_display_finished(True)
    fsroot = libdar.path("/etc")
    print("creating the archive")
    libdar.archive(ui,
                   fsroot,
                   sauv_path,
                   arch_name,
                   ext,
                   opt)

# a the difference of C++ here several constructors and method
# like op_diff, op_test, etc. where a libdar::statistics *progresive_report
# field is present, the python binding has two equivalent methods, one
# without this field, and a second with a plain libdar.statistics field.
# this later object can be read from another thread while a libdar operation runs
# with it given as argument. This let the user see the progression of the
# operation (mainly counters on the number of inode
# treated, skipped, errored, etc.). More detail in the API reference guide

# by the way you will see user interaction in action as we
# tend to overwrite the archive created in f1(), assuming you
# run f1(), f2().... in order for the demo

def f2():
    opt = libdar.archive_options_create()
    opt.set_info_details(True)
    opt.set_display_treated(False, False)
    opt.set_display_finished(True)
    fsroot = libdar.path("/etc")
    stats = libdar.statistics()

    print("creating the archive")
    libdar.archive(ui,
                   fsroot,
                   sauv_path,
                   arch_name,
                   ext,
                   opt,
                   stats)
    display_stats(stats)


# here we read an existing archive. Then first
# phase is to create a libdar.archive object
# the second is to act upon it. Several actions
# can be done in sequence on an existing object
# open that way (extracting several time, diff, test,
# an do on
def f3():
    opt = libdar.archive_options_read()
    opt.set_info_details(True)
    arch1 = libdar.archive(ui,
                           sauv_path,
                           arch_name,
                           ext,
                           opt);
    list_dir(arch1)
    stats = arch1.get_stats()
    display_entree_stats(stats, ui)

# below we will play with mask. Most operation have to
# operations to filter the file they will apply on. Then
# first "set_selection()" applies to filenames only
# the second 'set_subtree()" applies the whole path instead
# What type of libdar.mask() you setup for these is
# completely free. Pay attention that when a directory
# is excluded (by mean of set_subtree()) all its content
# and recursively all is subdirectories are skipped.

# the list of class inheriting from libdar.mask() are:
# - bool_mask(bool) either always true or always false
# - libdar.simple_mask(string) the provided string is read as a glob
# expression, which is the syntax most shell use like bash
# - libdar.regex_mask(string) the argument is read as a
# regular expression
# - simple_path_mask(string) matches if the string to
# compare to is a subdir of the string provided to the
# constructor, or if this string is a subdr of the string
# to compare to. This is mostly adapted to select a
# given directory for an operation, as all the path leading
# to it must match and all subdirectory in that directory
# must also match.
# - same_path_mask(string) matches only the given
# argument. This is intended for directory pruning
#
# most mask have in fact a second argument in their
# constructor (a boolean) that define whether the mask
# is case sensitive (True) or not (False)

# - not_mask(mask) gives the negation of the mask
# provided in argument
# - et_mask() + add_mask(mask) + add_mask(mask) +...
# makes a logical AND between the added masks
# - ou_mask() + add_mask(mask) + add_mask(mask) +...
# makes a logical OR between the added masks
# why this French "et" and "ou" words? because at that
# time they were added this code was internal to dar
# and I frequently use French words to designate my
# own datastructure to differentiate with English symbols
# brought from outside. This code has not change since then
# so is the reason.
# of course you can add_mask() a ou_mask(), a not_mask()
# or yet a et_mask() recursively at will and make arbitrarily complex
# masks mixing them with simple_mask(), regular_mask(), and so on.

def f4():
    opt = libdar.archive_options_read()
    arch1 = libdar.archive(ui,
                           sauv_path,
                           arch_name,
                           ext,
                           opt);

    opt = libdar.archive_options_test()

    # defining which file to test bases on filename (set_selection)
    mask_file1 = libdar.simple_mask("*.*", True)
    mask_file2 = libdar.regular_mask(".*\.pub$", True)
    mask_filenames = libdar.ou_mask() # doing the logical OR between what we will add to it:
    mask_filenames.add_mask(mask_file1)
    mask_filenames.add_mask(mask_file2)
    opt.set_selection(mask_filenames)

    # reducing the testing in subdirectories
    tree1 = libdar.simple_path_mask("/etc/ssh", False)
    tree2 = libdar.simple_path_mask("/etc/grub.d", False)
    tree = libdar.et_mask() # doing the loical AND betwen what we will add to it:
    tree.add_mask(libdar.not_mask(tree1))
    tree.add_mask(libdar.not_mask(tree2))
    opt.set_subtree(tree)

    opt.set_info_details(True)
    opt.set_display_skipped(True)

    arch1.op_test(opt)

# nothing much more different as previously
# except that we compare the archive with
# the filesystem (op_diff) while we tested the
# archive coherence previously (op_test)
def f5():
    opt = libdar.archive_options_read()
    arch1 = libdar.archive(ui,
                           sauv_path,
                           arch_name,
                           ext,
                           opt);

    tree1 = libdar.simple_path_mask("/etc/ssh", False)
    tree2 = libdar.simple_path_mask("/etc/grub.d", False)
    tree = libdar.ou_mask()
    tree.add_mask(tree1)
    tree.add_mask(tree2)

    opt = libdar.archive_options_diff()
    opt.set_subtree(tree)
    opt.set_info_details(True)
    opt.set_display_treated(True, False)
    opt.set_ea_mask(libdar.bool_mask(True))
    opt.set_furtive_read_mode(False)

    arch1.op_diff(libdar.path("/etc"),
                  opt)

    rest = libdar.path("./Restore")
    try:
        os.rmdir(rest.display())
    except:
        pass
    os.mkdir(rest.display())

    opt = libdar.archive_options_extract()

    # the overwriting policy can receive
    # objects from many different crit_action_* classes
    # - crit_constant_action() used here does always the same
    # action on Data and EA+FSA when a conflict arise that
    # would lead to overwriting
    # - testing(criterium) the action depends on the evaluation
    # of the provided criterium (see below)
    # - crit_chain() + add(crit_action) performs the different
    # crit_actions added in sequence the first one that provides
    # an action for Data and/or EA+FSA is retained. If no action
    # is left undefined the following crit_action of the chain are
    # not evaluated
    #
    # for the testing crit_action inherited class, we need to provide
    # a criterium object. Here too there is a set of inherited classes
    # that come to help:
    # - crit_in_place_is_inode
    # - crit_in_place_is_dir
    # - crit_in_place_is_file
    # - ...
    # - crit_not (to take the negation of the given criterium)
    # - crit_or + add_crit() + add_crit() ... makes the logical OR
    # - crit_and + add_crit() + add_crit()... for the logical AND
    # - crit_invert for the in_place/to_be_added inversion
    # Read the manual page about overwriting policy for details
    # but in substance the criterum return true of false for each
    # file in conflict and the object if class testing that uses
    # this criterium applies the action given as "go_true" or the
    # action given as "go_false" in regard of the provided result

    over_policy = libdar.crit_constant_action(libdar.over_action_data.data_preserve,
                                           libdar.over_action_ea.EA_preserve)
    opt.set_overwriting_rules(over_policy)

    # fsa_scope is a std::set in C++ side and translates to a
    # python set on python side. Use the add() method to add
    # values to the set:
    fsa_scope = set()
    fsa_scope.add(libdar.fsa_family.fsaf_hfs_plus)
    fsa_scope.add(libdar.fsa_family.fsaf_linux_extX)
    opt.set_fsa_scope(fsa_scope)

    stats = libdar.statistics()
    arch1.op_extract(rest, opt, stats)
    display_stats(stats)


# last, all operation that interact with filesystem use by default
# a libdar.entrepot_local object (provided by the archive_options_*
# object, this makes the archive written and read from local filesystem.
# However you can replace this entrepot by an object of class
# libdar.libcurl_entrepot to read or write an archive over the network
# directly from libdar by mean of FTP of SFTP protocols. Follows an
# illustration of this possibility:
def f6():
    opt = libdar.archive_options_read()
    passwd ="joe@the.shmoe"
    secu_pass = libdar.secu_string(passwd, len(passwd))
    entrepot = libdar.entrepot_libcurl(ui,
                                       libdar.mycurl_protocol.proto_ftp,
                                       "anonymous",
                                       secu_pass,
                                       "ftp.edrusb.org",
                                       "",
                                       False,
                                       "",
                                       "",
                                       "",
                                       5)
    print(entrepot.get_url())
    opt.set_entrepot(entrepot)
    opt.set_info_details(True)

    arch2 = libdar.archive(ui,
                           libdar.path("/dar.linux.free.fr/Python_tutorial"),
                           "example",
                           "dar",
                           opt)

    opt2 = libdar.archive_options_test()
    opt2.set_display_treated(True, False)
    arch2.op_test(opt2)

# other classes of interest:
# - libdar.database for the dar_manager featues
# - libdar.libdar_xform for the dar_xform features
# - libdar.libdar_slave for the dar_slave features

# they are all three accessible from python and follow
# very closely the C++ syntax and usage
# thanks to refer to the API documentation or to the
# C++ tutorial