Update examples and docs

This commit is contained in:
Roman Mogilatov 2016-04-20 14:19:54 +03:00
parent bd5ea87ef7
commit 4cf3bdb1fb
33 changed files with 363 additions and 391 deletions

View File

@ -55,259 +55,66 @@ Installation
Examples
--------
API client example:
.. code-block:: python
"""Pythonic way for Dependency Injection - API Client."""
from dependency_injector import providers
from mock import Mock
class ApiClient(object):
"""Some API client."""
def __init__(self, host, api_key):
"""Initializer."""
self.host = host
self.api_key = api_key
def call(self, operation, data):
"""Make some network operations."""
print 'API call [{0}:{1}], method - {2}, data - {3}'.format(
self.host, self.api_key, operation, repr(data))
class User(object):
"""User model."""
def __init__(self, id, api_client):
"""Initializer."""
self.id = id
self.api_client = api_client
def register(self):
"""Register user."""
self.api_client.call('register', {'id': self.id})
# Creating ApiClient and User providers:
api_client = providers.Singleton(ApiClient,
host='production.com',
api_key='PROD_API_KEY')
user_factory = providers.Factory(User,
api_client=api_client)
# Creating several users and register them:
user1 = user_factory(1)
user1.register()
# API call [production.com:PROD_API_KEY], method - register, data - {'id': 1}
user2 = user_factory(2)
user2.register()
# API call [production.com:PROD_API_KEY], method - register, data - {'id': 2}
# Mock ApiClient for testing:
with api_client.override(Mock(ApiClient)) as api_client_mock:
user = user_factory('test')
user.register()
api_client_mock().call.assert_called_with('register', {'id': 'test'})
# Overriding of ApiClient on dev environment:
api_client.override(providers.Singleton(ApiClient,
host='localhost',
api_key='DEV_API_KEY'))
user3 = user_factory(3)
user3.register()
# API call [localhost:DEV_API_KEY], method - register, data - {'id': 3}
Auth system example:
.. code-block:: python
"""Pythonic way for Dependency Injection - Auth System."""
from dependency_injector import providers
from dependency_injector import injections
@providers.DelegatedCallable
def get_user_info(user_id):
"""Return user info."""
raise NotImplementedError()
@providers.Factory
@injections.inject(get_user_info=get_user_info)
class AuthComponent(object):
"""Some authentication component."""
def __init__(self, get_user_info):
"""Initializer."""
self.get_user_info = get_user_info
def authenticate_user(self, token):
"""Authenticate user by token."""
user_info = self.get_user_info(user_id=token + '1')
return user_info
print AuthComponent
print get_user_info
@providers.override(get_user_info)
@providers.DelegatedCallable
def get_user_info(user_id):
"""Return user info."""
return {'user_id': user_id}
print AuthComponent().authenticate_user(token='abc')
# {'user_id': 'abc1'}
Service providers catalog example:
.. code-block:: python
"""Pythonic way for Dependency Injection - Service Providers Catalog."""
"""Dependency Injector initial example."""
import sys
import sqlite3
import boto.s3.connection
import services
from dependency_injector import catalogs
from dependency_injector import providers
from dependency_injector import injections
class UsersService(object):
"""Users service, that has dependency on database."""
def __init__(self, db):
"""Initializer."""
self.db = db
class AuthService(object):
"""Auth service, that has dependencies on users service and database."""
def __init__(self, db, users_service):
"""Initializer."""
self.db = db
self.users_service = users_service
class Services(catalogs.DeclarativeCatalog):
"""Catalog of service providers."""
class Platform(catalogs.DeclarativeCatalog):
"""Catalog of platform service providers."""
database = providers.Singleton(sqlite3.connect, ':memory:')
users = providers.Factory(UsersService,
db=database)
auth = providers.Factory(AuthService,
db=database,
users_service=users)
# Retrieving catalog providers:
users_service = Services.users()
auth_service = Services.auth()
# Making some asserts:
assert users_service.db is auth_service.db is Services.database()
assert isinstance(auth_service.users_service, UsersService)
assert users_service is not Services.users()
assert auth_service is not Services.auth()
# Making some "inline" injections:
@injections.inject(users_service=Services.users)
@injections.inject(auth_service=Services.auth)
@injections.inject(database=Services.database)
def example(users_service, auth_service, database):
"""Example callback."""
assert users_service.db is auth_service.db
assert auth_service.db is database
assert database is Services.database()
# Making a call of decorated callback:
example()
Providing callbacks catalog example:
.. code-block:: python
"""Pythonic way for Dependency Injection - Providing Callbacks Catalog."""
import sqlite3
from dependency_injector import catalogs
from dependency_injector import providers
from dependency_injector import injections
class UsersService(object):
"""Users service, that has dependency on database."""
def __init__(self, db):
"""Initializer."""
self.db = db
class AuthService(object):
"""Auth service, that has dependencies on users service and database."""
def __init__(self, db, users_service):
"""Initializer."""
self.db = db
self.users_service = users_service
s3 = providers.Singleton(boto.s3.connection.S3Connection,
aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(catalogs.DeclarativeCatalog):
"""Catalog of service providers."""
"""Catalog of business service providers."""
@providers.Singleton
def database():
"""Provide database connection.
users = providers.Factory(services.Users,
db=Platform.database)
:rtype: providers.Provider -> sqlite3.Connection
"""
return sqlite3.connect(':memory:')
photos = providers.Factory(services.Photos,
db=Platform.database,
s3=Platform.s3)
@providers.Factory
@injections.inject(db=database)
def users(**kwargs):
"""Provide users service.
:rtype: providers.Provider -> UsersService
"""
return UsersService(**kwargs)
@providers.Factory
@injections.inject(db=database)
@injections.inject(users_service=users)
def auth(**kwargs):
"""Provide users service.
:rtype: providers.Provider -> AuthService
"""
return AuthService(**kwargs)
auth = providers.Factory(services.Auth,
db=Platform.database,
token_ttl=3600)
# Retrieving catalog providers:
users_service = Services.users()
auth_service = Services.auth()
@injections.inject(users_service=Services.users)
@injections.inject(auth_service=Services.auth)
def main(argv, users_service, auth_service):
"""Main function."""
login, password, photo_path = argv[1:]
# Making some asserts:
assert users_service.db is auth_service.db is Services.database()
assert isinstance(auth_service.users_service, UsersService)
assert users_service is not Services.users()
assert auth_service is not Services.auth()
user = users_service.get_user(login)
auth_service.authenticate(user, password)
upload_photo(user, photo_path)
@injections.inject(photos_service=Services.photos)
def upload_photo(user, photo_path, photos_service):
"""Upload photo."""
photos_service.upload_photo(user['id'], photo_path)
if __name__ == '__main__':
main(sys.argv)
You can get more *Dependency Injector* examples in ``/examples`` directory on
GitHub:
@ -334,8 +141,8 @@ Your feedback is quite important!
.. _PyPi: https://pypi.python.org/pypi/dependency_injector
.. _User's guide: http://dependency_injector.readthedocs.org/en/stable/
.. _API docs: http://dependency-injector.readthedocs.org/en/stable/api/
.. _User's guide: http://dependency-injector.ets-labs.org/en/stable/
.. _API docs: http://dependency-injector.ets-labs.org/en/stable/api/
.. _SLOC: http://en.wikipedia.org/wiki/Source_lines_of_code
.. _SOLID: http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29
.. _IoC: http://en.wikipedia.org/wiki/Inversion_of_control

View File

@ -0,0 +1,18 @@
Examples
========
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: Current section of documentation is designed to provide
several example mini applications that are built on the top
of inversion of control principle and powered by
"Dependency Injector" framework.
Current section of documentation is designed to provide several example mini
applications that are built on the top of inversion of control principle and
powered by *Dependency Injector* framework.
.. toctree::
:maxdepth: 2
movie_lister

View File

@ -0,0 +1,104 @@
Movie lister naive example
--------------------------
.. meta::
:description: Dependency Injector is a Python dependency injection
framework. It was designed to be unified, developer's
friendly tool for managing any kind of Python objects and
their dependencies in formal, pretty way.
This naive example was taken from Martin Fowler's article about dependency
injection and inversion of control: http://www.martinfowler.com/articles/injection.html
Like Martin says:
.. pull-quote::
*Like all of my examples it's one of those super-simple examples;
small enough to be unreal, but hopefully enough for you to visualize
what's going on without falling into the bog of a real example.*
While original Martin's MovieLister example was a bit modified here, it
makes sense to provide some description. So, the idea of this example is to
create ``movies`` library that can be configurable to work with different
movie databases (csv, sqlite) and provide 2 main features:
1. List all movies that were directed by certain person.
2. List all movies that were released in certain year.
Also this example contains 3 mini applications that are based on ``movies``
library :
1. ``app_csv.py`` - list movies by certain criteria from csv file database.
2. ``app_db.py`` - list movies by certain criteria from sqlite database.
3. ``app_db_csv.py`` - list movies by certain criteria from csv file and
sqlite databases.
Instructions for running:
.. code-block:: bash
python create_db.py
python app_csv.py
python app_db.py
python app_db_csv.py
Full code of example could be found on GitHub_.
Movies library
~~~~~~~~~~~~~~
Classes diagram:
.. image:: /images/miniapps/movie_lister/classes.png
:width: 100%
:align: center
Movies library structure:
.. code-block:: bash
/movies
/__init__.py
/finders.py
/listers.py
/models.py
Listing of ``movies/__init__.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/movies/__init__.py
:language: python
:linenos:
Csv application
~~~~~~~~~~~~~~~
Listing of ``app_csv.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/app_csv.py
:language: python
:linenos:
Database application
~~~~~~~~~~~~~~~~~~~~
Listing of ``app_db.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/app_db.py
:language: python
:linenos:
Csv and database application
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Listing of ``app_db_csv.py``:
.. literalinclude:: ../../examples/miniapps/movie_lister/app_db_csv.py
:language: python
:linenos:
.. _GitHub: https://github.com/ets-labs/dependency_injector/tree/master/examples/miniapps/movie_lister

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -2,8 +2,7 @@ Dependency Injector --- Python dependency injection framework
=============================================================
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:google-site-verification: mPdgVBidFHVWRNh0lhxj7q9zi-CpwIU970jINTBKLYQ
:google-site-verification: 6it89zX0_wccKEhAqbAiYQooS95f0BA8YfesHk6bsNA
:description: Dependency Injector is a Python dependency injection
framework. It was designed to be unified, developer's
friendly tool for managing any kind of Python objects and
@ -63,6 +62,7 @@ Contents
providers/index
catalogs/index
advanced_usage/index
examples/index
api/index
main/feedback
main/changelog

View File

@ -2,7 +2,6 @@ Dependency injection and inversion of control in Python
-------------------------------------------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes benefits of dependency injection and
inversion of control for Python applications. Also it
contains some Python examples that show how dependency

View File

@ -2,7 +2,6 @@ Introduction
============
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: Current section of documentation is designed to give some
overview about dependency injection pattern, inversion of
control principle and "Dependency Injector" framework.

View File

@ -2,7 +2,6 @@ Key features of Dependency Injector
-----------------------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes key features of "Dependency Injector"
framework. It also provides some cases and recommendations
about usage of "Dependency Injector" framework.

View File

@ -2,7 +2,6 @@ Structure of Dependency Injector
--------------------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article describes "Dependency Injector" framework
components and their interaction between each other.
Catalogs, providers and injections are the former

View File

@ -2,7 +2,6 @@ What is dependency injection and inversion of control?
------------------------------------------------------
.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control
:description: This article provides definition of dependency injection,
inversion of control and dependency inversion. It contains
example code in Python that is refactored to be following

View File

@ -11,6 +11,8 @@ Development version
-------------------
- Add ``@copy`` decorator for copying declarative catalog providers.
- Add line numbers for all code samples in documentation.
- Move project documentation into organisation's domain
(dependency-injector.ets-labs.org).
1.15.2
------

View File

@ -24,7 +24,6 @@ class Catalog(catalogs.DeclarativeCatalog):
fee=config.FEE,
price=config.PRICE,
timezone=config.GLOBAL.TIMEZONE)
""":type: providers.Provider -> ObjectA"""
# Setting config value and making some tests.

