Module tf.advanced.app

Expand source code Browse git
import os
import types
import traceback

from ..parameters import ORG, APP_CODE, APP_APP, OMAP
from ..fabric import Fabric
from ..parameters import APIREF, TEMP_DIR
from ..lib import readSets
from ..core.helpers import console, setDir, mergeDict, normpath, abspath, expanduser
from .find import findAppConfig, findAppClass
from .helpers import getText, dm, dh
from .settings import setAppSpecs, setAppSpecsApi
from .volumes import volumesApi
from .links import linksApi, outLink
from .text import textApi
from .sections import sectionsApi
from .display import displayApi
from .search import searchApi
from .data import getModulesData
from .repo import checkoutRepo


# SET UP A TF API FOR AN APP


FROM_TF_METHODS = """
    banner
    silentOn
    silentOff
    isSilent
    setSilent
    info
    warning
    error
    indent
    featuresOnly
""".strip().split()


class App:
    def __init__(
        self,
        cfg,
        appName,
        appPath,
        commit,
        release,
        local,
        backend,
        _browse,
        hoist=False,
        version=None,
        checkout="",
        mod=[],
        locations=None,
        modules=None,
        volume=None,
        collection=None,
        api=None,
        setFile="",
        silent=False,
        _withGc=True,
        **configOverrides,
    ):
        """Set up the advanced TF API.

        Most parameters are explained in `tf.app.use`.

        Parameters
        ----------
        appName, appPath, checkout, version: string
        commit, release, local: string
        backend: string
        checkout: string, optional `""`
        mod: string or iterable, optional `[]`
        version: string, optional `None`
        locations, modules: string, optional `None`
        collection, volume: string, optional None
        api: object, optional, `None`
        setFile: string, optional, `None`
        silent: boolean, optional `False`
        hoist: dict, optional `False`
        configOverrides: key value pairs

        _withGc: boolean, optional True
            If False, it disables the Python garbage collector before
            loading features. Used to experiment with performance.
        """

        self.context = None
        """Result of interpreting all configuration options in `config.yaml`.

        See Also
        --------
        tf.advanced.settings.showContext
        """

        mergeDict(cfg, configOverrides)

        for (key, value) in dict(
            isCompatible=cfg.get("isCompatible", None),
            backend=backend,
            appName=appName,
            api=api,
            version=version,
            volume=volume,
            collection=collection,
            silent=silent,
            _browse=_browse,
        ).items():
            setattr(self, key, value)

        setattr(self, "dm", dm)
        setattr(self, "dh", dh)

        setAppSpecs(self, cfg)
        aContext = self.context
        version = aContext.version

        setDir(self)

        if not self.api:
            self.sets = None
            if setFile:
                sets = readSets(setFile)
                if sets:
                    self.sets = sets
                    console(f'Sets from {setFile}: {", ".join(sets)}')
            specs = getModulesData(
                self, backend, mod, locations, modules, version, checkout, silent
            )
            if specs:
                (locations, modules) = specs
                self.tempDir = f"{self.repoLocation}/{TEMP_DIR}"
                TF = Fabric(
                    locations=locations,
                    modules=modules,
                    volume=volume,
                    collection=collection,
                    silent=silent,
                    _withGc=_withGc,
                )
                api = TF.load("", silent=True)
                if api:
                    self.api = api
                    excludedFeatures = aContext.excludedFeatures
                    allFeatures = TF.explore(silent=True, show=True)
                    loadableFeatures = allFeatures["nodes"] + allFeatures["edges"]
                    useFeatures = [
                        f
                        for f in loadableFeatures
                        if f not in excludedFeatures and not f.startswith(OMAP)
                    ]
                    result = TF.load(useFeatures, add=True, silent=True)
                    if result is False:
                        self.api = None
            else:
                self.api = None

        if self.api:
            self.TF = self.api.TF
            for m in FROM_TF_METHODS:
                setattr(self, m, getattr(self.TF, m))

            featuresOnly = self.featuresOnly

            if not featuresOnly:
                self.getText = types.MethodType(getText, self)
            volumesApi(self)
            linksApi(self, silent)
            if not featuresOnly:
                searchApi(self)
                sectionsApi(self)
                setAppSpecsApi(self, cfg)
                displayApi(self, silent)
                textApi(self)
            setattr(self, "isLoaded", self.api.isLoaded)
            if hoist:
                # docs = self.api.makeAvailableIn(hoist)
                self.api.makeAvailableIn(hoist)
                if not silent:
                    dh(
                        "<div><b>Text-Fabric API:</b> names "
                        + outLink(
                            "N F E L T S C TF",
                            APIREF,
                            title="doc",
                        )
                        + " directly usable</div><hr>"
                    )

            silentOff = self.silentOff
            silentOff()
        else:
            if not _browse:
                console(
                    f"""
There were problems with loading data.
The Text-Fabric API has not been loaded!
The app "{appName}" will not work!
""",
                    error=True,
                )

    def load(self, features, silent=False):
        """Loads extra features in addition to the main dataset.

        This is the same as `tf.fabric.Fabric.load` when called with `add=True`.

        Parameters
        ----------
        features: string | iterable
            Either a string containing space separated feature names, or an
            iterable of feature names.
            The feature names are just the names of `.tf` files
            without directory information and without extension.
        silent: boolean, optional `None`
            If `False`, the features will be loaded rather silently,
            most messages will be suppressed.
            Time consuming operations will always be announced,
            so that you know what Text-Fabric is doing.
            If `True` is passed, all informational messages will be suppressed.
            This is handy I you want to load data as part of other methods, on-the-fly.

        Returns
        -------
        boolean
            Whether the feature has been successfully loaded.
        """

        TF = self.TF
        return TF.load(features, add=True, silent=silent)

    def reinit(self):
        """TF-Apps may override this method.
        It is called by `reuse`. Hence it needs to be present.
        """

        pass

    def reuse(self, hoist=False):
        """Re-initialize the app.

        The app's settings are read again, the app's code is re-imported,
        the app's stylesheets are applied again.
        But the data is left untouched, and no time-consuming reloading of data
        takes place.

        Handy when you are developing a new app and want to experiment with it
        without the costly re-loading of the data in every cycle.

        Parameters
        ----------
        hoist: boolean, optional `False`
            Same as in `App`.

        !!! hint "the effect of the config settings"
            If you are developing a TF app and need to see the effects of
            the configuration settings in detail, you can conveniently
            call `reuse` and `tf.advanced.settings.showContext` in tandem.
        """

        backend = self.backend
        aContext = self.context
        appPath = aContext.appPath
        appName = aContext.appName
        local = aContext.local
        commit = aContext.commit
        release = aContext.release
        version = aContext.version
        api = self.api

        cfg = findAppConfig(
            appName,
            appPath,
            commit,
            release,
            local,
            backend,
            org=aContext.org,
            repo=aContext.repo,
            version=version,
        )
        findAppClass(appName, appPath)

        setAppSpecs(self, cfg, reset=True)

        if api:
            TF = self.TF
            TF._makeApi()
            api = TF.api
            self.api = api
            self.reinit()  # may be used by custom TF apps
            linksApi(self, True)
            searchApi(self)
            sectionsApi(self)
            setAppSpecsApi(self, cfg)
            displayApi(self, True)
            textApi(self)
            if hoist:
                api.makeAvailableIn(hoist)


