The core of a service layer that integrates with the Pyramid Web Framework.

Overview

pyramid_services

Travis-CI Build Status

The core of a service layer that integrates with the Pyramid Web Framework.

pyramid_services defines a pattern and helper methods for accessing a pluggable service layer from within your Pyramid apps.

Installation

Install from PyPI using pip or easy_install inside a virtual environment.

$ $VENV/bin/pip install pyramid_services

Or install directly from source.

$ git clone https://github.com/mmerickel/pyramid_services.git
$ cd pyramid_services
$ $VENV/bin/pip install -e .

Setup

Activate pyramid_services by including it into your pyramid application.

config.include('pyramid_services')

This will add some new directives to your Configurator.

  • config.register_service(obj, iface=Interface, context=Interface, name='')

    This method will register a service object for the supplied iface, context, and name. This effectively registers a singleton for your application as the obj will always be returned when looking for a service.

  • config.register_service_factory(factory, iface=Interface, context=Interface, name='')

    This method will register a factory for the supplied iface, context, and name. The factory should be a callable accepting a context and a request and should return a service object. The factory will be used at most once per request/context/name combination.

  • config.set_service_registry(registry)

    This method will let you set a custom wired.ServiceRegistry instance which is the backing registry for all services.

Usage

After registering services with the Configurator, they are now accessible from the request object during a request lifecycle via the request.find_service(iface=Interface, context=_marker, name='') method. Unless a custom context is passed to find_service, the lookup will default to using request.context. The context will default to None if a service is searched for during or before traversal in Pyramid when there may not be a request.context.

svc = request.find_service(ILoginService)

Registering per-request services

Some services (like your database connection) may need a transaction manager and the best way to do that is by using pyramid_tm and hooking the request.tm transaction manager into your service container. The request object itself is already added to the container for the pyramid.interfaces.IRequest interface and can be used in factories that require the request.

This can be done before any services are instantiated by subscribing to the pyramid_services.NewServiceContainer event:

from pyramid_services import NewServiceContainer

def on_new_container(event):
    container = event.container
    request = event.request
    container.set(request.tm, name='tm')

config.add_subscriber(on_new_container, NewServiceContainer)

Examples

Let's create a login service by progressively building up from scratch what we want to use in our app.

Basically all of the steps in configuring an interface are optional, but they are shown here as best practices.

# myapp/interfaces.py

from zope.interface import Interface

class ILoginService(Interface):
  def create_token_for_login(name):
    pass

With our interface we can now define a conforming instance.

# myapp/services.py

class DummyLoginService(object):
  def create_token_for_login(self, name):
    return 'u:{0}'.format(name)

Let's hook it up to our application.

# myapp/main.py

from pyramid.config import Configurator

from myapp.services import DummyLoginService

def main(global_config, **settings):
  config = Configurator()
  config.include('pyramid_services')

  config.register_service(DummyLoginService(), ILoginService)

  config.add_route('home', '/')
  config.scan('.views')
  return config.make_wsgi_app()

Finally, let's create our view that utilizes the service.

# myapp/views.py

@view_config(route_name='home', renderer='json')
def home_view(request):
  name = request.params.get('name', 'bob')

  login_svc = request.find_service(ILoginService)
  token = login_svc.create_token_for_login(name)

  return {'access_token': token}

If you start up this application, you will find that you can access the home url and get custom tokens!

This is cool, but what's even better is swapping in a new service without changing our view at all. Let's define a new PersistentLoginService that gets tokens from a database. We're going to need to setup some database handling, but again nothing changes in the view.

# myapp/services.py

from uuid import uuid4

from myapp.model import AccessToken

class PersistentLoginService(object):
  def __init__(self, dbsession):
    self.dbsession = dbsession

  def create_token_for_login(self, name):
    token = AccessToken(key=uuid4(), user=name)
    self.dbsession.add(token)
    return token.key

Below is some boilerplate for configuring a model using the excellent SQLAlchemy ORM.

# myapp/model.py

from sqlalchemy import engine_from_config
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.schema import Column
from sqlalchemy.types import Text