View File

@ -1,65 +0,0 @@
"""Pythonic way for Dependency Injection - API Client."""
from dependency_injector import providers
from mock import Mock
class ApiClient(object):
"""Some API client."""
def __init__(self, host, api_key):
"""Initializer."""
self.host = host
self.api_key = api_key
def call(self, operation, data):
"""Make some network operations."""
print 'API call [{0}:{1}], method - {2}, data - {3}'.format(
self.host, self.api_key, operation, repr(data))
class User(object):
"""User model."""
def __init__(self, id, api_client):
"""Initializer."""
self.id = id
self.api_client = api_client
def register(self):
"""Register user."""
self.api_client.call('register', {'id': self.id})
# Creating ApiClient and User providers:
api_client = providers.Singleton(ApiClient,
host='production.com',
api_key='PROD_API_KEY')
user_factory = providers.Factory(User,
api_client=api_client)
# Creating several users and register them:
user1 = user_factory(1)
user1.register()
# API call [production.com:PROD_API_KEY], method - register, data - {'id': 1}
user2 = user_factory(2)
user2.register()
# API call [production.com:PROD_API_KEY], method - register, data - {'id': 2}
# Mock ApiClient for testing:
with api_client.override(Mock(ApiClient)) as api_client_mock:
user = user_factory('test')
user.register()
api_client_mock().call.assert_called_with('register', {'id': 'test'})
# Overriding of ApiClient on dev environment:
api_client.override(providers.Singleton(ApiClient,
host='localhost',
api_key='DEV_API_KEY'))
user3 = user_factory(3)
user3.register()
# API call [localhost:DEV_API_KEY], method - register, data - {'id': 3}

