Skip to content

Commit

Permalink
Merge pull request #75 from koenedaele/DEV_0.9
Browse files Browse the repository at this point in the history
Dev 0.9
  • Loading branch information
koenedaele committed Aug 6, 2020
2 parents 9bb3baf + 1394f46 commit 41b43ac
Show file tree
Hide file tree
Showing 20 changed files with 699 additions and 120 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ sudo: false
language: python
python:
- 2.7
- 3.5
- 3.6
- 3.7
- 3.8
install:
- pip install -r requirements-dev.txt
- python setup.py develop
Expand Down
13 changes: 13 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
0.9.0 (2020-08-06)
------------------

- Support running a registry per request, as opposed to per application as
before. (#44)
- Add the `infer_concept_relations` attribute to the collection renderer. (#73)
- Add JSON-LD output to the REST service. (#63)
- Add support for match and match_type search parameters to search for concepts
that match a certain URI and optionally have a certain type. (#68)
- Drop support for Python 3.4, add support for 3.7 and 3.8. This is the last version
that will support Python 2. (#66)
- Remove the JSON renderers from the utils module.

0.8.0 (2017-07-12)
------------------

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2013-2016 Koen Van Daele
Copyright (c) 2013-2020 Koen Van Daele

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Expand Down
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,16 @@

# General information about the project.
project = u'Pyramid Skosprovider'
copyright = u'2013-2017, Koen Van Daele'
copyright = u'2013-2020, Koen Van Daele'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.8'
version = '0.9'
# The full version, including alpha/beta/rc tags.
release = '0.8.0'
release = '0.9.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
131 changes: 128 additions & 3 deletions docs/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,137 @@ To install `pyramid_skosprovider`, use pip
pip install pyramid_skosprovider
To activate `pyramid_skosprovider`, just include it.
To activate `pyramid_skosprovider`, you need to include it and configure your
:class:`skosprovider.registry.Registry`. Older versions, before `0.9.0`,
attached the `skosregistry` to the Pyramid application registry. Starting with
`0.9.0` it's possible and recommended to attach the skos registry to a request.
The old way is still allowed for backward compatibility, but should only be
used with local providers that store all their data in memory, such as a
:class:`skosprovider.providers.DictionaryProvider`.

To maintain the old way and enable the new one, two new settings were added in
`pyramid_skosprovider` `0.9.0`:

*skosprovider.skosregistry_location* deteremines where your registry lives in
the application. Currently two options are supported: `registry` or `request`.
The first attaches the :class:`skosprovider.registry.Registry` to the Pyramid
application registry as the Pyramid application is started. The second option
attaches the skos registry to the Pyramid request. This ensures every request
has it's own registry.

*skosprovider.skosregistry_factory* allows you to specify a factory function
for instantiating the :class:`skosprovider.registry.Registry`. This function
will receive the Pyramid request as a first argument if you are using a
registry attached to a request. If you do not specify a factory, an empty skos
registry will be created and used.

Please be aware that attaching a registry to the Pyramid application registry
was the only option before `0.9.0`. It is still supported because it makes
sense for a registry that only contains providers that load all their data in
memory on initialisation. Providers that require a database connection should
always be attached to a request.

Supposing you want to attach your registry to requests, you would write some
configuration. A possible configuration for a `myskos` application would be:

.. code-block:: ini
skosprovider.skosregistry_location: request
skosprovider.skosregistry_factory: myskos.skos.build_registry
To actually use `pyramid_skosprovider`, you still need to include it in your
pyramid application and write the factory function. In your Pyramid startup
function:

.. code-block:: python
config = Configurator()
config.include('pyramid_skosprovider')
This will create a :class:`skosprovider.registry.Registry` and add it to
the pyramid application registry.
This will add some views and configuration. Every request will now contain a
`skos_registry` attribute. The first time this attribute is accessed, the
SKOS registy will be build for you, using the specified factory function.

Your `myskos.skos` python module should contain this factory function. In our
example config, we called it `build_registry`. This is a function that receives
a Pyramid request, creates the skos registry and returns it:

.. code-block:: python
from skosprovider.registry import Registry
from skosprovider.providers import DictionaryProvider
def build_registry(request):
r = Registry(
instance_scope='threaded_thread'
)
dictprovider = DictionaryProvider(
{
'id': 'TREES',
'default_language': 'nl',
'subject': ['biology'],
'dataset': {
'uri': 'http://id.trees.org/dataset'
}
},
[],
uri_generator=UriPatternGenerator('http://id.trees.org/types/%s'),
concept_scheme=ConceptScheme('http://id.trees.org')
)
r.register_provider(dictprovider)
return r
This is a very simple example. A typical real-life application would have
several providers. Some of them might be DictionaryProviders, others might
reaf from rdf files and still others might read from a SQL Databases. If you're
using the `skosprovider_sqlalchemy` provider, you would attach your database
session maker to the request and then pass it on to the SQLAlchemy provider in
your factory function.

If you want to attach the SKOS registry to the Pyramid registry, and not the
request, you would have the following config:


.. code-block:: ini
skosprovider.skosregistry_location: registry
skosprovider.skosregistry_factory: myskos.skos.build_registry
The `build_registry` factory would be very similar, but it does not have acces
to the request. This makes it a bad fit for threaded web-servers and leads to
bugs. But something like a :class:`skosprovider.providers.DictionaryProvider`
wpuld be fine. The factory function is almost identical, but we would also set
the Registry instance_scope to `threaded_global`. This can alert providers that
register with the registry that they might not be compatible.
`

.. code-block:: python

from skosprovider.registry import Registry
from skosprovider.providers import DictionaryProvider

def build_registry():

r = Registry(
instance_scope='threaded_global'
)

dictprovider = DictionaryProvider(
{
'id': 'TREES',
'default_language': 'nl',
'subject': ['biology'],
'dataset': {
'uri': 'http://id.trees.org/dataset'
}
},
[],
uri_generator=UriPatternGenerator('http://id.trees.org/types/%s'),
concept_scheme=ConceptScheme('http://id.trees.org')
)
r.register_provider(dictprovider)

return r
8 changes: 8 additions & 0 deletions docs/services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ The following API can be used by clients:
:query sort: Define if you want to sort the results by a given field. Otherwise items are returned
in an indeterminate order. Prefix with '+' to sort ascending, '-' to sort descending.
eg. ``?sort=-label`` to sort all results descending by label.
:query match: A URI for an external concept. Searches if any of the
providers have a matching concept.
:query match_type: A type of match: exact, close, related, broader,
narrower. Only used if a match URI is present as well.
:query providers.ids: A comma separated list of concept scheme id's. The query
will only be passed to the providers with these id's. eg.
``?providers.ids=TREES, PARROTS`` will only list concepts from these two providers.
Expand Down Expand Up @@ -398,6 +402,10 @@ The following API can be used by clients:
Expects to be passed an id of a collection in this scheme. Will restrict
the search to concepts or collections that are a member of this collection
or a narrower concept of a member.
:query match: A URI for an external concept. Searches if any of the
providers have a matching concept.
:query match_type: A type of match: exact, close, related, broader,
narrower. Only used if a match URI is present as well.
:query language: Returns the label with the corresponding language-tag if present.
If the language is not present for this concept/collection, it falls back to
1) the default language of the provider. 2) 'en' 3) any label.
Expand Down
11 changes: 5 additions & 6 deletions docs/usage.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Usage
=====

To get a :class:`skosprovider.registry.Registry` instance, call
:func:`pyramid_skosprovider.get_skos_registry` with the current
application registry.
To get a :class:`skosprovider.registry.Registry` instance that
was configured globally, call :func:`pyramid_skosprovider.get_skos_registry`
with the current application registry.

Eg. in a view:

Expand All @@ -16,12 +16,11 @@ Eg. in a view:
providers = skos.get_providers()
# ...
Alternatively you can get the registry as an attribute of a pyramid request:
Since this only works for globally configured registries, it's not the preferred
way. Alternatively you can get the registry as an attribute of a pyramid request:

.. code-block:: python
from pyramid_skosprovider import get_skos_registry
def my_view(request):
skos = request.skos_registry
providers = skos.get_providers()
Expand Down
100 changes: 89 additions & 11 deletions pyramid_skosprovider/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,126 @@

from skosprovider.registry import Registry

from pyramid_skosprovider.utils import (
json_renderer
from pyramid_skosprovider.renderers import (
json_renderer,
jsonld_renderer
)

from pyramid.path import (
DottedNameResolver
)


class ISkosRegistry(Interface):
pass


def _build_skos_registry(registry):
skos_registry = registry.queryUtility(ISkosRegistry)
if skos_registry is not None:
return skos_registry
def _parse_settings(settings):
defaults = {
'skosregistry_location': 'registry',
}
args = defaults.copy()

# string setting
for short_key_name in ('skosregistry_location', 'skosregistry_factory'):
key_name = "skosprovider.%s" % short_key_name
if key_name in settings:
args[short_key_name] = settings.get(key_name)

return args


def _register_global_skos_registry(registry):
'''
Build a :class:`skosprovider.registry.Registry` and attach it to the
Pyramid registry.
skos_registry = Registry()
:param registry: The Pyramid registry
:rtype: :class:`skosprovider.registry.Registry`
'''
settings = _parse_settings(registry.settings)

if 'skosregistry_factory' in settings:
r = DottedNameResolver()
skos_registry = r.resolve(settings['skosregistry_factory'])()
else:
skos_registry = Registry(instance_scope='threaded_global')

registry.registerUtility(skos_registry, ISkosRegistry)
return registry.queryUtility(ISkosRegistry)


def _register_request_skos_registry(request):
'''
Get the :class:`skosprovider.registry.Registry` attached to this request.
:param request: The Pyramid request
:rtype: :class:`skosprovider.registry.Registry`
'''
settings = _parse_settings(request.registry.settings)

if 'skosregistry_factory' in settings:
r = DottedNameResolver()
skos_registry = r.resolve(settings['skosregistry_factory'])(request)
else:
skos_registry = Registry(instance_scope='threaded_thread')

return skos_registry


def get_skos_registry(registry):
'''
Get the :class:`skosprovider.registry.Registry` attached to this pyramid
application.
:param registry: A Pyramid registry, request or config.
:rtype: :class:`skosprovider.registry.Registry`
'''
# Argument might be a config or request
# Argument might be a registry or have it as an attribute
regis = getattr(registry, 'registry', None)
if regis is None:
regis = registry
return regis.queryUtility(ISkosRegistry)
settings = _parse_settings(regis.settings)

print(settings)

if settings['skosregistry_location'] == 'registry':
return regis.queryUtility(ISkosRegistry)
else:
raise RuntimeError('This is an older method that \
is maintained for Backward Compatibility. It should \
only be called for a global registry.')


def includeme(config):
_build_skos_registry(config.registry)
settings = _parse_settings(config.registry.settings)

if settings['skosregistry_location'] == 'registry':
_register_global_skos_registry(config.registry)
config.add_request_method(
get_skos_registry,
'skos_registry',
reify=True
)
else:
config.add_request_method(
_register_request_skos_registry,
'skos_registry',
reify=True
)

config.add_renderer('skosjson', json_renderer)
config.add_renderer('skosjsonld', jsonld_renderer)

config.add_directive('get_skos_registry', get_skos_registry)
config.add_request_method(get_skos_registry, 'skos_registry', reify=True)

config.add_route(
'skosprovider.context',
'/jsonld/context/skos'
)
config.add_route(
'skosprovider.uri.deprecated',
'/uris/{uri:.*}'
Expand Down

0 comments on commit 41b43ac

Please sign in to comment.