... |
... |
@@ -496,7 +496,7 @@ outline the exceptions to the rules discussed here. |
496
|
496
|
|
497
|
497
|
.. _contributing_public_api_surface:
|
498
|
498
|
|
499
|
|
-Public API Surface
|
|
499
|
+Public API surface
|
500
|
500
|
~~~~~~~~~~~~~~~~~~
|
501
|
501
|
BuildStream exposes what we call a *"Public API Surface"* which is stable
|
502
|
502
|
and unchanging. This is for the sake of stability of the interfaces which
|
... |
... |
@@ -546,6 +546,28 @@ prefixed with a single underscore, and are not imported in BuildStream's the mas |
546
|
546
|
|
547
|
547
|
In this case, the *"API Private"* functions are prefixed with a single underscore.
|
548
|
548
|
|
|
549
|
+Any objects which are a part of the *"Public API Surface"* should be exposed via the
|
|
550
|
+toplevel ``__init__.py`` of the ``buildstream`` package.
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+File naming convention
|
|
554
|
+~~~~~~~~~~~~~~~~~~~~~~
|
|
555
|
+With the exception of a few helper objects and data structures, we structure
|
|
556
|
+the code in BuildStream such that every filename is named after the object it
|
|
557
|
+implements. E.g. The ``Project`` object is implemented in ``_project.py``, the
|
|
558
|
+``Context`` object in ``_context.py``, the base ``Element`` class in ``element.py``,
|
|
559
|
+etc.
|
|
560
|
+
|
|
561
|
+As mentioned in the previous section, objects which are not a part of the
|
|
562
|
+:ref:`public, plugin facing API surface <contributing_public_api_surface>` have their
|
|
563
|
+filenames prefixed with a leading underscore (like ``_context.py`` and ``_project.py``
|
|
564
|
+in the examples above).
|
|
565
|
+
|
|
566
|
+When an object name has multiple words in it, e.g. ``ArtifactCache``, then the
|
|
567
|
+resulting file is named all in lower case without any underscore to separate
|
|
568
|
+words. In the case of ``ArtifactCache``, the filename implementing this object
|
|
569
|
+is found at ``_artifactcache/artifactcache.py``.
|
|
570
|
+
|
549
|
571
|
|
550
|
572
|
Imports
|
551
|
573
|
~~~~~~~
|
... |
... |
@@ -553,11 +575,11 @@ Module imports inside BuildStream are done with relative ``.`` notation |
553
|
575
|
|
554
|
576
|
**Good**::
|
555
|
577
|
|
556
|
|
- from .context import Context
|
|
578
|
+ from ._context import Context
|
557
|
579
|
|
558
|
580
|
**Bad**::
|
559
|
581
|
|
560
|
|
- from buildstream.context import Context
|
|
582
|
+ from buildstream._context import Context
|
561
|
583
|
|
562
|
584
|
The exception to the above rule is when authoring plugins,
|
563
|
585
|
plugins do not reside in the same namespace so they must
|
... |
... |
@@ -576,7 +598,9 @@ This makes things clear when reading code that said functions |
576
|
598
|
are not defined in the same file but come from utils.py for example.
|
577
|
599
|
|
578
|
600
|
|
579
|
|
-Instance Variables
|
|
601
|
+.. _contributing_instance_variables:
|
|
602
|
+
|
|
603
|
+Instance variables
|
580
|
604
|
~~~~~~~~~~~~~~~~~~
|
581
|
605
|
It is preferred that all instance state variables be declared as :ref:`private symbols
|
582
|
606
|
<contributing_public_and_private>`, however in some cases, especially when the state
|
... |
... |
@@ -607,7 +631,7 @@ variable. |
607
|
631
|
|
608
|
632
|
.. _contributing_accessor_mutator:
|
609
|
633
|
|
610
|
|
-Accessors and Mutators
|
|
634
|
+Accessors and mutators
|
611
|
635
|
~~~~~~~~~~~~~~~~~~~~~~
|
612
|
636
|
An accessor and mutator, are methods defined on the object class to access (get)
|
613
|
637
|
or mutate (set) a value owned by the declaring class, respectively.
|
... |
... |
@@ -667,7 +691,7 @@ respectively, e.g.:: |
667
|
691
|
|
668
|
692
|
.. _contributing_abstract_methods:
|
669
|
693
|
|
670
|
|
-Abstract Methods
|
|
694
|
+Abstract methods
|
671
|
695
|
~~~~~~~~~~~~~~~~
|
672
|
696
|
In BuildStream, an *"Abstract Method"* is a bit of a misnomer and does
|
673
|
697
|
not match up to how Python defines abstract methods, we need to seek out
|
... |
... |
@@ -695,7 +719,7 @@ to do so, and outlined the API contract for this purpose. No surprises |
695
|
719
|
are allowed.
|
696
|
720
|
|
697
|
721
|
|
698
|
|
-Error Handling
|
|
722
|
+Error handling
|
699
|
723
|
~~~~~~~~~~~~~~
|
700
|
724
|
In BuildStream, all non recoverable errors are expressed via
|
701
|
725
|
subclasses of the ``BstError`` exception.
|
... |
... |
@@ -704,7 +728,7 @@ This exception is handled deep in the core in a few places, and |
704
|
728
|
it is rarely necessary to handle a ``BstError``.
|
705
|
729
|
|
706
|
730
|
|
707
|
|
-Raising Exceptions
|
|
731
|
+Raising exceptions
|
708
|
732
|
''''''''''''''''''
|
709
|
733
|
When writing code in the BuildStream core, ensure that all system
|
710
|
734
|
calls and third party library calls are wrapped in a ``try:`` block,
|
... |
... |
@@ -721,7 +745,7 @@ original call trace, example:: |
721
|
745
|
raise ArtifactError("Attempt to access unavailable artifact: {}".format(e)) from e
|
722
|
746
|
|
723
|
747
|
|
724
|
|
-Enhancing Exceptions
|
|
748
|
+Enhancing exceptions
|
725
|
749
|
''''''''''''''''''''
|
726
|
750
|
Sometimes the ``BstError`` originates from a lower level component,
|
727
|
751
|
and the code segment which raised the exception did not have enough context
|
... |
... |
@@ -913,7 +937,7 @@ if the developer does not take special care of ensuring this does not |
913
|
937
|
happen.
|
914
|
938
|
|
915
|
939
|
|
916
|
|
-Use less arguments in methods
|
|
940
|
+Minimize arguments in methods
|
917
|
941
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
918
|
942
|
When creating an object, or adding a new API method to an existing
|
919
|
943
|
object, always strive to keep as much context as possible on the
|
... |
... |
@@ -926,6 +950,48 @@ that value or object should be passed in the constructor instead of |
926
|
950
|
via a method call.
|
927
|
951
|
|
928
|
952
|
|
|
953
|
+Minimize API surfaces
|
|
954
|
+~~~~~~~~~~~~~~~~~~~~~
|
|
955
|
+When creating an object, or adding new functionality in any way,
|
|
956
|
+try to keep the number of :ref:`public, outward facing <contributing_public_and_private>`
|
|
957
|
+symbols to a minimum, this is important for both
|
|
958
|
+:ref:`internal and public, plugin facing API surfaces <contributing_public_api_surface>`.
|
|
959
|
+
|
|
960
|
+When anyone visits a file, there are two levels of comprehension:
|
|
961
|
+
|
|
962
|
+* What do I need to know in order to *use* this object
|
|
963
|
+
|
|
964
|
+* What do I need to know in order to *modify* this object
|
|
965
|
+
|
|
966
|
+For the former, we want the reader to understand with as little effort
|
|
967
|
+as possible, what the public API contract is for a given object and consequently,
|
|
968
|
+how it is expected to be used. This is also why we
|
|
969
|
+:ref:`order the symbols of a class <contributing_class_order>` in such a way
|
|
970
|
+as to keep all outward facing public API surfaces at the top of the file, so that the
|
|
971
|
+reader never needs to dig deep into the bottom of the file to find something they
|
|
972
|
+might need to use.
|
|
973
|
+
|
|
974
|
+For the latter, when it comes to having to modify the file or add functionality,
|
|
975
|
+you want to retain as much freedom as possible to modify internals, while
|
|
976
|
+being sure that nothing external will be affected by internal modifications.
|
|
977
|
+Less client facing API means that you have less surrounding code to modify
|
|
978
|
+when your API changes. Further, ensuring that there is minimal outward facing
|
|
979
|
+API for any module minimizes the complexity for the developer working on
|
|
980
|
+that module, by limiting the considerations needed regarding external side
|
|
981
|
+effects of their modifications to the module.
|
|
982
|
+
|
|
983
|
+When modifying a file, one should not have to understand or think too
|
|
984
|
+much about external side effects, when the API surface of the file is
|
|
985
|
+well documented and minimal.
|
|
986
|
+
|
|
987
|
+When adding new API to a given object for a new purpose, consider whether
|
|
988
|
+the new API is in any way redundant with other API (should this value now
|
|
989
|
+go into the constructor, since we use it more than once ? could this
|
|
990
|
+value be passed along with another function, and the other function renamed,
|
|
991
|
+to better suit the new purposes of this module/object ?) and repurpose
|
|
992
|
+the outward facing API of an object as a whole every time.
|
|
993
|
+
|
|
994
|
+
|
929
|
995
|
Avoid transient state on instances
|
930
|
996
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
931
|
997
|
At times, it can be tempting to store transient state that is
|