Base = declarative_base()

def init_model(settings):
  engine = engine_from_config(settings)
  dbmaker = sessionmaker()
  dbmaker.configure(bind=engine)
  return dbmaker

class AccessToken(Base):
  __tablename__ = 'access_token'

  key = Column(Text, primary_key=True)
  user = Column(Text, nullable=False)

Now we will update the application to use the new PersistentLoginService. However, we may have other services and it'd be silly to create a new database connection for each service in a request. So we'll also add a service that encapsulates the database connection. Using this technique we can wire services together in the service layer.

# myapp/main.py

from pyramid.config import Configurator
import transaction
import zope.sqlalchemy

from myapp.model import init_model
from myapp.services import PersistentLoginService

def main(global_config, **settings):
  config = Configurator()
  config.include('pyramid_services')
  config.include('pyramid_tm')

  dbmaker = init_model(settings)

  def dbsession_factory(context, request):
    dbsession = dbmaker()
    # register the session with pyramid_tm for managing transactions
    zope.sqlalchemy.register(dbsession, transaction_manager=request.tm)
    return dbsession

  config.register_service_factory(dbsession_factory, name='db')

  def login_factory(context, request):
    dbsession = request.find_service(name='db')
    svc = PersistentLoginService(dbsession)
    return svc

  config.register_service_factory(login_factory, ILoginService)

  config.add_route('home', '/')
  config.scan('.views')
  return config.make_wsgi_app()

And finally the home view will remain unchanged.

# myapp/views.py

@view_config(route_name='home', renderer='json')
def home_view(request):
  name = request.params.get('name', 'bob')

  login_svc = request.find_service(ILoginService)
  token = login_svc.create_token_for_login(name)

  return {'access_token': token}

Hopefully this pattern is clear. It has several advantages over most basic Pyramid tutorials.

  • The model is completely abstracted from the views, making both easy to test on their own.
  • The service layer can be developed independently of the views, allowing for dummy implementations for easy creation of templates and frontend logic. Later, the real service layer can be swapped in as it's developed, building out the backend functionality.
  • Most services may be implemented in such a way that they do not depend on Pyramid or a particular request object.
  • Different services may be returned based on a context, such as the result of traversal or some other application-defined discriminator.

Testing Examples

If you are writing an application that uses pyramid_services you may want to do some integration testing that verifies that your application has successfully called register_service or register_service_factory. Using Pyramid's testing module to create a Configurator and after calling config.include('pyramid_services') you may use find_service_factory to get information about a registered service.

Take as an example this test that verifies that dbsession_factory has been correctly registered. This assumes you have a myapp.services package that contains an includeme() function.

# myapp/tests/test_integration.py

from myapp.services import dbsession_factory, login_factory, ILoginService

class TestIntegration_services(unittest.TestCase):
  def setUp(self):
    self.config = pyramid.testing.setUp()
    self.config.include('pyramid_services')
    self.config.include('myapp.services')

  def tearDown(self):
    pyramid.testing.tearDown()

  def test_db_maker(self):
    result = self.config.find_service_factory(name='db')
    self.assertEqual(result, dbsession_factory)

  def test_login_factory(self):
    result = self.config.find_service_factory(ILoginService)
    self.assertEqual(result, login_factory)
aiohttp-ratelimiter is a rate limiter for the aiohttp.web framework.

aiohttp-ratelimiter aiohttp-ratelimiter is a rate limiter for the aiohttp.web fr

JGL Technologies 4 Dec 11, 2022
Web framework based on type hint。

Hint API 中文 | English 基于 Type hint 的 Web 框架 hintapi 文档 hintapi 实现了 WSGI 接口,并使用 Radix Tree 进行路由查找。是最快的 Python web 框架之一。一切特性都服务于快速开发高性能的 Web 服务。 大量正确的类型

Aber 19 Dec 02, 2022
Async Python 3.6+ web server/framework | Build fast. Run fast.

Sanic | Build fast. Run fast. Build Docs Package Support Stats Sanic is a Python 3.6+ web server and web framework that's written to go fast. It allow

