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

+ 21 - 6
docs/usage.rst

@@ -134,18 +134,33 @@ Python API
 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.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
-    >>> env.app_globals = AppGlobals(config)
+    >>> config['application']['data_dir'] = '/data/ext/mystore'
+    >>> env.setup(config=config)
 
 Create and replace resources
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 197 - 7
lakesuperior/__init__.py

@@ -1,8 +1,13 @@
+import logging
 import threading
 
+from collections import deque
+from importlib import import_module
 from os import path
 
 
+logger = logging.getLogger(__name__)
+
 version = '1.0 alpha'
 release = '1.0.0a22'
 
@@ -23,13 +28,42 @@ class Env:
     Instances of this class contain the environment necessary to run a
     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()
 """
 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.
 The most important values to be stored are app_conf (either from
 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.::
 
-    >>> from lakesuperior.config_parser import config
-    >>> from lakesuperior.globals import AppGlobals
     >>> 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
 """
@@ -57,3 +99,151 @@ timestamps.
 
 :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
 
 from lakesuperior import env
-from lakesuperior.config_parser import parse_config
-from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.exceptions import (
         ChecksumValidationError, IncompatibleLdpTypeError)
 from lakesuperior.migrator import Migrator
-from lakesuperior.store.ldp_nr.default_layout import DefaultLayout as FileLayout
 
 __doc__ = """
 Admin API.
@@ -25,7 +22,7 @@ def stats():
     :rtype: dict
     :return: Store statistics, resource statistics.
     """
-    import lakesuperior.env_setup
+    env.setup()
     with env.app_globals.rdf_store.txn_ctx():
         repo_stats = {
             '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
     be added and triggered by different argument flags.
     """
+    env.setup()
     with env.app_globals.rdfly.store.txn_ctx():
         return set(env.app_globals.rdfly.find_refint_violations())
 
@@ -82,7 +80,9 @@ def fixity_check(uid):
     :raises: lakesuperior.exceptions.IncompatibleLdpTypeError: if the
         resource is not an LDP-NR.
     """
+    env.setup()
     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
 
     rsrc = rsrc_api.get(uid)

+ 3 - 4
lakesuperior/api/resource.py

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

+ 12 - 12
lakesuperior/config_parser.py

@@ -3,7 +3,6 @@ import sys
 
 from os import chdir, environ, getcwd, path
 
-import hiyapyco
 import yaml
 
 import lakesuperior
@@ -11,9 +10,7 @@ import lakesuperior
 logger = logging.getLogger(__name__)
 
 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.
 
@@ -24,6 +21,8 @@ This value can still be overridden by custom applications by passing the
 ``config_dir`` value to :func:`parse_config` explicitly.
 """
 
+core_config_dir = path.join(lakesuperior.basedir, 'core_config')
+
 
 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
     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.
 
     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.
     _config = {}
 
-    print('Reading configuration at {}'.format(config_dir))
+    logger.info(f'Reading configuration at {config_dir}')
 
     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']:
         _config['application']['data_dir'] = path.join(
@@ -84,8 +83,9 @@ def parse_config(config_dir=None):
     logger.info('Binary store location: {}'.format(
         _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.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())
 

+ 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.dictionaries.namespaces import ns_collection as nsc
 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_nr import LdpNr
 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.toolbox import RequestUtils
 

+ 7 - 3
lakesuperior/env_setup.py

@@ -1,6 +1,4 @@
 from lakesuperior import env
-from lakesuperior.config_parser import config
-from lakesuperior.globals import AppGlobals
 
 __doc__="""
 Default configuration.
@@ -10,6 +8,12 @@ Import this module to initialize the configuration for a production setup::
     >>> import lakesuperior.env_setup
 
 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
 
 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.globals import AppGlobals
 
 __doc__="""
 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.
     """
-    import lakesuperior.env_setup
+    env.setup()
 
     rdfly = env.app_globals.rdfly
     nonrdfly = env.app_globals.nonrdfly
@@ -81,6 +81,8 @@ def stats(human=False):
     @param human (bool) Whether to output the data in human-readable
     format.
     """
+    env.setup()
+    from lakesuperior.api import admin as admin_api
     stat_data = admin_api.stats()
     if human:
         click.echo(
@@ -96,7 +98,8 @@ def check_fixity(uid):
     """
     Check the fixity of a resource.
     """
-    import lakesuperior.env_setup
+    env.setup()
+    from lakesuperior.api import admin as admin_api
 
     try:
         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
     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()
 
@@ -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
     from this location.
     """
+    env.setup()
+    from lakesuperior.api import admin as admin_api
+
     logger.info('Migrating {} into a new repository on {}.'.format(
             src, dest))
     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 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):