View File

@ -1,63 +0,0 @@
"""Pythonic way for Dependency Injection - Service Providers Catalog."""
import sqlite3
from dependency_injector import catalogs
from dependency_injector import providers
from dependency_injector import injections
class UsersService(object):
"""Users service, that has dependency on database."""
def __init__(self, db):
"""Initializer."""
self.db = db
class AuthService(object):
"""Auth service, that has dependencies on users service and database."""
def __init__(self, db, users_service):
"""Initializer."""
self.db = db
self.users_service = users_service
class Services(catalogs.DeclarativeCatalog):
"""Catalog of service providers."""
database = providers.Singleton(sqlite3.connect, ':memory:')
users = providers.Factory(UsersService,
db=database)
auth = providers.Factory(AuthService,
db=database,
users_service=users)
# Retrieving catalog providers:
users_service = Services.users()
auth_service = Services.auth()
# Making some asserts:
assert users_service.db is auth_service.db is Services.database()
assert isinstance(auth_service.users_service, UsersService)
assert users_service is not Services.users()
assert auth_service is not Services.auth()
# Making some "inline" injections:
@injections.inject(users_service=Services.users)
@injections.inject(auth_service=Services.auth)
@injections.inject(database=Services.database)
def example(users_service, auth_service, database):
"""Example callback."""
assert users_service.db is auth_service.db
assert auth_service.db is database
assert database is Services.database()
# Making a call of decorated callback:
example()