Sanic Community Organization 16.7k Dec 28, 2022
Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed.

Tornado Web Server Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed. By using non-blocking ne

20.9k Jan 01, 2023
A boilerplate Flask API for a Fullstack Project with some additional packages and configuration prebuilt. ⚙

Flask Boilerplate to quickly get started with production grade flask application with some additional packages and configuration prebuilt.

Yasser Tahiri 32 Dec 24, 2022
🦍 The Cloud-Native API Gateway

Kong or Kong API Gateway is a cloud-native, platform-agnostic, scalable API Gateway distinguished for its high performance and extensibility via plugi

Kong 33.8k Jan 09, 2023
Pyrin is an application framework built on top of Flask micro-framework to make life easier for developers who want to develop an enterprise application using Flask

Pyrin A rich, fast, performant and easy to use application framework to build apps using Flask on top of it. Pyrin is an application framework built o

Mohamad Nobakht 10 Jan 25, 2022
Embrace the APIs of the future. Hug aims to make developing APIs as simple as possible, but no simpler.

Read Latest Documentation - Browse GitHub Code Repository hug aims to make developing Python driven APIs as simple as possible, but no simpler. As a r

Hug API Framework 6.7k Dec 27, 2022
A Python package to easily create APIs in Python.

API_Easy An Python Package for easily create APIs in Python pip install easy-api-builder Requiremnets: = python 3.6 Required modules -- Flask Docume

Envyre-Coding 2 Jan 04, 2022
APIFlask is a lightweight Python web API framework based on Flask and marshmallow-code projects

APIFlask APIFlask is a lightweight Python web API framework based on Flask and marshmallow-code projects. It's easy to use, highly customizable, ORM/O

Grey Li 705 Jan 04, 2023
Sierra is a lightweight Python framework for building and integrating web applications

A lightweight Python framework for building and Integrating Web Applications. Sierra is a Python3 library for building and integrating web applications with HTML and CSS using simple enough syntax. Y

83 Sep 23, 2022
Asynchronous HTTP client/server framework for asyncio and Python

Async http client/server framework Key Features Supports both client and server side of HTTP protocol. Supports both client and server Web-Sockets out

aio-libs 13.2k Jan 05, 2023
The Python micro framework for building web applications.

Flask Flask is a lightweight WSGI web application framework. It is designed to make getting started quick and easy, with the ability to scale up to co

The Pallets Projects 61.5k Jan 06, 2023
Microservice example with Python, Faust-Streaming and Kafka (Redpanda)

Microservices Orchestration with Python, Faust-Streaming and Kafka (Redpanda) Example project for PythonBenin meetup. It demonstrates how to use Faust

Lé 3 Jun 13, 2022
WebSocket and WAMP in Python for Twisted and asyncio

Autobahn|Python WebSocket & WAMP for Python on Twisted and asyncio. Quick Links: Source Code - Documentation - WebSocket Examples - WAMP Examples Comm

Crossbar.io 2.4k Jan 06, 2023
bottle.py is a fast and simple micro-framework for python web-applications.

Bottle: Python Web Framework Bottle is a fast, simple and lightweight WSGI micro web-framework for Python. It is distributed as a single file module a

Bottle Micro Web Framework 7.8k Dec 31, 2022
Lemon is an async and lightweight API framework for python

Lemon is an async and lightweight API framework for python . Inspired by Koa and Sanic .

Joway 29 Nov 20, 2022
The core of a service layer that integrates with the Pyramid Web Framework.

pyramid_services The core of a service layer that integrates with the Pyramid Web Framework. pyramid_services defines a pattern and helper methods for

Michael Merickel 78 Apr 15, 2022
FastAPI framework, high performance, easy to learn, fast to code, ready for production

FastAPI framework, high performance, easy to learn, fast to code, ready for production Documentation: https://fastapi.tiangolo.com Source Code: https:

Sebastián Ramírez 53k Jan 02, 2023
A beginners course for Django

The Definitive Django Learning Platform. Getting started with Django This is the code from the course "Getting Started With Django", found on YouTube

JustDjango 288 Jan 08, 2023