File: utils.rst

package info (click to toggle)
buildbot 4.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 21,080 kB
  • sloc: python: 174,183; sh: 1,204; makefile: 332; javascript: 119; xml: 16
file content (1602 lines) | stat: -rw-r--r-- 67,095 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
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
Utilities
=========

.. py:module:: buildbot.util

Several small utilities are available at the top-level :mod:`buildbot.util` package.

.. py:function:: naturalSort(list)

    :param list: list of strings
    :returns: sorted strings

    This function sorts strings "naturally", with embedded numbers sorted numerically.
    This ordering is good for objects which might have a numeric suffix, e.g., ``winworker1``, ``winworker2``

.. py:function:: formatInterval(interval)

    :param interval: duration in seconds
    :returns: human-readable (English) equivalent

    This function will return a human-readable string describing a length of time, given a number of seconds.

.. py:class:: ComparableMixin

    This mixin class adds comparability to a subclass.
    Use it like this:

    .. code-block:: python

        class Widget(FactoryProduct, ComparableMixin):
            compare_attrs = ( 'radius', 'thickness' )
            # ...

    Any attributes not in ``compare_attrs`` will not be considered when comparing objects. This is
    used to implement Buildbot's reconfig logic, where a comparison between the new and existing
    objects is used to determine whether the new object should replace the existing object. If the
    comparison shows the objects to be equivalent, then the old object is left in place. If they
    differ, the old object is removed from the buildmaster, and the new object is added.

    For use in configuration objects (schedulers, changesources, etc.), include any attributes
    which are set in the constructor based on the user's configuration. Be sure to also include the
    superclass's list, e.g.:

    .. code-block:: python

        class MyScheduler(base.BaseScheduler):
            compare_attrs = base.BaseScheduler.compare_attrs + ('arg1', 'arg2')


    A point to note is that the compare_attrs list is cumulative; that is, when a subclass also has
    a compare_attrs and the parent class has a compare_attrs, the subclass' compare_attrs also
    includes the parent class' compare_attrs.

    This class also implements the :py:class:`buildbot.interfaces.IConfigured` interface.
    The configuration is automatically generated, being the dict of all ``compare_attrs``.

.. py:function:: safeTranslate(str)

    :param str: input string
    :returns: safe version of the input

    This function will filter out some inappropriate characters for filenames; it is suitable for
    adapting strings from the configuration for use as filenames. It is not suitable for use with
    strings from untrusted sources.

.. py:function:: epoch2datetime(epoch)

    :param epoch: an epoch time (integer)
    :returns: equivalent datetime object

    Convert a UNIX epoch timestamp to a Python datetime object, in the UTC timezone.
    Note that timestamps specify UTC time (modulo leap seconds and a few other minor details).
    If the argument is None, returns None.

.. py:function:: datetime2epoch(datetime)

    :param datetime: a datetime object
    :returns: equivalent epoch time (integer)

    Convert an arbitrary Python datetime object into a UNIX epoch timestamp.
    If the argument is None, returns None.

.. py:data:: UTC

    A ``datetime.tzinfo`` subclass representing UTC time. A similar class has finally been added to
    Python in version 3.2, but the implementation is simple enough to include here. This is mostly
    used in tests to create timezone-aware datetime objects in UTC:

    .. code-block:: python

        dt = datetime.datetime(1978, 6, 15, 12, 31, 15, tzinfo=UTC)

.. py:function:: diffSets(old, new)

    :param old: old set
    :type old: set or iterable
    :param new: new set
    :type new: set or iterable
    :returns: a (removed, added) tuple

    This function compares two sets of objects, returning elements that were added and elements that were removed.
    This is largely a convenience function for reconfiguring services.

.. py:function:: makeList(input)

    :param input: a thing
    :returns: a list of zero or more things

    This function is intended to support the many places in Buildbot where the user can specify
    either a string or a list of strings, but the implementation wishes to always consider lists.
    It converts any string to a single-element list, ``None`` to an empty list, and any iterable to
    a list. Input lists are copied, avoiding aliasing issues.

.. py:function:: now()

    :returns: epoch time (integer)

    Return the current time, using either ``reactor.seconds`` or ``time.time()``.

.. py:function:: flatten(list, [types])

    :param list: potentially nested list
    :param types: An optional iterable of the types to flatten.
        By default, if unspecified, this flattens both lists and tuples
    :returns: flat list

    Flatten nested lists into a list containing no other lists. For example:

    .. code-block:: python

        >>> flatten([ [  1, 2 ], 3, [ [ 4 ], 5 ] ])
        [ 1, 2, 3, 4, 5 ]

    Both lists and tuples are looked at by default.

.. py:function:: flattened_iterator(list, [types])

    :param list: potentially nested list
    :param types: An optional iterable of the types to flatten.
        By default, if unspecified, this flattens both lists and tuples.
    :returns: iterator over every element whose type isn't in types

    Returns a generator that doesn't yield any lists/tuples.  For example:

    .. code-block:: none

        >>> for x in flattened_iterator([ [  1, 2 ], 3, [ [ 4 ] ] ]):
        >>>     print x
        1
        2
        3
        4

     Use this for extremely large lists to keep memory-usage down and improve performance when
     you only need to iterate once.

.. py:function:: none_or_str(obj)

    :param obj: input value
    :returns: string or ``None``

    If ``obj`` is not None, return its string representation.

.. py:function:: bytes2unicode(bytestr, encoding='utf-8', errors='strict')

    :param bytestr: bytes
    :param encoding: unicode encoding to pass to :py:func:`str.encode`, default ``utf-8``.
    :param errors: error handler to pass to :py:func:`str.encode`, default ``strict``.
    :returns: string as unicode

    This function is intended to convert bytes to unicode for user convenience.
    If given a bytestring, it returns the string decoded using ``encoding``.
    If given a unicode string, it returns it directly.

.. py:function:: string2boolean(str)

    :param str: string
    :raises KeyError:
    :returns: boolean

    This function converts a string to a boolean.
    It is intended to be liberal in what it accepts: case-insensitive "true", "on", "yes", "1", etc.
    It raises :py:exc:`KeyError` if the value is not recognized.

.. py:function:: toJson(obj)

    :param obj: object
    :returns: UNIX epoch timestamp

    This function is a helper for json.dump, that allows to convert non-json able objects to json.
    For now it supports converting datetime.datetime objects to unix timestamp.

.. py:data:: NotABranch

    This is a sentinel value used to indicate that no branch is specified.
    It is necessary since schedulers and change sources consider ``None`` a valid name for a branch.
    This is generally used as a default value in a method signature, and then tested against with ``is``:

    .. code-block:: python

        if branch is NotABranch:
            pass # ...

.. py:function:: in_reactor(fn)

    This decorator will cause the wrapped function to be run in the Twisted reactor, with the
    reactor stopped when the function completes. It returns the result of the wrapped function. If
    the wrapped function fails, its traceback will be printed, the reactor halted, and ``None``
    returned.