def findApp(
    appName,
    checkoutApp,
    dataLoc,
    backend,
    _browse,
    *args,
    silent=False,
    version=None,
    legacy=False,
    **kwargs,
):
    """Find a TF app by name and initialize an object of its main class.

    Parameters
    ----------
    appName: string or None
        Either:

        * None, but then dataLoc should have a value
        * `app:`*path/to/tf/app*
        * *org*/*repo*
        * *org*/*repo*/*relative*
        * *app*, i.e. the plain name of an official TF app
          (e.g. `bhsa`, `oldbabylonian`)

        The last case is legacy: instead of *app*, pass *org*/*repo*.

    dataLoc: string or None
        Either:

        * None, but then appName should have a value
        * path to a local directory
        * *org*/*repo*
        * *org*/*repo*/*relative*

        Except for the first two cases, a trailing checkout specifier
        is allowed, like `:clone`, `:local`, `:latest`, `:hot`

        It is assumed that the location is a TF data directory;
        a vanilla app without extra configuration is initialized.

    checkoutApp: string
        The checkout specifier for the TF-app. See `tf.advanced.app.App`.

    args: mixed
        Arguments that will be passed to the initializer of the `tf.advanced.app.App`
        class.

    backend: string
        `github` or `gitlab` or a GitLab instance such as `gitlab.huc.knaw.nl`.

    kwargs: mixed
        Keyword arguments that will be passed to the initializer of the
        `tf.advanced.app.App` class.

    legacy: boolean, optional False
        If true, accept that a legacy-app is called.
        Do not give warning, and do not try to load the app in the non-legacy way.
    """

    (commit, release, local) = (None, None, None)
    extraMod = None

    appLoc = None
    if appName is not None and appName.startswith("app:"):
        appLoc = normpath(appName[4:])
    else:
        appName = normpath(appName)

    if dataLoc is None and appName is None:
        console("No TF-app and no data location specified", error=True)
        return None

    if dataLoc is not None and appName is not None:
        console("Both a TF-app and a data location are specified", error=True)
        return None

    dataOrg = None
    dataRepo = None

    if dataLoc is None:
        if appLoc:
            if ":" in appLoc:
                console(
                    "When passing an app by `app:fullpath` you cannot use :-specifiers"
                )
                return None
            appPath = expanduser(appLoc) if appLoc else ""
            absPath = abspath(appPath)

            if os.path.isdir(absPath):
                appDir = absPath
                appBase = ""
            else:
                console(f"{absPath} is not an existing directory", error=True)
                appBase = False
                appDir = None
            appPath = appDir
        elif "/" in appName:
            (dataOrg, rest) = appName.split("/", maxsplit=1)
            parts = rest.split("/", maxsplit=1)
            if len(parts) == 1:
                parts.append(APP_APP)
            (dataRepo, folder) = parts
            (commit, release, local, appBase, appDir) = checkoutRepo(
                backend,
                _browse=_browse,
                org=dataOrg,
                repo=dataRepo,
                folder=folder,
                checkout=checkoutApp,
                withPaths=True,
                keep=False,
                silent=silent,
                label="TF-app",
            )
            appBaseRep = f"{appBase}/" if appBase else ""
            appPath = f"{appBaseRep}{appDir}"
        else:
            (commit, release, local, appBase, appDir) = checkoutRepo(
                backend,
                _browse=_browse,
                org=ORG,
                repo=f"app-{appName}",
                folder=APP_CODE,
                checkout=checkoutApp,
                withPaths=True,
                keep=False,
                silent=silent,
                label="TF-app",
            )
            appBaseRep = f"{appBase}/" if appBase else ""
            appPath = f"{appBaseRep}{appDir}"
            cfg = findAppConfig(appName, appPath, commit, release, local, backend)
            provenanceSpec = kwargs.get("provenanceSpec", {})
            if provenanceSpec:
                for k in ("org", "repo", "relative"):
                    value = provenanceSpec.get(k, None)
                    if value:
                        cfg[k] = value

            dataOrg = cfg.get("provenanceSpec", {}).get("org", None)
            dataRepo = cfg.get("provenanceSpec", {}).get("repo", None)

            if not legacy:
                if dataOrg:
                    console(
                        (
                            "WARNING: in the future, pass "
                            f"`{dataOrg}/{appName}` instead of `{appName}`"
                        ),
                        error=True,
                    )
                    (commit, release, local, appBase, appDir) = checkoutRepo(
                        backend,
                        _browse=_browse,
                        org=dataOrg,
                        repo=dataRepo,
                        folder=APP_APP,
                        checkout=checkoutApp,
                        withPaths=True,
                        keep=False,
                        silent=silent,
                        label="TF-app",
                    )
                    appBaseRep = f"{appBase}/" if appBase else ""
                    appPath = f"{appBaseRep}{appDir}"
    else:
        parts = dataLoc.split(":", maxsplit=1)
        if len(parts) == 1:
            parts.append("")
        (dataLoc, checkoutData) = parts
        if checkoutData == "":
            appPath = expanduser(dataLoc) if dataLoc else ""
            absPath = abspath(appPath)

            if os.path.isdir(absPath):
                (appDir, appName) = os.path.split(absPath)
                appBase = ""
            else:
                console(f"{absPath} is not an existing directory", error=True)
                appBase = False
                appDir = None
            appPath = appDir
        else:
            appBase = ""
            appDir = ""
            appPath = ""
            extraMod = f"{dataLoc}:{checkoutData}"

    cfg = findAppConfig(
        appName,
        appPath,
        commit,
        release,
        local,
        backend,
        org=dataOrg,
        repo=dataRepo,
        version=version,
    )
    version = cfg["provenanceSpec"].get("version", None)
    isCompatible = cfg["isCompatible"]
    if isCompatible is None:
        appClass = App
    elif not isCompatible:
        return None
    else:
        appBaseRep = f"{appBase}/" if appBase else ""
        appPath = f"{appBaseRep}{appDir}"

        appClass = findAppClass(appName, appPath) or App

    mod = kwargs.get("mod", [])
    mod = [] if mod is None else mod.split(",") if type(mod) is str else list(mod)
    if extraMod:
        if len(mod) > 0:
            mod = [extraMod, *mod]
        else:
            mod = [extraMod]
    kwargs["mod"] = mod
    try:
        app = appClass(
            cfg,
            appName,
            appPath,
            commit,
            release,
            local,
            backend,
            _browse,
            *args,
            version=version,
            silent=silent,
            **kwargs,
        )
    except Exception as e:
        if appClass is not App:
            console(
                f"There was an error loading TF-app {appName} from {appPath}",
                error=True,
            )
            console(repr(e), error=True)
        traceback.print_exc()
        console("Text-Fabric is not loaded", error=True)
        return None
    return app

