packages: vendor authomatic to provide bitbucket oath2 capabilities.

- regular release doesn't have it yet.
This commit is contained in:
Marcin Kuzminski 2019-09-02 17:42:26 +02:00
parent 569321aa23
commit 483b3813b0
19 changed files with 8603 additions and 23 deletions

View file

@ -84,17 +84,6 @@ self: super: {
license = [ pkgs.lib.licenses.mit ];
};
};
"authomatic" = super.buildPythonPackage {
name = "authomatic-0.1.0.post1";
doCheck = false;
src = fetchurl {
url = "https://code.rhodecode.com/upstream/authomatic/artifacts/download/0-4fe9c041-a567-4f84-be4c-7efa2a606d3c.tar.gz?md5=f6bdc3c769688212db68233e8d2b0383";
sha256 = "0pc716mva0ym6xd8jwzjbjp8dqxy9069wwwv2aqwb8lyhl4757ab";
};
meta = {
license = [ pkgs.lib.licenses.mit ];
};
};
"babel" = super.buildPythonPackage {
name = "babel-1.3";
doCheck = false;
@ -920,11 +909,11 @@ self: super: {
};
};
"meld3" = super.buildPythonPackage {
name = "meld3-1.0.2";
name = "meld3-2.0.0";
doCheck = false;
src = fetchurl {
url = "https://files.pythonhosted.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
sha256 = "0n4mkwlpsqnmn0dm0wm5hn9nkda0nafl0jdy5sdl5977znh59dzp";
url = "https://files.pythonhosted.org/packages/00/3b/023446ddc1bf0b519c369cbe88269c30c6a64bd10af4817c73f560c302f7/meld3-2.0.0.tar.gz";
sha256 = "1fbyafwi0d54394hkmp65nf6vk0qm4kipf5z60pdp4244rvadz8y";
};
meta = {
license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
@ -1271,11 +1260,11 @@ self: super: {
};
};
"pyasn1" = super.buildPythonPackage {
name = "pyasn1-0.4.6";
name = "pyasn1-0.4.7";
doCheck = false;
src = fetchurl {
url = "https://files.pythonhosted.org/packages/e3/12/dfffc84b783e280e942409d6b651fe4a5a746433c34589da7362db2c99c6/pyasn1-0.4.6.tar.gz";
sha256 = "11mwdsvrbwvjmny40cxa76h81bbc8jfr1prvw6hw7yvg374xawxp";
url = "https://files.pythonhosted.org/packages/ca/f8/2a60a2c88a97558bdd289b6dc9eb75b00bd90ff34155d681ba6dbbcb46b2/pyasn1-0.4.7.tar.gz";
sha256 = "0146ryp4g09ycy8p3l2vigmgfg42n4gb8whgg8cysrhxr9b56jd9";
};
meta = {
license = [ pkgs.lib.licenses.bsdOriginal ];
@ -1738,7 +1727,6 @@ self: super: {
doCheck = true;
propagatedBuildInputs = [
self."amqp"
self."authomatic"
self."babel"
self."beaker"
self."bleach"
@ -1916,11 +1904,11 @@ self: super: {
};
};
"setuptools" = super.buildPythonPackage {
name = "setuptools-41.1.0";
name = "setuptools-41.2.0";
doCheck = false;
src = fetchurl {
url = "https://files.pythonhosted.org/packages/68/0c/e470db6866aedbff3c4c88faf7f81b90343d8ff32cd68b62db1b65037fb4/setuptools-41.1.0.zip";
sha256 = "1a246z6cikg42adqmpswzjp59hkqwr7xxqs7xyags4cr556bh6f5";
url = "https://files.pythonhosted.org/packages/d9/ca/7279974e489e8b65003fe618a1a741d6350227fa2bf48d16be76c7422423/setuptools-41.2.0.zip";
sha256 = "04k0dp9msmlv3g3zx7f5p8wdjr6hdf5c0bgmczlc4yncwyx6pf36";
};
meta = {
license = [ pkgs.lib.licenses.mit ];

View file

@ -1,8 +1,6 @@
## dependencies
amqp==2.3.1
# not released authomatic that has updated some oauth providers
https://code.rhodecode.com/upstream/authomatic/artifacts/download/0-4fe9c041-a567-4f84-be4c-7efa2a606d3c.tar.gz?md5=f6bdc3c769688212db68233e8d2b0383#egg=authomatic==0.1.0.post1
babel==1.3
beaker==1.9.1

View file

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2012-2019 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
# This package contains non rhodecode licensed packages that are
# vendored for various reasons
import os
import sys
vendor_dir = os.path.abspath(os.path.dirname(__file__))
sys.path.append(vendor_dir)

View file

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
"""
Helper functions for use with :class:`Authomatic`.
.. autosummary::
:nosignatures:
authomatic.provider_id
"""
from . import six
from .core import Authomatic
from .core import provider_id

View file

@ -0,0 +1,282 @@
# -*- coding: utf-8 -*-
"""
Adapters
--------
.. contents::
:backlinks: none
The :func:`authomatic.login` function needs access to functionality like
getting the **URL** of the handler where it is being called, getting the
**request params** and **cookies** and **writing the body**, **headers**
and **status** to the response.
Since implementation of these features varies across Python web frameworks,
the Authomatic library uses **adapters** to unify these differences into a
single interface.
Available Adapters
^^^^^^^^^^^^^^^^^^
If you are missing an adapter for the framework of your choice, please
open an `enhancement issue <https://github.com/authomatic/authomatic/issues>`_
or consider a contribution to this module by
:ref:`implementing <implement_adapters>` one by yourself.
Its very easy and shouldn't take you more than a few minutes.
.. autoclass:: DjangoAdapter
:members:
.. autoclass:: Webapp2Adapter
:members:
.. autoclass:: WebObAdapter
:members:
.. autoclass:: WerkzeugAdapter
:members:
.. _implement_adapters:
Implementing an Adapter
^^^^^^^^^^^^^^^^^^^^^^^
Implementing an adapter for a Python web framework is pretty easy.
Do it by subclassing the :class:`.BaseAdapter` abstract class.
There are only **six** members that you need to implement.
Moreover if your framework is based on the |webob|_ or |werkzeug|_ package
you can subclass the :class:`.WebObAdapter` or :class:`.WerkzeugAdapter`
respectively.
.. autoclass:: BaseAdapter
:members:
"""
import abc
from authomatic.core import Response
class BaseAdapter(object):
"""
Base class for platform adapters.
Defines common interface for WSGI framework specific functionality.
"""
__metaclass__ = abc.ABCMeta
@abc.abstractproperty
def params(self):
"""
Must return a :class:`dict` of all request parameters of any HTTP
method.
:returns:
:class:`dict`
"""
@abc.abstractproperty
def url(self):
"""
Must return the url of the actual request including path but without
query and fragment.
:returns:
:class:`str`
"""
@abc.abstractproperty
def cookies(self):
"""
Must return cookies as a :class:`dict`.
:returns:
:class:`dict`
"""
@abc.abstractmethod
def write(self, value):
"""
Must write specified value to response.
:param str value:
String to be written to response.
"""
@abc.abstractmethod
def set_header(self, key, value):
"""
Must set response headers to ``Key: value``.
:param str key:
Header name.
:param str value:
Header value.
"""
@abc.abstractmethod
def set_status(self, status):
"""
Must set the response status e.g. ``'302 Found'``.
:param str status:
The HTTP response status.
"""
class DjangoAdapter(BaseAdapter):
"""
Adapter for the |django|_ framework.
"""
def __init__(self, request, response):
"""
:param request:
An instance of the :class:`django.http.HttpRequest` class.
:param response:
An instance of the :class:`django.http.HttpResponse` class.
"""
self.request = request
self.response = response
@property
def params(self):
params = {}
params.update(self.request.GET.dict())
params.update(self.request.POST.dict())
return params
@property
def url(self):
return self.request.build_absolute_uri(self.request.path)
@property
def cookies(self):
return dict(self.request.COOKIES)
def write(self, value):
self.response.write(value)
def set_header(self, key, value):
self.response[key] = value
def set_status(self, status):
status_code, reason = status.split(' ', 1)
self.response.status_code = int(status_code)
class WebObAdapter(BaseAdapter):
"""
Adapter for the |webob|_ package.
"""
def __init__(self, request, response):
"""
:param request:
A |webob|_ :class:`Request` instance.
:param response:
A |webob|_ :class:`Response` instance.
"""
self.request = request
self.response = response
# =========================================================================
# Request
# =========================================================================
@property
def url(self):
return self.request.path_url
@property
def params(self):
return dict(self.request.params)
@property
def cookies(self):
return dict(self.request.cookies)
# =========================================================================
# Response
# =========================================================================
def write(self, value):
self.response.write(value)
def set_header(self, key, value):
self.response.headers[key] = str(value)
def set_status(self, status):
self.response.status = status
class Webapp2Adapter(WebObAdapter):
"""
Adapter for the |webapp2|_ framework.
Inherits from the :class:`.WebObAdapter`.
"""
def __init__(self, handler):
"""
:param handler:
A :class:`webapp2.RequestHandler` instance.
"""
self.request = handler.request
self.response = handler.response
class WerkzeugAdapter(BaseAdapter):
"""
Adapter for |flask|_ and other |werkzeug|_ based frameworks.
Thanks to `Mark Steve Samson <http://marksteve.com>`_.
"""
@property
def params(self):
return self.request.args
@property
def url(self):
return self.request.base_url
@property
def cookies(self):
return self.request.cookies
def __init__(self, request, response):
"""
:param request:
Instance of the :class:`werkzeug.wrappers.Request` class.
:param response:
Instance of the :class:`werkzeug.wrappers.Response` class.
"""
self.request = request
self.response = response
def write(self, value):
self.response.data = self.response.data.decode('utf-8') + value
def set_header(self, key, value):
self.response.headers[key] = value
def set_status(self, status):
self.response.status = status

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
"""
Provides various exception types for the library.
"""
class BaseError(Exception):
"""
Base error for all errors.
"""
def __init__(self, message, original_message='', url='', status=None):
super(BaseError, self).__init__(message)
#: Error message.
self.message = message
#: Original message.
self.original_message = original_message
#: URL related with the error.
self.url = url
#: HTTP status code related with the error.
self.status = status
def to_dict(self):
return self.__dict__
class ConfigError(BaseError):
pass
class SessionError(BaseError):
pass
class CredentialsError(BaseError):
pass
class HTTPError(BaseError):
pass
class CSRFError(BaseError):
pass
class ImportStringError(BaseError):
pass
class AuthenticationError(BaseError):
pass
class OAuth1Error(BaseError):
pass
class OAuth2Error(BaseError):
pass
class OpenIDError(BaseError):
pass
class CancellationError(BaseError):
pass
class FailureError(BaseError):
pass
class FetchError(BaseError):
pass
class RequestElementsError(BaseError):
pass

View file

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
"""
|flask| Extras
--------------
Utilities you can use when using this library with the |flask|_ framework.
Thanks to `Mark Steve Samson <http://marksteve.com>`_.
"""
from __future__ import absolute_import
from functools import wraps
from authomatic.adapters import WerkzeugAdapter
from authomatic import Authomatic
from flask import make_response, request, session
class FlaskAuthomatic(Authomatic):
"""
Flask Plugin for authomatic support.
"""
result = None
def login(self, *login_args, **login_kwargs):
"""
Decorator for Flask view functions.
"""
def decorator(f):
@wraps(f)
def decorated(*args, **kwargs):
self.response = make_response()
adapter = WerkzeugAdapter(request, self.response)
login_kwargs.setdefault('session', session)
login_kwargs.setdefault('session_saver', self.session_saver)
self.result = super(FlaskAuthomatic, self).login(
adapter,
*login_args,
**login_kwargs)
return f(*args, **kwargs)
return decorated
return decorator
def session_saver(self):
session.modified = True

View file

@ -0,0 +1,241 @@
# -*- coding: utf-8 -*-
"""
|gae| Extras
------------
Utilities you can use when using this library on |gae|_.
"""
from google.appengine.ext import ndb
from webapp2_extras import sessions
from authomatic import exceptions
from authomatic.extras import interfaces
from authomatic.extras.gae.openid import NDBOpenIDStore
__all__ = ['ndb_config', 'Webapp2Session']
class GAEError(exceptions.BaseError):
pass
class Webapp2Session(interfaces.BaseSession):
"""
A simple wrapper for |webapp2|_ sessions. If you provide a session it wraps
it and adds the :meth:`.save` method.
If you don't provide a session it creates a new one but you must provide
the :data:`.secret`.
For more about |webapp2| sessions see:
http://webapp-improved.appspot.com/api/webapp2_extras/sessions.html.
"""
def __init__(self, handler, session=None, secret=None,
cookie_name='webapp2authomatic', backend='memcache',
config=None):
"""
.. warning::
Do not use the ``'securecookie'`` backend with
:class:`.providers.OpenID` provider. The
`python-openid`_ library saves **non json serializable** objects
to session which the ``'securecookie'`` backend cannot cope with.
:param handler:
A :class:`webapp2.RequestHandler` instance.
:param session:
A :class:`webapp2_extras.session.SessionDict` instance.
:param str secret:
The session secret.
:param str cookie_name:
The name of the session cookie.
:param backend:
The session backend. One of ``'memcache'`` or ``'datastore'``.
:param config:
The session config.
"""
self.handler = handler
if session is None:
if not secret:
raise GAEError('Either session or secret must be specified!')
else:
# Create new session.
cfg = config or dict(
secret_key=secret, cookie_name=cookie_name)
session_store = sessions.SessionStore(handler.request, cfg)
self.session_dict = session_store.get_session(backend=backend)
else:
# Use supplied session.
self.session_dict = session
def save(self):
return self.session_dict.container.save_session(self.handler.response)
def __setitem__(self, key, value):
return self.session_dict.__setitem__(key, value)
def __getitem__(self, key):
return self.session_dict.__getitem__(key)
def __delitem__(self, key):
return self.session_dict.__delitem__(key)
def get(self, key):
return self.session_dict.get(key)
class NDBConfig(ndb.Model):
"""
|gae| `NDB <https://developers.google.com/appengine/docs/python/ndb/>`_
based :doc:`config`.
.. note::
By :class:`.OpenID` provider uses :class:`.NDBOpenIDStore`
as default :attr:`.OpenID.store`.
"""
# General properties
provider_name = ndb.StringProperty()
class_ = ndb.StringProperty()
# AuthorizationProvider properties
provider_id = ndb.IntegerProperty()
consumer_key = ndb.StringProperty()
consumer_secret = ndb.StringProperty()
# OAuth2 properties
scope = ndb.StringProperty()
offline = ndb.BooleanProperty()
# AuthenticationProvider properties
identifier_param = ndb.StringProperty()
@classmethod
def get(cls, key, default=None):
"""
Resembles the :meth:`dict.get` method.
:returns:
A configuration dictionary for specified provider.
"""
# Query datastore.
result = cls.query(cls.provider_name == key).get()
if result:
result_dict = result.to_dict()
# Use NDBOpenIDStore by default
result_dict['store'] = NDBOpenIDStore
# Convert coma-separated values to list. Currently only scope is
# csv.
for i in ('scope', ):
prop = result_dict.get(i)
if prop:
result_dict[i] = [s.strip() for s in prop.split(',')]
else:
result_dict[i] = None
return result_dict
else:
return default
@classmethod
def values(cls):
"""
Resembles the :meth:`dict.values` method.
"""
# get all items
results = cls.query().fetch()
# return list of dictionaries
return [result.to_dict() for result in results]
@classmethod
def initialize(cls):
"""
Creates an **"Example"** entity of kind **"NDBConfig"** in the
datastore if the model is empty and raises and error to inform you that
you should populate the model with data.
.. note::
The *Datastore Viewer* in the ``_ah/admin/`` won't let you add
properties to a model if there is not an entity with that
property already. Therefore it is a good idea to keep the
**"Example"** entity (which has all possible properties set) in
the datastore.
"""
if not len(cls.query().fetch()):
example = cls.get_or_insert('Example')
example.class_ = 'Provider class e.g. ' + \
'"authomatic.providers.oauth2.Facebook".'
example.provider_name = 'Your custom provider name e.g. "fb".'
# AuthorizationProvider
example.consumer_key = 'Consumer key.'
example.consumer_secret = 'Consumer secret'
example.provider_id = 1
# OAuth2
example.scope = 'coma, separated, list, of, scopes'
# AuthenticationProvider
example.identifier_param = 'Querystring parameter for claimed ' + \
'id. default is "id"'
# Save the example
example.put()
# Raise an information error.
raise GAEError(
'A NDBConfig data model was created! Go to Datastore Viewer '
'in your dashboard and populate it with data!')
def ndb_config():
"""
Allows you to have a **datastore** :doc:`config` instead of a hardcoded
one.
This function creates an **"Example"** entity of kind **"NDBConfig"** in
the datastore if the model is empty and raises and error to inform you
that you should populate the model with data.
.. note::
The *Datastore Viewer* of the |gae|_ admin won't let you add
properties to a model if there is not an entity with that property
already. Therefore it is a good idea to keep the **"Example"**
entity (which has all properties set) in the datastore.
:raises:
:exc:`.GAEError`
:returns:
:class:`.NDBConfig`
"""
NDBConfig.initialize()
return NDBConfig

View file

@ -0,0 +1,156 @@
# -*- coding: utf-8 -*-
# We need absolute import to import from openid library which has the same
# name as this module
from __future__ import absolute_import
import logging
import datetime
from google.appengine.ext import ndb
import openid.store.interface
class NDBOpenIDStore(ndb.Expando, openid.store.interface.OpenIDStore):
"""
|gae| `NDB <https://developers.google.com/appengine/docs/python/ndb/>`_
based implementation of the :class:`openid.store.interface.OpenIDStore`
interface of the `python-openid`_ library.
"""
serialized = ndb.StringProperty()
expiration_date = ndb.DateTimeProperty()
# we need issued to sort by most recently issued
issued = ndb.IntegerProperty()
@staticmethod
def _log(*args, **kwargs):
pass
@classmethod
def storeAssociation(cls, server_url, association):
# store an entity with key = server_url
issued = datetime.datetime.fromtimestamp(association.issued)
lifetime = datetime.timedelta(0, association.lifetime)
expiration_date = issued + lifetime
entity = cls.get_or_insert(
association.handle, parent=ndb.Key(
'ServerUrl', server_url))
entity.serialized = association.serialize()
entity.expiration_date = expiration_date
entity.issued = association.issued
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Putting OpenID association to datastore.')
entity.put()
@classmethod
def cleanupAssociations(cls):
# query for all expired
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Querying datastore for OpenID associations.')
query = cls.query(cls.expiration_date <= datetime.datetime.now())
# fetch keys only
expired = query.fetch(keys_only=True)
# delete all expired
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Deleting expired OpenID associations from datastore.')
ndb.delete_multi(expired)
return len(expired)
@classmethod
def getAssociation(cls, server_url, handle=None):
cls.cleanupAssociations()
if handle:
key = ndb.Key('ServerUrl', server_url, cls, handle)
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Getting OpenID association from datastore by key.')
entity = key.get()
else:
# return most recently issued association
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Querying datastore for OpenID associations by ancestor.')
entity = cls.query(ancestor=ndb.Key(
'ServerUrl', server_url)).order(-cls.issued).get()
if entity and entity.serialized:
return openid.association.Association.deserialize(
entity.serialized)
@classmethod
def removeAssociation(cls, server_url, handle):
key = ndb.Key('ServerUrl', server_url, cls, handle)
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Getting OpenID association from datastore by key.')
if key.get():
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Deleting OpenID association from datastore.')
key.delete()
return True
@classmethod
def useNonce(cls, server_url, timestamp, salt):
# check whether there is already an entity with the same ancestor path
# in the datastore
key = ndb.Key(
'ServerUrl',
str(server_url) or 'x',
'TimeStamp',
str(timestamp),
cls,
str(salt))
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Getting OpenID nonce from datastore by key.')
result = key.get()
if result:
# if so, the nonce is not valid so return False
cls._log(
logging.WARNING,
u'NDBOpenIDStore: Nonce was already used!')
return False
else:
# if not, store the key to datastore and return True
nonce = cls(key=key)
nonce.expiration_date = datetime.datetime.fromtimestamp(
timestamp) + datetime.timedelta(0, openid.store.nonce.SKEW)
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Putting new nonce to datastore.')
nonce.put()
return True
@classmethod
def cleanupNonces(cls):
# get all expired nonces
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Querying datastore for OpenID nonces ordered by expiration date.')
expired = cls.query().filter(
cls.expiration_date <= datetime.datetime.now()).fetch(
keys_only=True)
# delete all expired
cls._log(
logging.DEBUG,
u'NDBOpenIDStore: Deleting expired OpenID nonces from datastore.')
ndb.delete_multi(expired)
return len(expired)

View file

@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
"""
Interfaces
^^^^^^^^^^
If you want to implement framework specific extras, use these abstract
classes as bases:
"""
import abc
class BaseSession(object):
"""
Abstract class for custom session implementations.
"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def save(self):
"""
Called only once per request.
Should implement a mechanism for setting the the session
**cookie** and saving the session **data** to storage.
"""
@abc.abstractmethod
def __setitem__(self, key, value):
"""
Same as :meth:`dict.__setitem__`.
"""
@abc.abstractmethod
def __getitem__(self, key):
"""
Same as :meth:`dict.__getitem__`.
"""
@abc.abstractmethod
def __delitem__(self, key):
"""
Same as :meth:`dict.__delitem__`.
"""
@abc.abstractmethod
def get(self, key):
"""
Same as :meth:`dict.get`.
"""
class BaseConfig(object):
"""
Abstract class for :doc:`config` implementations.
"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get(self, key):
"""
Same as :attr:`dict.get`.
"""
@abc.abstractmethod
def values(self):
"""
Same as :meth:`dict.values`.
"""

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
"""
Google App Engine OpenID Providers
----------------------------------
|openid|_ provider implementations based on the |gae_users_api|_.
.. note::
When using the :class:`GAEOpenID` provider, the :class:`.User` object
will always have only the
:attr:`.User.user_id`,
:attr:`.User.email`,
:attr:`.User.gae_user`
attributes populated with data.
Moreover the :attr:`.User.user_id` will always be empty on the
`GAE Development Server
<https://developers.google.com/appengine/docs/python/tools/devserver>`_.
.. autosummary::
GAEOpenID
Yahoo
Google
"""
import logging
from google.appengine.api import users
import authomatic.core as core
from authomatic import providers
from authomatic.exceptions import FailureError
__all__ = ['GAEOpenID', 'Yahoo', 'Google']
class GAEOpenID(providers.AuthenticationProvider):
"""
|openid|_ provider based on the |gae_users_api|_.
Accepts additional keyword arguments inherited from
:class:`.AuthenticationProvider`.
"""
@providers.login_decorator
def login(self):
"""
Launches the OpenID authentication procedure.
"""
if self.params.get(self.identifier_param):
# =================================================================
# Phase 1 before redirect.
# =================================================================
self._log(
logging.INFO,
u'Starting OpenID authentication procedure.')
url = users.create_login_url(
dest_url=self.url, federated_identity=self.identifier)
self._log(logging.INFO, u'Redirecting user to {0}.'.format(url))
self.redirect(url)
else:
# =================================================================
# Phase 2 after redirect.
# =================================================================
self._log(
logging.INFO,
u'Continuing OpenID authentication procedure after redirect.')
user = users.get_current_user()
if user:
self._log(logging.INFO, u'Authentication successful.')
self._log(logging.INFO, u'Creating user.')
self.user = core.User(self,
id=user.federated_identity(),
email=user.email(),
gae_user=user)
# =============================================================
# We're done
# =============================================================
else:
raise FailureError(
'Unable to authenticate identifier "{0}"!'.format(
self.identifier))
class Yahoo(GAEOpenID):
"""
:class:`.GAEOpenID` provider with the :attr:`.identifier` set to
``"me.yahoo.com"``.
"""
identifier = 'me.yahoo.com'
class Google(GAEOpenID):
"""
:class:`.GAEOpenID` provider with the :attr:`.identifier` set to
``"https://www.google.com/accounts/o8/id"``.
"""
identifier = 'https://www.google.com/accounts/o8/id'

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,505 @@
# -*- coding: utf-8 -*-
"""
|openid| Providers
----------------------------------
Providers which implement the |openid|_ protocol based on the
`python-openid`_ library.
.. warning::
This providers are dependent on the |pyopenid|_ package.
.. autosummary::
OpenID
Yahoo
Google
"""
# We need absolute import to import from openid library which has the same
# name as this module
from __future__ import absolute_import
import datetime
import logging
import time
from openid import oidutil
from openid.consumer import consumer
from openid.extensions import ax, pape, sreg
from openid.association import Association
from authomatic import providers
from authomatic.exceptions import FailureError, CancellationError, OpenIDError
__all__ = ['OpenID', 'Yahoo', 'Google']
# Suppress openid logging.
oidutil.log = lambda message, level=0: None
REALM_HTML = \
"""
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-XRDS-Location" content="{xrds_location}" />
</head>
<body>{body}</body>
</html>
"""
XRDS_XML = \
"""
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="1">
<Type>http://specs.openid.net/auth/2.0/return_to</Type>
<URI>{return_to}</URI>
</Service>
</XRD>
</xrds:XRDS>
"""
class SessionOpenIDStore(object):
"""
A very primitive session-based implementation of the.
:class:`openid.store.interface.OpenIDStore` interface of the
`python-openid`_ library.
.. warning::
Nonces get verified only by their timeout. Use on your own risk!
"""
@staticmethod
def _log(level, message):
return None
ASSOCIATION_KEY = ('authomatic.providers.openid.SessionOpenIDStore:'
'association')
def __init__(self, session, nonce_timeout=None):
"""
:param int nonce_timeout:
Nonces older than this in seconds will be considered expired.
Default is 600.
"""
self.session = session
self.nonce_timeout = nonce_timeout or 600
def storeAssociation(self, server_url, association):
self._log(logging.DEBUG,
'SessionOpenIDStore: Storing association to session.')
serialized = association.serialize()
decoded = serialized.decode('latin-1')
assoc = decoded
# assoc = serialized
# Always store only one association as a tuple.
self.session[self.ASSOCIATION_KEY] = (server_url, association.handle,
assoc)
def getAssociation(self, server_url, handle=None):
# Try to get association.
assoc = self.session.get(self.ASSOCIATION_KEY)
if assoc and assoc[0] == server_url:
# If found deserialize and return it.
self._log(logging.DEBUG, u'SessionOpenIDStore: Association found.')
return Association.deserialize(assoc[2].encode('latin-1'))
else:
self._log(logging.DEBUG,
u'SessionOpenIDStore: Association not found.')
def removeAssociation(self, server_url, handle):
# Just inform the caller that it's gone.
return True
def useNonce(self, server_url, timestamp, salt):
# Evaluate expired nonces as false.
age = int(time.time()) - int(timestamp)
if age < self.nonce_timeout:
return True
else:
self._log(logging.ERROR, u'SessionOpenIDStore: Expired nonce!')
return False
class OpenID(providers.AuthenticationProvider):
"""
|openid|_ provider based on the `python-openid`_ library.
"""
AX = ['http://axschema.org/contact/email',
'http://schema.openid.net/contact/email',
'http://axschema.org/namePerson',
'http://openid.net/schema/namePerson/first',
'http://openid.net/schema/namePerson/last',
'http://openid.net/schema/gender',
'http://openid.net/schema/language/pref',
'http://openid.net/schema/contact/web/default',
'http://openid.net/schema/media/image',
'http://openid.net/schema/timezone']
AX_REQUIRED = ['http://schema.openid.net/contact/email']
SREG = ['nickname',
'email',
'fullname',
'dob',
'gender',
'postcode',
'country',
'language',
'timezone']
PAPE = [
'http://schemas.openid.net/pape/policies/2007/06/'
'multi-factor-physical',
'http://schemas.openid.net/pape/policies/2007/06/multi-factor',
'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant'
]
def __init__(self, *args, **kwargs):
"""
Accepts additional keyword arguments:
:param store:
Any object which implements
:class:`openid.store.interface.OpenIDStore`
of the `python-openid`_ library.
:param bool use_realm:
Whether to use `OpenID realm
<http://openid.net/specs/openid-authentication-2_0-12.html#realms>`_
If ``True`` the realm HTML document will be accessible at
``{current url}?{realm_param}={realm_param}``
e.g. ``http://example.com/path?realm=realm``.
:param str realm_body:
Contents of the HTML body tag of the realm.
:param str realm_param:
Name of the query parameter to be used to serve the realm.
:param str xrds_param:
The name of the query parameter to be used to serve the
`XRDS document
<http://openid.net/specs/openid-authentication-2_0-12.html#XRDS_Sample>`_.
:param list sreg:
List of strings of optional
`SREG
<http://openid.net/specs/openid-simple-registration-extension-1_0.html>`_
fields.
Default = :attr:`OpenID.SREG`.
:param list sreg_required:
List of strings of required
`SREG
<http://openid.net/specs/openid-simple-registration-extension-1_0.html>`_
fields.
Default = ``[]``.
:param list ax:
List of strings of optional
`AX
<http://openid.net/specs/openid-attribute-exchange-1_0.html>`_
schemas.
Default = :attr:`OpenID.AX`.
:param list ax_required:
List of strings of required
`AX
<http://openid.net/specs/openid-attribute-exchange-1_0.html>`_
schemas.
Default = :attr:`OpenID.AX_REQUIRED`.
:param list pape:
of requested
`PAPE
<http://openid.net/specs/openid-provider-authentication-policy-extension-1_0.html>`_
policies.
Default = :attr:`OpenID.PAPE`.
As well as those inherited from :class:`.AuthenticationProvider`
constructor.
"""
super(OpenID, self).__init__(*args, **kwargs)
# Allow for other openid store implementations.
self.store = self._kwarg(
kwargs, 'store', SessionOpenIDStore(
self.session))
# Realm
self.use_realm = self._kwarg(kwargs, 'use_realm', True)
self.realm_body = self._kwarg(kwargs, 'realm_body', '')
self.realm_param = self._kwarg(kwargs, 'realm_param', 'realm')
self.xrds_param = self._kwarg(kwargs, 'xrds_param', 'xrds')
# SREG
self.sreg = self._kwarg(kwargs, 'sreg', self.SREG)
self.sreg_required = self._kwarg(kwargs, 'sreg_required', [])
# AX
self.ax = self._kwarg(kwargs, 'ax', self.AX)
self.ax_required = self._kwarg(kwargs, 'ax_required', self.AX_REQUIRED)
# add required schemas to schemas if not already there
for i in self.ax_required:
if i not in self.ax:
self.ax.append(i)
# PAPE
self.pape = self._kwarg(kwargs, 'pape', self.PAPE)
@staticmethod
def _x_user_parser(user, data):
user.first_name = data.get('ax', {}).get(
'http://openid.net/schema/namePerson/first')
user.last_name = data.get('ax', {}).get(
'http://openid.net/schema/namePerson/last')
user.id = data.get('guid')
user.link = data.get('ax', {}).get(
'http://openid.net/schema/contact/web/default')
user.picture = data.get('ax', {}).get(
'http://openid.net/schema/media/image')
user.nickname = data.get('sreg', {}).get('nickname')
user.country = data.get('sreg', {}).get('country')
user.postal_code = data.get('sreg', {}).get('postcode')
user.name = data.get('sreg', {}).get('fullname') or \
data.get('ax', {}).get('http://axschema.org/namePerson')
user.gender = data.get('sreg', {}).get('gender') or \
data.get('ax', {}).get('http://openid.net/schema/gender')
user.locale = data.get('sreg', {}).get('language') or \
data.get('ax', {}).get('http://openid.net/schema/language/pref')
user.timezone = data.get('sreg', {}).get('timezone') or \
data.get('ax', {}).get('http://openid.net/schema/timezone')
user.email = data.get('sreg', {}).get('email') or \
data.get('ax', {}).get('http://axschema.org/contact/email') or \
data.get('ax', {}).get('http://schema.openid.net/contact/email')
if data.get('sreg', {}).get('dob'):
user.birth_date = datetime.datetime.strptime(
data.get('sreg', {}).get('dob'),
'%Y-%m-%d'
)
else:
user.birth_date = None
return user
@providers.login_decorator
def login(self):
# Instantiate consumer
self.store._log = self._log
oi_consumer = consumer.Consumer(self.session, self.store)
# handle realm and XRDS if there is only one query parameter
if self.use_realm and len(self.params) == 1:
realm_request = self.params.get(self.realm_param)
xrds_request = self.params.get(self.xrds_param)
else:
realm_request = None
xrds_request = None
# determine type of request
if realm_request:
# =================================================================
# Realm HTML
# =================================================================
self._log(
logging.INFO,
u'Writing OpenID realm HTML to the response.')
xrds_location = '{u}?{x}={x}'.format(u=self.url, x=self.xrds_param)
self.write(
REALM_HTML.format(
xrds_location=xrds_location,
body=self.realm_body))
elif xrds_request:
# =================================================================
# XRDS XML
# =================================================================
self._log(
logging.INFO,
u'Writing XRDS XML document to the response.')
self.set_header('Content-Type', 'application/xrds+xml')
self.write(XRDS_XML.format(return_to=self.url))
elif self.params.get('openid.mode'):
# =================================================================
# Phase 2 after redirect
# =================================================================
self._log(
logging.INFO,
u'Continuing OpenID authentication procedure after redirect.')
# complete the authentication process
response = oi_consumer.complete(self.params, self.url)
# on success
if response.status == consumer.SUCCESS:
data = {}
# get user ID
data['guid'] = response.getDisplayIdentifier()
self._log(logging.INFO, u'Authentication successful.')
# get user data from AX response
ax_response = ax.FetchResponse.fromSuccessResponse(response)
if ax_response and ax_response.data:
self._log(logging.INFO, u'Got AX data.')
ax_data = {}
# convert iterable values to their first item
for k, v in ax_response.data.items():
if v and isinstance(v, (list, tuple)):
ax_data[k] = v[0]
data['ax'] = ax_data
# get user data from SREG response
sreg_response = sreg.SRegResponse.fromSuccessResponse(response)
if sreg_response and sreg_response.data:
self._log(logging.INFO, u'Got SREG data.')
data['sreg'] = sreg_response.data
# get data from PAPE response
pape_response = pape.Response.fromSuccessResponse(response)
if pape_response and pape_response.auth_policies:
self._log(logging.INFO, u'Got PAPE data.')
data['pape'] = pape_response.auth_policies
# create user
self._update_or_create_user(data)
# =============================================================
# We're done!
# =============================================================
elif response.status == consumer.CANCEL:
raise CancellationError(
u'User cancelled the verification of ID "{0}"!'.format(
response.getDisplayIdentifier()))
elif response.status == consumer.FAILURE:
raise FailureError(response.message)
elif self.identifier: # As set in AuthenticationProvider.__init__
# =================================================================
# Phase 1 before redirect
# =================================================================
self._log(
logging.INFO,
u'Starting OpenID authentication procedure.')
# get AuthRequest object
try:
auth_request = oi_consumer.begin(self.identifier)
except consumer.DiscoveryFailure as e:
raise FailureError(
u'Discovery failed for identifier {0}!'.format(
self.identifier
),
url=self.identifier,
original_message=e.message)
self._log(
logging.INFO,
u'Service discovery for identifier {0} successful.'.format(
self.identifier))
# add SREG extension
# we need to remove required fields from optional fields because
# addExtension then raises an error
self.sreg = [i for i in self.sreg if i not in self.sreg_required]
auth_request.addExtension(
sreg.SRegRequest(
optional=self.sreg,
required=self.sreg_required)
)
# add AX extension
ax_request = ax.FetchRequest()
# set AX schemas
for i in self.ax:
required = i in self.ax_required
ax_request.add(ax.AttrInfo(i, required=required))
auth_request.addExtension(ax_request)
# add PAPE extension
auth_request.addExtension(pape.Request(self.pape))
# prepare realm and return_to URLs
if self.use_realm:
realm = return_to = '{u}?{r}={r}'.format(
u=self.url, r=self.realm_param)
else:
realm = return_to = self.url
url = auth_request.redirectURL(realm, return_to)
if auth_request.shouldSendRedirect():
# can be redirected
url = auth_request.redirectURL(realm, return_to)
self._log(
logging.INFO,
u'Redirecting user to {0}.'.format(url))
self.redirect(url)
else:
# must be sent as POST
# this writes a html post form with auto-submit
self._log(
logging.INFO,
u'Writing an auto-submit HTML form to the response.')
form = auth_request.htmlMarkup(
realm, return_to, False, dict(
id='openid_form'))
self.write(form)
else:
raise OpenIDError('No identifier specified!')
class Yahoo(OpenID):
"""
Yahoo :class:`.OpenID` provider with the :attr:`.identifier` predefined to
``"me.yahoo.com"``.
"""
identifier = 'me.yahoo.com'
class Google(OpenID):
"""
Google :class:`.OpenID` provider with the :attr:`.identifier` predefined to
``"https://www.google.com/accounts/o8/id"``.
"""
identifier = 'https://www.google.com/accounts/o8/id'

View file

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from authomatic import providers
class MozillaPersona(providers.AuthenticationProvider):
pass

View file

@ -0,0 +1,839 @@
# -*- coding: utf-8 -*-
"""Utilities for writing code that runs on Python 2 and 3"""
# Copyright (c) 2010-2015 Benjamin Peterson
#
# 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:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import absolute_import
import functools
import itertools
import operator
import sys
import types
__author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.9.0"
# Useful for very coarse version differentiation.
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY3:
string_types = str,
integer_types = int,
class_types = type,
text_type = str
binary_type = bytes
MAXSIZE = sys.maxsize
else:
string_types = basestring,
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
if sys.platform.startswith("java"):
# Jython always uses 32 bits.
MAXSIZE = int((1 << 31) - 1)
else:
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object):
def __len__(self):
return 1 << 31
try:
len(X())
except OverflowError:
# 32-bit
MAXSIZE = int((1 << 31) - 1)
else:
# 64-bit
MAXSIZE = int((1 << 63) - 1)
del X
def _add_doc(func, doc):
"""Add documentation to a function."""
func.__doc__ = doc
def _import_module(name):
"""Import module, returning the module after the last dot."""
__import__(name)
return sys.modules[name]
class _LazyDescr(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, tp):
result = self._resolve()
setattr(obj, self.name, result) # Invokes __set__.
try:
# This is a bit ugly, but it avoids running this again by
# removing this descriptor.
delattr(obj.__class__, self.name)
except AttributeError:
pass
return result
class MovedModule(_LazyDescr):
def __init__(self, name, old, new=None):
super(MovedModule, self).__init__(name)
if PY3:
if new is None:
new = name
self.mod = new
else:
self.mod = old
def _resolve(self):
return _import_module(self.mod)
def __getattr__(self, attr):
_module = self._resolve()
value = getattr(_module, attr)
setattr(self, attr, value)
return value
class _LazyModule(types.ModuleType):
def __init__(self, name):
super(_LazyModule, self).__init__(name)
self.__doc__ = self.__class__.__doc__
def __dir__(self):
attrs = ["__doc__", "__name__"]
attrs += [attr.name for attr in self._moved_attributes]
return attrs
# Subclasses should override this
_moved_attributes = []
class MovedAttribute(_LazyDescr):
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
super(MovedAttribute, self).__init__(name)
if PY3:
if new_mod is None:
new_mod = name
self.mod = new_mod
if new_attr is None:
if old_attr is None:
new_attr = name
else:
new_attr = old_attr
self.attr = new_attr
else:
self.mod = old_mod
if old_attr is None:
old_attr = name
self.attr = old_attr
def _resolve(self):
module = _import_module(self.mod)
return getattr(module, self.attr)
class _SixMetaPathImporter(object):
"""
A meta path importer to import six.moves and its submodules.
This class implements a PEP302 finder and loader. It should be compatible
with Python 2.5 and all existing versions of Python3
"""
def __init__(self, six_module_name):
self.name = six_module_name
self.known_modules = {}
def _add_module(self, mod, *fullnames):
for fullname in fullnames:
self.known_modules[self.name + "." + fullname] = mod
def _get_module(self, fullname):
return self.known_modules[self.name + "." + fullname]
def find_module(self, fullname, path=None):
if fullname in self.known_modules:
return self
return None
def __get_module(self, fullname):
try:
return self.known_modules[fullname]
except KeyError:
raise ImportError("This loader does not know module " + fullname)
def load_module(self, fullname):
try:
# in case of a reload
return sys.modules[fullname]
except KeyError:
pass
mod = self.__get_module(fullname)
if isinstance(mod, MovedModule):
mod = mod._resolve()
else:
mod.__loader__ = self
sys.modules[fullname] = mod
return mod
def is_package(self, fullname):
"""
Return true, if the named module is a package.
We need this method to get correct spec objects with
Python 3.4 (see PEP451)
"""
return hasattr(self.__get_module(fullname), "__path__")
def get_code(self, fullname):
"""Return None
Required, if is_package is implemented"""
self.__get_module(fullname) # eventually raises ImportError
return None
get_source = get_code # same as get_code
_importer = _SixMetaPathImporter(__name__)
class _MovedItems(_LazyModule):
"""Lazy loading of moved objects"""
__path__ = [] # mark as package
_moved_attributes = [
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
MovedAttribute("intern", "__builtin__", "sys"),
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
MovedAttribute("reduce", "__builtin__", "functools"),
MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
MovedAttribute("StringIO", "StringIO", "io"),
MovedAttribute("UserDict", "UserDict", "collections"),
MovedAttribute("UserList", "UserList", "collections"),
MovedAttribute("UserString", "UserString", "collections"),
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
MovedModule("builtins", "__builtin__"),
MovedModule("configparser", "ConfigParser"),
MovedModule("copyreg", "copy_reg"),
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
MovedModule("http_cookies", "Cookie", "http.cookies"),
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
MovedModule("html_parser", "HTMLParser", "html.parser"),
MovedModule("http_client", "httplib", "http.client"),
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
MovedModule("cPickle", "cPickle", "pickle"),
MovedModule("queue", "Queue"),
MovedModule("reprlib", "repr"),
MovedModule("socketserver", "SocketServer"),
MovedModule("_thread", "thread", "_thread"),
MovedModule("tkinter", "Tkinter"),
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
MovedModule("tkinter_colorchooser", "tkColorChooser",
"tkinter.colorchooser"),
MovedModule("tkinter_commondialog", "tkCommonDialog",
"tkinter.commondialog"),
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
"tkinter.simpledialog"),
MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
MovedModule("winreg", "_winreg"),
]
for attr in _moved_attributes:
setattr(_MovedItems, attr.name, attr)
if isinstance(attr, MovedModule):
_importer._add_module(attr, "moves." + attr.name)
del attr
_MovedItems._moved_attributes = _moved_attributes
moves = _MovedItems(__name__ + ".moves")
_importer._add_module(moves, "moves")
class Module_six_moves_urllib_parse(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_parse"""
_urllib_parse_moved_attributes = [
MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
MovedAttribute("urljoin", "urlparse", "urllib.parse"),
MovedAttribute("urlparse", "urlparse", "urllib.parse"),
MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
MovedAttribute("quote", "urllib", "urllib.parse"),
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
MovedAttribute("unquote", "urllib", "urllib.parse"),
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
MovedAttribute("urlencode", "urllib", "urllib.parse"),
MovedAttribute("splitquery", "urllib", "urllib.parse"),
MovedAttribute("splittag", "urllib", "urllib.parse"),
MovedAttribute("splituser", "urllib", "urllib.parse"),
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
MovedAttribute("uses_query", "urlparse", "urllib.parse"),
MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
]
for attr in _urllib_parse_moved_attributes:
setattr(Module_six_moves_urllib_parse, attr.name, attr)
del attr
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
"moves.urllib_parse", "moves.urllib.parse")
class Module_six_moves_urllib_error(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_error"""
_urllib_error_moved_attributes = [
MovedAttribute("URLError", "urllib2", "urllib.error"),
MovedAttribute("HTTPError", "urllib2", "urllib.error"),
MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
]
for attr in _urllib_error_moved_attributes:
setattr(Module_six_moves_urllib_error, attr.name, attr)
del attr
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
"moves.urllib_error", "moves.urllib.error")
class Module_six_moves_urllib_request(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_request"""
_urllib_request_moved_attributes = [
MovedAttribute("urlopen", "urllib2", "urllib.request"),
MovedAttribute("install_opener", "urllib2", "urllib.request"),
MovedAttribute("build_opener", "urllib2", "urllib.request"),
MovedAttribute("pathname2url", "urllib", "urllib.request"),
MovedAttribute("url2pathname", "urllib", "urllib.request"),
MovedAttribute("getproxies", "urllib", "urllib.request"),
MovedAttribute("Request", "urllib2", "urllib.request"),
MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
MovedAttribute("FileHandler", "urllib2", "urllib.request"),
MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
MovedAttribute("urlretrieve", "urllib", "urllib.request"),
MovedAttribute("urlcleanup", "urllib", "urllib.request"),
MovedAttribute("URLopener", "urllib", "urllib.request"),
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
]
for attr in _urllib_request_moved_attributes:
setattr(Module_six_moves_urllib_request, attr.name, attr)
del attr
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
"moves.urllib_request", "moves.urllib.request")
class Module_six_moves_urllib_response(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_response"""
_urllib_response_moved_attributes = [
MovedAttribute("addbase", "urllib", "urllib.response"),
MovedAttribute("addclosehook", "urllib", "urllib.response"),
MovedAttribute("addinfo", "urllib", "urllib.response"),
MovedAttribute("addinfourl", "urllib", "urllib.response"),
]
for attr in _urllib_response_moved_attributes:
setattr(Module_six_moves_urllib_response, attr.name, attr)
del attr
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
"moves.urllib_response", "moves.urllib.response")
class Module_six_moves_urllib_robotparser(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_robotparser"""
_urllib_robotparser_moved_attributes = [
MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
]
for attr in _urllib_robotparser_moved_attributes:
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
del attr
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
"moves.urllib_robotparser", "moves.urllib.robotparser")
class Module_six_moves_urllib(types.ModuleType):
"""Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
__path__ = [] # mark as package
parse = _importer._get_module("moves.urllib_parse")
error = _importer._get_module("moves.urllib_error")
request = _importer._get_module("moves.urllib_request")
response = _importer._get_module("moves.urllib_response")
robotparser = _importer._get_module("moves.urllib_robotparser")
def __dir__(self):
return ['parse', 'error', 'request', 'response', 'robotparser']
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
"moves.urllib")
def add_move(move):
"""Add an item to six.moves."""
setattr(_MovedItems, move.name, move)
def remove_move(name):
"""Remove item from six.moves."""
try:
delattr(_MovedItems, name)
except AttributeError:
try:
del moves.__dict__[name]
except KeyError:
raise AttributeError("no such move, %r" % (name,))
if PY3:
_meth_func = "__func__"
_meth_self = "__self__"
_func_closure = "__closure__"
_func_code = "__code__"
_func_defaults = "__defaults__"
_func_globals = "__globals__"
else:
_meth_func = "im_func"
_meth_self = "im_self"
_func_closure = "func_closure"
_func_code = "func_code"
_func_defaults = "func_defaults"
_func_globals = "func_globals"
try:
advance_iterator = next
except NameError:
def advance_iterator(it):
return it.next()
next = advance_iterator
try:
callable = callable
except NameError:
def callable(obj):
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
if PY3:
def get_unbound_function(unbound):
return unbound
create_bound_method = types.MethodType
Iterator = object
else:
def get_unbound_function(unbound):
return unbound.im_func
def create_bound_method(func, obj):
return types.MethodType(func, obj, obj.__class__)
class Iterator(object):
def next(self):
return type(self).__next__(self)
callable = callable
_add_doc(get_unbound_function,
"""Get the function out of a possibly unbound function""")
get_method_function = operator.attrgetter(_meth_func)
get_method_self = operator.attrgetter(_meth_self)
get_function_closure = operator.attrgetter(_func_closure)
get_function_code = operator.attrgetter(_func_code)
get_function_defaults = operator.attrgetter(_func_defaults)
get_function_globals = operator.attrgetter(_func_globals)
if PY3:
def iterkeys(d, **kw):
return iter(d.keys(**kw))
def itervalues(d, **kw):
return iter(d.values(**kw))
def iteritems(d, **kw):
return iter(d.items(**kw))
def iterlists(d, **kw):
return iter(d.lists(**kw))
viewkeys = operator.methodcaller("keys")
viewvalues = operator.methodcaller("values")
viewitems = operator.methodcaller("items")
else:
def iterkeys(d, **kw):
return iter(d.iterkeys(**kw))
def itervalues(d, **kw):
return iter(d.itervalues(**kw))
def iteritems(d, **kw):
return iter(d.iteritems(**kw))
def iterlists(d, **kw):
return iter(d.iterlists(**kw))
viewkeys = operator.methodcaller("viewkeys")
viewvalues = operator.methodcaller("viewvalues")
viewitems = operator.methodcaller("viewitems")
_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
_add_doc(iteritems,
"Return an iterator over the (key, value) pairs of a dictionary.")
_add_doc(iterlists,
"Return an iterator over the (key, [values]) pairs of a dictionary.")
if PY3:
def b(s):
return s.encode("latin-1")
def u(s):
return s
unichr = chr
if sys.version_info[1] <= 1:
def int2byte(i):
return bytes((i,))
else:
# This is about 2x faster than the implementation above on 3.2+
int2byte = operator.methodcaller("to_bytes", 1, "big")
byte2int = operator.itemgetter(0)
indexbytes = operator.getitem
iterbytes = iter
import io
StringIO = io.StringIO
BytesIO = io.BytesIO
_assertCountEqual = "assertCountEqual"
_assertRaisesRegex = "assertRaisesRegex"
_assertRegex = "assertRegex"
else:
def b(s):
return s
# Workaround for standalone backslash
def u(s):
return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
unichr = unichr
int2byte = chr
def byte2int(bs):
return ord(bs[0])
def indexbytes(buf, i):
return ord(buf[i])
iterbytes = functools.partial(itertools.imap, ord)
import StringIO
StringIO = BytesIO = StringIO.StringIO
_assertCountEqual = "assertItemsEqual"
_assertRaisesRegex = "assertRaisesRegexp"
_assertRegex = "assertRegexpMatches"
_add_doc(b, """Byte literal""")
_add_doc(u, """Text literal""")
def assertCountEqual(self, *args, **kwargs):
return getattr(self, _assertCountEqual)(*args, **kwargs)
def assertRaisesRegex(self, *args, **kwargs):
return getattr(self, _assertRaisesRegex)(*args, **kwargs)
def assertRegex(self, *args, **kwargs):
return getattr(self, _assertRegex)(*args, **kwargs)
if PY3:
exec_ = getattr(moves.builtins, "exec")
def reraise(tp, value, tb=None):
if value is None:
value = tp()
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
else:
def exec_(_code_, _globs_=None, _locs_=None):
"""Execute code in a namespace."""
if _globs_ is None:
frame = sys._getframe(1)
_globs_ = frame.f_globals
if _locs_ is None:
_locs_ = frame.f_locals
del frame
elif _locs_ is None:
_locs_ = _globs_
exec("""exec _code_ in _globs_, _locs_""")
exec_("""def reraise(tp, value, tb=None):
raise tp, value, tb
""")
if sys.version_info[:2] == (3, 2):
exec_("""def raise_from(value, from_value):
if from_value is None:
raise value
raise value from from_value
""")
elif sys.version_info[:2] > (3, 2):
exec_("""def raise_from(value, from_value):
raise value from from_value
""")
else:
def raise_from(value, from_value):
raise value
print_ = getattr(moves.builtins, "print", None)
if print_ is None:
def print_(*args, **kwargs):
"""The new-style print function for Python 2.4 and 2.5."""
fp = kwargs.pop("file", sys.stdout)
if fp is None:
return
def write(data):
if not isinstance(data, basestring):
data = str(data)
# If the file has an encoding, encode unicode with it.
if (isinstance(fp, file) and
isinstance(data, unicode) and
fp.encoding is not None):
errors = getattr(fp, "errors", None)
if errors is None:
errors = "strict"
data = data.encode(fp.encoding, errors)
fp.write(data)
want_unicode = False
sep = kwargs.pop("sep", None)
if sep is not None:
if isinstance(sep, unicode):
want_unicode = True
elif not isinstance(sep, str):
raise TypeError("sep must be None or a string")
end = kwargs.pop("end", None)
if end is not None:
if isinstance(end, unicode):
want_unicode = True
elif not isinstance(end, str):
raise TypeError("end must be None or a string")
if kwargs:
raise TypeError("invalid keyword arguments to print()")
if not want_unicode:
for arg in args:
if isinstance(arg, unicode):
want_unicode = True
break
if want_unicode:
newline = unicode("\n")
space = unicode(" ")
else:
newline = "\n"
space = " "
if sep is None:
sep = space
if end is None:
end = newline
for i, arg in enumerate(args):
if i:
write(sep)
write(arg)
write(end)
if sys.version_info[:2] < (3, 3):
_print = print_
def print_(*args, **kwargs):
fp = kwargs.get("file", sys.stdout)
flush = kwargs.pop("flush", False)
_print(*args, **kwargs)
if flush and fp is not None:
fp.flush()
_add_doc(reraise, """Reraise an exception.""")
if sys.version_info[0:2] < (3, 4):
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
updated=functools.WRAPPER_UPDATES):
def wrapper(f):
f = functools.wraps(wrapped, assigned, updated)(f)
f.__wrapped__ = wrapped
return f
return wrapper
else:
wraps = functools.wraps
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
# This requires a bit of explanation: the basic idea is to make a dummy
# metaclass for one level of class instantiation that replaces itself with
# the actual metaclass.
class metaclass(meta):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
def add_metaclass(metaclass):
"""Class decorator for creating a class with a metaclass."""
def wrapper(cls):
orig_vars = cls.__dict__.copy()
slots = orig_vars.get('__slots__')
if slots is not None:
if isinstance(slots, str):
slots = [slots]
for slots_var in slots:
orig_vars.pop(slots_var)
orig_vars.pop('__dict__', None)
orig_vars.pop('__weakref__', None)
return metaclass(cls.__name__, cls.__bases__, orig_vars)
return wrapper
def python_2_unicode_compatible(klass):
"""
A decorator that defines __unicode__ and __str__ methods under Python 2.
Under Python 3 it does nothing.
To support Python 2 and 3 with a single code base, define a __str__ method
returning text and apply this decorator to the class.
"""
if PY2:
if '__str__' not in klass.__dict__:
raise ValueError("@python_2_unicode_compatible cannot be applied "
"to %s because it doesn't define __str__()." %
klass.__name__)
klass.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass
# Complete the moves implementation.
# This code is at the end of this module to speed up module loading.
# Turn this module into a package.
__path__ = [] # required for PEP 302 and PEP 451
__package__ = __name__ # see PEP 366 @ReservedAssignment
if globals().get("__spec__") is not None:
__spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
# Remove other six meta path importers, since they cause problems. This can
# happen if six is removed from sys.modules and then reloaded. (Setuptools does
# this for some reason.)
if sys.meta_path:
for i, importer in enumerate(sys.meta_path):
# Here's some real nastiness: Another "instance" of the six module might
# be floating around. Therefore, we can't use isinstance() to check for
# the six meta path importer, since the other six instance will have
# inserted an importer with different class.
if (type(importer).__name__ == "_SixMetaPathImporter" and
importer.name == __name__):
del sys.meta_path[i]
break
del i, importer
# Finally, add the importer to the meta path import hook.
sys.meta_path.append(_importer)