.. py:function:: asyncSleep(secs, reactor=None)

    Yield a deferred that will fire with no result after ``secs`` seconds.
    This is the asynchronous equivalent to ``time.sleep``, and can be useful in tests.
    In case a custom reactor is used, the ``reactor`` parameter may be set.
    By default, ``twisted.internet.reactor`` is used.

.. py:function:: stripUrlPassword(url)

    :param url: a URL
    :returns: URL with any password component replaced with ``xxxx``

    Sanitize a URL; use this before logging or displaying a DB URL.

.. py:function:: join_list(maybe_list)

    :param maybe_list: list, tuple, byte string, or unicode
    :returns: unicode string

    If ``maybe_list`` is a list or tuple, join it with spaces, casting any strings into unicode
    using :py:func:`bytes2unicode`. This is useful for configuration parameters that may be strings
    or lists of strings.

.. py:class:: Notifier()

    This is a helper for firing multiple deferreds with the same result.

    .. py:method:: wait()

        Return a deferred that will fire when when the notifier is notified.

    .. py:method:: notify(value)

        Fire all the outstanding deferreds with the given value.

.. py:function:: giturlparse(url)

    :param url: a git url
    :returns: a :py:class:`GitUrl` with results of parsed url

    This function is intended to help various components to parse git urls. It helps to find the
    ``<owner>/<repo>`` of a git repository url coming from a change, in order to call urls.
    ``owner`` and ``repo`` is a common scheme for identifying a git repository between various git
    hosting services, like GitHub, GitLab, BitBucket, etc. Each service has their own naming for
    similar things, but we choose to use the GitHub naming as a de-facto standard. To simplify
    implementation, the parser is accepting invalid urls, but it should always parse valid urls
    correctly. The unit tests in ``test_util_giturlparse.py`` are the references on what the parser
    accepts. Please feel free to update the parser and the unit tests.

    Example use:

    .. code-block:: python

            from buildbot.util import giturlparse
            repourl = giturlparse(sourcestamp['repository'])
            repoOwner = repourl.owner
            repoName = repourl.repo

.. py:class:: GitUrl()

    .. py:attribute:: proto

        The protocol of the url

    .. py:attribute:: user

        The user of the url (as in ``user@domain``)

    .. py:attribute:: domain

        The domain part of the url

    .. py:attribute:: port

        The optional port of the url

    .. py:attribute:: owner

        The owner of the repository (in case of GitLab might be a nested group, i.e contain ``/``,
        e.g ``repo/subrepo/subsubrepo``)

    .. py:attribute:: repo

        The name of the repository (in case of GitLab might be a nested group, i.e contain ``/``)


:py:mod:`buildbot.util.lru`
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.lru

.. py:class:: LRUCache(miss_fn, max_size=50)

    :param miss_fn: function to call, with key as parameter, for cache misses. The function should
        return the value associated with the key argument, or None if there is no value associated with
        the key.
    :param max_size: maximum number of objects in the cache.

    This is a simple least-recently-used cache. When the cache grows beyond the maximum size, the
    least-recently used items will be automatically removed from the cache.

    This cache is designed to control memory usage by minimizing duplication of objects, while
    avoiding unnecessary re-fetching of the same rows from the database.

    All values are also stored in a weak valued dictionary, even after they have expired from the
    cache. This allows values that are used elsewhere in Buildbot to "stick" in the cache in case
    they are needed by another component. Weak references cannot be used for some types, so these
    types are not compatible with this class. Note that dictionaries can be weakly referenced if
    they are an instance of a subclass of ``dict``.

    If the result of the ``miss_fn`` is ``None``, then the value is not cached; this is intended to
    avoid caching negative results.

    This is based on `Raymond Hettinger's implementation
    <http://code.activestate.com/recipes/498245-lru-and-lfu-cache-decorators/>`_, licensed under
    the PSF license, which is GPL-compatible.

    .. py:attribute:: hits

        cache hits so far

    .. py:attribute:: refhits

        cache misses found in the weak ref dictionary, so far

    .. py:attribute:: misses

        cache misses leading to re-fetches, so far

    .. py:attribute:: max_size

        maximum allowed size of the cache

    .. py:method:: get(key, **miss_fn_kwargs)

        :param key: cache key
        :param miss_fn_kwargs: keyword arguments to the ``miss_fn``
        :returns: value via Deferred

        Fetch a value from the cache by key, invoking ``miss_fn(key, **miss_fn_kwargs)`` if the key
        is not in the cache.

        Any additional keyword arguments are passed to the ``miss_fn`` as keyword arguments; these
        can supply additional information relating to the key. It is up to the caller to ensure
        that this information is functionally identical for each key value: if the key is already
        in the cache, the ``miss_fn`` will not be invoked, even if the keyword arguments differ.

    .. py:method:: put(key, value)

        :param key: key at which to place the value
        :param value: value to place there

        Add the given key and value into the cache. The purpose of this method is to insert a new
        value into the cache *without* invoking the miss_fn (e.g., to avoid unnecessary overhead).

    .. py:method set_max_size(max_size)

        :param max_size: new maximum cache size

        Change the cache's maximum size.
        If the size is reduced, cached elements will be evicted.
        This method exists to support dynamic reconfiguration of cache sizes in a running process.

    .. py:method:: inv()

        Check invariants on the cache.
        This is intended for debugging purposes.

.. py:class:: AsyncLRUCache(miss_fn, max_size=50)

    :param miss_fn: This is the same as the miss_fn for class LRUCache, with the difference that
        this function *must* return a Deferred.
    :param max_size: maximum number of objects in the cache.

    This class has the same functional interface as LRUCache, but asynchronous locking is used to
    ensure that in the common case of multiple concurrent requests for the same key, only one fetch
    is performed.

:py:mod:`buildbot.util.bbcollections`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.bbcollections

This package provides a few useful collection objects.

.. note::

    This module used to be named ``collections``, but without absolute imports (:pep:`328`), this
    precluded using the standard library's ``collections`` module.

.. py:class:: defaultdict

    This is a clone of the Python :class:`collections.defaultdict` for use in Python-2.4. In later
    versions, this is simply a reference to the built-in :class:`defaultdict`, so Buildbot code can
    simply use :class:`buildbot.util.collections.defaultdict` everywhere.

.. py:class:: KeyedSets

    This is a collection of named sets. In principle, it contains an empty set for every name, and
    you can add things to sets, discard things from sets, and so on.

    .. code-block:: python

        >>> ks = KeyedSets()
        >>> ks['tim']                   # get a named set
        set([])
        >>> ks.add('tim', 'friendly')   # add an element to a set
        >>> ks.add('tim', 'dexterous')
        >>> ks['tim']
        set(['friendly', 'dexterous'])
        >>> 'tim' in ks                 # membership testing
        True
        >>> 'ron' in ks
        False
        >>> ks.discard('tim', 'friendly')# discard set element
        >>> ks.pop('tim')               # return set and reset to empty
        set(['dexterous'])
        >>> ks['tim']
        set([])

    This class is careful to conserve memory space - empty sets do not occupy any space.