View File

@ -13,13 +13,10 @@ class Services(catalogs.DeclarativeCatalog):
"""Example catalog of service providers."""
users = providers.Factory(services.Users)
""":type: providers.Provider -> services.Users"""
auth = providers.Factory(services.Auth)
""":type: providers.Provider -> services.Auth"""
photos = providers.Factory(services.Photos)
""":type: providers.Provider -> services.Photos"""
# Declaring views catalog:
@ -29,12 +26,10 @@ class Views(catalogs.DeclarativeCatalog):
auth = providers.Factory(views.Auth,
services=Services.Bundle(Services.users,
Services.auth))
""":type: providers.Provider -> views.Auth"""
photos = providers.Factory(views.Photos,
services=Services.Bundle(Services.users,
Services.photos))
""":type: providers.Provider -> views.Photos"""
# Creating example views:

View File

@ -9,10 +9,8 @@ class Catalog(catalogs.DeclarativeCatalog):
"""Providers catalog."""
factory1 = providers.Factory(object)
""":type: providers.Provider -> object"""
factory2 = providers.Factory(object)
""":type: providers.Provider -> object"""
# Creating some objects:
object1 = Catalog.factory1()

View File

@ -8,14 +8,12 @@ class CatalogA(catalogs.DeclarativeCatalog):
"""Example catalog A."""
provider1 = providers.Factory(object)
""":type: providers.Provider -> object"""
class CatalogB(CatalogA):
"""Example catalog B."""
provider2 = providers.Singleton(object)
""":type: providers.Provider -> object"""
# Making some asserts for `providers` attribute:

