Since Qibolab is now being used, and we transitioned to proper releases, in order to make the semver conversion even more meaningful we should define what is part of the public API of Qibolab.
The convention in Python is that everything that is accessible passing through a chain of non-underscore-led identifiers is public, i.e.:
# public
ciao
ciao.come
ciao.come.va
# private
_ciao
ciao._come
ciao.come._va
# and so also:
ciao._come.va
# as parte of `ciao._come`
However, if you use ciao._come.va inside ciao, importing directly the identifier, i.e.
# src/ciao.py
from ._come import va
this becomes accessible as ciao.va, and thus public again.
In general, the idea is to keep all the definitions internal, and exports only the public API through suitable __init__.py and modules imported by that, as a restricted hierarchy within the package, just made for re-exports.
Often, a _core subpackage, or something like that, is used to separate what is internal from the public API.
To simplify the management of the public API, there are different tools available in Python's module system:
__all__: is an attribute that specifies what is imported from this module when doing from mymodule import * - usually I would discourage import *, but it's convenient for re-exports
__getattr__ is customizable at module-level, and it could be used to provide dynamic loading (that for us could be useful to export the drivers)
similarly to the previous, even __dir__ is customizable in the same way
NumPy is quite a full-fledged example of how all of these are used to define the public API. It is also over-complicate for us, since they have decades of legacy code and users to support, but it's interesting to navigate their __init__.py structure.
This will help to identify which changes are actually breaking, and which are not.
(In principle, whatever is not part of the public API should also be kept internal, and not documented in Sphinx - I guess this should work automatically, ensuring meaningful __dir__ values - and it should not be an incentive to avoid docstrings, even for internal elements)
Since Qibolab is now being used, and we transitioned to proper releases, in order to make the semver conversion even more meaningful we should define what is part of the public API of Qibolab.
The convention in Python is that everything that is accessible passing through a chain of non-underscore-led identifiers is public, i.e.:
However, if you use
ciao._come.va
insideciao
, importing directly the identifier, i.e.this becomes accessible as
ciao.va
, and thus public again.In general, the idea is to keep all the definitions internal, and exports only the public API through suitable
__init__.py
and modules imported by that, as a restricted hierarchy within the package, just made for re-exports. Often, a_core
subpackage, or something like that, is used to separate what is internal from the public API.To simplify the management of the public API, there are different tools available in Python's module system:
__all__
: is an attribute that specifies what is imported from this module when doingfrom mymodule import *
- usually I would discourageimport *
, but it's convenient for re-exports__getattr__
is customizable at module-level, and it could be used to provide dynamic loading (that for us could be useful to export the drivers)__dir__
is customizable in the same wayNumPy is quite a full-fledged example of how all of these are used to define the public API. It is also over-complicate for us, since they have decades of legacy code and users to support, but it's interesting to navigate their
__init__.py
structure.This will help to identify which changes are actually breaking, and which are not.
(In principle, whatever is not part of the public API should also be kept internal, and not documented in Sphinx - I guess this should work automatically, ensuring meaningful
__dir__
values - and it should not be an incentive to avoid docstrings, even for internal elements)