Browse Source

Config loading and parsing restructuring:

* Move AppGlobals to lakesuperior module and remove app_globals module
* Change env setup method
* Add deprecation notice for lakesuperior.env_setup
* Do not load WSGI config if not running the WSGI server
* Do not set LMDB readers based on WSGI parameters
* Move some constants to more specific modules
* Move core namespaces to new `core_config` folder
Stefano Cossu 4 years ago
parent
commit
5dea35d6d3

+ 1 - 2
conftest.py

@@ -7,7 +7,6 @@ from tempfile import gettempdir
 
 
 from lakesuperior import env
 from lakesuperior import env
 from lakesuperior.config_parser import parse_config
 from lakesuperior.config_parser import parse_config
-from lakesuperior.globals import AppGlobals
 from lakesuperior.util.generators import random_image
 from lakesuperior.util.generators import random_image
 
 
 
 
@@ -20,7 +19,7 @@ config['application']['store']['ldp_nr']['location'] = (
 config['application']['store']['ldp_rs']['location'] = (
 config['application']['store']['ldp_rs']['location'] = (
         path.join(data_dir, 'ldprs_store'))
         path.join(data_dir, 'ldprs_store'))
 
 
-env.app_globals = AppGlobals(config)
+env.setup(config=config)
 from lakesuperior.app import create_app
 from lakesuperior.app import create_app
 
 
 
 

+ 21 - 6
docs/usage.rst

@@ -134,18 +134,33 @@ Python API
 Set up the environment
 Set up the environment
 ~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~
 
 
-Before using the API, either do::
+Before importing the API modules or other Lakesuperior modules, the environment
+must be set up. This can be done in several ways. The simplest one is to rely
+on a default configuration directory set up by the ``FCREPO_CONFIG_DIR``
+environment variable::
 
 
-    >>> import lakesuperior.env_setup
+    >>> from lakesuperior import env
+    >>> env.setup()
+    Reading configuration at /my/default/config_dir
+
+If ``FCREPO_CONFIG_DIR`` is not set up, the ``etc.defaults`` location under
+the library root is used.
+
+Alternatively, a custom configuration directory can be specified::
+
+    >>> from lakesuperior import env
+    >>> env.setup('/my/config/dir')
+    Reading configuration at /my/custom/config_dir
 
 
-Or, to specify an alternative configuration::
+A configuration can also be loaded and modified before setting up the
+environment::
 
 
     >>> from lakesuperior import env
     >>> from lakesuperior import env
     >>> from lakesuperior.config_parser import parse_config
     >>> from lakesuperior.config_parser import parse_config
-    >>> from lakesuperior.globals import AppGlobals
-    >>> config = parse_config('/my/custom/config_dir')
+    >>> config = parse_config('/my/config/dir')
     Reading configuration at /my/custom/config_dir
     Reading configuration at /my/custom/config_dir
-    >>> env.app_globals = AppGlobals(config)
+    >>> config['application']['data_dir'] = '/data/ext/mystore'
+    >>> env.setup(config=config)
 
 
 Create and replace resources
 Create and replace resources
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 197 - 7
lakesuperior/__init__.py

@@ -1,8 +1,13 @@
+import logging
 import threading
 import threading
 
 
+from collections import deque
+from importlib import import_module
 from os import path
 from os import path
 
 
 
 
+logger = logging.getLogger(__name__)
+
 version = '1.0 alpha'
 version = '1.0 alpha'
 release = '1.0.0a22'
 release = '1.0.0a22'
 
 
@@ -23,13 +28,42 @@ class Env:
     Instances of this class contain the environment necessary to run a
     Instances of this class contain the environment necessary to run a
     self-standing instance of Lakesuperior in a Python environment.
     self-standing instance of Lakesuperior in a Python environment.
     """
     """
-    pass
+
+    def setup(self, config_dir=None, config=None):
+        """
+        Set the environment up.
+
+        This must be done before using the application.
+
+        This method will warn and not do anything if it has already been
+        called in the same runtime environment,
+
+        :param str config_dir: Path to a directory containing the
+            configuration ``.yml`` files. If this and ``config`` are omitted,
+            the configuration files are read from the default directory defined
+            in :py:meth:`~lakesuperior.config_parser.parse_config()`.
+
+        :param dict config: Fully-formed configuration as a dictionary. If
+            this is provided, ``config_dir`` is ignored. This is useful to
+            call ``parse_config()`` separately and modify the configuration
+            manually before passing it to the setup.
+        """
+        if hasattr(self, 'app_globals'):
+            logger.warn('The environment is already set up.')
+            return
+
+        if not config:
+            from .config_parser import parse_config
+            config = parse_config(config_dir)
+
+        self.app_globals = _AppGlobals(config)
+
 
 
 env = Env()
 env = Env()
 """
 """
 A pox on "globals are evil".
 A pox on "globals are evil".
 
 
-All-purpose bucket for storing global variables. Different environments
+Object for storing global variables. Different environments
 (e.g. webapp, test suite) put the appropriate value in it.
 (e.g. webapp, test suite) put the appropriate value in it.
 The most important values to be stored are app_conf (either from
 The most important values to be stored are app_conf (either from
 lakesuperior.config_parser.config or lakesuperior.config_parser.test_config)
 lakesuperior.config_parser.config or lakesuperior.config_parser.test_config)
@@ -37,13 +71,21 @@ and app_globals (obtained by an instance of lakesuperior.globals.AppGlobals).
 
 
 e.g.::
 e.g.::
 
 
-    >>> from lakesuperior.config_parser import config
-    >>> from lakesuperior.globals import AppGlobals
     >>> from lakesuperior import env
     >>> from lakesuperior import env
-    >>> env.app_globals = AppGlobals(config)
+    >>> env.setup()
+
+Or, with a custom configuration directory::
+
+    >>> from lakesuperior import env
+    >>> env.setup('/my/config/dir')
 
 
-This is automated in non-test environments by importing
-`lakesuperior.env_setup`.
+Or, to load a configuration and modify it before setting up the environment::
+
+    >>> from lakesuperior import env
+    >>> from lakesuperior.config_parser import parse_config
+    >>> config = parse_config(config_dir)
+    >>> config['application']['data_dir'] = '/data/ext/mystore'
+    >>> env.setup(config=config)
 
 
 :rtype: Object
 :rtype: Object
 """
 """
@@ -57,3 +99,151 @@ timestamps.
 
 
 :rtype: threading.local
 :rtype: threading.local
 """
 """
+
+
+## Private members. Nothing interesting here.
+
+class _AppGlobals:
+    """
+    Application Globals.
+
+    This class is instantiated and used as a carrier for all connections and
+    various global variables outside of the Flask app context.
+
+    The variables are set on initialization by passing a configuration dict.
+    Usually this is done when starting an application. The instance with the
+    loaded variables is then assigned to the :data:`lakesuperior.env`
+    global variable.
+
+    :see_also: lakesuperior.env.setup()
+
+    """
+    def __init__(self, config):
+        """
+        Generate global variables from configuration.
+        """
+        ## Initialize metadata store.
+        #from lakesuperior.store.metadata_store import MetadataStore
+
+        # Exposed globals.
+        self._config = config
+        #self._md_store = MetadataStore(path.join(
+        #        self.config['application']['data_dir'], 'metadata'),
+        #        create=True)
+        self._changelog = deque()
+
+
+    @property
+    def config(self):
+        """
+        Global configuration.
+
+        This is a collection of all configuration options **except** for the
+        WSGI configuration which is initialized at a different time and is
+        stored under :data:`lakesuperior.env.wsgi_options`.
+
+        *TODO:* Update class reference when interface will be separated from
+        implementation.
+        """
+        return self._config
+
+    @property
+    def rdfly(self):
+        """
+        Current RDF layout.
+
+        Lazy loaded because it needs the config to be set up.
+
+        This is an instance of
+        :class:`~lakesuperior.store.ldp_rs.rsrc_centric_layout.RsrcCentricLayout`.
+
+        *TODO:* Update class reference when interface will be separated from
+        implementation.
+        """
+        if not hasattr(self, '_rdfly'):
+            # Initialize RDF layout.
+            rdfly_mod_name = (
+                    self.config['application']['store']['ldp_rs']['layout'])
+            rdfly_mod = import_module('lakesuperior.store.ldp_rs.{}'.format(
+                    rdfly_mod_name))
+            rdfly_cls = getattr(rdfly_mod, self.camelcase(rdfly_mod_name))
+            #logger.info('RDF layout: {}'.format(rdfly_mod_name))
+            self._rdfly = rdfly_cls(
+                    self.config['application']['store']['ldp_rs'])
+
+        return self._rdfly
+
+    @property
+    def rdf_store(self):
+        """
+        Current RDF low-level store.
+
+        Lazy loaded because it needs the config to be set up.
+
+        This is an instance of
+        :class:`~lakesuperior.store.ldp_rs.lmdb_store.LmdbStore`.
+        """
+        return self.rdfly.store
+
+    @property
+    def nonrdfly(self):
+        """
+        Current non-RDF (binary contents) layout.
+
+        Lazy loaded because it needs the config to be set up.
+
+        This is an instance of
+        :class:`~lakesuperior.store.ldp_nr.base_non_rdf_layout.BaseNonRdfLayout`.
+        """
+        if not hasattr(self, '_nonrdfly'):
+            # Initialize file layout.
+            nonrdfly_mod_name = (
+                    self.config['application']['store']['ldp_nr']['layout'])
+            nonrdfly_mod = import_module('lakesuperior.store.ldp_nr.{}'.format(
+                    nonrdfly_mod_name))
+            nonrdfly_cls = getattr(nonrdfly_mod, self.camelcase(nonrdfly_mod_name))
+            #logger.info('Non-RDF layout: {}'.format(nonrdfly_mod_name))
+            self._nonrdfly = nonrdfly_cls(
+                    self.config['application']['store']['ldp_nr'])
+        return self._nonrdfly
+
+    #@property
+    #def md_store(self):
+    #    """
+    #    Metadata store (LMDB).
+
+    #    This is an instance of
+    #    :class:`~lakesuperior.store.metadata_store.MetadataStore`.
+    #    """
+    #    return self._md_store
+
+    @property
+    def messenger(self):
+        """
+        Current message handler.
+
+        Lazy loaded because it needs the config to be set up.
+
+        This is an instance of
+        :class:`~lakesuperior.messaging.messenger.Messenger`.
+        """
+        if not hasattr(self, '_messenger'):
+            from lakesuperior.messaging.messenger import Messenger
+            self._messenger  = Messenger(
+                    self.config['application']['messaging'])
+
+        return self._messenger
+
+    @property
+    def changelog(self):
+        return self._changelog
+
+
+    @staticmethod
+    def camelcase(word):
+        """
+        Convert a string with underscores to a camel-cased one.
+
+        Ripped from https://stackoverflow.com/a/6425628
+        """
+        return ''.join(x.capitalize() or '_' for x in word.split('_'))

+ 4 - 4
lakesuperior/api/admin.py

@@ -2,12 +2,9 @@ import hashlib
 import logging
 import logging
 
 
 from lakesuperior import env
 from lakesuperior import env
-from lakesuperior.config_parser import parse_config
-from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.exceptions import (
 from lakesuperior.exceptions import (
         ChecksumValidationError, IncompatibleLdpTypeError)
         ChecksumValidationError, IncompatibleLdpTypeError)
 from lakesuperior.migrator import Migrator
 from lakesuperior.migrator import Migrator
-from lakesuperior.store.ldp_nr.default_layout import DefaultLayout as FileLayout
 
 
 __doc__ = """
 __doc__ = """
 Admin API.
 Admin API.
@@ -25,7 +22,7 @@ def stats():
     :rtype: dict
     :rtype: dict
     :return: Store statistics, resource statistics.
     :return: Store statistics, resource statistics.
     """
     """
-    import lakesuperior.env_setup
+    env.setup()
     with env.app_globals.rdf_store.txn_ctx():
     with env.app_globals.rdf_store.txn_ctx():
         repo_stats = {
         repo_stats = {
             'rsrc_stats': env.app_globals.rdfly.count_rsrc(),
             'rsrc_stats': env.app_globals.rdfly.count_rsrc(),
@@ -62,6 +59,7 @@ def integrity_check():
     At the moment this is limited to referential integrity. Other checks can
     At the moment this is limited to referential integrity. Other checks can
     be added and triggered by different argument flags.
     be added and triggered by different argument flags.
     """
     """
+    env.setup()
     with env.app_globals.rdfly.store.txn_ctx():
     with env.app_globals.rdfly.store.txn_ctx():
         return set(env.app_globals.rdfly.find_refint_violations())
         return set(env.app_globals.rdfly.find_refint_violations())
 
 
@@ -82,7 +80,9 @@ def fixity_check(uid):
     :raises: lakesuperior.exceptions.IncompatibleLdpTypeError: if the
     :raises: lakesuperior.exceptions.IncompatibleLdpTypeError: if the
         resource is not an LDP-NR.
         resource is not an LDP-NR.
     """
     """
+    env.setup()
     from lakesuperior.api import resource as rsrc_api
     from lakesuperior.api import resource as rsrc_api
+    from lakesuperior.dictionaries.namespaces import ns_collection as nsc
     from lakesuperior.model.ldp.ldp_factory import LDP_NR_TYPE
     from lakesuperior.model.ldp.ldp_factory import LDP_NR_TYPE
 
 
     rsrc = rsrc_api.get(uid)
     rsrc = rsrc_api.get(uid)

+ 3 - 4
lakesuperior/api/resource.py

@@ -10,14 +10,12 @@ import arrow
 from rdflib import Literal
 from rdflib import Literal
 from rdflib.namespace import XSD
 from rdflib.namespace import XSD
 
 
-from lakesuperior.config_parser import config
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.exceptions import (
 from lakesuperior.exceptions import (
         InvalidResourceError, ResourceNotExistsError, TombstoneError)
         InvalidResourceError, ResourceNotExistsError, TombstoneError)
 from lakesuperior import env, thread_env
 from lakesuperior import env, thread_env
-from lakesuperior.globals import RES_DELETED, RES_UPDATED
 from lakesuperior.model.ldp.ldp_factory import LDP_NR_TYPE, LdpFactory
 from lakesuperior.model.ldp.ldp_factory import LDP_NR_TYPE, LdpFactory
-from lakesuperior.model.ldp.ldpr import Ldpr
+from lakesuperior.model.ldp.ldpr import RES_UPDATED, Ldpr
 from lakesuperior.util.toolbox import rel_uri_to_urn
 from lakesuperior.util.toolbox import rel_uri_to_urn
 
 
 
 
@@ -29,7 +27,8 @@ Primary API for resource manipulation.
 Quickstart::
 Quickstart::
 
 
     >>> # First import default configuration and globals—only done once.
     >>> # First import default configuration and globals—only done once.
-    >>> import lakesuperior.default_env
+    >>> from lakesuperior import env
+    >>> env.setup()
     >>> from lakesuperior.api import resource
     >>> from lakesuperior.api import resource
     >>> # Get root resource.
     >>> # Get root resource.
     >>> rsrc = resource.get('/')
     >>> rsrc = resource.get('/')

+ 12 - 12
lakesuperior/config_parser.py

@@ -3,7 +3,6 @@ import sys
 
 
 from os import chdir, environ, getcwd, path
 from os import chdir, environ, getcwd, path
 
 
-import hiyapyco
 import yaml
 import yaml
 
 
 import lakesuperior
 import lakesuperior
@@ -11,9 +10,7 @@ import lakesuperior
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
 default_config_dir = environ.get(
 default_config_dir = environ.get(
-        'FCREPO_CONFIG_DIR',
-        path.join(
-            path.dirname(path.abspath(lakesuperior.__file__)), 'etc.defaults'))
+        'FCREPO_CONFIG_DIR', path.join(lakesuperior.basedir, 'etc.defaults'))
 """
 """
 Default configuration directory.
 Default configuration directory.
 
 
@@ -24,6 +21,8 @@ This value can still be overridden by custom applications by passing the
 ``config_dir`` value to :func:`parse_config` explicitly.
 ``config_dir`` value to :func:`parse_config` explicitly.
 """
 """
 
 
+core_config_dir = path.join(lakesuperior.basedir, 'core_config')
+
 
 
 def parse_config(config_dir=None):
 def parse_config(config_dir=None):
     """
     """
@@ -31,7 +30,7 @@ def parse_config(config_dir=None):
 
 
     This is normally called by the standard endpoints (``lsup_admin``, web
     This is normally called by the standard endpoints (``lsup_admin``, web
     server, etc.) or by a Python client by importing
     server, etc.) or by a Python client by importing
-    :py:mod:`lakesuperior.env_setup` but an application using a non-default
+    :py:mod:`lakesuperior.env.setup()` but an application using a non-default
     configuration may specify an alternative configuration directory.
     configuration may specify an alternative configuration directory.
 
 
     The directory must have the same structure as the one provided in
     The directory must have the same structure as the one provided in
@@ -54,12 +53,12 @@ def parse_config(config_dir=None):
     # This will hold a dict of all configuration values.
     # This will hold a dict of all configuration values.
     _config = {}
     _config = {}
 
 
-    print('Reading configuration at {}'.format(config_dir))
+    logger.info(f'Reading configuration at {config_dir}')
 
 
     for cname in configs:
     for cname in configs:
-        file = path.join(config_dir, '{}.yml'.format(cname))
-        with open(file, 'r') as stream:
-            _config[cname] = yaml.load(stream, yaml.SafeLoader)
+        fname = path.join(config_dir, f'{cname}.yml')
+        with open(fname, 'r') as fh:
+            _config[cname] = yaml.load(fh, yaml.SafeLoader)
 
 
     if not _config['application']['data_dir']:
     if not _config['application']['data_dir']:
         _config['application']['data_dir'] = path.join(
         _config['application']['data_dir'] = path.join(
@@ -84,8 +83,9 @@ def parse_config(config_dir=None):
     logger.info('Binary store location: {}'.format(
     logger.info('Binary store location: {}'.format(
         _config['application']['store']['ldp_nr']['location']))
         _config['application']['store']['ldp_nr']['location']))
 
 
-    return _config
+    # Merge (and if overlapping, override) custom namespaces with core ones
+    with open(path.join(core_config_dir, 'namespaces.yml')) as fh:
+        _config['namespaces'].update(yaml.load(fh, yaml.SafeLoader))
 
 
 
 
-# Load default configuration.
-config = parse_config()
+    return _config

+ 22 - 0
lakesuperior/core_config/namespaces.yml

@@ -0,0 +1,22 @@
+# Core namespace prefixes. These add to and override any user-defined prefixes.
+# @TODO Some of these have been copy-pasted from FCREPO4 and may be deprecated.
+
+dc : "http://purl.org/dc/elements/1.1/"
+dcterms : "http://purl.org/dc/terms/"
+ebucore: "http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#"
+fcrepo: "http://fedora.info/definitions/fcrepo#"
+fcadmin: "info:fcsystem/graph/admin"
+fcres: "info:fcres"
+fcmain: "info:fcsystem/graph/userdata/_main"
+fcstruct: "info:fcsystem/graph/structure"
+fcsystem: "info:fcsystem/"
+foaf: "http://xmlns.com/foaf/0.1/"
+iana: "http://www.iana.org/assignments/relation/"
+ldp: "http://www.w3.org/ns/ldp#"
+pcdm: "http://pcdm.org/models#"
+premis: "http://www.loc.gov/premis/rdf/v1#"
+rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+rdfs : "http://www.w3.org/2000/01/rdf-schema#"
+webac: "http://www.w3.org/ns/auth/acl#"
+xsd : "http://www.w3.org/2001/XMLSchema#"
+

+ 4 - 27
lakesuperior/dictionaries/namespaces.py

@@ -3,34 +3,11 @@ import rdflib
 from rdflib import Graph
 from rdflib import Graph
 from rdflib.namespace import Namespace, NamespaceManager
 from rdflib.namespace import Namespace, NamespaceManager
 
 
-from lakesuperior.config_parser import config
+from lakesuperior import env
 
 
-# Core namespace prefixes. These add to and override any user-defined prefixes.
-# @TODO Some of these have been copy-pasted from FCREPO4 and may be deprecated.
-core_namespaces = {
-    'dc' : rdflib.namespace.DC,
-    'dcterms' : rdflib.namespace.DCTERMS,
-    'ebucore' : Namespace(
-        'http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#'),
-    'fcrepo' : Namespace('http://fedora.info/definitions/fcrepo#'),
-    'fcadmin' : Namespace('info:fcsystem/graph/admin'),
-    'fcres' : Namespace('info:fcres'),
-    'fcmain' : Namespace('info:fcsystem/graph/userdata/_main'),
-    'fcstruct' : Namespace('info:fcsystem/graph/structure'),
-    'fcsystem' : Namespace('info:fcsystem/'),
-    'foaf': Namespace('http://xmlns.com/foaf/0.1/'),
-    'iana' : Namespace('http://www.iana.org/assignments/relation/'),
-    'ldp' : Namespace('http://www.w3.org/ns/ldp#'),
-    'pcdm': Namespace('http://pcdm.org/models#'),
-    'premis' : Namespace('http://www.loc.gov/premis/rdf/v1#'),
-    'rdf' : rdflib.namespace.RDF,
-    'rdfs' : rdflib.namespace.RDFS,
-    'webac' : Namespace('http://www.w3.org/ns/auth/acl#'),
-    'xsd' : rdflib.namespace.XSD,
-}
-
-ns_collection  = {pfx: Namespace(ns) for pfx, ns in config['namespaces'].items()}
-ns_collection.update(core_namespaces)
+ns_collection  = {
+        pfx: Namespace(ns)
+        for pfx, ns in env.app_globals.config['namespaces'].items()}
 
 
 ns_mgr = NamespaceManager(Graph())
 ns_mgr = NamespaceManager(Graph())
 
 

+ 1 - 2
lakesuperior/endpoints/ldp.py

@@ -21,11 +21,10 @@ from lakesuperior import exceptions as exc
 from lakesuperior.api import resource as rsrc_api
 from lakesuperior.api import resource as rsrc_api
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
 from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
-from lakesuperior.globals import RES_CREATED
 from lakesuperior.model.ldp.ldp_factory import LdpFactory
 from lakesuperior.model.ldp.ldp_factory import LdpFactory
 from lakesuperior.model.ldp.ldp_nr import LdpNr
 from lakesuperior.model.ldp.ldp_nr import LdpNr
 from lakesuperior.model.ldp.ldp_rs import LdpRs
 from lakesuperior.model.ldp.ldp_rs import LdpRs
-from lakesuperior.model.ldp.ldpr import Ldpr
+from lakesuperior.model.ldp.ldpr import RES_CREATED, Ldpr
 from lakesuperior.util import toolbox
 from lakesuperior.util import toolbox
 from lakesuperior.util.toolbox import RequestUtils
 from lakesuperior.util.toolbox import RequestUtils
 
 

+ 7 - 3
lakesuperior/env_setup.py

@@ -1,6 +1,4 @@
 from lakesuperior import env
 from lakesuperior import env
-from lakesuperior.config_parser import config
-from lakesuperior.globals import AppGlobals
 
 
 __doc__="""
 __doc__="""
 Default configuration.
 Default configuration.
@@ -10,6 +8,12 @@ Import this module to initialize the configuration for a production setup::
     >>> import lakesuperior.env_setup
     >>> import lakesuperior.env_setup
 
 
 Will load the default configuration.
 Will load the default configuration.
+
+**Note:** this will be deprecated because it's just as easy to do the same with
+
+::
+    >>> import env # which in most cases is imported anyways
+    >>> env.setup()
 """
 """
 
 
-env.app_globals = AppGlobals(config)
+env.setup()

+ 0 - 161
lakesuperior/globals.py

@@ -1,161 +0,0 @@
-import logging
-
-from collections import deque
-from importlib import import_module
-from os import path
-
-from lakesuperior.dictionaries.namespaces import ns_collection as nsc
-
-RES_CREATED = '_create_'
-"""A resource was created."""
-RES_DELETED = '_delete_'
-"""A resource was deleted."""
-RES_UPDATED = '_update_'
-"""A resource was updated."""
-
-ROOT_UID = '/'
-"""Root node UID."""
-ROOT_RSRC_URI = nsc['fcres'][ROOT_UID]
-"""Internal URI of root resource."""
-
-
-class AppGlobals:
-    """
-    Application Globals.
-
-    This class is instantiated and used as a carrier for all connections and
-    various global variables outside of the Flask app context.
-
-    The variables are set on initialization by passing a configuration dict.
-    Usually this is done when starting an application. The instance with the
-    loaded variables is then assigned to the :data:`lakesuperior.env`
-    global variable.
-
-    You can either load the default configuration::
-
-        >>>from lakesuperior import env_setup
-
-    Or set up an environment with a custom configuration::
-
-        >>> from lakesuperior import env
-        >>> from lakesuperior.app_globals import AppGlobals
-        >>> my_config = {'name': 'value', '...': '...'}
-        >>> env.app_globals = AppGlobals(my_config)
-
-    """
-    def __init__(self, config):
-        """
-        Generate global variables from configuration.
-        """
-        from lakesuperior.messaging.messenger import Messenger
-
-        app_conf = config['application']
-
-        # Initialize RDF layout.
-        rdfly_mod_name = app_conf['store']['ldp_rs']['layout']
-        rdfly_mod = import_module('lakesuperior.store.ldp_rs.{}'.format(
-                rdfly_mod_name))
-        rdfly_cls = getattr(rdfly_mod, self.camelcase(rdfly_mod_name))
-        #logger.info('RDF layout: {}'.format(rdfly_mod_name))
-
-        # Initialize file layout.
-        nonrdfly_mod_name = app_conf['store']['ldp_nr']['layout']
-        nonrdfly_mod = import_module('lakesuperior.store.ldp_nr.{}'.format(
-                nonrdfly_mod_name))
-        nonrdfly_cls = getattr(nonrdfly_mod, self.camelcase(nonrdfly_mod_name))
-        #logger.info('Non-RDF layout: {}'.format(nonrdfly_mod_name))
-
-        ## Initialize metadata store.
-        #from lakesuperior.store.metadata_store import MetadataStore
-
-        # Set up messaging.
-        self._messenger  = Messenger(app_conf['messaging'])
-
-        # Exposed globals.
-        self._config = config
-        self._rdfly = rdfly_cls(app_conf['store']['ldp_rs'])
-        self._nonrdfly = nonrdfly_cls(app_conf['store']['ldp_nr'])
-        #self._md_store = MetadataStore(path.join(
-        #        app_conf['data_dir'], 'metadata'), create=True)
-        self._changelog = deque()
-
-
-    @property
-    def config(self):
-        """
-        Global configuration.
-
-        This is a collection of all configuration options **except** for the
-        WSGI configuration which is initialized at a different time and is
-        stored under :data:`lakesuperior.env.wsgi_options`.
-
-        *TODO:* Update class reference when interface will be separated from
-        implementation.
-        """
-        return self._config
-
-    @property
-    def rdfly(self):
-        """
-        Current RDF layout.
-
-        This is an instance of
-        :class:`~lakesuperior.store.ldp_rs.rsrc_centric_layout.RsrcCentricLayout`.
-
-        *TODO:* Update class reference when interface will be separated from
-        implementation.
-        """
-        return self._rdfly
-
-    @property
-    def rdf_store(self):
-        """
-        Current RDF low-level store.
-
-        This is an instance of
-        :class:`~lakesuperior.store.ldp_rs.lmdb_store.LmdbStore`.
-        """
-        return self._rdfly.store
-
-    @property
-    def nonrdfly(self):
-        """
-        Current non-RDF (binary contents) layout.
-
-        This is an instance of
-        :class:`~lakesuperior.store.ldp_nr.base_non_rdf_layout.BaseNonRdfLayout`.
-        """
-        return self._nonrdfly
-
-    #@property
-    #def md_store(self):
-    #    """
-    #    Metadata store (LMDB).
-
-    #    This is an instance of
-    #    :class:`~lakesuperior.store.metadata_store.MetadataStore`.
-    #    """
-    #    return self._md_store
-
-    @property
-    def messenger(self):
-        """
-        Current message handler.
-
-        This is an instance of
-        :class:`~lakesuperior.messaging.messenger.Messenger`.
-        """
-        return self._messenger
-
-    @property
-    def changelog(self):
-        return self._changelog
-
-
-    def camelcase(self, word):
-        """
-        Convert a string with underscores to a camel-cased one.
-
-        Ripped from https://stackoverflow.com/a/6425628
-        """
-        return ''.join(x.capitalize() or '_' for x in word.split('_'))

+ 13 - 9
lakesuperior/lsup_admin.py

@@ -10,10 +10,10 @@ from os import getcwd, path
 import arrow
 import arrow
 
 
 from lakesuperior import env
 from lakesuperior import env
-from lakesuperior.api import admin as admin_api
-from lakesuperior.config_parser import config
+# Do not set up environment yet. Some methods need special setup folders.
+# As a consequence, API modules must be imported inside each function after
+# setup.
 from lakesuperior.exceptions import ChecksumValidationError
 from lakesuperior.exceptions import ChecksumValidationError
-from lakesuperior.globals import AppGlobals
 
 
 __doc__="""
 __doc__="""
 Utility to perform core maintenance tasks via console command-line.
 Utility to perform core maintenance tasks via console command-line.
@@ -44,7 +44,7 @@ def bootstrap():
 
 
     Additional scaffolding files may be parsed to create initial contents.
     Additional scaffolding files may be parsed to create initial contents.
     """
     """
-    import lakesuperior.env_setup
+    env.setup()
 
 
     rdfly = env.app_globals.rdfly
     rdfly = env.app_globals.rdfly
     nonrdfly = env.app_globals.nonrdfly
     nonrdfly = env.app_globals.nonrdfly
@@ -81,6 +81,8 @@ def stats(human=False):
     @param human (bool) Whether to output the data in human-readable
     @param human (bool) Whether to output the data in human-readable
     format.
     format.
     """
     """
+    env.setup()
+    from lakesuperior.api import admin as admin_api
     stat_data = admin_api.stats()
     stat_data = admin_api.stats()
     if human:
     if human:
         click.echo(
         click.echo(
@@ -96,7 +98,8 @@ def check_fixity(uid):
     """
     """
     Check the fixity of a resource.
     Check the fixity of a resource.
     """
     """
-    import lakesuperior.env_setup
+    env.setup()
+    from lakesuperior.api import admin as admin_api
 
 
     try:
     try:
         admin_api.fixity_check(uid)
         admin_api.fixity_check(uid)
@@ -132,10 +135,8 @@ def check_refint(config_folder=None, output=None):
     Note: this check can be run regardless of whether the repository enforces
     Note: this check can be run regardless of whether the repository enforces
     referential integrity.
     referential integrity.
     """
     """
-    if config_folder:
-        env.app_globals = AppGlobals(parse_config(config_dir))
-    else:
-        import lakesuperior.env_setup
+    env.setup(config_folder)
+    from lakesuperior.api import admin as admin_api
 
 
     check_results = admin_api.integrity_check()
     check_results = admin_api.integrity_check()
 
 
@@ -211,6 +212,9 @@ def migrate(src, dest, auth, start, list_file, zero_binaries, skip_errors):
     configuration directory. The new repository can be immediately started
     configuration directory. The new repository can be immediately started
     from this location.
     from this location.
     """
     """
+    env.setup()
+    from lakesuperior.api import admin as admin_api
+
     logger.info('Migrating {} into a new repository on {}.'.format(
     logger.info('Migrating {} into a new repository on {}.'.format(
             src, dest))
             src, dest))
     src_auth = tuple(auth.split(':')) if auth else None
     src_auth = tuple(auth.split(':')) if auth else None

+ 1 - 1
lakesuperior/messaging/formatters.py

@@ -4,7 +4,7 @@ import uuid
 
 
 from abc import ABCMeta, abstractmethod
 from abc import ABCMeta, abstractmethod
 
 
-from lakesuperior.globals import RES_CREATED, RES_DELETED, RES_UPDATED
+from lakesuperior.model.ldp.ldpr import RES_CREATED, RES_DELETED, RES_UPDATED
 
 
 
 
 class BaseASFormatter(metaclass=ABCMeta):
 class BaseASFormatter(metaclass=ABCMeta):

+ 2 - 3
lakesuperior/migrator.py

@@ -13,8 +13,7 @@ from rdflib import Graph, URIRef
 from lakesuperior import env, basedir
 from lakesuperior import env, basedir
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.exceptions import InvalidResourceError
 from lakesuperior.exceptions import InvalidResourceError
-from lakesuperior.globals import AppGlobals, ROOT_UID
-from lakesuperior.config_parser import parse_config
+from lakesuperior.store.ldp_rs import ROOT_UID
 
 
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
@@ -119,7 +118,7 @@ class Migrator:
                     as config_file:
                     as config_file:
                 config_file.write(yaml.dump(orig_config['application']))
                 config_file.write(yaml.dump(orig_config['application']))
 
 
-        env.app_globals = AppGlobals(parse_config(self.config_dir))
+        env.setup(self.config_dir)
 
 
         self.rdfly = env.app_globals.rdfly
         self.rdfly = env.app_globals.rdfly
         self.nonrdfly = env.app_globals.nonrdfly
         self.nonrdfly = env.app_globals.nonrdfly

+ 0 - 1
lakesuperior/model/ldp/ldp_factory.py

@@ -11,7 +11,6 @@ from lakesuperior import exceptions as exc
 from lakesuperior.model.ldp.ldpr import Ldpr
 from lakesuperior.model.ldp.ldpr import Ldpr
 from lakesuperior.model.ldp.ldp_nr import LdpNr
 from lakesuperior.model.ldp.ldp_nr import LdpNr
 from lakesuperior.model.ldp.ldp_rs import LdpRs, Ldpc, LdpDc, LdpIc
 from lakesuperior.model.ldp.ldp_rs import LdpRs, Ldpc, LdpDc, LdpIc
-from lakesuperior.config_parser import config
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.model.rdf.graph import Graph, from_rdf
 from lakesuperior.model.rdf.graph import Graph, from_rdf
 from lakesuperior.util.toolbox import rel_uri_to_urn
 from lakesuperior.util.toolbox import rel_uri_to_urn

+ 1 - 2
lakesuperior/model/ldp/ldp_rs.py

@@ -3,9 +3,8 @@ import logging
 from rdflib import Graph
 from rdflib import Graph
 
 
 from lakesuperior import env
 from lakesuperior import env
-from lakesuperior.globals import RES_UPDATED
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
-from lakesuperior.model.ldp.ldpr import Ldpr
+from lakesuperior.model.ldp.ldpr import RES_UPDATED, Ldpr
 
 
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)

+ 9 - 2
lakesuperior/model/ldp/ldpr.py

@@ -19,8 +19,6 @@ from rdflib.compare import to_isomorphic
 from rdflib.namespace import RDF
 from rdflib.namespace import RDF
 
 
 from lakesuperior import env, thread_env
 from lakesuperior import env, thread_env
-from lakesuperior.globals import (
-    RES_CREATED, RES_DELETED, RES_UPDATED, ROOT_UID)
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.srv_mgd_terms import (
 from lakesuperior.dictionaries.srv_mgd_terms import (
     srv_mgd_subjects, srv_mgd_predicates, srv_mgd_types)
     srv_mgd_subjects, srv_mgd_predicates, srv_mgd_types)
@@ -28,10 +26,19 @@ from lakesuperior.exceptions import (
     InvalidResourceError, RefIntViolationError, ResourceNotExistsError,
     InvalidResourceError, RefIntViolationError, ResourceNotExistsError,
     ServerManagedTermError, TombstoneError)
     ServerManagedTermError, TombstoneError)
 from lakesuperior.model.rdf.graph import Graph
 from lakesuperior.model.rdf.graph import Graph
+from lakesuperior.store.ldp_rs import ROOT_UID
 from lakesuperior.store.ldp_rs.rsrc_centric_layout import VERS_CONT_LABEL
 from lakesuperior.store.ldp_rs.rsrc_centric_layout import VERS_CONT_LABEL
 from lakesuperior.util.toolbox import (
 from lakesuperior.util.toolbox import (
         rel_uri_to_urn_string, replace_term_domain)
         rel_uri_to_urn_string, replace_term_domain)
 
 
+
+RES_CREATED = '_create_'
+"""A resource was created."""
+RES_DELETED = '_delete_'
+"""A resource was deleted."""
+RES_UPDATED = '_update_'
+"""A resource was updated."""
+
 DEF_MBR_REL_URI = nsc['ldp'].member
 DEF_MBR_REL_URI = nsc['ldp'].member
 DEF_INS_CNT_REL_URI = nsc['ldp'].memberSubject
 DEF_INS_CNT_REL_URI = nsc['ldp'].memberSubject
 
 

+ 2 - 1
lakesuperior/model/rdf/graph.pyx

@@ -82,7 +82,8 @@ cdef class Graph:
         opened::
         opened::
 
 
             >>> from rdflib import URIRef
             >>> from rdflib import URIRef
-            >>> from lakesuperior import env_setup, env
+            >>> from lakesuperior import env
+            >>> env.setup()
             >>> store = env.app_globals.rdf_store
             >>> store = env.app_globals.rdf_store
             >>> # Or alternatively:
             >>> # Or alternatively:
             >>> # from lakesuperior.store.ldp_rs.lmdb_store import LmdbStore
             >>> # from lakesuperior.store.ldp_rs.lmdb_store import LmdbStore

+ 3 - 6
lakesuperior/profiler.py

@@ -3,21 +3,18 @@ from logging.config import dictConfig
 from werkzeug.contrib.profiler import ProfilerMiddleware
 from werkzeug.contrib.profiler import ProfilerMiddleware
 
 
 # Environment must be set before importing the app factory function.
 # Environment must be set before importing the app factory function.
-import lakesuperior.env_setup
-
 from lakesuperior import env
 from lakesuperior import env
-from lakesuperior.config_parser import config
-from lakesuperior.globals import AppGlobals
+env.setup()
 
 
 options = {
 options = {
     'restrictions': [50],
     'restrictions': [50],
-    'profile_dir': '/var/tmp/lsup_profiling'
+    #'profile_dir': '/var/tmp/lsup_profiling'
 }
 }
 
 
 from lakesuperior.app import create_app
 from lakesuperior.app import create_app
 
 
 def run():
 def run():
-    fcrepo = create_app(config['application'])
+    fcrepo = create_app(env.app_globals.config['application'])
     fcrepo.wsgi_app = ProfilerMiddleware(fcrepo.wsgi_app, **options)
     fcrepo.wsgi_app = ProfilerMiddleware(fcrepo.wsgi_app, **options)
     fcrepo.config['PROFILE'] = True
     fcrepo.config['PROFILE'] = True
     fcrepo.run(debug = True)
     fcrepo.run(debug = True)

+ 2 - 5
lakesuperior/server.py

@@ -2,12 +2,9 @@ import logging
 
 
 from logging.config import dictConfig
 from logging.config import dictConfig
 
 
-# Environment must be set before importing the app factory function.
-import lakesuperior.env_setup
-
 from lakesuperior import env
 from lakesuperior import env
-from lakesuperior.config_parser import config
-from lakesuperior.globals import AppGlobals
+# Environment must be set before importing the app factory function.
+env.setup()
 
 
 from lakesuperior.app import create_app
 from lakesuperior.app import create_app
 
 

+ 1 - 14
lakesuperior/store/base_lmdb_store.pyx

@@ -7,7 +7,7 @@ from contextlib import contextmanager
 from os import makedirs, path
 from os import makedirs, path
 from shutil import rmtree
 from shutil import rmtree
 
 
-from lakesuperior import env, wsgi
+from lakesuperior import env
 
 
 from lakesuperior.cy_include cimport cylmdb as lmdb
 from lakesuperior.cy_include cimport cylmdb as lmdb
 
 
@@ -128,11 +128,6 @@ cdef class BaseLmdbStore:
     :rtype: dict
     :rtype: dict
     """
     """
 
 
-    readers_mult = 4
-    """
-    Number to multiply WSGI workers by to set the numer of LMDB reader slots.
-    """
-
     ### INIT & TEARDOWN ###
     ### INIT & TEARDOWN ###
 
 
     def __init__(self, env_path, open_env=True, create=True):
     def __init__(self, env_path, open_env=True, create=True):
@@ -211,14 +206,6 @@ cdef class BaseLmdbStore:
                 lmdb.mdb_env_set_maxdbs(self.dbenv, max_dbs),
                 lmdb.mdb_env_set_maxdbs(self.dbenv, max_dbs),
                 'Error setting max. databases: {}')
                 'Error setting max. databases: {}')
 
 
-        # Set max readers.
-        self._readers = self.options.get(
-                'max_spare_txns', wsgi.workers * self.readers_mult)
-        _check(
-                lmdb.mdb_env_set_maxreaders(self.dbenv, self._readers),
-                'Error setting max. readers: {}')
-        logger.debug('Max. readers: {}'.format(self._readers))
-
         # Clear stale readers.
         # Clear stale readers.
         self._clear_stale_readers()
         self._clear_stale_readers()
 
 

+ 7 - 0
lakesuperior/store/ldp_rs/__init__.py

@@ -0,0 +1,7 @@
+from lakesuperior.dictionaries.namespaces import ns_collection as nsc
+
+ROOT_UID = '/'
+"""Root node UID."""
+ROOT_RSRC_URI = nsc['fcres'][ROOT_UID]
+"""Internal URI of root resource."""
+

+ 0 - 1
lakesuperior/store/ldp_rs/rsrc_centric_layout.py

@@ -21,7 +21,6 @@ from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
 from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
 from lakesuperior.dictionaries.srv_mgd_terms import  srv_mgd_subjects, \
 from lakesuperior.dictionaries.srv_mgd_terms import  srv_mgd_subjects, \
         srv_mgd_predicates, srv_mgd_types
         srv_mgd_predicates, srv_mgd_types
-from lakesuperior.globals import ROOT_RSRC_URI
 from lakesuperior.exceptions import (InvalidResourceError,
 from lakesuperior.exceptions import (InvalidResourceError,
         ResourceNotExistsError, TombstoneError, PathSegmentError)
         ResourceNotExistsError, TombstoneError, PathSegmentError)
 from lakesuperior.model.rdf.graph import Graph
 from lakesuperior.model.rdf.graph import Graph

+ 2 - 1
lakesuperior/util/benchmark.py

@@ -124,7 +124,8 @@ def run(
         requests.put(parent)
         requests.put(parent)
 
 
     elif mode == 'python':
     elif mode == 'python':
-        from lakesuperior import env_setup
+        from lakesuperior import env
+        env.setup()
         from lakesuperior.api import resource as rsrc_api
         from lakesuperior.api import resource as rsrc_api
 
 
         if delete_container:
         if delete_container:

+ 1 - 1
lakesuperior/util/toolbox.py

@@ -9,7 +9,7 @@ from rdflib import Graph
 from rdflib.term import URIRef, Variable
 from rdflib.term import URIRef, Variable
 
 
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
-from lakesuperior.globals import ROOT_RSRC_URI
+from lakesuperior.store.ldp_rs import ROOT_RSRC_URI
 
 
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)

+ 2 - 2
lakesuperior/wsgi.py

@@ -3,8 +3,7 @@ import os
 import yaml
 import yaml
 
 
 # DO NOT load env_setup here. It must be loaded after workers have been forked.
 # DO NOT load env_setup here. It must be loaded after workers have been forked.
-from lakesuperior.config_parser import (
-        config as main_config, default_config_dir)
+from lakesuperior.config_parser import default_config_dir, parse_config
 
 
 
 
 __doc__ = """
 __doc__ = """
@@ -41,6 +40,7 @@ Not sure if this does anything—GUnicorn doesn't seem to use ``import *``—but
 at least good for maintainers of this code.
 at least good for maintainers of this code.
 """
 """
 
 
+main_config = parse_config()
 
 
 class __Defaults:
 class __Defaults:
     """
     """

+ 0 - 1
setup.py

@@ -178,7 +178,6 @@ extensions = [
 install_requires = [
 install_requires = [
     'CoilMQ',
     'CoilMQ',
     'Flask',
     'Flask',
-    'HiYaPyCo',
     'PyYAML',
     'PyYAML',
     'arrow',
     'arrow',
     'click',
     'click',

+ 1 - 2
tests/2_api/test_2_0_resource_api.py

@@ -12,8 +12,7 @@ from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.exceptions import (
 from lakesuperior.exceptions import (
         IncompatibleLdpTypeError, InvalidResourceError, ResourceNotExistsError,
         IncompatibleLdpTypeError, InvalidResourceError, ResourceNotExistsError,
         TombstoneError)
         TombstoneError)
-from lakesuperior.globals import RES_CREATED, RES_UPDATED
-from lakesuperior.model.ldp.ldpr import Ldpr
+from lakesuperior.model.ldp.ldpr import Ldpr, RES_CREATED, RES_UPDATED
 from lakesuperior.model.rdf.graph import Graph, from_rdf
 from lakesuperior.model.rdf.graph import Graph, from_rdf