:py:mod:`buildbot.util.eventual`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.eventual

This function provides a simple way to say "please do this later".
For example

.. code-block:: python

    from buildbot.util.eventual import eventually
    def do_what_I_say(what, where):
        # ...
        return d
    eventually(do_what_I_say, "clean up", "your bedroom")

The package defines "later" as "next time the reactor has control", so this is a good way to avoid
long loops that block another activity in the reactor.

.. py:function:: eventually(cb, *args, **kwargs)

    :param cb: callable to invoke later
    :param args: args to pass to ``cb``
    :param kwargs: kwargs to pass to ``cb``

    Invoke the callable ``cb`` in a later reactor turn.

    Callables given to :func:`eventually` are guaranteed to be called in the same order as the
    calls to :func:`eventually` -- writing ``eventually(a); eventually(b)`` guarantees that ``a``
    will be called before ``b``.

    Any exceptions that occur in the callable will be logged with ``log.err()``.
    If you really want to ignore them, provide a callable that catches those exceptions.

    This function returns None.
    If you care to know when the callable was run, be sure to provide a callable that notifies somebody.

.. py:function:: fireEventually(value=None)

    :param value: value with which the Deferred should fire
    :returns: Deferred

    This function returns a Deferred which will fire in a later reactor turn, after the current
    call stack has been completed, and after all other Deferreds previously scheduled with
    :py:func:`eventually`. The returned Deferred will never fail.

.. py:function:: flushEventualQueue()

    :returns: Deferred

    This returns a Deferred which fires when the eventual-send queue is finally empty. This is
    useful for tests and other circumstances where it is useful to know that "later" has arrived.

:py:mod:`buildbot.util.debounce`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.debounce

It's often necessary to perform some action in response to a particular type of event. For example,
steps need to update their status after updates arrive from the worker. However, when many events
arrive in quick succession, it's more efficient to only perform the action once, after the last
event has occurred.

The ``debounce.method(wait, until_idle=False)`` decorator is the tool for the job.

.. py:function:: method(wait, until_idle=False, get_reactor)

    :param wait: time to wait before invoking, in seconds
    :param until_idle: resets the timer on every call
    :param get_reactor: A callable that takes the underlying instance and returns the reactor to use.
        Defaults to ``instance.master.reactor``.

    Returns a decorator that debounces the underlying method.
    The underlying method must take no arguments (except ``self``).

    Calls are "debounced", meaning that multiple calls to the decorated method will result in a
    single invocation.

    When `until_idle` is `True`, the underlying method will be called after *wait* seconds have
    elapsed since the last time the decorated method have been called. In case of constant stream,
    it will never be called.

    When `until_idle` is `False`, the underlying method will be called after *wait* seconds have
    elapsed since the first time the decorated method have been called. In case of constant stream,
    it will called about once every *wait* seconds (plus the time the method takes to execute)

    The decorated method is an instance of :py:class:`Debouncer`, allowing it to be started and
    stopped. This is useful when the method is a part of a Buildbot service: call
    ``method.start()`` from ``startService`` and ``method.stop()`` from ``stopService``, handling
    its Deferred appropriately.

.. py:class:: Debouncer

    .. py:method:: stop()

        :returns: Deferred

        Stop the debouncer.
        While the debouncer is stopped, calls to the decorated method will be ignored.
        If a call is pending when ``stop`` is called, that call will occur immediately.
        When the Deferred that ``stop`` returns fires, the underlying method is not executing.

    .. py:method:: start()

        Start the debouncer.
        This reverses the effects of ``stop``.
        This method can be called on a started debouncer without issues.

:py:mod:`buildbot.util.poll`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.poll

Many Buildbot services perform some periodic, asynchronous operation.
Change sources, for example, contact the repositories they monitor on a regular basis.
The tricky bit is, the periodic operation must complete before the service stops.

The ``@poll.method`` decorator makes this behavior easy and reliable.

.. py:function:: method

    This decorator replaces the decorated method with a :py:class:`Poller` instance configured to
    call the decorated method periodically. The poller is initially stopped, so periodic calls will
    not begin until its ``start`` method is called. The start polling interval is specified when
    the poller is started. A random delay may optionally be supplied. This allows to avoid the
    situation of multiple services with the same interval are executing at exactly the same time.

    If the decorated method fails or raises an exception, the Poller logs the error and
    re-schedules the call for the next interval.

    If a previous invocation of the method has not completed when the interval expires, then the
    next invocation is skipped and the interval timer starts again.

    A common idiom is to call ``start`` and ``stop`` from ``startService`` and ``stopService``:

    .. code-block:: python

        class WatchThings(object):

            @poll.method
            def watch(self):
                d = self.beginCheckingSomething()
                return d

            def startService(self):
                self.watch.start(interval=self.pollingInterval, now=False)

            def stopService(self):
                return self.watch.stop()


.. py:class:: Poller

    .. py:method:: start(interval=N, now=False, random_delay_min=0, random_delay_max=0)

        :param interval: time, in seconds, between invocations
        :param now: if true, call the decorated method immediately on startup.
        :param random_delay_min: Minimum random delay to apply to the start time of the decorated method.
        :param random_delay_min: Maximum random delay to apply to the start time of the decorated method.

        Start the poller.

    .. py:method:: stop()

        :returns: Deferred

        Stop the poller.
        The returned Deferred fires when the decorated method is complete.

    .. py:method:: __call__()

        Force a call to the decorated method now. If the decorated method is currently running,
        another call will begin as soon as it completes unless the poller is currently stopping.

:py:mod:`buildbot.util.maildir`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.maildir

Several Buildbot components make use of `maildirs <http://www.courier-mta.org/maildir.html>`_ to
hand off messages between components. On the receiving end, there's a need to watch a maildir for
incoming messages and trigger some action when one arrives.

.. py:class:: MaildirService(basedir)

        :param basedir: (optional) base directory of the maildir

    A :py:class:`MaildirService` instance watches a maildir for new messages. It should be a child
    service of some :py:class:`~twisted.application.service.MultiService` instance. When running,
    this class uses the linux dirwatcher API (if available) or polls for new files in the 'new'
    maildir subdirectory. When it discovers a new message, it invokes its
    :py:meth:`messageReceived` method.

    To use this class, subclass it and implement a more interesting :py:meth:`messageReceived` function.

    .. py:method:: setBasedir(basedir)

        :param basedir: base directory of the maildir

        If no ``basedir`` is provided to the constructor, this method must be used to set the
        basedir before the service starts.

    .. py:method:: messageReceived(filename)

        :param filename: unqualified filename of the new message

        This method is called with the short filename of the new message. The full name of the new
        file can be obtained with ``os.path.join(maildir, 'new', filename)``. The method is
        un-implemented in the :py:class:`MaildirService` class, and must be implemented in
        subclasses.

    .. py:method:: moveToCurDir(filename)

        :param filename: unqualified filename of the new message
        :returns: open file object

        Call this from :py:meth:`messageReceived` to start processing the message; this moves the
        message file to the 'cur' directory and returns an open file handle for it.

