django-ddp/dddp/__init__.py
Tyson Clugg e213f55437 Merge branch 'develop' into feature/pub_query_refactor
Conflicts:
	dddp/__init__.py
	dddp/accounts/ddp.py
2015-08-28 14:12:25 +10:00

162 lines
4.3 KiB
Python

"""Django/PostgreSQL implementation of the Meteor DDP service."""
from __future__ import unicode_literals
import os.path
import sys
from pkg_resources import get_distribution, DistributionNotFound
from gevent.local import local
from dddp import alea
try:
_dist = get_distribution('django-ddp')
if not __file__.startswith(os.path.join(_dist.location, 'django-ddp', '')):
# not installed, but there is another version that *is*
raise DistributionNotFound
except DistributionNotFound:
__version__ = 'development'
else:
__version__ = _dist.version
default_app_config = 'dddp.apps.DjangoDDPConfig'
ADDED = 'added'
CHANGED = 'changed'
REMOVED = 'removed'
def greenify():
"""Patch threading and psycopg2 modules for green threads."""
from gevent.monkey import patch_all, saved
if ('threading' in sys.modules) and ('threading' not in saved):
raise Exception('threading module loaded before patching!')
patch_all()
from psycogreen.gevent import patch_psycopg
patch_psycopg()
class AlreadyRegistered(Exception):
"""Raised when registering over the top of an existing registration."""
pass
class ThreadLocal(local):
"""Thread local storage for greenlet state."""
_init_done = False
def __init__(self):
"""Create new thread storage instance."""
if self._init_done:
raise SystemError('__init__ called too many times')
self._init_done = True
def __getattr__(self, name):
"""Create missing attributes using default factories."""
try:
factory = THREAD_LOCAL_FACTORIES[name]
except KeyError:
raise AttributeError(name)
return self.get(name, factory)
def get(self, name, factory, *factory_args, **factory_kwargs):
"""Get attribute, creating if required using specified factory."""
update_thread_local = getattr(factory, 'update_thread_local', True)
if (not update_thread_local) or (not hasattr(self, name)):
obj = factory(*factory_args, **factory_kwargs)
if update_thread_local:
setattr(self, name, obj)
return obj
return getattr(self, name)
class RandomStreams(object):
def __init__(self):
self._streams = {}
self._seed = THREAD_LOCAL.alea_random.hex_string(20)
def get_seed(self):
return self._seed
def set_seed(self, val):
self._streams = {}
self._seed = val
random_seed = property(get_seed, set_seed)
def __getitem__(self, key):
if key not in self._streams:
return self._streams.setdefault(key, alea.Alea(self._seed, key))
return self._streams[key]
def serializer_factory():
"""Make a new DDP serializer."""
from django.core.serializers import get_serializer
return get_serializer('python')()
def tagged(tag_class, obj):
"""
Return tagged reference to obj via tag_class.
Use `isinstance(obj, tag_class)` to check if obj has been tagged.
>>> class Foo(object): pass
>>> x = tagged(Foo, 4)
>>> x
4
>>> isinstance(x, int)
True
>>> isinstance(x, Foo)
True
>>> y = x + 2
>>> isinstance(y, Foo)
False
>>> z = 2 + x
>>> isinstance(z, Foo)
False
>>> n = tagged(Foo, None)
>>> n
None
>>> isinstance(n, Foo)
True
>>> n is None # this might not be what you expected, be aware!
False
>>> isinstance(n, type(None)) # we can still check isinstance of NoneType.
True
"""
return type(
type(obj).__name__,
(
tag_class,
type(obj),
),
{},
)(*[arg for arg in [obj] if arg is not None])
THREAD_LOCAL_FACTORIES = {
'alea_random': alea.Alea,
'random_streams': RandomStreams,
'serializer': serializer_factory,
}
THREAD_LOCAL = ThreadLocal()
METEOR_ID_CHARS = u'23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz'
def meteor_random_id(name=None, length=17):
if name is None:
stream = THREAD_LOCAL.alea_random
else:
stream = THREAD_LOCAL.random_streams[name]
return stream.random_string(length, METEOR_ID_CHARS)
def autodiscover():
from django.utils.module_loading import autodiscover_modules
from dddp.api import API
autodiscover_modules('ddp', register_to=API)
return API