# Copyright © 2017 STRG.AT GmbH, Vienna, Austria
#
# This file is part of the The SCORE Framework.
#
# The SCORE Framework and all its parts are free software: you can redistribute
# them and/or modify them under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation which is in the
# file named COPYING.LESSER.txt.
#
# The SCORE Framework and all its parts are distributed without any WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. For more details see the GNU Lesser General Public
# License.
#
# If you have not received a copy of the GNU Lesser General Public License see
# http://www.gnu.org/licenses/.
#
# The License-Agreement realised between you as Licensee and STRG.AT GmbH as
# Licenser including the issue of its valid conclusion and its pre- and
# post-contractual effects is governed by the laws of Austria. Any disputes
# concerning this License-Agreement including the issue of its valid conclusion
# and its pre- and post-contractual effects are exclusively decided by the
# competent court, in whose district STRG.AT GmbH has its registered seat, at
# the discretion of STRG.AT GmbH also the competent court, in whose district the
# Licensee has his registered seat, an establishment or assets.
from collections import namedtuple
import enum
from fs import open_fs
import keyword
from score.init import extract_conf, ConfiguredModule, InitializationError
import urllib.parse
defaults = {
'ctx.member': 'fs',
}
[docs]def init(confdict, ctx=None):
"""
Initializes this module acoording to the :ref:`SCORE module initialization
guidelines <module_initialization>` with the following configuration keys:
:confkey:`ctx.member.url` :confdefault:`fs`
The name of the :term:`context member`, that should be registered with
the configured :mod:`score.ctx` module (if there is one). The default
value allows you to always access configured file systems within a
:class:`score.ctx.Context` like the following:
>>> ctx.fs['my_configured_fs']
:confkey:`path.*`
Configuration keys like `path.foo` are file system definitions that must
contain a valid :ref:`pyfilesystem URL <pyfilesystem:fs-urls>`. These
values automatically :meth:`register
<ConfiguredPyfilesystemModule.register_path>` paths during
initialization. The following example is for registering a memory file
system called `foo`:
.. code-block:: ini
path.foo = mem://
The only customization is a *scope* parameter, which can also define the
:term:`scope <pyfilesystem scope>` of a file system. Valid values for
this parameter are ``global`` (the default) and ``ctx``. The same
filesystem as above with a scope bound to a context would look like
this:
.. code-block:: ini
path.foo = mem://?scope=ctx
"""
conf = defaults.copy()
conf.update(confdict)
fsconf = ConfiguredPyfilesystemModule()
for name, url in extract_conf(conf, 'path.').items():
scope = 'global'
if '://' in url:
url = urllib.parse.urlsplit(url)
query = []
for key, value in urllib.parse.parse_qsl(url.query):
if key == 'scope':
scope = Scope(value)
else:
query.append((key, value))
url = [*url]
url[3] = query
url = urllib.parse.urlunsplit(url)
if '://' not in url:
# the URL probably does not contain a netloc part and urlunsplit
# generates something like 'mem:', which is actually valid.
# pyfilesystem, on the other hand, interprets this as a path on
# the local filesystem and tries to open it with OSFS.
# the fix is simple: append two slashes to the first colon
url = url.replace(':', '://', 1)
if scope == Scope.CONTEXT and not ctx:
import score.pyfilesystem
raise InitializationError(
score.pyfilesystem,
'Cannot register object with scope=ctx '
'without a configured ctx module')
try:
fsconf.register_path(name, url, scope=scope)
except Exception as e:
import score.pyfilesystem
raise InitializationError(score.pyfilesystem, str(e)) from e
if ctx and conf['ctx.member'].lower() != 'none':
ctx.register(conf['ctx.member'], fsconf._ctx_constructor)
return fsconf
Path = namedtuple('Path', ('name', 'url', 'scope'))
class Scope(enum.Enum):
"""
Valid scope values for registered paths.
"""
GLOBAL = 'global'
CONTEXT = 'ctx'
CTX = 'ctx'
class ContextProxy:
"""
A proxy object used for accessing file systems on a
:class:`context object <score.ctx.Context>`.
"""
def __init__(self, conf):
self._conf = conf
def __getattr__(self, name):
try:
return self[name]
except KeyError:
pass
raise AttributeError(name)
def __getitem__(self, name):
if not isinstance(name, str):
raise TypeError('Key must be string, received %s' % (str(name),))
if name not in self._conf.paths:
raise KeyError(name)
path = self._conf.paths[name]
if path.scope == Scope.GLOBAL:
return getattr(self._conf, name)
value = open_fs(path.url)
setattr(self, name, value)
return value