:py:mod:`buildbot.util.misc`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.misc

.. py:function:: deferredLocked(lock)

    :param lock: a :py:class:`twisted.internet.defer.DeferredLock` instance or a string naming an
        instance attribute containing one

    This is a decorator to wrap an event-driven method (one returning a ``Deferred``) in an
    acquire/release pair of a designated :py:class:`~twisted.internet.defer.DeferredLock`. For
    simple functions with a static lock, this is as easy as:

    .. code-block:: python

        someLock = defer.DeferredLock()

        @util.deferredLocked(someLock)
        def someLockedFunction():
            # ..
            return d

    For class methods which must access a lock that is an instance attribute, the lock can be
    specified by a string, which will be dynamically resolved to the specific instance at runtime:

    .. code-block:: python

        def __init__(self):
            self.someLock = defer.DeferredLock()

        @util.deferredLocked('someLock')
        def someLockedFunction():
            # ..
            return d

.. py:function:: cancelAfter(seconds, deferred)

    :param seconds: timeout in seconds
    :param deferred: deferred to cancel after timeout expires
    :returns: the deferred passed to the function

    Cancel the given deferred after the given time has elapsed, if it has not already been fired.
    When this occurs, the deferred's errback will be fired with a
    :py:class:`twisted.internet.defer.CancelledError` failure.

:py:mod:`buildbot.util.netstrings`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.netstrings

Similar to maildirs, `netstrings <http://cr.yp.to/proto/netstrings.txt>`_ are used occasionally in
Buildbot to encode data for interchange. While Twisted supports a basic netstring receiver
protocol, it does not have a simple way to apply that to a non-network situation.

.. py:class:: NetstringParser

    This class parses strings piece by piece, either collecting the accumulated strings or invoking a callback for each one.

    .. py:method:: feed(data)

        :param data: a portion of netstring-formatted data
        :raises: :py:exc:`twisted.protocols.basic.NetstringParseError`

        Add arbitrarily-sized ``data`` to the incoming-data buffer.
        Any complete netstrings will trigger a call to the :py:meth:`stringReceived` method.

        Note that this method (like the Twisted class it is based on) cannot detect a trailing
        partial netstring at EOF - the data will be silently ignored.

    .. py:method:: stringReceived(string):

        :param string: the decoded string

        This method is called for each decoded string as soon as it is read completely. The default
        implementation appends the string to the :py:attr:`strings` attribute, but subclasses can
        do anything.

    .. py:attribute:: strings

        The strings decoded so far, if :py:meth:`stringReceived` is not overridden.

:py:mod:`buildbot.util.sautils`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.sautils

This module contains a few utilities that are not included with SQLAlchemy.

.. py:class:: InsertFromSelect(table, select)

    :param table: table into which insert should be performed
    :param select: select query from which data should be drawn

    This class is taken directly from SQLAlchemy's `compiler.html
    <http://www.sqlalchemy.org/docs/core/compiler.html#compiling-sub-elements-of-a-custom-expression-construct>`_,
    and allows a Pythonic representation of ``INSERT INTO .. SELECT ..`` queries.

.. py:function:: sa_version()

    Return a 3-tuple representing the SQLAlchemy version.
    Note that older versions that did not have a ``__version__`` attribute are represented by ``(0,0,0)``.

:py:mod:`buildbot.util.pathmatch`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.pathmatch

.. py:class:: Matcher

    This class implements the path-matching algorithm used by the data API.

    Patterns are tuples of strings, with strings beginning with a colon (``:``) denoting variables.
    A character can precede the colon to indicate the variable type:

    * ``i`` specifies an identifier (:ref:`identifier <type-identifier>`).
    * ``n`` specifies a number (parseable by ``int``).

    A tuple of strings matches a pattern if the lengths are identical, every variable matches and
    has the correct type, and every non-variable pattern element matches exactly.

    A matcher object takes patterns using dictionary-assignment syntax:

    .. code-block:: python

        ep = ChangeEndpoint()
        matcher[('change', 'n:changeid')] = ep

    and performs matching using the dictionary-lookup syntax:

    .. code-block:: python

        changeEndpoint, kwargs = matcher[('change', '13')]
        # -> (ep, {'changeid': 13})

    where the result is a tuple of the original assigned object (the ``Change`` instance in this
    case) and the values of any variables in the path.

    .. py:method:: iterPatterns()

        Returns an iterator which yields all patterns in the matcher as tuples of (pattern, endpoint).

:py:mod:`buildbot.util.topicmatch`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.topicmatch