View File

@ -27,16 +27,13 @@ class Services(catalogs.DeclarativeCatalog):
"""Catalog of service providers."""
database = providers.Singleton(sqlite3.connect, ':memory:')
""":type: providers.Provider -> sqlite3.Connection"""
users = providers.Factory(UsersService,
db=database)
""":type: providers.Provider -> UsersService"""
auth = providers.Factory(AuthService,
db=database,
users_service=users)
""":type: providers.Provider -> AuthService"""
# Retrieving service providers from catalog:

View File

@ -31,13 +31,11 @@ class Services(services.Catalog):
users = services.Provider(UsersService,
config={'option1': '111',
'option2': '222'})
""":type: services.Provider -> UsersService"""
auth = services.Provider(AuthService,
config={'option3': '333',
'option4': '444'},
users_service=users)
""":type: services.Provider -> AuthService"""
# Creating users & auth services:

View File

@ -18,18 +18,15 @@ class Catalog(catalogs.DeclarativeCatalog):
object1_factory = providers.Factory(Object1,
arg1=1,
arg2=2)
""":type: providers.Provider -> Object1"""
object2_factory = providers.Factory(Object2,
object1=object1_factory)
""":type: providers.Provider -> Object2"""
class AnotherCatalog(catalogs.DeclarativeCatalog):
"""Overriding catalog."""
object2_factory = providers.Factory(ExtendedObject2)
""":type: providers.Provider -> ExtendedObject2"""
# Overriding `Catalog` with `AnotherCatalog`:

View File

@ -18,11 +18,9 @@ class Catalog(catalogs.DeclarativeCatalog):
object1_factory = providers.Factory(Object1,
arg1=1,
arg2=2)
""":type: providers.Provider -> Object1"""
object2_factory = providers.Factory(Object2,
object1=object1_factory)
""":type: providers.Provider -> Object2"""
# Overriding `Catalog` with some `DynamicCatalog` instance:

View File

@ -17,11 +17,9 @@ class Catalog(catalogs.DeclarativeCatalog):
object1_factory = providers.Factory(Object1,
arg1=1,
arg2=2)
""":type: providers.Provider -> Object1"""
object2_factory = providers.Factory(Object2,
object1=object1_factory)
""":type: providers.Provider -> Object2"""
# Overriding `Catalog` with `AnotherCatalog`:
@ -30,7 +28,6 @@ class AnotherCatalog(catalogs.DeclarativeCatalog):
"""Overriding catalog."""
object2_factory = providers.Factory(ExtendedObject2)
""":type: providers.Provider -> ExtendedObject2"""
# Creating some objects using overridden catalog:

58
examples/initial.py Normal file
View File