Functions

def findApp(appName, checkoutApp, dataLoc, backend, _browse, *args, silent=False, version=None, legacy=False, **kwargs)

Find a TF app by name and initialize an object of its main class.

Parameters

appName : string or None

Either:

  • None, but then dataLoc should have a value
  • app:path/to/tf/app
  • org/repo
  • org/repo/relative
  • app, i.e. the plain name of an official TF app (e.g. bhsa, oldbabylonian)

The last case is legacy: instead of app, pass org/repo.

dataLoc : string or None

Either:

  • None, but then appName should have a value
  • path to a local directory
  • org/repo
  • org/repo/relative

Except for the first two cases, a trailing checkout specifier is allowed, like :clone, :local, :latest, :hot

It is assumed that the location is a TF data directory; a vanilla app without extra configuration is initialized.

checkoutApp : string
The checkout specifier for the TF-app. See App.
args : mixed
Arguments that will be passed to the initializer of the App class.
backend : string
github or gitlab or a GitLab instance such as gitlab.huc.knaw.nl.
kwargs : mixed
Keyword arguments that will be passed to the initializer of the App class.
legacy : boolean, optional False
If true, accept that a legacy-app is called. Do not give warning, and do not try to load the app in the non-legacy way.
Expand source code Browse git
def findApp(
    appName,
    checkoutApp,
    dataLoc,
    backend,
    _browse,
    *args,
    silent=False,
    version=None,
    legacy=False,
    **kwargs,
):
    """Find a TF app by name and initialize an object of its main class.

    Parameters
    ----------
    appName: string or None
        Either:

        * None, but then dataLoc should have a value
        * `app:`*path/to/tf/app*
        * *org*/*repo*
        * *org*/*repo*/*relative*
        * *app*, i.e. the plain name of an official TF app
          (e.g. `bhsa`, `oldbabylonian`)

        The last case is legacy: instead of *app*, pass *org*/*repo*.

    dataLoc: string or None
        Either:

        * None, but then appName should have a value
        * path to a local directory
        * *org*/*repo*
        * *org*/*repo*/*relative*

        Except for the first two cases, a trailing checkout specifier
        is allowed, like `:clone`, `:local`, `:latest`, `:hot`

        It is assumed that the location is a TF data directory;
        a vanilla app without extra configuration is initialized.

    checkoutApp: string
        The checkout specifier for the TF-app. See `tf.advanced.app.App`.

    args: mixed
        Arguments that will be passed to the initializer of the `tf.advanced.app.App`
        class.

    backend: string
        `github` or `gitlab` or a GitLab instance such as `gitlab.huc.knaw.nl`.

    kwargs: mixed
        Keyword arguments that will be passed to the initializer of the
        `tf.advanced.app.App` class.

    legacy: boolean, optional False
        If true, accept that a legacy-app is called.
        Do not give warning, and do not try to load the app in the non-legacy way.
    """

    (commit, release, local) = (None, None, None)
    extraMod = None

    appLoc = None
    if appName is not None and appName.startswith("app:"):
        appLoc = normpath(appName[4:])
    else:
        appName = normpath(appName)

    if dataLoc is None and appName is None:
        console("No TF-app and no data location specified", error=True)
        return None

    if dataLoc is not None and appName is not None:
        console("Both a TF-app and a data location are specified", error=True)
        return None

    dataOrg = None
    dataRepo = None

    if dataLoc is None:
        if appLoc:
            if ":" in appLoc:
                console(
                    "When passing an app by `app:fullpath` you cannot use :-specifiers"
                )
                return None
            appPath = expanduser(appLoc) if appLoc else ""
            absPath = abspath(appPath)

            if os.path.isdir(absPath):
                appDir = absPath
                appBase = ""
            else:
                console(f"{absPath} is not an existing directory", error=True)
                appBase = False
                appDir = None
            appPath = appDir
        elif "/" in appName:
            (dataOrg, rest) = appName.split("/", maxsplit=1)
            parts = rest.split("/", maxsplit=1)
            if len(parts) == 1:
                parts.append(APP_APP)
            (dataRepo, folder) = parts
            (commit, release, local, appBase, appDir) = checkoutRepo(
                backend,
                _browse=_browse,
                org=dataOrg,
                repo=dataRepo,
                folder=folder,
                checkout=checkoutApp,
                withPaths=True,
                keep=False,
                silent=silent,
                label="TF-app",
            )
            appBaseRep = f"{appBase}/" if appBase else ""
            appPath = f"{appBaseRep}{appDir}"
        else:
            (commit, release, local, appBase, appDir) = checkoutRepo(
                backend,
                _browse=_browse,
                org=ORG,
                repo=f"app-{appName}",
                folder=APP_CODE,
                checkout=checkoutApp,
                withPaths=True,
                keep=False,
                silent=silent,
                label="TF-app",
            )
            appBaseRep = f"{appBase}/" if appBase else ""
            appPath = f"{appBaseRep}{appDir}"
            cfg = findAppConfig(appName, appPath, commit, release, local, backend)
            provenanceSpec = kwargs.get("provenanceSpec", {})
            if provenanceSpec:
                for k in ("org", "repo", "relative"):
                    value = provenanceSpec.get(k, None)
                    if value:
                        cfg[k] = value

            dataOrg = cfg.get("provenanceSpec", {}).get("org", None)
            dataRepo = cfg.get("provenanceSpec", {}).get("repo", None)

            if not legacy:
                if dataOrg:
                    console(
                        (
                            "WARNING: in the future, pass "
                            f"`{dataOrg}/{appName}` instead of `{appName}`"
                        ),
                        error=True,
                    )
                    (commit, release, local, appBase, appDir) = checkoutRepo(
                        backend,
                        _browse=_browse,
                        org=dataOrg,
                        repo=dataRepo,
                        folder=APP_APP,
                        checkout=checkoutApp,
                        withPaths=True,
                        keep=False,
                        silent=silent,
                        label="TF-app",
                    )
                    appBaseRep = f"{appBase}/" if appBase else ""
                    appPath = f"{appBaseRep}{appDir}"
    else:
        parts = dataLoc.split(":", maxsplit=1)
        if len(parts) == 1:
            parts.append("")
        (dataLoc, checkoutData) = parts
        if checkoutData == "":
            appPath = expanduser(dataLoc) if dataLoc else ""
            absPath = abspath(appPath)

            if os.path.isdir(absPath):
                (appDir, appName) = os.path.split(absPath)
                appBase = ""
            else:
                console(f"{absPath} is not an existing directory", error=True)
                appBase = False
                appDir = None
            appPath = appDir
        else:
            appBase = ""
            appDir = ""
            appPath = ""
            extraMod = f"{dataLoc}:{checkoutData}"

    cfg = findAppConfig(
        appName,
        appPath,
        commit,
        release,
        local,
        backend,
        org=dataOrg,
        repo=dataRepo,
        version=version,
    )
    version = cfg["provenanceSpec"].get("version", None)
    isCompatible = cfg["isCompatible"]
    if isCompatible is None:
        appClass = App
    elif not isCompatible:
        return None
    else:
        appBaseRep = f"{appBase}/" if appBase else ""
        appPath = f"{appBaseRep}{appDir}"

        appClass = findAppClass(appName, appPath) or App

    mod = kwargs.get("mod", [])
    mod = [] if mod is None else mod.split(",") if type(mod) is str else list(mod)
    if extraMod:
        if len(mod) > 0:
            mod = [extraMod, *mod]
        else:
            mod = [extraMod]
    kwargs["mod"] = mod
    try:
        app = appClass(
            cfg,
            appName,
            appPath,
            commit,
            release,
            local,
            backend,
            _browse,
            *args,
            version=version,
            silent=silent,
            **kwargs,
        )
    except Exception as e:
        if appClass is not App:
            console(
                f"There was an error loading TF-app {appName} from {appPath}",
                error=True,
            )
            console(repr(e), error=True)
        traceback.print_exc()
        console("Text-Fabric is not loaded", error=True)
        return None
    return app