+ 2 - 3
lakesuperior/migrator.py

@@ -13,8 +13,7 @@ from rdflib import Graph, URIRef
 from lakesuperior import env, basedir
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 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__)
@@ -119,7 +118,7 @@ class Migrator:
                     as config_file:
                 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.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.ldp_nr import LdpNr
 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.model.rdf.graph import Graph, from_rdf
 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 lakesuperior import env
-from lakesuperior.globals import RES_UPDATED
 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__)

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

@@ -19,8 +19,6 @@ from rdflib.compare import to_isomorphic
 from rdflib.namespace import RDF
 
 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.srv_mgd_terms import (
     srv_mgd_subjects, srv_mgd_predicates, srv_mgd_types)
@@ -28,10 +26,19 @@ from lakesuperior.exceptions import (
     InvalidResourceError, RefIntViolationError, ResourceNotExistsError,
     ServerManagedTermError, TombstoneError)
 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.util.toolbox import (
         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_INS_CNT_REL_URI = nsc['ldp'].memberSubject
 

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

@@ -82,7 +82,8 @@ cdef class Graph:
         opened::
 
             >>> from rdflib import URIRef
-            >>> from lakesuperior import env_setup, env
+            >>> from lakesuperior import env
+            >>> env.setup()
             >>> store = env.app_globals.rdf_store
             >>> # Or alternatively:
             >>> # 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
 
 # Environment must be set before importing the app factory function.
-import lakesuperior.env_setup
-
 from lakesuperior import env
-from lakesuperior.config_parser import config
-from lakesuperior.globals import AppGlobals
+env.setup()
 
 options = {
     'restrictions': [50],
-    'profile_dir': '/var/tmp/lsup_profiling'
+    #'profile_dir': '/var/tmp/lsup_profiling'
 }
 
 from lakesuperior.app import create_app
 
 def run():
-    fcrepo = create_app(config['application'])
+    fcrepo = create_app(env.app_globals.config['application'])
     fcrepo.wsgi_app = ProfilerMiddleware(fcrepo.wsgi_app, **options)
     fcrepo.config['PROFILE'] = True
     fcrepo.run(debug = True)

+ 2 - 5
lakesuperior/server.py

@@ -2,12 +2,9 @@ import logging
 
 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.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
 

+ 1 - 14
lakesuperior/store/base_lmdb_store.pyx

@@ -7,7 +7,7 @@ from contextlib import contextmanager
 from os import makedirs, path
 from shutil import rmtree
 
-from lakesuperior import env, wsgi
+from lakesuperior import env
 
 from lakesuperior.cy_include cimport cylmdb as lmdb
 
@@ -128,11 +128,6 @@ cdef class BaseLmdbStore:
     :rtype: dict
     """
 
-    readers_mult = 4
-    """
-    Number to multiply WSGI workers by to set the numer of LMDB reader slots.
-    """
-
     ### INIT & TEARDOWN ###
 
     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),
                 '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.
         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.srv_mgd_terms import  srv_mgd_subjects, \
         srv_mgd_predicates, srv_mgd_types
-from lakesuperior.globals import ROOT_RSRC_URI
 from lakesuperior.exceptions import (InvalidResourceError,
         ResourceNotExistsError, TombstoneError, PathSegmentError)
 from lakesuperior.model.rdf.graph import Graph

+ 2 - 1
lakesuperior/util/benchmark.py

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

+ 1 - 1
lakesuperior/util/toolbox.py

@@ -9,7 +9,7 @@ from rdflib import Graph
 from rdflib.term import URIRef, Variable
 
 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__)

+ 2 - 2
lakesuperior/wsgi.py

@@ -3,8 +3,7 @@ import os
 import yaml
 
 # 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__ = """
@@ -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.
 """
 
+main_config = parse_config()
 
 class __Defaults:
     """

+ 0 - 1
setup.py

@@ -178,7 +178,6 @@ extensions = [
 install_requires = [
     'CoilMQ',
     'Flask',
-    'HiYaPyCo',
     'PyYAML',
     'arrow',
     '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 (
         IncompatibleLdpTypeError, InvalidResourceError, ResourceNotExistsError,
         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