@ -0,0 +1,58 @@
"""Dependency Injector initial example."""
import sys
import sqlite3
import boto.s3.connection
import services
from dependency_injector import catalogs
from dependency_injector import providers
from dependency_injector import injections
class Platform(catalogs.DeclarativeCatalog):
"""Catalog of platform service providers."""
database = providers.Singleton(sqlite3.connect, ':memory:')
s3 = providers.Singleton(boto.s3.connection.S3Connection,
aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(catalogs.DeclarativeCatalog):
"""Catalog of business service providers."""
users = providers.Factory(services.Users,
db=Platform.database)
photos = providers.Factory(services.Photos,
db=Platform.database,
s3=Platform.s3)
auth = providers.Factory(services.Auth,
db=Platform.database,
token_ttl=3600)
@injections.inject(users_service=Services.users)
@injections.inject(auth_service=Services.auth)
def main(argv, users_service, auth_service):
"""Main function."""
login, password, photo_path = argv[1:]
user = users_service.get_user(login)
auth_service.authenticate(user, password)
upload_photo(user, photo_path)
@injections.inject(photos_service=Services.photos)
def upload_photo(user, photo_path, photos_service):
"""Upload photo."""
photos_service.upload_photo(user['id'], photo_path)
if __name__ == '__main__':
main(sys.argv)

View File

@ -0,0 +1,15 @@
"""asd."""
class ApiClient(object):
"""Some API client."""
def __init__(self, host, api_key):
"""Initializer."""
self.host = host
self.api_key = api_key
def call(self, operation, data):
"""Make some network operations."""
print 'API call [{0}:{1}], method - {2}, data - {3}'.format(
self.host, self.api_key, operation, repr(data))

View File

@ -0,0 +1,36 @@
"""asd."""
from dependency_injector import providers
import api
import models
# Creating ApiClient and User providers:
api_client = providers.Singleton(api.ApiClient,
host='production.com',
api_key='PROD_API_KEY')
user_factory = providers.Factory(models.User,
api_client=api_client)
if __name__ == '__main__':
# Creating several users and register them:
user1 = user_factory(1)
user1.register()
# API call [production.com:PROD_API_KEY], method - register, data -
# {'id': 1}
user2 = user_factory(2)
user2.register()
# API call [production.com:PROD_API_KEY], method - register, data -
# {'id': 2}
# Overriding of ApiClient on dev environment:
api_client.override(providers.Singleton(api.ApiClient,
host='localhost',
api_key='DEV_API_KEY'))
user3 = user_factory(3)
user3.register()
# API call [localhost:DEV_API_KEY], method - register, data - {'id': 3}

View File

@ -0,0 +1,14 @@
"""asd."""
class User(object):
"""User model."""
def __init__(self, id, api_client):
"""Initializer."""
self.id = id
self.api_client = api_client
def register(self):
"""Register user."""
self.api_client.call('register', {'id': self.id})

View File

@ -0,0 +1,12 @@
"""asd."""
from mock import Mock
import main
import api
# Mock ApiClient for testing:
with main.api_client.override(Mock(api.ApiClient)) as api_client_mock:
user = main.user_factory('test')
user.register()
api_client_mock().call.assert_called_with('register', {'id': 'test'})

View File

@ -0,0 +1,10 @@
"""Flask application."""
import flask
app = flask.Flask(__name__)
if __name__ == '__main__':
app.run()

View File

@ -0,0 +1,8 @@
"""Services package."""
from dependency_injector import catalogs
from dependency_injector import providers
class ServicesModule(catalogs.DeclarativeCatalog):
"""Service providers module."""

View File

@ -5,6 +5,10 @@ module component providers - ``MoviesModule``. It is recommended to use movies
library functionality by fetching required instances from ``MoviesModule``
providers.
``MoviesModule.movie_finder`` is a factory that provides abstract component
``finders.MovieFinder``. This provider should be overridden by provider of
concrete finder implementation in terms of library configuration.
Each of ``MoviesModule`` providers could be overridden.
"""

40
examples/services.py Normal file
View File

@ -0,0 +1,40 @@
"""Services module."""
class Users(object):
"""Users service."""
def __init__(self, db):
"""Initializer."""
self.db = db
def get_user(self, login):
"""Return user's information by login."""
return {'id': 1,
'login': login,
'password_hash': 'secret_hash'}
class Auth(object):
"""Auth service."""
def __init__(self, db, token_ttl):
"""Initializer."""
self.db = db
self.token_ttl = token_ttl
def authenticate(self, user, password):
"""Authenticate user."""
assert user['password_hash'] == '_'.join((password, 'hash'))
class Photos(object):
"""Photos service."""
def __init__(self, db, s3):
"""Initializer."""
self.db = db
self.s3 = s3
def upload_photo(self, user_id, photo_path):
"""Upload user photo."""