Classes

class App (cfg, appName, appPath, commit, release, local, backend, _browse, hoist=False, version=None, checkout='', mod=[], locations=None, modules=None, volume=None, collection=None, api=None, setFile='', silent=False, **configOverrides)

Set up the advanced TF API.

Most parameters are explained in use().

Parameters

appName, appPath, checkout, version : string
 
commit, release, local : string
 
backend : string
 
checkout : string, optional ""
 
mod : string or iterable, optional []
 
version : string, optional None
 
locations, modules : string, optional None
 
collection, volume : string, optional None
 
api : object, optional, None``
 
setFile : string, optional, None``
 
silent : boolean, optional False
 
hoist : dict, optional False
 
configOverrides : key value pairs
 
_withGc : boolean, optional True
If False, it disables the Python garbage collector before loading features. Used to experiment with performance.
Expand source code Browse git
class App:
    def __init__(
        self,
        cfg,
        appName,
        appPath,
        commit,
        release,
        local,
        backend,
        _browse,
        hoist=False,
        version=None,
        checkout="",
        mod=[],
        locations=None,
        modules=None,
        volume=None,
        collection=None,
        api=None,
        setFile="",
        silent=False,
        _withGc=True,
        **configOverrides,
    ):
        """Set up the advanced TF API.

        Most parameters are explained in `tf.app.use`.

        Parameters
        ----------
        appName, appPath, checkout, version: string
        commit, release, local: string
        backend: string
        checkout: string, optional `""`
        mod: string or iterable, optional `[]`
        version: string, optional `None`
        locations, modules: string, optional `None`
        collection, volume: string, optional None
        api: object, optional, `None`
        setFile: string, optional, `None`
        silent: boolean, optional `False`
        hoist: dict, optional `False`
        configOverrides: key value pairs

        _withGc: boolean, optional True
            If False, it disables the Python garbage collector before
            loading features. Used to experiment with performance.
        """

        self.context = None
        """Result of interpreting all configuration options in `config.yaml`.

        See Also
        --------
        tf.advanced.settings.showContext
        """

        mergeDict(cfg, configOverrides)

        for (key, value) in dict(
            isCompatible=cfg.get("isCompatible", None),
            backend=backend,
            appName=appName,
            api=api,
            version=version,
            volume=volume,
            collection=collection,
            silent=silent,
            _browse=_browse,
        ).items():
            setattr(self, key, value)

        setattr(self, "dm", dm)
        setattr(self, "dh", dh)

        setAppSpecs(self, cfg)
        aContext = self.context
        version = aContext.version

        setDir(self)

        if not self.api:
            self.sets = None
            if setFile:
                sets = readSets(setFile)
                if sets:
                    self.sets = sets
                    console(f'Sets from {setFile}: {", ".join(sets)}')
            specs = getModulesData(
                self, backend, mod, locations, modules, version, checkout, silent
            )
            if specs:
                (locations, modules) = specs
                self.tempDir = f"{self.repoLocation}/{TEMP_DIR}"
                TF = Fabric(
                    locations=locations,
                    modules=modules,
                    volume=volume,
                    collection=collection,
                    silent=silent,
                    _withGc=_withGc,
                )
                api = TF.load("", silent=True)
                if api:
                    self.api = api
                    excludedFeatures = aContext.excludedFeatures
                    allFeatures = TF.explore(silent=True, show=True)
                    loadableFeatures = allFeatures["nodes"] + allFeatures["edges"]
                    useFeatures = [
                        f
                        for f in loadableFeatures
                        if f not in excludedFeatures and not f.startswith(OMAP)
                    ]
                    result = TF.load(useFeatures, add=True, silent=True)
                    if result is False:
                        self.api = None
            else:
                self.api = None

        if self.api:
            self.TF = self.api.TF
            for m in FROM_TF_METHODS:
                setattr(self, m, getattr(self.TF, m))

            featuresOnly = self.featuresOnly

            if not featuresOnly:
                self.getText = types.MethodType(getText, self)
            volumesApi(self)
            linksApi(self, silent)
            if not featuresOnly:
                searchApi(self)
                sectionsApi(self)
                setAppSpecsApi(self, cfg)
                displayApi(self, silent)
                textApi(self)
            setattr(self, "isLoaded", self.api.isLoaded)
            if hoist:
                # docs = self.api.makeAvailableIn(hoist)
                self.api.makeAvailableIn(hoist)
                if not silent:
                    dh(
                        "<div><b>Text-Fabric API:</b> names "
                        + outLink(
                            "N F E L T S C TF",
                            APIREF,
                            title="doc",
                        )
                        + " directly usable</div><hr>"
                    )

            silentOff = self.silentOff
            silentOff()
        else:
            if not _browse:
                console(
                    f"""
There were problems with loading data.
The Text-Fabric API has not been loaded!
The app "{appName}" will not work!
""",
                    error=True,
                )

    def load(self, features, silent=False):
        """Loads extra features in addition to the main dataset.

        This is the same as `tf.fabric.Fabric.load` when called with `add=True`.

        Parameters
        ----------
        features: string | iterable
            Either a string containing space separated feature names, or an
            iterable of feature names.
            The feature names are just the names of `.tf` files
            without directory information and without extension.
        silent: boolean, optional `None`
            If `False`, the features will be loaded rather silently,
            most messages will be suppressed.
            Time consuming operations will always be announced,
            so that you know what Text-Fabric is doing.
            If `True` is passed, all informational messages will be suppressed.
            This is handy I you want to load data as part of other methods, on-the-fly.

        Returns
        -------
        boolean
            Whether the feature has been successfully loaded.
        """

        TF = self.TF
        return TF.load(features, add=True, silent=silent)

    def reinit(self):
        """TF-Apps may override this method.
        It is called by `reuse`. Hence it needs to be present.
        """

        pass

    def reuse(self, hoist=False):
        """Re-initialize the app.

        The app's settings are read again, the app's code is re-imported,
        the app's stylesheets are applied again.
        But the data is left untouched, and no time-consuming reloading of data
        takes place.

        Handy when you are developing a new app and want to experiment with it
        without the costly re-loading of the data in every cycle.

        Parameters
        ----------
        hoist: boolean, optional `False`
            Same as in `App`.

        !!! hint "the effect of the config settings"
            If you are developing a TF app and need to see the effects of
            the configuration settings in detail, you can conveniently
            call `reuse` and `tf.advanced.settings.showContext` in tandem.
        """

        backend = self.backend
        aContext = self.context
        appPath = aContext.appPath
        appName = aContext.appName
        local = aContext.local
        commit = aContext.commit
        release = aContext.release
        version = aContext.version
        api = self.api

        cfg = findAppConfig(
            appName,
            appPath,
            commit,
            release,
            local,
            backend,
            org=aContext.org,
            repo=aContext.repo,
            version=version,
        )
        findAppClass(appName, appPath)

        setAppSpecs(self, cfg, reset=True)

        if api:
            TF = self.TF
            TF._makeApi()
            api = TF.api
            self.api = api
            self.reinit()  # may be used by custom TF apps
            linksApi(self, True)
            searchApi(self)
            sectionsApi(self)
            setAppSpecsApi(self, cfg)
            displayApi(self, True)
            textApi(self)
            if hoist:
                api.makeAvailableIn(hoist)