.. py:class:: TopicMatcher(topics)

    :param list topics: topics to match

    This class implements the AMQP-defined syntax: routing keys are treated as dot-separated
    sequences of words and matched against topics. A star (``*``) in the topic will match any
    single word, while an octothorpe (``#``) will match zero or more words.

    .. py:method:: matches(routingKey)

        :param string routingKey: routing key to examine
        :returns: True if the routing key matches a topic

:py:mod:`buildbot.util.subscription`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The classes in the :py:mod:`buildbot.util.subscription` module are used for master-local
subscriptions. In the near future, all uses of this module will be replaced with message-queueing
implementations that allow subscriptions and subscribers to span multiple masters.

:py:mod:`buildbot.util.croniter`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(deprecated)

This module is a copy of https://github.com/taichino/croniter, and provides support for converting
cron-like time specifications to actual times.

:py:mod:`buildbot.util.state`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.state

The classes in the :py:mod:`buildbot.util.state` module are used for dealing with object state
stored in the database.

.. py:class:: StateMixin

    This class provides helper methods for accessing the object state stored in the database.

    .. py:attribute:: name

         This must be set to the name to be used to identify this object in the database.

    .. py:attribute:: master

         This must point to the :py:class:`BuildMaster` object.

    .. py:method:: getState(name, default)

        :param name: name of the value to retrieve
        :param default: (optional) value to return if `name` is not present
        :returns: state value via a Deferred
        :raises KeyError: if `name` is not present and no default is given
        :raises TypeError: if JSON parsing fails

        Get a named state value from the object's state.

    .. py:method:: setState(name, value)

        :param name: the name of the value to change
        :param value: the value to set - must be a JSONable object
        :param returns: Deferred
        :raises TypeError: if JSONification fails

        Set a named state value in the object's persistent state.
        Note that value must be json-able.

:py:mod:`buildbot.util.identifiers`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.identifiers

This module makes it easy to manipulate identifiers.

.. py:function:: isIdentifier(maxLength, object)

    :param maxLength: maximum length of the identifier
    :param object: object to test for identifier-ness
    :returns: boolean

    Is object a :ref:`identifier <type-identifier>`?

.. py:function:: forceIdentifier(maxLength, str)

    :param maxLength: maximum length of the identifier
    :param str: string to coerce to an identifier
    :returns: identifier of maximum length ``maxLength``

    Coerce a string (assuming UTF-8 for bytestrings) into an identifier.
    This method will replace any invalid characters with ``_`` and truncate to the given length.

.. py:function:: incrementIdentifier(maxLength, str)

    :param maxLength: maximum length of the identifier
    :param str: identifier to increment
    :returns: identifier of maximum length ``maxLength``
    :raises: ValueError if no suitable identifier can be constructed

    "Increment" an identifier by adding a numeric suffix, while keeping the total length limited.
    This is useful when selecting a unique identifier for an object.
    Maximum-length identifiers like ``_999999`` cannot be incremented and will raise :py:exc:`ValueError`.

:py:mod:`buildbot.util.lineboundaries`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.lineboundaries

.. py:class:: LineBoundaryFinder

    This class accepts a sequence of arbitrary strings and computes newline-terminated substrings.
    Input strings are accepted in append function, and newline-terminated substrings are returned.

    The class buffers any partial lines until a subsequent newline is seen. It considers any of
    ``\r``, ``\n``, and ``\r\n`` to be newlines. Because of the ambiguity of an append operation
    ending in the character ``\r`` (it may be a bare ``\r`` or half of ``\r\n``), the last line of
    such an append operation will be buffered until the next append or flush.

    .. py:method:: append(text)

        :param text: text to append to the boundary finder
        :returns: a newline-terminated substring or None

        Add additional text to the boundary finder.
        If the addition of this text completes at least one line, as many complete lines as possible are selected as a result.
        If no lines are completed, the result will be ``None``.

    .. py:method:: flush()

        :returns: a newline-terminated substring or None

        Flush any remaining partial line by adding a newline.


:py:mod:`buildbot.util.service`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.service

This module implements some useful subclasses of Twisted services.

The first two classes are more robust implementations of two Twisted classes, and should be used universally in Buildbot code.

.. class:: AsyncMultiService

    This class is similar to :py:class:`twisted.application.service.MultiService`, except that it
    handles Deferreds returned from child services ``startService`` and ``stopService`` methods.

    Twisted's service implementation does not support asynchronous ``startService`` methods. The
    reasoning is that all services should start at process startup, with no need to coordinate
    between them. For Buildbot, this is not sufficient. The framework needs to know when startup
    has completed, so it can begin scheduling builds. This class implements the desired
    functionality, with a parent service's ``startService`` returning a Deferred which will only
    fire when all child services ``startService`` methods have completed.

    This class also fixes a bug with Twisted's implementation of ``stopService`` which ignores
    failures in the ``stopService`` process. With :py:class:`AsyncMultiService`, any errors in a
    child's ``stopService`` will be propagated to the parent's ``stopService`` method.

.. py:class:: AsyncService

    This class is similar to :py:class:`twisted.application.service.Service`, except that its
    ``setServiceParent`` method will return a Deferred. That Deferred will fire after the
    ``startService`` method has completed, if the service was started because the new parent was
    already running.

.. index:: Service utilities; ClusteredService

Some services in buildbot must have only one "active" instance at any given time. In a
single-master configuration, this requirement is trivial to maintain. In a multiple-master
configuration, some arbitration is required to ensure that the service is always active on exactly
one master in the cluster.

For example, a particular daily scheduler could be configured on multiple masters, but only one of
them should actually trigger the required builds.

.. py:class:: ClusteredService

    A base class for a service that must have only one "active" instance in a buildbot configuration.

    Each instance of the service is started and stopped via the usual twisted ``startService`` and
    ``stopService`` methods. This utility class hooks into those methods in order to run an
    arbitration strategy to pick the one instance that should actually be "active".

    The arbitration strategy is implemented via a polling loop. When each service instance starts,
    it immediately offers to take over as the active instance (via ``_claimService``).

    If successful, the ``activate`` method is called. Once active, the instance remains active
    until it is explicitly stopped (eg, via ``stopService``) or otherwise fails. When this happens,
    the ``deactivate`` method is invoked and the "active" status is given back to the cluster (via
    ``_unclaimService``).

    If another instance is already active, this offer fails, and the instance will poll
    periodically to try again. The polling strategy helps guard against active instances that might
    silently disappear and leave the service without any active instance running.

    Subclasses should use these methods to hook into this activation scheme:

    .. method:: activate()

        When a particular instance of the service is chosen to be the one "active" instance, this
        method is invoked. It is the corollary to twisted's ``startService``.

    .. method:: deactivate()

        When the one "active" instance must be deactivated, this method is invoked.
        It is the corollary to twisted's ``stopService``.

    .. method:: isActive()

        Returns whether this particular instance is the active one.

    The arbitration strategy is implemented via the following required methods:

    .. method:: _getServiceId()

        The "service id" uniquely represents this service in the cluster. Each instance of this
        service must have this same id, which will be used in the arbitration to identify
        candidates for activation. This method may return a Deferred.

    .. method:: _claimService()

        An instance is attempting to become the one active instance in the cluster. This method
        must return `True` or `False` (optionally via a Deferred) to represent whether this
        instance's offer to be the active one was accepted. If this returns `True`, the
        ``activate`` method will be called for this instance.

    .. method:: _unclaimService()

        Surrender the "active" status back to the cluster and make it available for another
        instance. This will only be called on an instance that successfully claimed the service and
        has been activated and after its ``deactivate`` has been called. Therefore, in this method
        it is safe to reassign the "active" status to another instance. This method may return a
        Deferred.

.. py:class:: SharedService

    This class implements a generic Service that needs to be instantiated only once according to
    its parameters. It is a common use case to need this for accessing remote services. Having a
    shared service allows to limit the number of simultaneous access to the same remote service.
    Thus, several completely independent Buildbot services can use that :py:class:`SharedService`
    to access the remote service, and automatically synchronize themselves to not overwhelm it.

    .. py:method:: __init__(self, *args, **kwargs)

        Constructor of the service.

        Note that unlike :py:class:`BuildbotService`, :py:class:`SharedService` is not
        reconfigurable and uses the classical constructor method.

        Reconfigurability would mean to add some kind of reference counting of the users, which
        will make the design much more complicated to use. This means that the SharedService will
        not be destroyed when there is no more users, it will be destroyed at the master's
        stopService It is important that those :py:class:`SharedService` life cycles are properly
        handled. Twisted will indeed wait for any thread pool to finish at master stop, which will
        not happen if the thread pools are not properly closed.

        The lifecycle of the SharedService is the same as a service, it must implement startService
        and stopService in order to allocate and free its resources.

    .. py:method:: getName(cls, *args, **kwargs)

        Class method. Takes same arguments as the constructor of the service. Get a unique name for
        that instance of a service. This returned name is the key inside the parent's service
        dictionary that is used to decide if the instance has already been created before or if
        there is a need to create a new object. Default implementation will hash args and kwargs
        and use ``<classname>_<hash>`` as the name.

    .. py:method:: getService(cls, parentService, *args, **kwargs)

        :param parentService: an :py:class:`AsyncMultiService` where to lookup and register the
            :py:class:`SharedService` (usually the root service, the master)
        :returns: instance of the service via Deferred

        Class method. Takes same arguments as the constructor of the service (plus the
        `parentService` at the beginning of the list). Construct an instance of the service if
        needed, and place it at the beginning of the `parentService` service list. Placing it at
        the beginning will guarantee that the :py:class:`SharedService` will be stopped after the
        other services.

.. py:class:: BuildbotService

    This class is the combinations of all `Service` classes implemented in buildbot.
    It is Async, MultiService, and Reconfigurable, and designed to be eventually the base class for all buildbot services.
    This class makes it easy to manage (re)configured services.

    The design separates the check of the config and the actual configuration/start.
    A service sibling is a configured object that has the same name of a previously started service.
    The sibling configuration will be used to configure the running service.

    Service lifecycle is as follow:

    * Buildbot master start

    * Buildbot is evaluating the configuration file.
      BuildbotServices are created, and checkConfig() are called by the generic constructor.

    * If everything is fine, all services are started.
      BuildbotServices startService() is called, and call reconfigService() for the first time.

    * User reconfigures buildbot.

    * Buildbot is evaluating the configuration file.
      BuildbotServices siblings are created, and checkConfig() are called by the generic constructor.

    * BuildbotServiceManager is figuring out added services, removed services, unchanged services

    * BuildbotServiceManager calls stopService() for services that disappeared from the configuration.

    * BuildbotServiceManager calls startService() like in buildbot start phase for services that appeared from the configuration.

    * BuildbotServiceManager calls reconfigService() for the second time for services that have their configuration changed.


    .. py:method:: __init__(self, *args, **kwargs)

        Constructor of the service.
        The constructor initializes the service, calls checkConfig() and stores the config arguments in private attributes.

        This should *not* be overridden by subclasses, as they should rather override checkConfig.

    .. py:method:: canReconfigWithSibling(self, sibling)
        This method is used to check if we are able to call
        :py:func:`reconfigServiceWithSibling` with the given sibling.
        If it returns `False`, we stop the old service and start a new one,
        instead of attempting a reconfig.

    .. py:method:: checkConfig(self, *args, **kwargs)

        Please override this method to check the parameters of your config. Please use
        :py:func:`buildbot.config.error` for error reporting. You can replace them ``*args,
        **kwargs`` by actual constructor like arguments with default args, and it have to match
        self.reconfigService This method is synchronous, and executed in the context of the
        master.cfg. Please don't block, or use deferreds in this method. Remember that the object
        that runs checkConfig is not always the object that is actually started. The checked
        configuration can be passed to another sibling service. Any actual resource creation shall
        be handled in reconfigService() or startService()

    .. py:method:: reconfigService(self, *args, **kwargs)

        This method is called at buildbot startup, and buildbot reconfig. `*args` and `**kwargs`
        are the configuration arguments passed to the constructor in master.cfg. You can replace
        ``them *args, **kwargs`` by actual constructor like arguments with default args, and it
        have to match self.checkConfig

        Returns a deferred that should fire when the service is ready.
        Builds are not started until all services are configured.

        BuildbotServices must be aware that during reconfiguration, their methods can still be
        called by running builds. So they should atomically switch old configuration and new
        configuration, so that the service is always available.

        If this method raises :py:class:`NotImplementedError`, it means the service is legacy, and
        do not support reconfiguration. The :py:class:`BuildbotServiceManager` parent, will detect
        this, and swap old service with new service. This behaviour allow smooth transition of old
        code to new reconfigurable service lifecycle but shall not be used for new code.

    .. py:method:: reconfigServiceWithSibling(self, sibling)

        Internal method that finds the configuration bits in a sibling, an object with same class
        that is supposed to replace it from a new configuration. We want to reuse the service
        started at master startup and just reconfigure it. This method handles necessary steps to
        detect if the config has changed, and eventually call self.reconfigService()

    .. py:method:: renderSecrets(self, *args)

        Utility method which renders a list of parameters which can be interpolated as a secret.
        This is meant for services which have their secrets parameter configurable as positional
        arguments. If there are several argument, the secrets are interpolated in parallel, and a
        list of result is returned via deferred. If there is one argument, the result is directly
        returned.

        .. note::

            For keyword arguments, a simpler method is to use the ``secrets`` class variable, whose items
            will be automatically interpolated just before reconfiguration.

        .. code-block:: python

                def reconfigService(self, user, password, ...)
                    user, password = yield self.renderSecrets(user, password)

        .. code-block:: python

                def reconfigService(self, token, ...)
                    token = yield self.renderSecrets(token)

        .. code-block:: python

                secrets = ("user", "password")
                def reconfigService(self, user=None, password=None, ...):
                    # nothing to do; user and password will be automatically interpolated


    Advanced users can derive this class to make their own services that run inside buildbot, and
    follow the application lifecycle of buildbot master.

    Such services are singletons accessible to nearly every object in Buildbot (buildsteps, status,
    changesources, etc) using self.master.namedServices['<nameOfYourService>'].

    As such, they can be used to factorize access to external services, available e.g using a REST api.
    Having a single service will help with caching, and rate-limiting access of those APIs.

    Here is an example on how you would integrate and configure a simple service in your `master.cfg`:

    .. code-block:: python

        class MyShellCommand(ShellCommand):

            def getResultSummary(self):
                # access the service attribute
                service = self.master.namedServices['myService']
                return dict(step="arg value: %d" % (service.arg1,))

        class MyService(BuildbotService):
            name = "myService"

            def checkConfig(self, arg1):
                if not isinstance(arg1, int):
                    config.error("arg1 must be an integer while it is %r" % (arg1,))
                    return
                if arg1 < 0:
                    config.error("arg1 must be positive while it is %d" % (arg1,))

            def reconfigService(self, arg1):
                self.arg1 = arg1
                return defer.succeed(None)

        c['schedulers'] = [
            ForceScheduler(
                name="force",
                builderNames=["testy"])]

        f = BuildFactory()
        f.addStep(MyShellCommand(command='echo hei'))
        c['builders'] = [
            BuilderConfig(name="testy",
                          workernames=["local1"],
                          factory=f)]

        c['services'] = [
            MyService(arg1=1)
        ]

:py:mod:`buildbot.util.httpclientservice`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.httpclientservice

.. py:class:: HTTPClientService

    This class implements a SharedService for doing http client access. The module automatically
    chooses from `txrequests`_ and `treq`_ and uses whichever is installed. It provides
    minimalistic API similar to the one from `txrequests`_ and `treq`_. Having a SharedService for
    this allows to limits the number of simultaneous connection for the same host. While twisted
    application can managed thousands of connections at the same time, this is often not the case
    for the services buildbot controls. Both `txrequests`_ and `treq`_ use keep-alive connection
    polling. Lots of HTTP REST API will however force a connection close in the end of a
    transaction.

    .. note::

        The API described here is voluntary minimalistic, and reflects what is tested. As most of
        this module is implemented as a pass-through to the underlying libraries, other options can
        work but have not been tested to work in both backends. If there is a need for more
        functionality, please add new tests before using them.

    .. py:staticmethod:: getService(master, base_url, auth=None, headers=None, debug=None, verify=None)

        :param master: the instance of the master service (available in self.master for all the :py:class:`BuildbotService` instances)
        :param base_url: The base http url of the service to access. e.g. ``http://github.com/``
        :param auth: Authentication information. If auth is a tuple then ``BasicAuth`` will be used. e.g ``('user', 'passwd')``
            It can also be a :mod:`requests.auth` authentication plugin.
            In this case `txrequests`_ will be forced, and `treq`_ cannot be used.
        :param headers: The headers to pass to every requests for this url
        :param debug: log every requests and every response.
        :param verify: disable the SSL verification.

        :returns: instance of :`HTTPClientService`

        Get an instance of the SharedService.
        There is one instance per base_url and auth.

        The constructor initialize the service, and store the config arguments in private attributes.

        This should *not* be overridden by subclasses, as they should rather override checkConfig.

        This function has been deprecated. Please use ``HTTPSession``.


    .. py:method:: get(endpoint, params=None)

        :param endpoint: endpoint. It must either be a full URL (starts with ``http://`` or
            ``https://``) or relative to the base_url (starts with ``/``)
        :param params: optional dictionary that will be encoded in the query part of the url
            (e.g. ``?param1=foo``)
        :returns: implementation of :`IHTTPResponse` via deferred

        Performs a HTTP ``GET``. This function has been deprecated. Please use ``HTTPSession``.


    .. py:method:: delete(endpoint, params=None)

        :param endpoint: endpoint. It must either be a full URL (starts with ``http://`` or
            ``https://``) or relative to the base_url (starts with ``/``)
        :param params: optional dictionary that will be encoded in the query part of the url
            (e.g. ``?param1=foo``)
        :returns: implementation of :`IHTTPResponse` via deferred

        Performs a HTTP ``DELETE``. This function has been deprecated. Please use ``HTTPSession``.

    .. py:method:: post(endpoint, data=None, json=None, params=None)

        :param endpoint: endpoint. It must either be a full URL (starts with ``http://`` or
            ``https://``) or relative to the base_url (starts with ``/``)
        :param data: optional dictionary that will be encoded in the body of the http requests
            as ``application/x-www-form-urlencoded``
        :param json: optional dictionary that will be encoded in the body of the http requests
            as ``application/json``
        :param params: optional dictionary that will be encoded in the query part of the url
            (e.g. ``?param1=foo``)
        :returns: implementation of :`IHTTPResponse` via deferred

        Performs a HTTP ``POST``. This function has been deprecated. Please use ``HTTPSession``.

        .. note::

            json and data cannot be used at the same time.

    .. py:method:: put(endpoint, data=None, json=None, params=None)

        :param endpoint: endpoint. It must either be a full URL (starts with ``http://`` or
            ``https://``) or relative to the base_url (starts with ``/``)
        :param data: optional dictionary that will be encoded in the body of the http requests
            as ``application/x-www-form-urlencoded``
        :param json: optional dictionary that will be encoded in the body of the http requests
            as ``application/json``
        :param params: optional dictionary that will be encoded in the query part of the url
            (e.g. ``?param1=foo``)
        :returns: implementation of :`IHTTPResponse` via deferred

        Performs a HTTP ``PUT``. This function has been deprecated. Please use ``HTTPSession``.

        .. note::

            json and data cannot be used at the same time.

    .. py:method:: update_headers(headers)

        :param headers: dictionary of string key-value pairs containing headers to add to the
            session.

        Adds or updates the session with the given headers. All subsequent HTTP requests will
        contain the additional headers specified in this call.

.. py:class:: HTTPSession

    A class that encapsulates certain parameters of connection to HTTP URLs and allows to perform
    connections to them.

    Example usage in a service.

    .. code-block:: python

        s = HTTPSession(self.master.httpservice, "https://api.github.com")
        r = await s.get("/repos/buildbot/buildbot/releases")
        print(r.json())

    Usually ``HTTPSession`` is used by creating an instance of it in service constructor and reusing
    it throughout the life of the service.

    .. py:method:: __init__(http: HTTPClientService, base_url: str, auth=None, headers=None, debug=None, verify=None)

        :param http: the instance of HTTPClientService to use. It is available as
            ``self.master.httpservice`` for all the :py:class:`BuildbotService` instances.
        :param base_url: The base http url of the server to access. e.g. ``http://github.com/``
        :param auth: Authentication information. If auth is a tuple then ``BasicAuth`` will be used. e.g ``('user', 'passwd')``
            It can also be a :mod:`requests.auth` authentication plugin.
            In this case `txrequests`_ will be forced, and `treq`_ cannot be used.
        :param headers: The headers to pass to every requests for this url
        :param debug: log every requests and every response.
        :param verify: disable the SSL verification.

        Creates a ``HTTPSession`` instance.

    .. py:method:: get(endpoint, params=None)

        :param endpoint: endpoint. It must either be a full URL (starts with ``http://`` or
            ``https://``) or relative to the base_url (starts with ``/``)
        :param params: optional dictionary that will be encoded in the query part of the url
            (e.g. ``?param1=foo``)
        :returns: implementation of :`IHTTPResponse` via deferred

        Performs a HTTP ``GET``

    .. py:method:: delete(endpoint, params=None)

        :param endpoint: endpoint. It must either be a full URL (starts with ``http://`` or
            ``https://``) or relative to the base_url (starts with ``/``)
        :param params: optional dictionary that will be encoded in the query part of the url
            (e.g. ``?param1=foo``)
        :returns: implementation of :`IHTTPResponse` via deferred

        Performs a HTTP ``DELETE``

    .. py:method:: post(endpoint, data=None, json=None, params=None)

        :param endpoint: endpoint. It must either be a full URL (starts with ``http://`` or
            ``https://``) or relative to the base_url (starts with ``/``)
        :param data: optional dictionary that will be encoded in the body of the http requests
            as ``application/x-www-form-urlencoded``
        :param json: optional dictionary that will be encoded in the body of the http requests
            as ``application/json``
        :param params: optional dictionary that will be encoded in the query part of the url
            (e.g. ``?param1=foo``)
        :returns: implementation of :`IHTTPResponse` via deferred

        Performs a HTTP ``POST``

        .. note::

            json and data cannot be used at the same time.

    .. py:method:: put(endpoint, data=None, json=None, params=None)

        :param endpoint: endpoint. It must either be a full URL (starts with ``http://`` or
            ``https://``) or relative to the base_url (starts with ``/``)
        :param data: optional dictionary that will be encoded in the body of the http requests
            as ``application/x-www-form-urlencoded``
        :param json: optional dictionary that will be encoded in the body of the http requests
            as ``application/json``
        :param params: optional dictionary that will be encoded in the query part of the url
            (e.g. ``?param1=foo``)
        :returns: implementation of :`IHTTPResponse` via deferred

        Performs a HTTP ``PUT``

        .. note::

            json and data cannot be used at the same time.

.. py:class:: IHTTPResponse

    .. note::

        :class:`IHTTPResponse` is a subset of `treq`_ :py:class:`Response` API described `here
        <https://treq.readthedocs.io/en/latest/api.html#module-treq.response>`_. The API it is
        voluntarily minimalistic and reflects what is tested and reliable to use with the three
        backends (including fake). The API is a subset of the `treq`_ API, which is itself a
        superset of `twisted IResponse API`_. `treq`_ is thus implemented as passthrough.

        Notably:

        * There is no API to automatically decode content, as this is not implemented the same in
          both backends.
        * There is no API to stream content as the two libraries have very different way for doing
          it, and we do not see use-case where buildbot would need to transfer large content to the
          master.

    .. py:method:: content()

        :returns: raw (``bytes``) content of the response via deferred

    .. py:method:: json()

        :returns: json decoded content of the response via deferred

    .. py:attribute:: code

        :returns: http status code of the request's response (e.g 200)

    .. py:attribute:: url

        :returns: request's url (e.g https://api.github.com/endpoint')

.. _txrequests: https://pypi.python.org/pypi/txrequests
.. _treq: https://pypi.python.org/pypi/treq
.. _twisted IResponse API: https://twistedmatrix.com/documents/current/api/twisted.web.iweb.IResponse.html

:py:mod:`buildbot.test.fake.httpclientservice`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.test.fake.httpclientservice

.. py:class:: HTTPClientService

    This class implements a fake version of the
    :class:`buildbot.util.httpclientservice.HTTPClientService` that needs to be used for testing
    services which need http client access. It implements the same APIs as
    :class:`buildbot.util.httpclientservice.HTTPClientService`, plus one that should be used to
    register the expectations. It should be registered by the test case before the tested service
    actually requests an HTTPClientService instance, with the same parameters. It will then replace
    the original implementation automatically (no need to patch anything).

    .. py:method:: getService(cls, master, case, *args, **kwargs)

        :param master: the instance of a fake master service
        :param case: a :py:class:`twisted.python.unittest.TestCase` instance

        :py:meth:`getService` returns a fake :py:class:`HTTPClientService`, and should be used just
        like the regular :py:meth:`getService`.

        It will make sure the original :py:class:`HTTPClientService` is not called, and assert that
        all expected http requests have been described in the test case.


    .. py:method:: expect(self, method, ep, params=None, data=None, json=None, code=200, content=None, content_json=None, processing_delay_s=None)

        :param method: expected HTTP method
        :param ep: expected endpoint
        :param params: optional expected query parameters
        :param data: optional expected non-json data (bytes)
        :param json: optional expected json data (dictionary or list or string)
        :param code: optional http code that will be received
        :param content: optional content that will be received
        :param content_json: optional content encoded in json that will be received
        :param processing_delay_s: optional delay that the handling of the request will take

        Records an expectation of HTTP requests that will happen during the test.
        The order of the requests is important.
        All the request expectation must be defined in the test.

        For example:

        .. code-block:: python

            from twisted.internet import defer
            from twisted.trial import unittest

            from buildbot.test.fake import httpclientservice as fakehttpclientservice
            from buildbot.util import httpclientservice
            from buildbot.util import service


            class MyTestedService(service.BuildbotService):
                name = 'myTestedService'

                @defer.inlineCallbacks
                def reconfigService(self, baseurl):
                    self._http = yield httpclientservice.HTTPSession(
                        self.master.httpservice, baseurl)

                @defer.inlineCallbacks
                def doGetRoot(self):
                    res = yield self._http.get("/")
                    # note that at this point, only the http response headers are received
                    if res.code != 200:
                        raise RuntimeError("%d: server did not succeed" % (res.code))
                    res_json = yield res.json()
                    # res.json() returns a deferred to account for the time needed to fetch the
                    # entire body
                    return res_json


            class Test(unittest.TestCase):

                @defer.inlineCallbacks
                def setUp(self):
                    baseurl = 'http://127.0.0.1:8080'
                    self.parent = service.MasterService()
                    self._http = \
                        yield fakehttpclientservice.HTTPClientService.getService(self.parent, self,
                                                                                 baseurl))
                    self.tested = myTestedService(baseurl)

                    yield self.tested.setServiceParent(self.parent)
                    yield self.parent.startService()

                def test_root(self):
                    self._http.expect("get", "/", content_json={'foo': 'bar'})

                    response = yield self.tested.doGetRoot()
                    self.assertEqual(response, {'foo': 'bar'})

                def test_root_error(self):
                    self._http.expect("get", "/", content_json={'foo': 'bar'}, code=404)

                    with self.assertRaises(RuntimeError) as e:
                        yield self.tested.doGetRoot()
                    self.assertIn('404: server did not succeed', str(e.exception))

:py:mod:`buildbot.util.ssl`
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. py:module:: buildbot.util.ssl

This module is a copy of :py:mod:`twisted.internet.ssl` except it won't crash with
:py:class:`ImportError` if :py:mod:`pyopenssl` is not installed. If you need to use
:py:mod:`twisted.internet.ssl`, please instead use :py:mod:`buildbot.util.ssl`, and call
:py:func:`ssl.ensureHasSSL` in :py:meth:`checkConfig` to provide helpful message to the user, only
if they enabled SSL for your plugin.

.. py:function:: ensureHasSSL(plugin_name)

    :param plugin_name: name of the plugin. Usually ``self.__class__.__name__``

    Call this function to provide helpful config error to the user in case of ``OpenSSL`` not installed.


.. py:function:: skipUnless(f)

    :param f: decorated test

    Test decorator which will skip the test if ``OpenSSL`` is not installed.