Instance variables

var context

Result of interpreting all configuration options in config.yaml.

See Also

showContext()

Methods

def load(self, features, silent=False)

Loads extra features in addition to the main dataset.

This is the same as FabricCore.load() when called with add=True.

Parameters

features : string | iterable
Either a string containing space separated feature names, or an iterable of feature names. The feature names are just the names of .tf files without directory information and without extension.
silent : boolean, optional None
If False, the features will be loaded rather silently, most messages will be suppressed. Time consuming operations will always be announced, so that you know what Text-Fabric is doing. If True is passed, all informational messages will be suppressed. This is handy I you want to load data as part of other methods, on-the-fly.

Returns

boolean
Whether the feature has been successfully loaded.
Expand source code Browse git
def load(self, features, silent=False):
    """Loads extra features in addition to the main dataset.

    This is the same as `tf.fabric.Fabric.load` when called with `add=True`.

    Parameters
    ----------
    features: string | iterable
        Either a string containing space separated feature names, or an
        iterable of feature names.
        The feature names are just the names of `.tf` files
        without directory information and without extension.
    silent: boolean, optional `None`
        If `False`, the features will be loaded rather silently,
        most messages will be suppressed.
        Time consuming operations will always be announced,
        so that you know what Text-Fabric is doing.
        If `True` is passed, all informational messages will be suppressed.
        This is handy I you want to load data as part of other methods, on-the-fly.

    Returns
    -------
    boolean
        Whether the feature has been successfully loaded.
    """

    TF = self.TF
    return TF.load(features, add=True, silent=silent)
def reinit(self)

TF-Apps may override this method. It is called by reuse. Hence it needs to be present.

Expand source code Browse git
def reinit(self):
    """TF-Apps may override this method.
    It is called by `reuse`. Hence it needs to be present.
    """

    pass
def reuse(self, hoist=False)

Re-initialize the app.

The app's settings are read again, the app's code is re-imported, the app's stylesheets are applied again. But the data is left untouched, and no time-consuming reloading of data takes place.

Handy when you are developing a new app and want to experiment with it without the costly re-loading of the data in every cycle.

Parameters

hoist : boolean, optional False
Same as in App.

the effect of the config settings

If you are developing a TF app and need to see the effects of the configuration settings in detail, you can conveniently call reuse and showContext() in tandem.

Expand source code Browse git
def reuse(self, hoist=False):
    """Re-initialize the app.

    The app's settings are read again, the app's code is re-imported,
    the app's stylesheets are applied again.
    But the data is left untouched, and no time-consuming reloading of data
    takes place.

    Handy when you are developing a new app and want to experiment with it
    without the costly re-loading of the data in every cycle.

    Parameters
    ----------
    hoist: boolean, optional `False`
        Same as in `App`.

    !!! hint "the effect of the config settings"
        If you are developing a TF app and need to see the effects of
        the configuration settings in detail, you can conveniently
        call `reuse` and `tf.advanced.settings.showContext` in tandem.
    """

    backend = self.backend
    aContext = self.context
    appPath = aContext.appPath
    appName = aContext.appName
    local = aContext.local
    commit = aContext.commit
    release = aContext.release
    version = aContext.version
    api = self.api

    cfg = findAppConfig(
        appName,
        appPath,
        commit,
        release,
        local,
        backend,
        org=aContext.org,
        repo=aContext.repo,
        version=version,
    )
    findAppClass(appName, appPath)

    setAppSpecs(self, cfg, reset=True)

    if api:
        TF = self.TF
        TF._makeApi()
        api = TF.api
        self.api = api
        self.reinit()  # may be used by custom TF apps
        linksApi(self, True)
        searchApi(self)
        sectionsApi(self)
        setAppSpecsApi(self, cfg)
        displayApi(self, True)
        textApi(self)
        if hoist:
            api.makeAvailableIn(hoist)