<commit_before>__author__ = 'arimorcos'

import praw
from redditDataset import *

if __name__ == '__main__':

    # handle arguments
    startDate = sys.argv[1]
    endDate = sys.argv[2]
    dbName = sys.argv[3]
    fineScale = int(sys.argv[4])

    newDefaults = ['art', 'askscience', 'blog', 'creepy', 'dataisbeautiful', 'DIY', 'Documentaries', 'fitness',
                   'food', 'futurology', 'gadgets', 'getmotivated', 'history', 'internetisbeautiful', 'jokes',
                   'lifeprotips', 'listentothis', 'mildlyinteresting', 'nosleep', 'nottheonion', 'oldschoolcool',
                   'personalfinance', 'philosophy', 'photoshopbattles', 'showerthoughts', 'space', 'sports', 'tifu',
                   'twoxchromosomes', 'upliftingnews', 'writingprompts']

    r = praw.Reddit(user_agent='grab_defaults')
    sub = getSubreddits(r, newDefaults)
    createDataset(r, subreddits, startDate=startDate, endDate=endDate, dbName=dbName, fineScale=fineScale)
<commit_msg>Added ability to grab new datasets within small range<commit_after>__author__ = 'arimorcos'

import datetime
from redditDataset import *

if __name__ == '__main__':

    shouldOneHour = False

    # handle arguments
    startDate = sys.argv[1]
    endDate = sys.argv[2]
    dbName = sys.argv[3]
    fineScale = int(sys.argv[4])
    shouldOneHour = bool(sys.argv[5])

    newDefaults = ['art', 'askscience', 'blog', 'creepy', 'dataisbeautiful', 'DIY', 'Documentaries', 'fitness',
                   'food', 'futurology', 'gadgets', 'getmotivated', 'history', 'internetisbeautiful', 'jokes',
                   'lifeprotips', 'listentothis', 'mildlyinteresting', 'nosleep', 'nottheonion', 'oldschoolcool',
                   'personalfinance', 'philosophy', 'photoshopbattles', 'showerthoughts', 'space', 'sports', 'tifu',
                   'twoxchromosomes', 'upliftingnews', 'writingprompts']

    r = praw.Reddit(user_agent='grab_defaults')
    sub = getSubreddits(r, newDefaults)
    if shouldOneHour:
        hour = 9
        # get number of days between dates
        startDay = datetime.date(int(startDate[0:2]), int(startDate[2:4]), int(startDate[4:6]))
        endDay = datetime.date(int(endDate[0:2]), int(endDate[2:4]), int(endDate[4:6]))
        nDays = (endDay - startDay).days

        # loop through each day and create dataset
        for day in range(nDays):
            # get temp start and stop
            tempStartObj = datetime.datetime(int(startDate[0:2]), int(startDate[2:4]), int(startDate[4:6]),
                                             hour, int(startDate[8:10]), int(startDate[10:12]))
            tempStartObj = tempStartObj + datetime.timedelta(days=day)
            tempStopObj = tempStartObj + datetime.timedelta(hours=1)

            # convert to strings
            tempStartStr = tempStartObj.strftime('%y%m%d%H%M%S')
            tempStopStr = tempStopObj.strftime('%y%m%d%H%M%S')

            # create dataset
            createDataset(r, subreddits, startDate=tempStartStr, endDate=tempStopStr, dbName=dbName + '_day_' + str(day),
                          fineScale=fineScale)
    else:
        createDataset(r, subreddits, startDate=startDate, endDate=endDate, dbName=dbName, fineScale=fineScale)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from openprocurement.api.utils import (
    get_now,
    raise_operation_error
)
from openprocurement.api.validation import OPERATIONS
from openprocurement.tender.core.utils import (
    optendersresource, calculate_business_date
)
from openprocurement.tender.openeu.views.tender_document import (
    TenderEUDocumentResource
)
from openprocurement.tender.openua.views.tender_document import (
    TenderUaDocumentResource
)
from openprocurement.tender.openua.constants import TENDERING_EXTRA_PERIOD
from openprocurement.tender.competitivedialogue.constants import (
    STAGE_2_UA_TYPE, STAGE_2_EU_TYPE, STAGE2_STATUS
)

@optendersresource(name='{}:Tender Documents'.format(STAGE_2_EU_TYPE),
                   collection_path='/tenders/{tender_id}/documents',
                   path='/tenders/{tender_id}/documents/{document_id}',
                   procurementMethodType=STAGE_2_EU_TYPE,
                   description="Competitive Dialogue Stage 2 EU related binary files (PDFs, etc.)")
class CompetitiveDialogueStage2EUDocumentResource(TenderEUDocumentResource):

   def validate_update_tender(self):
        """ TODO move validators
        This class is inherited in openua package, but validate_update_tender function has different validators.
        For now, we have no way to use different validators on methods according to procedure type.
        """
        if self.request.authenticated_role != 'auction' and self.request.validated['tender_status'] not in ['active.tendering', STAGE2_STATUS] or \
           self.request.authenticated_role == 'auction' and self.request.validated['tender_status'] not in ['active.auction', 'active.qualification']:
            raise_operation_error(self.request, 'Can\'t {} document in current ({}) tender status'.format(OPERATIONS.get(self.request.method), self.request.validated['tender_status']))
        if self.request.validated['tender_status'] == 'active.tendering' and calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, self.request.validated['tender']) > self.request.validated['tender'].tenderPeriod.endDate:
            raise_operation_error(self.request, 'tenderPeriod should be extended by {0.days} days'.format(TENDERING_EXTRA_PERIOD))
        return True

@optendersresource(name='{}:Tender Documents'.format(STAGE_2_UA_TYPE),
                   collection_path='/tenders/{tender_id}/documents',
                   path='/tenders/{tender_id}/documents/{document_id}',
                   procurementMethodType=STAGE_2_UA_TYPE,
                   description="Competitive Dialogue Stage 2 UA related binary files (PDFs, etc.)")
class CompetitiveDialogueStage2UADocumentResource(TenderUaDocumentResource):
    pass
<commit_msg>Add validator in tender doc view<commit_after># -*- coding: utf-8 -*-
from openprocurement.api.utils import (
    get_now,
    raise_operation_error
)
from openprocurement.api.validation import OPERATIONS
from openprocurement.tender.core.validation import validate_tender_document_update_not_by_author_or_tender_owner
from openprocurement.tender.core.utils import (
    optendersresource, calculate_business_date
)
from openprocurement.tender.openeu.views.tender_document import (
    TenderEUDocumentResource
)
from openprocurement.tender.openua.views.tender_document import (
    TenderUaDocumentResource
)
from openprocurement.tender.openua.constants import TENDERING_EXTRA_PERIOD
from openprocurement.tender.competitivedialogue.constants import (
    STAGE_2_UA_TYPE, STAGE_2_EU_TYPE, STAGE2_STATUS
)

@optendersresource(name='{}:Tender Documents'.format(STAGE_2_EU_TYPE),
                   collection_path='/tenders/{tender_id}/documents',
                   path='/tenders/{tender_id}/documents/{document_id}',
                   procurementMethodType=STAGE_2_EU_TYPE,
                   description="Competitive Dialogue Stage 2 EU related binary files (PDFs, etc.)")
class CompetitiveDialogueStage2EUDocumentResource(TenderEUDocumentResource):

   def validate_update_tender(self):
        """ TODO move validators
        This class is inherited in openua package, but validate_update_tender function has different validators.
        For now, we have no way to use different validators on methods according to procedure type.
        """
        if self.request.authenticated_role != 'auction' and self.request.validated['tender_status'] not in ['active.tendering', STAGE2_STATUS] or \
           self.request.authenticated_role == 'auction' and self.request.validated['tender_status'] not in ['active.auction', 'active.qualification']:
            raise_operation_error(self.request, 'Can\'t {} document in current ({}) tender status'.format(OPERATIONS.get(self.request.method), self.request.validated['tender_status']))
        if self.request.validated['tender_status'] == 'active.tendering' and calculate_business_date(get_now(), TENDERING_EXTRA_PERIOD, self.request.validated['tender']) > self.request.validated['tender'].tenderPeriod.endDate:
            raise_operation_error(self.request, 'tenderPeriod should be extended by {0.days} days'.format(TENDERING_EXTRA_PERIOD))
        if self.request.method in ['PUT', 'PATCH']:
            validate_tender_document_update_not_by_author_or_tender_owner(self.request)
        return True

@optendersresource(name='{}:Tender Documents'.format(STAGE_2_UA_TYPE),
                   collection_path='/tenders/{tender_id}/documents',
                   path='/tenders/{tender_id}/documents/{document_id}',
                   procurementMethodType=STAGE_2_UA_TYPE,
                   description="Competitive Dialogue Stage 2 UA related binary files (PDFs, etc.)")
class CompetitiveDialogueStage2UADocumentResource(TenderUaDocumentResource):
    pass
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
The Renderer class provides the infrastructure for generating template-based
code. It's used by the .grammars module for parser generation.
"""
from __future__ import print_function, division, absolute_import, unicode_literals
import itertools
from .util import trim, ustr

def render(item, join='', **fields):
    """ Render the given item
    """
    if item is None:
        return ''
    elif isinstance(item, Renderer):
        return item.render(join=join, **fields)
    elif isinstance(item, list):
        return join.join(render(e, join=join, **fields) for e in item)
    else:
        return ustr(item)

class Renderer(object):
    template = ''
    _counter = itertools.count()

    def __init__(self, template=None):
        if template is not None:
            self.template = template

    def counter(self):
        return next(self._counter)

    def render_fields(self, fields):
        pass

    def render(self, template=None, **fields):
        fields.update({k:v for k, v in vars(self).items() if not k.startswith('_')})

        override = self.render_fields(fields)
        if template is None:
            if override is not None:
                template = override
            else:
                template = self.template

        fields.update(fields)
        fields = {k:render(v) for k, v in fields.items()}
        try:
            return trim(template).format(**fields)
        except KeyError as e:
            raise KeyError(str(e), type(self))

<commit_msg>Do not render list items that are None.<commit_after># -*- coding: utf-8 -*-
"""
The Renderer class provides the infrastructure for generating template-based
code. It's used by the .grammars module for parser generation.
"""
from __future__ import print_function, division, absolute_import, unicode_literals
import itertools
from .util import trim, ustr

def render(item, join='', **fields):
    """ Render the given item
    """
    if item is None:
        return ''
    elif isinstance(item, Renderer):
        return item.render(join=join, **fields)
    elif isinstance(item, list):
        return join.join(render(e, join=join, **fields) for e in item if e is not None)
    else:
        return ustr(item)

class Renderer(object):
    template = ''
    _counter = itertools.count()

    def __init__(self, template=None):
        if template is not None:
            self.template = template

    def counter(self):
        return next(self._counter)

    def render_fields(self, fields):
        pass

    def render(self, template=None, **fields):
        fields.update({k:v for k, v in vars(self).items() if not k.startswith('_')})

        override = self.render_fields(fields)
        if template is None:
            if override is not None:
                template = override
            else:
                template = self.template

        fields.update(fields)
        fields = {k:render(v) for k, v in fields.items()}
        try:
            return trim(template).format(**fields)
        except KeyError as e:
            raise KeyError(str(e), type(self))

<|endoftext|>


<commit_before>import os
import time

from enum import Enum
from itertools import cycle

import pandas as pd
from pyqtgraph.Qt import QtGui, loadUiType

class FootType(Enum):
    none     = 'None'
    pressure = 'Pressure'
    velocity = 'Velocity'

class CsvRequest():
    def __init__(self, filepath  = "",
                       seperator = ",",
                       decimal   = ".",
                       dtfield  = None,
                       yfields = [],
                       datetime_format = "%Y-%m-%d %H:%M:%S,%f",
                       droplines = 0,
                       generatex = False,
                       samplerate = None):
        self.filepath = filepath
        self.seperator = seperator
        self.decimal = decimal
        self.dtfield = dtfield
        self.yfields = yfields
        self.datetime_format = datetime_format
        self.droplines = droplines
        self.generatex = generatex
        self.samplerate = samplerate

    @property
    def fields(self):
        dtfields = [] if self.dtfield is None else [self.dtfield]
        return dtfields + self.yfields

    @property
    def name(self):
        name, _ = os.path.splitext(os.path.basename(self.filepath))
        return name

    @property
    def folder(self):
        folder = os.path.dirname(self.filepath)
        return folder

class PlotData():
    def __init__(self, data = None,
                       fields = [],
                       filepath = ""):
        self.data = data
        self.fields = fields
        self.filepath = filepath

    @property
    def name(self):
        name, _ = os.path.splitext(os.path.basename(self.filepath))
        return name

    @property
    def folder(self):
        folder = os.path.dirname(self.filepath)
        return folder

# https://stackoverflow.com/questions/5478351/python-time-measure-function
def Timing(f):
    def wrap(*args):
        time1 = time.time()
        ret = f(*args)
        time2 = time.time()
        print ('{} function took {:.3f} ms'.format(f.__name__, (time2 - time1) * 1000))
        return ret
    return wrap

def Colors():
    qtcolors = [
        QtGui.QColor(0, 114, 189),
        QtGui.QColor(217, 83, 25),
        QtGui.QColor(237, 177, 32),
        QtGui.QColor(126, 47, 142),
        QtGui.QColor(119, 172, 48),
        QtGui.QColor(77, 190, 238),
        QtGui.QColor(162, 20, 47)
    ]
    return cycle(qtcolors)

curPath = os.path.dirname(os.path.abspath(__file__))
uiBasePath = os.path.join(curPath, 'ui')
def loadUiFile(uiFile):
    uiPath = os.path.join(uiBasePath, uiFile)
    uiClasses = loadUiType(uiPath)
    return uiClasses

def estimateSampleRate(series):
    idx = series.index.values
    timedelta = (idx[-1] - idx[0]) * 1e-9
    fs = len(idx) / timedelta
    return int(round(fs))
<commit_msg>Fix timing decorator<commit_after>import os
import time

from enum import Enum
from itertools import cycle

import pandas as pd
from pyqtgraph.Qt import QtGui, loadUiType

class FootType(Enum):
    none     = 'None'
    pressure = 'Pressure'
    velocity = 'Velocity'

class CsvRequest():
    def __init__(self, filepath  = "",
                       seperator = ",",
                       decimal   = ".",
                       dtfield  = None,
                       yfields = [],
                       datetime_format = "%Y-%m-%d %H:%M:%S,%f",
                       droplines = 0,
                       generatex = False,
                       samplerate = None):
        self.filepath = filepath
        self.seperator = seperator
        self.decimal = decimal
        self.dtfield = dtfield
        self.yfields = yfields
        self.datetime_format = datetime_format
        self.droplines = droplines
        self.generatex = generatex
        self.samplerate = samplerate

    @property
    def fields(self):
        dtfields = [] if self.dtfield is None else [self.dtfield]
        return dtfields + self.yfields

    @property
    def name(self):
        name, _ = os.path.splitext(os.path.basename(self.filepath))
        return name

    @property
    def folder(self):
        folder = os.path.dirname(self.filepath)
        return folder

class PlotData():
    def __init__(self, data = None,
                       fields = [],
                       filepath = ""):
        self.data = data
        self.fields = fields
        self.filepath = filepath

    @property
    def name(self):
        name, _ = os.path.splitext(os.path.basename(self.filepath))
        return name

    @property
    def folder(self):
        folder = os.path.dirname(self.filepath)
        return folder

# https://stackoverflow.com/questions/5478351/python-time-measure-function
def Timing(f):
    def wrap(*args, **kwargs):
        time1 = time.time()
        ret = f(*args, **kwargs)
        time2 = time.time()
        print ('{} function took {:.3f} ms'.format(f.__name__, (time2 - time1) * 1000))
        return ret
    return wrap

def Colors():
    qtcolors = [
        QtGui.QColor(0, 114, 189),
        QtGui.QColor(217, 83, 25),
        QtGui.QColor(237, 177, 32),
        QtGui.QColor(126, 47, 142),
        QtGui.QColor(119, 172, 48),
        QtGui.QColor(77, 190, 238),
        QtGui.QColor(162, 20, 47)
    ]
    return cycle(qtcolors)

curPath = os.path.dirname(os.path.abspath(__file__))
uiBasePath = os.path.join(curPath, 'ui')
def loadUiFile(uiFile):
    uiPath = os.path.join(uiBasePath, uiFile)
    uiClasses = loadUiType(uiPath)
    return uiClasses

def estimateSampleRate(series):
    idx = series.index.values
    timedelta = (idx[-1] - idx[0]) * 1e-9
    fs = len(idx) / timedelta
    return int(round(fs))
<|endoftext|>


<commit_before># Copyright 2009-2010 10gen, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""GridFS is a specification for storing large objects in Mongo.

The :mod:`gridfs` package is an implementation of GridFS on top of
:mod:`pymongo`, exposing a file-like interface.

.. mongodoc:: gridfs
"""

from gridfs.errors import (NoFile,
                           UnsupportedAPI)
from gridfs.grid_file import (GridIn,
                              GridOut)
from pymongo import (ASCENDING,
                     DESCENDING)
from pymongo.database import Database

class GridFS(object):
    """An instance of GridFS on top of a single Database.
    """
    def __init__(self, database, collection="fs"):
        """Create a new instance of :class:`GridFS`.

        Raises :class:`TypeError` if `database` is not an instance of
        :class:`~pymongo.database.Database`.

        :Parameters:
          - `database`: database to use
          - `collection` (optional): root collection to use

        .. versionadded:: 1.6
           The `collection` parameter.

        .. mongodoc:: gridfs
        """
        if not isinstance(database, Database):
            raise TypeError("database must be an instance of Database")

        self.__database = database
        self.__collection = database[collection]
        self.__files = self.__collection.files
        self.__chunks = self.__collection.chunks
        self.__chunks.ensure_index([("files_id", ASCENDING), ("n", ASCENDING)],
                                   unique=True)

    def new_file(self, **kwargs):
        """Create a new file in GridFS.

        Returns a new :class:`~gridfs.grid_file.GridIn` instance to
        which data can be written. Any keyword arguments will be
        passed through to :meth:`~gridfs.grid_file.GridIn`.

        If the ``"_id"`` of the file is manually specified, it must
        not already exist in GridFS. Otherwise
        :class:`~gridfs.errors.FileExists` is raised.

        :Parameters:
          - `**kwargs` (optional): keyword arguments for file creation

        .. versionadded:: 1.6
        """
        return GridIn(self.__collection, **kwargs)

    def put(self, data, **kwargs):
        """Put data in GridFS as a new file.

        Equivalent to doing:

        >>> f = new_file(**kwargs)
        >>> try:
        >>>     f.write(data)
        >>> finally:
        >>>     f.close()

        `data` can be either an instance of :class:`str` or a
        file-like object providing a :meth:`read` method. If an
        `encoding` keyword argument is passed, `data` can also be a
        :class:`unicode` instance, which will be encoded as `encoding`
        before being written. Any keyword arguments will be passed
        through to the created file - see
        :meth:`~gridfs.grid_file.GridIn` for possible
        arguments. Returns the ``"_id"`` of the created file.

        If the ``"_id"`` of the file is manually specified, it must
        not already exist in GridFS. Otherwise
        :class:`~gridfs.errors.FileExists` is raised.

        :Parameters:
          - `data`: data to be written as a file.
          - `**kwargs` (optional): keyword arguments for file creation

        .. versionadded:: 1.9
           The ability to write :class:`unicode`, if an `encoding` has
           been specified as a keyword argument.

        .. versionadded:: 1.6
        """
        grid_file = GridIn(self.__collection, **kwargs)
        try:
            grid_file.write(data)
        finally:
            grid_file.close()
        return grid_file._id

    def get(self, file_id):
        """Get a file from GridFS by ``"_id"``.

        Returns an instance of :class:`~gridfs.grid_file.GridOut`,
        which provides a file-like interface for reading.

        :Parameters:
          - `file_id`: ``"_id"`` of the file to get

        .. versionadded:: 1.6
        """
        return GridOut(self.__collection, file_id)

    def get_version(self, filename=None, version=-1, **kwargs):
        """Get a file from GridFS by ``"filename"`` or metadata fields.

        Returns a version of the file in GridFS whose filename matches
        `filename` and whose metadata fields match the supplied keyword
        arguments, as an instance of :class:`~gridfs.grid_file.GridOut`.

        Version numbering is a convenience atop the GridFS API provided
        by MongoDB. If more than one file matches the query (either by
        `filename` alone, by metadata fields, or by a combination of
        both), then version ``-1`` will be the most recently uploaded
        matching file, ``-2`` the second most recently
        uploaded, etc. Version ``0`` will be the first version
        uploaded, ``1`` the second version, etc. So if three versions
        have been uploaded, then version ``0`` is the same as version
        ``-3``, version ``1`` is the same as version ``-2``, and
        version ``2`` is the same as version ``-1``.

        Raises :class:`~gridfs.errors.NoFile` if no such version of
        that file exists.

        An index on ``{filename: 1, uploadDate: -1}`` will
        automatically be created when this method is called the first
        time.

        :Parameters:
          - `filename`: ``"filename"`` of the file to get, or `None`
          - `version` (optional): version of the file to get (defualts
            to -1, the most recent version uploaded)
          - `**kwargs` (optional): find files by custom metadata.

        .. versionadded:: 1.10.2
           `filename` defaults to None;
           accept keyword arguments to find files by custom metadata.
        .. versionadded:: 1.9
        """
        # TODO: should we automatically ensure indexes on other
        # queries we get from kwargs? should we not ensure this
        # index if querying by something other than filename?
        self.__files.ensure_index([("filename", ASCENDING),
                                   ("uploadDate", DESCENDING)])

        query = {}
        if filename is not None:
            query["filename"] = filename
        for key, value in kwargs.iteritems():
            query[key] = value

        cursor = self.__files.find(query, ["_id"])
        if version < 0:
            skip = abs(version) - 1
            cursor.limit(-1).skip(skip).sort("uploadDate", DESCENDING)
        else:
            cursor.limit(-1).skip(version).sort("uploadDate", ASCENDING)
        try:
            grid_file = cursor.next()
            return GridOut(self.__collection, grid_file["_id"])
        except StopIteration:
            raise NoFile("no version %d for filename %r" % (version, filename))

    def get_last_version(self, filename=None, **kwargs):
        """Get the most recent version of a file in GridFS by ``"filename"``
        or metadata fields.

        Equivalent to calling :meth:`get_version` with the default
        `version` (``-1``).

        :Parameters:
          - `filename`: ``"filename"`` of the file to get, or `None`
          - `**kwargs` (optional): find files by custom metadata.

        .. versionadded:: 1.10.2
           `filename` defaults to None;
           accept keyword arguments to find files by custom metadata. See
           :meth:`get_version`.
        .. versionadded:: 1.6
        """
        return self.get_version(filename=filename, **kwargs)

    # TODO add optional safe mode for chunk removal?
    def delete(self, file_id):
        """Delete a file from GridFS by ``"_id"``.

        Removes all data belonging to the file with ``"_id"``:
        `file_id`.

        .. warning:: Any processes/threads reading from the file while
           this method is executing will likely see an invalid/corrupt
           file. Care should be taken to avoid concurrent reads to a file
           while it is being deleted.

        .. note:: Deletes of non-existent files are considered successful
           since the end result is the same: no file with that _id remains.

        :Parameters:
          - `file_id`: ``"_id"`` of the file to delete

        .. versionadded:: 1.6
        """
        self.__files.remove({"_id": file_id}, safe=True)
        self.__chunks.remove({"files_id": file_id})

    def list(self):
        """List the names of all files stored in this instance of
        :class:`GridFS`.

        .. versionchanged:: 1.6
           Removed the `collection` argument.
        """
        return self.__files.distinct("filename")

    def exists(self, document_or_id=None, **kwargs):
        """Check if a file exists in this instance of :class:`GridFS`.

        The file to check for can be specified by the value of it's
        ``_id`` key, or by passing in a query document. A query
        document can be passed in as dictionary, or by using keyword
        arguments. Thus, the following three calls are equivalent:

        >>> fs.exists(file_id)
        >>> fs.exists({"_id": file_id})
        >>> fs.exists(_id=file_id)

        As are the following two calls:

        >>> fs.exists({"filename": "mike.txt"})
        >>> fs.exists(filename="mike.txt")

        And the following two:

        >>> fs.exists({"foo": {"$gt": 12}})
        >>> fs.exists(foo={"$gt": 12})

        Returns ``True`` if a matching file exists, ``False``
        otherwise. Calls to :meth:`exists` will not automatically
        create appropriate indexes; application developers should be
        sure to create indexes if needed and as appropriate.

        :Parameters:
          - `document_or_id` (optional): query document, or _id of the
            document to check for
          - `**kwargs` (optional): keyword arguments are used as a
            query document, if they're present.

        .. versionadded:: 1.8
        """
        if kwargs:
            return self.__files.find_one(kwargs, ["_id"]) is not None
        return self.__files.find_one(document_or_id, ["_id"]) is not None

    def open(self, *args, **kwargs):
        """No longer supported.

        .. versionchanged:: 1.6
           The open method is no longer supported.
        """
        raise UnsupportedAPI("The open method is no longer supported.")

    def remove(self, *args, **kwargs):
        """No longer supported.

        .. versionchanged:: 1.6
           The remove method is no longer supported.
        """
        raise UnsupportedAPI("The remove method is no longer supported. "
                             "Please use the delete method instead.")
<commit_msg>no need to copy the kwargs dict<commit_after># Copyright 2009-2010 10gen, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""GridFS is a specification for storing large objects in Mongo.

The :mod:`gridfs` package is an implementation of GridFS on top of
:mod:`pymongo`, exposing a file-like interface.

.. mongodoc:: gridfs
"""

from gridfs.errors import (NoFile,
                           UnsupportedAPI)
from gridfs.grid_file import (GridIn,
                              GridOut)
from pymongo import (ASCENDING,
                     DESCENDING)
from pymongo.database import Database

class GridFS(object):
    """An instance of GridFS on top of a single Database.
    """
    def __init__(self, database, collection="fs"):
        """Create a new instance of :class:`GridFS`.

        Raises :class:`TypeError` if `database` is not an instance of
        :class:`~pymongo.database.Database`.

        :Parameters:
          - `database`: database to use
          - `collection` (optional): root collection to use

        .. versionadded:: 1.6
           The `collection` parameter.

        .. mongodoc:: gridfs
        """
        if not isinstance(database, Database):
            raise TypeError("database must be an instance of Database")

        self.__database = database
        self.__collection = database[collection]
        self.__files = self.__collection.files
        self.__chunks = self.__collection.chunks
        self.__chunks.ensure_index([("files_id", ASCENDING), ("n", ASCENDING)],
                                   unique=True)

    def new_file(self, **kwargs):
        """Create a new file in GridFS.

        Returns a new :class:`~gridfs.grid_file.GridIn` instance to
        which data can be written. Any keyword arguments will be
        passed through to :meth:`~gridfs.grid_file.GridIn`.

        If the ``"_id"`` of the file is manually specified, it must
        not already exist in GridFS. Otherwise
        :class:`~gridfs.errors.FileExists` is raised.

        :Parameters:
          - `**kwargs` (optional): keyword arguments for file creation

        .. versionadded:: 1.6
        """
        return GridIn(self.__collection, **kwargs)

    def put(self, data, **kwargs):
        """Put data in GridFS as a new file.

        Equivalent to doing:

        >>> f = new_file(**kwargs)
        >>> try:
        >>>     f.write(data)
        >>> finally:
        >>>     f.close()

        `data` can be either an instance of :class:`str` or a
        file-like object providing a :meth:`read` method. If an
        `encoding` keyword argument is passed, `data` can also be a
        :class:`unicode` instance, which will be encoded as `encoding`
        before being written. Any keyword arguments will be passed
        through to the created file - see
        :meth:`~gridfs.grid_file.GridIn` for possible
        arguments. Returns the ``"_id"`` of the created file.

        If the ``"_id"`` of the file is manually specified, it must
        not already exist in GridFS. Otherwise
        :class:`~gridfs.errors.FileExists` is raised.

        :Parameters:
          - `data`: data to be written as a file.
          - `**kwargs` (optional): keyword arguments for file creation

        .. versionadded:: 1.9
           The ability to write :class:`unicode`, if an `encoding` has
           been specified as a keyword argument.

        .. versionadded:: 1.6
        """
        grid_file = GridIn(self.__collection, **kwargs)
        try:
            grid_file.write(data)
        finally:
            grid_file.close()
        return grid_file._id

    def get(self, file_id):
        """Get a file from GridFS by ``"_id"``.

        Returns an instance of :class:`~gridfs.grid_file.GridOut`,
        which provides a file-like interface for reading.

        :Parameters:
          - `file_id`: ``"_id"`` of the file to get

        .. versionadded:: 1.6
        """
        return GridOut(self.__collection, file_id)

    def get_version(self, filename=None, version=-1, **kwargs):
        """Get a file from GridFS by ``"filename"`` or metadata fields.

        Returns a version of the file in GridFS whose filename matches
        `filename` and whose metadata fields match the supplied keyword
        arguments, as an instance of :class:`~gridfs.grid_file.GridOut`.

        Version numbering is a convenience atop the GridFS API provided
        by MongoDB. If more than one file matches the query (either by
        `filename` alone, by metadata fields, or by a combination of
        both), then version ``-1`` will be the most recently uploaded
        matching file, ``-2`` the second most recently
        uploaded, etc. Version ``0`` will be the first version
        uploaded, ``1`` the second version, etc. So if three versions
        have been uploaded, then version ``0`` is the same as version
        ``-3``, version ``1`` is the same as version ``-2``, and
        version ``2`` is the same as version ``-1``.

        Raises :class:`~gridfs.errors.NoFile` if no such version of
        that file exists.

        An index on ``{filename: 1, uploadDate: -1}`` will
        automatically be created when this method is called the first
        time.

        :Parameters:
          - `filename`: ``"filename"`` of the file to get, or `None`
          - `version` (optional): version of the file to get (defualts
            to -1, the most recent version uploaded)
          - `**kwargs` (optional): find files by custom metadata.

        .. versionadded:: 1.10.2
           `filename` defaults to None;
           accept keyword arguments to find files by custom metadata.
        .. versionadded:: 1.9
        """
        # TODO: should we automatically ensure indexes on other
        # queries we get from kwargs? should we not ensure this
        # index if querying by something other than filename?
        self.__files.ensure_index([("filename", ASCENDING),
                                   ("uploadDate", DESCENDING)])

        query = kwargs
        if filename is not None:
            query["filename"] = filename

        cursor = self.__files.find(query, ["_id"])
        if version < 0:
            skip = abs(version) - 1
            cursor.limit(-1).skip(skip).sort("uploadDate", DESCENDING)
        else:
            cursor.limit(-1).skip(version).sort("uploadDate", ASCENDING)
        try:
            grid_file = cursor.next()
            return GridOut(self.__collection, grid_file["_id"])
        except StopIteration:
            raise NoFile("no version %d for filename %r" % (version, filename))

    def get_last_version(self, filename=None, **kwargs):
        """Get the most recent version of a file in GridFS by ``"filename"``
        or metadata fields.

        Equivalent to calling :meth:`get_version` with the default
        `version` (``-1``).

        :Parameters:
          - `filename`: ``"filename"`` of the file to get, or `None`
          - `**kwargs` (optional): find files by custom metadata.

        .. versionadded:: 1.10.2
           `filename` defaults to None;
           accept keyword arguments to find files by custom metadata. See
           :meth:`get_version`.
        .. versionadded:: 1.6
        """
        return self.get_version(filename=filename, **kwargs)

    # TODO add optional safe mode for chunk removal?
    def delete(self, file_id):
        """Delete a file from GridFS by ``"_id"``.

        Removes all data belonging to the file with ``"_id"``:
        `file_id`.

        .. warning:: Any processes/threads reading from the file while
           this method is executing will likely see an invalid/corrupt
           file. Care should be taken to avoid concurrent reads to a file
           while it is being deleted.

        .. note:: Deletes of non-existent files are considered successful
           since the end result is the same: no file with that _id remains.

        :Parameters:
          - `file_id`: ``"_id"`` of the file to delete

        .. versionadded:: 1.6
        """
        self.__files.remove({"_id": file_id}, safe=True)
        self.__chunks.remove({"files_id": file_id})

    def list(self):
        """List the names of all files stored in this instance of
        :class:`GridFS`.

        .. versionchanged:: 1.6
           Removed the `collection` argument.
        """
        return self.__files.distinct("filename")

    def exists(self, document_or_id=None, **kwargs):
        """Check if a file exists in this instance of :class:`GridFS`.

        The file to check for can be specified by the value of it's
        ``_id`` key, or by passing in a query document. A query
        document can be passed in as dictionary, or by using keyword
        arguments. Thus, the following three calls are equivalent:

        >>> fs.exists(file_id)
        >>> fs.exists({"_id": file_id})
        >>> fs.exists(_id=file_id)

        As are the following two calls:

        >>> fs.exists({"filename": "mike.txt"})
        >>> fs.exists(filename="mike.txt")

        And the following two:

        >>> fs.exists({"foo": {"$gt": 12}})
        >>> fs.exists(foo={"$gt": 12})

        Returns ``True`` if a matching file exists, ``False``
        otherwise. Calls to :meth:`exists` will not automatically
        create appropriate indexes; application developers should be
        sure to create indexes if needed and as appropriate.

        :Parameters:
          - `document_or_id` (optional): query document, or _id of the
            document to check for
          - `**kwargs` (optional): keyword arguments are used as a
            query document, if they're present.

        .. versionadded:: 1.8
        """
        if kwargs:
            return self.__files.find_one(kwargs, ["_id"]) is not None
        return self.__files.find_one(document_or_id, ["_id"]) is not None

    def open(self, *args, **kwargs):
        """No longer supported.

        .. versionchanged:: 1.6
           The open method is no longer supported.
        """
        raise UnsupportedAPI("The open method is no longer supported.")

    def remove(self, *args, **kwargs):
        """No longer supported.

        .. versionchanged:: 1.6
           The remove method is no longer supported.
        """
        raise UnsupportedAPI("The remove method is no longer supported. "
                             "Please use the delete method instead.")
<|endoftext|>


<commit_before>#
from .gsea import call, replot, prerank

from .enrichr import enrichr, get_libary_name

<commit_msg>version<commit_after>#
from .gsea import call, replot, prerank

from .enrichr import enrichr, get_libary_name

__version__='0.6.0'

<|endoftext|>


<commit_before>from __future__ import absolute_import

import testify
from testify import TestCase, assert_equal, setup, teardown, run

from fakemtpd.signals import _Signals, Signalable

class SignalClass(Signalable):
    _signals = ["foo", "bar"]

    def send(self):
        self._signal_foo()
        self._signal_bar()

class FancySignalClass(SignalClass):
    def __init__(self):
        super(FancySignalClass, self).__init__()
        self.foo_signaled = Counter()

class Counter(object):
    def __init__(self, start=0):
        self.val = start

    def incr(self):
        self.val += 1

    def decr(self):
        self.val -= 1

    def __eq__(self, other):
        return self.val == other

    def __repr__(self):
        return "<Counter=%d>" % self.val

class SignalableTestCase(TestCase):
    def test_full(self):
        obj = SignalClass()
        foo_ran = Counter()
        bar_ran = Counter()
        foo_callback = lambda: foo_ran.incr()
        bar_callback = lambda: bar_ran.incr()
        assert_equal(obj.on_foo(foo_callback), "foo")
        assert_equal(obj.on_bar(bar_callback), "bar")
        obj.send()
        assert_equal(foo_ran, 1)
        assert_equal(bar_ran, 1)

    def test_distinct(self):
        obj = SignalClass()
        foo_ran = Counter()
        bar_ran = Counter()
        foo_callback = lambda: foo_ran.incr()
        assert_equal(obj.on_foo(foo_callback), "foo")
        obj.send()
        assert_equal(foo_ran, 1)
        assert_equal(bar_ran, 0)

    def test_per_object_not_per_class(self):
        obj1 = SignalClass()
        obj2 = SignalClass()
        foo_ran = Counter()
        bar_ran = Counter()
        bar_callback = lambda: bar_ran.incr()
        assert_equal(obj1.on_bar(bar_callback), "bar")
        obj1.send()
        obj2.send()
        assert_equal(foo_ran, 0)
        assert_equal(bar_ran, 1)

if __name__ == "__main__":
    run()
<commit_msg>Add another test<commit_after>from __future__ import absolute_import

import testify
from testify import TestCase, assert_equal, setup, teardown, run

from fakemtpd.signals import _Signals, Signalable

class SignalClass(Signalable):
    _signals = ["foo", "bar"]

    def send(self):
        self._signal_foo()
        self._signal_bar()

class FancySignalClass(SignalClass):
    def __init__(self):
        super(FancySignalClass, self).__init__()
        self.foo_signaled = Counter()

class Counter(object):
    def __init__(self, start=0):
        self.val = start

    def incr(self):
        self.val += 1

    def decr(self):
        self.val -= 1

    def __eq__(self, other):
        return self.val == other

    def __repr__(self):
        return "<Counter=%d>" % self.val

class SignalableTestCase(TestCase):
    def test_full(self):
        obj = SignalClass()
        foo_ran = Counter()
        bar_ran = Counter()
        foo_callback = lambda: foo_ran.incr()
        bar_callback = lambda: bar_ran.incr()
        assert_equal(obj.on_foo(foo_callback), "foo")
        assert_equal(obj.on_bar(bar_callback), "bar")
        obj.send()
        assert_equal(foo_ran, 1)
        assert_equal(bar_ran, 1)

    def test_distinct(self):
        obj = SignalClass()
        foo_ran = Counter()
        bar_ran = Counter()
        foo_callback = lambda: foo_ran.incr()
        assert_equal(obj.on_foo(foo_callback), "foo")
        obj.send()
        assert_equal(foo_ran, 1)
        assert_equal(bar_ran, 0)

    def test_per_object_not_per_class(self):
        obj1 = SignalClass()
        obj2 = SignalClass()
        foo_ran = Counter()
        bar_ran = Counter()
        bar_callback = lambda: bar_ran.incr()
        assert_equal(obj1.on_bar(bar_callback), "bar")
        obj1.send()
        obj2.send()
        assert_equal(foo_ran, 0)
        assert_equal(bar_ran, 1)

    def test_multiple(self):
        obj = SignalClass()
        foo_ran = Counter()
        foo_3_ran = Counter()
        foo_callback_1 = lambda:foo_ran.incr()
        foo_callback_2 = lambda:foo_ran.incr()
        foo_callback_3 = lambda:foo_3_ran.incr()
        assert_equal(obj.on_foo(foo_callback_1), "foo")
        assert_equal(obj.on_foo(foo_callback_2), "foo")
        assert_equal(obj.on_foo(foo_callback_3), "foo")
        obj.send()
        assert_equal(foo_ran, 2)
        assert_equal(foo_3_ran, 1)

if __name__ == "__main__":
    run()
<|endoftext|>


<commit_before># -*- coding: utf-8  -*-
"""Test cases for the SPARQL API."""
#
# Distributed under the terms of the MIT license.
#
from __future__ import absolute_import, unicode_literals

import sys

import pywikibot.data.sparql as sparql

from tests.aspects import unittest, TestCase

if sys.version_info[0] > 2:
    from unittest.mock import patch
else:
    from mock import patch

# See: https://www.w3.org/TR/2013/REC-sparql11-results-json-20130321/

SQL_RESPONSE = """
{
  "head" : {
    "vars" : [ "cat", "d", "catLabel" ]
  },
  "results" : {
    "bindings" : [ {
      "cat" : {
        "type" : "uri",
        "value" : "http://www.wikidata.org/entity/Q498787"
      },
      "d" : {
        "datatype" : "http://www.w3.org/2001/XMLSchema#dateTime",
        "type" : "literal",
        "value" : "1955-01-01T00:00:00Z"
      },
      "catLabel" : {
        "xml:lang" : "en",
        "type" : "literal",
        "value" : "Muezza"
      }
    }, {
      "cat" : {
        "type" : "uri",
        "value" : "http://www.wikidata.org/entity/Q677525"
      },
      "d" : {
        "datatype" : "http://www.w3.org/2001/XMLSchema#dateTime",
        "type" : "literal",
        "value" : "2015-06-22T00:00:00Z"
      },
      "catLabel" : {
        "xml:lang" : "en",
        "type" : "literal",
        "value" : "Orangey"
      }
    } ]
  }
}
"""

RESPONSE_TRUE = """
{
  "head" : { },
  "boolean" : true
}
"""

RESPONSE_FALSE = """
{
  "head" : { },
  "boolean" : false
}
"""

class TestContainer(object):
    """Simple test container for return values."""

    def __init__(self, value):
        """Create container."""
        self.content = value

class TestSparql(TestCase):
    """Test SPARQL queries."""

    net = False

    @patch.object(sparql.http, 'fetch')
    def testQuerySelect(self, mock_method):
        """Test SELECT query."""
        mock_method.return_value = TestContainer(SQL_RESPONSE)
        q = sparql.SparqlQuery()
        res = q.select('SELECT * WHERE { ?x ?y ?z }')
        self.assertIsInstance(res, list, 'Result is not a list')
        self.assertEqual(len(res), 2)

        self.assertDictEqual(res[0],
                             {'cat': 'http://www.wikidata.org/entity/Q498787',
                              'catLabel': 'Muezza', 'd': '1955-01-01T00:00:00Z'},
                             'Bad result')
        self.assertDictEqual(res[1],
                             {'cat': 'http://www.wikidata.org/entity/Q677525',
                              'catLabel': 'Orangey', 'd': '2015-06-22T00:00:00Z'},
                             'Bad result')

    @patch.object(sparql.http, 'fetch')
    def testQuerySelectFull(self, mock_method):
        """Test SELECT query with full data."""
        mock_method.return_value = TestContainer(SQL_RESPONSE)
        q = sparql.SparqlQuery()
        res = q.select('SELECT * WHERE { ?x ?y ?z }', full_data=True)
        self.assertIsInstance(res, list, 'Result is not a list')
        self.assertEqual(len(res), 2)

        self.assertIsInstance(res[0]['cat'], sparql.URI, 'Wrong type for URI')
        self.assertEqual(repr(res[0]['cat']), '<http://www.wikidata.org/entity/Q498787>',
                         'Wrong URI representation')
        self.assertEqual(res[0]['cat'].getID(), 'Q498787', 'Wrong URI ID')

        self.assertIsInstance(res[0]['catLabel'], sparql.Literal, 'Wrong type for Literal')
        self.assertEqual(repr(res[0]['catLabel']), 'Muezza@en', 'Wrong literal representation')

        self.assertIsInstance(res[0]['d'], sparql.Literal, 'Wrong type for Literal')
        self.assertEqual(repr(res[0]['d']),
                         '1955-01-01T00:00:00Z^^http://www.w3.org/2001/XMLSchema#dateTime',
                         'Wrong URI representation')

    @patch.object(sparql.http, 'fetch')
    def testGetItems(self, mock_method):
        """Test item list retrieval via SPARQL."""
        mock_method.return_value = TestContainer(SQL_RESPONSE)
        q = sparql.SparqlQuery()
        res = q.get_items('SELECT * WHERE { ?x ?y ?z }', 'cat')
        self.assertSetEqual(res, set(['Q498787', 'Q677525']))

    @patch.object(sparql.http, 'fetch')
    def testQueryAsk(self, mock_method):
        """Test ASK query."""
        mock_method.return_value = TestContainer(RESPONSE_TRUE)
        q = sparql.SparqlQuery()

        res = q.ask('ASK { ?x ?y ?z }')
        self.assertTrue(res)

        mock_method.return_value = TestContainer(RESPONSE_FALSE)
        res = q.ask('ASK { ?x ?y ?z }')
        self.assertFalse(res)

if __name__ == '__main__':  # pragma: no cover
    try:
        unittest.main()
    except SystemExit:
        pass
<commit_msg>Rename TestContainer to Container<commit_after># -*- coding: utf-8  -*-
"""Test cases for the SPARQL API."""
#
# Distributed under the terms of the MIT license.
#
from __future__ import absolute_import, unicode_literals

import sys

import pywikibot.data.sparql as sparql

from tests.aspects import unittest, TestCase

if sys.version_info[0] > 2:
    from unittest.mock import patch
else:
    from mock import patch

# See: https://www.w3.org/TR/2013/REC-sparql11-results-json-20130321/

SQL_RESPONSE = """
{
  "head" : {
    "vars" : [ "cat", "d", "catLabel" ]
  },
  "results" : {
    "bindings" : [ {
      "cat" : {
        "type" : "uri",
        "value" : "http://www.wikidata.org/entity/Q498787"
      },
      "d" : {
        "datatype" : "http://www.w3.org/2001/XMLSchema#dateTime",
        "type" : "literal",
        "value" : "1955-01-01T00:00:00Z"
      },
      "catLabel" : {
        "xml:lang" : "en",
        "type" : "literal",
        "value" : "Muezza"
      }
    }, {
      "cat" : {
        "type" : "uri",
        "value" : "http://www.wikidata.org/entity/Q677525"
      },
      "d" : {
        "datatype" : "http://www.w3.org/2001/XMLSchema#dateTime",
        "type" : "literal",
        "value" : "2015-06-22T00:00:00Z"
      },
      "catLabel" : {
        "xml:lang" : "en",
        "type" : "literal",
        "value" : "Orangey"
      }
    } ]
  }
}
"""

RESPONSE_TRUE = """
{
  "head" : { },
  "boolean" : true
}
"""

RESPONSE_FALSE = """
{
  "head" : { },
  "boolean" : false
}
"""

class Container(object):
    """Simple test container for return values."""

    def __init__(self, value):
        """Create container."""
        self.content = value

class TestSparql(TestCase):
    """Test SPARQL queries."""

    net = False

    @patch.object(sparql.http, 'fetch')
    def testQuerySelect(self, mock_method):
        """Test SELECT query."""
        mock_method.return_value = Container(SQL_RESPONSE)
        q = sparql.SparqlQuery()
        res = q.select('SELECT * WHERE { ?x ?y ?z }')
        self.assertIsInstance(res, list, 'Result is not a list')
        self.assertEqual(len(res), 2)

        self.assertDictEqual(res[0],
                             {'cat': 'http://www.wikidata.org/entity/Q498787',
                              'catLabel': 'Muezza', 'd': '1955-01-01T00:00:00Z'},
                             'Bad result')
        self.assertDictEqual(res[1],
                             {'cat': 'http://www.wikidata.org/entity/Q677525',
                              'catLabel': 'Orangey', 'd': '2015-06-22T00:00:00Z'},
                             'Bad result')

    @patch.object(sparql.http, 'fetch')
    def testQuerySelectFull(self, mock_method):
        """Test SELECT query with full data."""
        mock_method.return_value = Container(SQL_RESPONSE)
        q = sparql.SparqlQuery()
        res = q.select('SELECT * WHERE { ?x ?y ?z }', full_data=True)
        self.assertIsInstance(res, list, 'Result is not a list')
        self.assertEqual(len(res), 2)

        self.assertIsInstance(res[0]['cat'], sparql.URI, 'Wrong type for URI')
        self.assertEqual(repr(res[0]['cat']), '<http://www.wikidata.org/entity/Q498787>',
                         'Wrong URI representation')
        self.assertEqual(res[0]['cat'].getID(), 'Q498787', 'Wrong URI ID')

        self.assertIsInstance(res[0]['catLabel'], sparql.Literal, 'Wrong type for Literal')
        self.assertEqual(repr(res[0]['catLabel']), 'Muezza@en', 'Wrong literal representation')

        self.assertIsInstance(res[0]['d'], sparql.Literal, 'Wrong type for Literal')
        self.assertEqual(repr(res[0]['d']),
                         '1955-01-01T00:00:00Z^^http://www.w3.org/2001/XMLSchema#dateTime',
                         'Wrong URI representation')

    @patch.object(sparql.http, 'fetch')
    def testGetItems(self, mock_method):
        """Test item list retrieval via SPARQL."""
        mock_method.return_value = Container(SQL_RESPONSE)
        q = sparql.SparqlQuery()
        res = q.get_items('SELECT * WHERE { ?x ?y ?z }', 'cat')
        self.assertSetEqual(res, set(['Q498787', 'Q677525']))

    @patch.object(sparql.http, 'fetch')
    def testQueryAsk(self, mock_method):
        """Test ASK query."""
        mock_method.return_value = Container(RESPONSE_TRUE)
        q = sparql.SparqlQuery()

        res = q.ask('ASK { ?x ?y ?z }')
        self.assertTrue(res)

        mock_method.return_value = Container(RESPONSE_FALSE)
        res = q.ask('ASK { ?x ?y ?z }')
        self.assertFalse(res)

if __name__ == '__main__':  # pragma: no cover
    try:
        unittest.main()
    except SystemExit:
        pass
<|endoftext|>


<commit_before>from __future__ import absolute_import

from copy import deepcopy

import ldap
from ldap.cidict import cidict
import ldap.dn

try:
    from passlib.hash import ldap_md5_crypt
except ImportError:
    pass

from .recording import SeedRequired, RecordableMethods, recorded

class LDAPObject(RecordableMethods):
    """
    :param directory: The initial content of this LDAP connection.
    :type directory: :class:`ldap.cidict.cidict`: ``{dn: {attr: [values]}}``

    Our mock replacement for :class:`ldap.LDAPObject`. This exports selected
    LDAP operations and allows you to set return values in advance as well as
    discover which methods were called after the fact.

    All of these methods take the same arguments as their python-ldap
    counterparts. Some are self-explanatory; those that are only partially
    implemented are documented as such.

    Ignore the *static* annotations; that's just a Sphinx artifact.

    .. attribute:: options

        *dict*: Options that have been set by
        :meth:`~mockldap.LDAPObject.set_option`.

    .. attribute:: tls_enabled

        *bool*: True if :meth:`~mockldap.LDAPObject.start_tls_s` was called.

    .. attribute:: bound_as

        *string*: DN of the last successful bind. None if unbound.
    """
    def __init__(self, directory):
        if not isinstance(directory, ldap.cidict.cidict):
            from . import map_keys
            directory = cidict(map_keys(lambda s: s.lower(), directory))

        self.directory = deepcopy(directory)
        self.async_results = []
        self.options = {}
        self.tls_enabled = False
        self.bound_as = None

    def _check_valid_dn(self, dn):
        try:
            ldap.dn.str2dn(dn)
        except ldap.DECODING_ERROR:
            raise ldap.INVALID_DN_SYNTAX

    #
    # Begin LDAP methods
    #

    @recorded
    def initialize(self, *args, **kwargs):
        """ This only exists for recording purposes. """
        pass

    @recorded
    def get_option(self, option):
        """
        """
        return self.options[option]

    @recorded
    def set_option(self, option, invalue):
        """
        """
        self.options[option] = invalue

    @recorded
    def simple_bind_s(self, who='', cred=''):
        """
        """
        success = False

        try:
            if(who == '' and cred == ''):
                success = True
            elif self._compare_s(who, 'userPassword', cred):
                success = True
        except ldap.NO_SUCH_OBJECT:
            pass

        if success:
            self.bound_as = who
            return (97, [])
        else:
            raise ldap.INVALID_CREDENTIALS('%s:%s' % (who, cred))

    @recorded
    def search(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
        """
        See :meth:`~mockldap.LDAPObject.search_s`.
        """
        value = self._search_s(base, scope, filterstr, attrlist, attrsonly)

        return self._add_async_result(value)

    @recorded
    def result(self, msgid, all=1, timeout=None):
        """
        """
        return ldap.RES_SEARCH_RESULT, self._pop_async_result(msgid)

    @recorded
    def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
        """
        Supports many, but not all, filter strings.

        Tests of the form ``'(foo=bar)'`` and ``'(foo=\*)'`` are supported, as
        are the &, \|, and !  operators. attrlist and attrsonly are also
        supported. Beyond that, this method must be seeded.
        """
        return self._search_s(base, scope, filterstr, attrlist, attrsonly)

    @recorded
    def start_tls_s(self):
        """
        """
        self.tls_enabled = True

    @recorded
    def compare_s(self, dn, attr, value):
        """
        """
        return self._compare_s(dn, attr, value)

    @recorded
    def modify_s(self, dn, mod_attrs):
        """
        """
        return self._modify_s(dn, mod_attrs)

    @recorded
    def add_s(self, dn, record):
        """
        """
        return self._add_s(dn, record)

    @recorded
    def rename_s(self, dn, newrdn, newsuperior=None):
        """
        """
        return self._rename_s(dn, newrdn, newsuperior)

    @recorded
    def delete_s(self, dn):
        """
        """
        return self._delete_s(dn)

    @recorded
    def unbind(self):
        """
        """
        self.bound_as = None

    @recorded
    def unbind_s(self):
        """
        """
        self.bound_as = None

    @recorded
    def whoami_s(self):
        """
        """
        return "dn:" + self.bound_as

    #
    # Internal implementations
    #

    def _compare_s(self, dn, attr, value):
        self._check_valid_dn(dn)

        try:
            values = self.directory[dn].get(attr, [])
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        if attr == 'userPassword':
            for password in values:
                try:
                    if ldap_md5_crypt.verify(value, password):
                        return 1
                except (NameError, ValueError):
                    pass

        return (1 if (value in values) else 0)

    def _search_s(self, base, scope, filterstr, attrlist, attrsonly):
        from .filter import parse, UnsupportedOp

        self._check_valid_dn(base)

        if base not in self.directory:
            raise ldap.NO_SUCH_OBJECT

        # Find directory entries within the requested scope
        base_parts = ldap.dn.explode_dn(base.lower())
        base_len = len(base_parts)
        dn_parts = dict((dn, ldap.dn.explode_dn(dn)) for dn in self.directory.keys())

        if scope == ldap.SCOPE_BASE:
            dns = (dn for dn, parts in dn_parts.items() if parts == base_parts)
        elif scope == ldap.SCOPE_ONELEVEL:
            dns = (dn for dn, parts in dn_parts.items() if parts[1:] == base_parts)
        elif scope == ldap.SCOPE_SUBTREE:
            dns = (dn for dn, parts in dn_parts.items() if parts[-base_len:] == base_parts)
        else:
            raise ValueError(u"Unrecognized scope: {0}".format(scope))

        # Apply the filter expression
        try:
            filter_expr = parse(filterstr)
        except UnsupportedOp as e:
            raise SeedRequired(e)

        results = ((dn, self.directory[dn]) for dn in dns
                   if filter_expr.matches(dn, self.directory[dn]))

        # Apply attribute filtering, if any
        if attrlist is not None:
            results = ((dn, dict((attr, values) for attr, values in attrs.items() if attr in attrlist))
                       for dn, attrs in results)

        if attrsonly:
            results = ((dn, dict((attr, []) for attr in attrs.keys()))
                       for dn, attrs in results)

        return list(results)

    def _modify_s(self, dn, mod_attrs):
        self._check_valid_dn(dn)

        for item in mod_attrs:
            op, key, value = item

            try:
                entry = self.directory[dn]
            except KeyError:
                raise ldap.NO_SUCH_OBJECT

            if value is None:
                value = []
            elif type(value) is str:
                value = [value]

            if op == ldap.MOD_ADD:
                if value == []:
                    raise ldap.PROTOCOL_ERROR

                if key not in entry:
                    entry[key] = value
                else:
                    for subvalue in value:
                        if subvalue not in entry[key]:
                            entry[key].append(subvalue)
            elif op == ldap.MOD_DELETE:
                if key not in entry:
                    pass
                elif value == []:
                    del entry[key]
                else:
                    entry[key] = [v for v in entry[key] if v not in value]
                    if entry[key] == []:
                        del entry[key]
            elif op == ldap.MOD_REPLACE:
                if value == []:
                    if key in entry:
                        del entry[key]
                else:
                    entry[key] = value

        return (103, [])

    def _add_s(self, dn, record):
        self._check_valid_dn(dn)

        entry = {}
        dn = str(dn)
        for item in record:
            entry[item[0]] = list(item[1])
        try:
            self.directory[dn]
            raise ldap.ALREADY_EXISTS
        except KeyError:
            self.directory[dn] = entry
            return (105, [], len(self.methods_called()), [])

    def _rename_s(self, dn, newrdn, newsuperior):
        self._check_valid_dn(dn)
        self._check_valid_dn(newrdn)
        if newsuperior:
            self._check_valid_dn(newsuperior)

        try:
            entry = self.directory[dn]
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        if newsuperior:
            superior = newsuperior
        else:
            superior = ','.join(dn.split(',')[1:])

        newfulldn = '%s,%s' % (newrdn, superior)
        oldattr, oldvalue = dn.split(',')[0].split('=')
        newattr, newvalue = newrdn.split('=')

        try:
            if newvalue not in entry[newattr]:
                entry[newattr].append(newvalue)
        except KeyError:
            entry[newattr] = [newvalue]

        if oldattr == newattr or len(entry[oldattr]) > 1:
            entry[oldattr].remove(oldvalue)
        else:
            del entry[oldattr]

        self.directory[newfulldn] = entry
        del self.directory[dn]

        return (109, [])

    def _delete_s(self, dn):
        self._check_valid_dn(dn)

        try:
            del self.directory[dn]
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        return (107, [])

    #
    # Async
    #

    def _add_async_result(self, value):
        self.async_results.append(value)

        return len(self.async_results) - 1

    def _pop_async_result(self, msgid):
        if msgid in range(len(self.async_results)):
            value = self.async_results[msgid]
            self.async_results[msgid] = None
        else:
            value = None

        return value
<commit_msg>Raise ldap.ALREADY_EXISTS on renaming to exists object<commit_after>from __future__ import absolute_import

from copy import deepcopy

import ldap
from ldap.cidict import cidict
import ldap.dn

try:
    from passlib.hash import ldap_md5_crypt
except ImportError:
    pass

from .recording import SeedRequired, RecordableMethods, recorded

class LDAPObject(RecordableMethods):
    """
    :param directory: The initial content of this LDAP connection.
    :type directory: :class:`ldap.cidict.cidict`: ``{dn: {attr: [values]}}``

    Our mock replacement for :class:`ldap.LDAPObject`. This exports selected
    LDAP operations and allows you to set return values in advance as well as
    discover which methods were called after the fact.

    All of these methods take the same arguments as their python-ldap
    counterparts. Some are self-explanatory; those that are only partially
    implemented are documented as such.

    Ignore the *static* annotations; that's just a Sphinx artifact.

    .. attribute:: options

        *dict*: Options that have been set by
        :meth:`~mockldap.LDAPObject.set_option`.

    .. attribute:: tls_enabled

        *bool*: True if :meth:`~mockldap.LDAPObject.start_tls_s` was called.

    .. attribute:: bound_as

        *string*: DN of the last successful bind. None if unbound.
    """
    def __init__(self, directory):
        if not isinstance(directory, ldap.cidict.cidict):
            from . import map_keys
            directory = cidict(map_keys(lambda s: s.lower(), directory))

        self.directory = deepcopy(directory)
        self.async_results = []
        self.options = {}
        self.tls_enabled = False
        self.bound_as = None

    def _check_valid_dn(self, dn):
        try:
            ldap.dn.str2dn(dn)
        except ldap.DECODING_ERROR:
            raise ldap.INVALID_DN_SYNTAX

    #
    # Begin LDAP methods
    #

    @recorded
    def initialize(self, *args, **kwargs):
        """ This only exists for recording purposes. """
        pass

    @recorded
    def get_option(self, option):
        """
        """
        return self.options[option]

    @recorded
    def set_option(self, option, invalue):
        """
        """
        self.options[option] = invalue

    @recorded
    def simple_bind_s(self, who='', cred=''):
        """
        """
        success = False

        try:
            if(who == '' and cred == ''):
                success = True
            elif self._compare_s(who, 'userPassword', cred):
                success = True
        except ldap.NO_SUCH_OBJECT:
            pass

        if success:
            self.bound_as = who
            return (97, [])
        else:
            raise ldap.INVALID_CREDENTIALS('%s:%s' % (who, cred))

    @recorded
    def search(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
        """
        See :meth:`~mockldap.LDAPObject.search_s`.
        """
        value = self._search_s(base, scope, filterstr, attrlist, attrsonly)

        return self._add_async_result(value)

    @recorded
    def result(self, msgid, all=1, timeout=None):
        """
        """
        return ldap.RES_SEARCH_RESULT, self._pop_async_result(msgid)

    @recorded
    def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
        """
        Supports many, but not all, filter strings.

        Tests of the form ``'(foo=bar)'`` and ``'(foo=\*)'`` are supported, as
        are the &, \|, and !  operators. attrlist and attrsonly are also
        supported. Beyond that, this method must be seeded.
        """
        return self._search_s(base, scope, filterstr, attrlist, attrsonly)

    @recorded
    def start_tls_s(self):
        """
        """
        self.tls_enabled = True

    @recorded
    def compare_s(self, dn, attr, value):
        """
        """
        return self._compare_s(dn, attr, value)

    @recorded
    def modify_s(self, dn, mod_attrs):
        """
        """
        return self._modify_s(dn, mod_attrs)

    @recorded
    def add_s(self, dn, record):
        """
        """
        return self._add_s(dn, record)

    @recorded
    def rename_s(self, dn, newrdn, newsuperior=None):
        """
        """
        return self._rename_s(dn, newrdn, newsuperior)

    @recorded
    def delete_s(self, dn):
        """
        """
        return self._delete_s(dn)

    @recorded
    def unbind(self):
        """
        """
        self.bound_as = None

    @recorded
    def unbind_s(self):
        """
        """
        self.bound_as = None

    @recorded
    def whoami_s(self):
        """
        """
        return "dn:" + self.bound_as

    #
    # Internal implementations
    #

    def _compare_s(self, dn, attr, value):
        self._check_valid_dn(dn)

        try:
            values = self.directory[dn].get(attr, [])
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        if attr == 'userPassword':
            for password in values:
                try:
                    if ldap_md5_crypt.verify(value, password):
                        return 1
                except (NameError, ValueError):
                    pass

        return (1 if (value in values) else 0)

    def _search_s(self, base, scope, filterstr, attrlist, attrsonly):
        from .filter import parse, UnsupportedOp

        self._check_valid_dn(base)

        if base not in self.directory:
            raise ldap.NO_SUCH_OBJECT

        # Find directory entries within the requested scope
        base_parts = ldap.dn.explode_dn(base.lower())
        base_len = len(base_parts)
        dn_parts = dict((dn, ldap.dn.explode_dn(dn)) for dn in self.directory.keys())

        if scope == ldap.SCOPE_BASE:
            dns = (dn for dn, parts in dn_parts.items() if parts == base_parts)
        elif scope == ldap.SCOPE_ONELEVEL:
            dns = (dn for dn, parts in dn_parts.items() if parts[1:] == base_parts)
        elif scope == ldap.SCOPE_SUBTREE:
            dns = (dn for dn, parts in dn_parts.items() if parts[-base_len:] == base_parts)
        else:
            raise ValueError(u"Unrecognized scope: {0}".format(scope))

        # Apply the filter expression
        try:
            filter_expr = parse(filterstr)
        except UnsupportedOp as e:
            raise SeedRequired(e)

        results = ((dn, self.directory[dn]) for dn in dns
                   if filter_expr.matches(dn, self.directory[dn]))

        # Apply attribute filtering, if any
        if attrlist is not None:
            results = ((dn, dict((attr, values) for attr, values in attrs.items() if attr in attrlist))
                       for dn, attrs in results)

        if attrsonly:
            results = ((dn, dict((attr, []) for attr in attrs.keys()))
                       for dn, attrs in results)

        return list(results)

    def _modify_s(self, dn, mod_attrs):
        self._check_valid_dn(dn)

        for item in mod_attrs:
            op, key, value = item

            try:
                entry = self.directory[dn]
            except KeyError:
                raise ldap.NO_SUCH_OBJECT

            if value is None:
                value = []
            elif type(value) is str:
                value = [value]

            if op == ldap.MOD_ADD:
                if value == []:
                    raise ldap.PROTOCOL_ERROR

                if key not in entry:
                    entry[key] = value
                else:
                    for subvalue in value:
                        if subvalue not in entry[key]:
                            entry[key].append(subvalue)
            elif op == ldap.MOD_DELETE:
                if key not in entry:
                    pass
                elif value == []:
                    del entry[key]
                else:
                    entry[key] = [v for v in entry[key] if v not in value]
                    if entry[key] == []:
                        del entry[key]
            elif op == ldap.MOD_REPLACE:
                if value == []:
                    if key in entry:
                        del entry[key]
                else:
                    entry[key] = value

        return (103, [])

    def _add_s(self, dn, record):
        self._check_valid_dn(dn)

        entry = {}
        dn = str(dn)
        for item in record:
            entry[item[0]] = list(item[1])
        try:
            self.directory[dn]
            raise ldap.ALREADY_EXISTS
        except KeyError:
            self.directory[dn] = entry
            return (105, [], len(self.methods_called()), [])

    def _rename_s(self, dn, newrdn, newsuperior):
        self._check_valid_dn(dn)
        self._check_valid_dn(newrdn)
        if newsuperior:
            self._check_valid_dn(newsuperior)

        try:
            entry = self.directory[dn]
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        if newsuperior:
            superior = newsuperior
        else:
            superior = ','.join(dn.split(',')[1:])

        newfulldn = '%s,%s' % (newrdn, superior)
        if newfulldn in self.directory:
            raise ldap.ALREADY_EXISTS

        oldattr, oldvalue = dn.split(',')[0].split('=')
        newattr, newvalue = newrdn.split('=')

        try:
            if newvalue not in entry[newattr]:
                entry[newattr].append(newvalue)
        except KeyError:
            entry[newattr] = [newvalue]

        if oldattr == newattr or len(entry[oldattr]) > 1:
            entry[oldattr].remove(oldvalue)
        else:
            del entry[oldattr]

        self.directory[newfulldn] = entry
        del self.directory[dn]

        return (109, [])

    def _delete_s(self, dn):
        self._check_valid_dn(dn)

        try:
            del self.directory[dn]
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        return (107, [])

    #
    # Async
    #

    def _add_async_result(self, value):
        self.async_results.append(value)

        return len(self.async_results) - 1

    def _pop_async_result(self, msgid):
        if msgid in range(len(self.async_results)):
            value = self.async_results[msgid]
            self.async_results[msgid] = None
        else:
            value = None

        return value
<|endoftext|>


<commit_before>from __future__ import absolute_import

import base64
from copy import deepcopy
from crypt import crypt
import hashlib
import re

import ldap
from ldap.cidict import cidict
import ldap.dn

from .recording import SeedRequired, RecordableMethods, recorded

class LDAPObject(RecordableMethods):
    """
    :param directory: The initial content of this LDAP connection.
    :type directory: :class:`ldap.cidict.cidict`: ``{dn: {attr: [values]}}``

    Our mock replacement for :class:`ldap.LDAPObject`. This exports selected
    LDAP operations and allows you to set return values in advance as well as
    discover which methods were called after the fact.

    All of these methods take the same arguments as their python-ldap
    counterparts. Some are self-explanatory; those that are only partially
    implemented are documented as such.

    Ignore the *static* annotations; that's just a Sphinx artifact.

    .. attribute:: options

        *dict*: Options that have been set by
        :meth:`~mockldap.LDAPObject.set_option`.

    .. attribute:: tls_enabled

        *bool*: True if :meth:`~mockldap.LDAPObject.start_tls_s` was called.

    .. attribute:: bound_as

        *string*: DN of the last successful bind. None if unbound.
    """
    def __init__(self, directory):
        if not isinstance(directory, ldap.cidict.cidict):
            from . import map_keys
            directory = cidict(map_keys(lambda s: s.lower(), directory))

        self.directory = deepcopy(directory)
        self.async_results = []
        self.options = {}
        self.tls_enabled = False
        self.bound_as = None

    def _check_valid_dn(self, dn):
        try:
            ldap.dn.str2dn(dn)
        except ldap.DECODING_ERROR:
            raise ldap.INVALID_DN_SYNTAX

    #
    # Begin LDAP methods
    #

    @recorded
    def initialize(self, *args, **kwargs):
        """ This only exists for recording purposes. """
        pass

    @recorded
    def get_option(self, option):
        """
        """
        return self.options[option]

    @recorded
    def set_option(self, option, invalue):
        """
        """
        self.options[option] = invalue

    @recorded
    def simple_bind_s(self, who='', cred=''):
        """
        """
        success = False

        try:
            if(who == '' and cred == ''):
                success = True
            elif self._compare_s(who, 'userPassword', cred):
                success = True
        except ldap.NO_SUCH_OBJECT:
            pass

        if success:
            self.bound_as = who
            return (97, [])
        else:
            raise ldap.INVALID_CREDENTIALS('%s:%s' % (who, cred))

    @recorded
    def search(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
        """
        See :meth:`~mockldap.LDAPObject.search_s`.
        """
        value = self._search_s(base, scope, filterstr, attrlist, attrsonly)

        return self._add_async_result(value)

    @recorded
    def result(self, msgid, all=1, timeout=None):
        """
        """
        return ldap.RES_SEARCH_RESULT, self._pop_async_result(msgid)

    @recorded
    def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
        """
        Supports many, but not all, filter strings.

        Tests of the form ``'(foo=bar)'`` and ``'(foo=\*)'`` are supported, as
        are the &, \|, and !  operators. attrlist and attrsonly are also
        supported. Beyond that, this method must be seeded.
        """
        return self._search_s(base, scope, filterstr, attrlist, attrsonly)

    @recorded
    def start_tls_s(self):
        """
        """
        self.tls_enabled = True

    @recorded
    def compare_s(self, dn, attr, value):
        """
        """
        return self._compare_s(dn, attr, value)

    @recorded
    def modify_s(self, dn, mod_attrs):
        """
        """
        return self._modify_s(dn, mod_attrs)

    @recorded
    def add_s(self, dn, record):
        """
        """
        return self._add_s(dn, record)

    @recorded
    def rename_s(self, dn, newrdn, newsuperior=None):
        """
        """
        return self._rename_s(dn, newrdn, newsuperior)

    @recorded
    def delete_s(self, dn):
        """
        """
        return self._delete_s(dn)

    @recorded
    def unbind(self):
        """
        """
        self.bound_as = None

    @recorded
    def unbind_s(self):
        """
        """
        self.bound_as = None

    @recorded
    def whoami_s(self):
        """
        """
        return "dn:" + self.bound_as

    #
    # Internal implementations
    #

    def _compare_s(self, dn, attr, value):
        self._check_valid_dn(dn)

        try:
            values = self.directory[dn].get(attr, [])
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        if attr == 'userPassword':
            result = 1 if any(self._compare_password(value, candidate) for candidate in values) else 0
        else:
            result = (1 if (value in values) else 0)

        return result

    PASSWORD_RE = re.compile(r'^{(.*?)}(.*)')

    def _compare_password(self, password, value):
        """
        Compare a password to a (possibly hashed) attribute value.

        This returns True iff ``password`` matches the given attribute value. A
        limited set of LDAP password hashing schemes is supported; feel free to
        add more if you need them.

        """
        match = self.PASSWORD_RE.match(value)

        if match is not None:
            scheme, raw = match.groups()

            if scheme == 'CRYPT':
                matches = (crypt(password, raw[:4]) == raw)
            elif scheme == 'SSHA':
                decoded = base64.b64decode(raw)
                h = hashlib.sha1(password.encode('utf-8'))
                h.update(decoded[-4:])
                matches = (h.digest() == decoded[:-4])
            else:
                matches = False
        else:
            matches = (value == password)

        return matches

    def _search_s(self, base, scope, filterstr, attrlist, attrsonly):
        from .filter import parse, UnsupportedOp

        self._check_valid_dn(base)

        if base not in self.directory:
            raise ldap.NO_SUCH_OBJECT

        # Find directory entries within the requested scope
        base_parts = ldap.dn.explode_dn(base.lower())
        base_len = len(base_parts)
        dn_parts = dict((dn, ldap.dn.explode_dn(dn)) for dn in self.directory.keys())

        if scope == ldap.SCOPE_BASE:
            dns = (dn for dn, parts in dn_parts.items() if parts == base_parts)
        elif scope == ldap.SCOPE_ONELEVEL:
            dns = (dn for dn, parts in dn_parts.items() if parts[1:] == base_parts)
        elif scope == ldap.SCOPE_SUBTREE:
            dns = (dn for dn, parts in dn_parts.items() if parts[-base_len:] == base_parts)
        else:
            raise ValueError(u"Unrecognized scope: {0}".format(scope))

        # Apply the filter expression
        try:
            filter_expr = parse(filterstr)
        except UnsupportedOp as e:
            raise SeedRequired(e)

        results = ((dn, self.directory[dn]) for dn in dns
                   if filter_expr.matches(dn, self.directory[dn]))

        # Apply attribute filtering, if any
        if attrlist is not None:
            results = ((dn, dict((attr, values) for attr, values in attrs.items() if attr in attrlist))
                       for dn, attrs in results)

        if attrsonly:
            results = ((dn, dict((attr, []) for attr in attrs.keys()))
                       for dn, attrs in results)

        return list(results)

    def _modify_s(self, dn, mod_attrs):
        self._check_valid_dn(dn)

        for item in mod_attrs:
            op, key, value = item

            try:
                entry = self.directory[dn]
            except KeyError:
                raise ldap.NO_SUCH_OBJECT

            if value is None:
                value = []
            elif type(value) is str:
                value = [value]

            if op == ldap.MOD_ADD:
                if value == []:
                    raise ldap.PROTOCOL_ERROR

                if key not in entry:
                    entry[key] = value
                else:
                    for subvalue in value:
                        if subvalue not in entry[key]:
                            entry[key].append(subvalue)
            elif op == ldap.MOD_DELETE:
                if key not in entry:
                    pass
                elif value == []:
                    del entry[key]
                else:
                    entry[key] = [v for v in entry[key] if v not in value]
                    if entry[key] == []:
                        del entry[key]
            elif op == ldap.MOD_REPLACE:
                if value == []:
                    if key in entry:
                        del entry[key]
                else:
                    entry[key] = value

        return (103, [])

    def _add_s(self, dn, record):
        self._check_valid_dn(dn)

        entry = {}
        dn = str(dn)
        for item in record:
            entry[item[0]] = list(item[1])
        try:
            self.directory[dn]
            raise ldap.ALREADY_EXISTS
        except KeyError:
            self.directory[dn] = entry
            return (105, [], len(self.methods_called()), [])

    def _rename_s(self, dn, newrdn, newsuperior):
        self._check_valid_dn(dn)
        self._check_valid_dn(newrdn)
        if newsuperior:
            self._check_valid_dn(newsuperior)

        try:
            entry = self.directory[dn]
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        if newsuperior:
            superior = newsuperior
        else:
            superior = ','.join(dn.split(',')[1:])

        newfulldn = '%s,%s' % (newrdn, superior)
        if newfulldn in self.directory:
            raise ldap.ALREADY_EXISTS

        oldattr, oldvalue = dn.split(',')[0].split('=')
        newattr, newvalue = newrdn.split('=')

        try:
            if newvalue not in entry[newattr]:
                entry[newattr].append(newvalue)
        except KeyError:
            entry[newattr] = [newvalue]

        if oldattr == newattr or len(entry[oldattr]) > 1:
            entry[oldattr].remove(oldvalue)
        else:
            del entry[oldattr]

        self.directory[newfulldn] = entry
        del self.directory[dn]

        return (109, [])

    def _delete_s(self, dn):
        self._check_valid_dn(dn)

        try:
            del self.directory[dn]
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        return (107, [])

    #
    # Async
    #

    def _add_async_result(self, value):
        self.async_results.append(value)

        return len(self.async_results) - 1

    def _pop_async_result(self, msgid):
        if msgid in range(len(self.async_results)):
            value = self.async_results[msgid]
            self.async_results[msgid] = None
        else:
            value = None

        return value
<commit_msg>Disable crypt support on Windows to avoid an ImportError.<commit_after>from __future__ import absolute_import

import base64
from copy import deepcopy
try:
    from crypt import crypt
except ImportError:
    crypt = None
import hashlib
import re

import ldap
from ldap.cidict import cidict
import ldap.dn

from .recording import SeedRequired, RecordableMethods, recorded

class LDAPObject(RecordableMethods):
    """
    :param directory: The initial content of this LDAP connection.
    :type directory: :class:`ldap.cidict.cidict`: ``{dn: {attr: [values]}}``

    Our mock replacement for :class:`ldap.LDAPObject`. This exports selected
    LDAP operations and allows you to set return values in advance as well as
    discover which methods were called after the fact.

    All of these methods take the same arguments as their python-ldap
    counterparts. Some are self-explanatory; those that are only partially
    implemented are documented as such.

    Ignore the *static* annotations; that's just a Sphinx artifact.

    .. attribute:: options

        *dict*: Options that have been set by
        :meth:`~mockldap.LDAPObject.set_option`.

    .. attribute:: tls_enabled

        *bool*: True if :meth:`~mockldap.LDAPObject.start_tls_s` was called.

    .. attribute:: bound_as

        *string*: DN of the last successful bind. None if unbound.
    """
    def __init__(self, directory):
        if not isinstance(directory, ldap.cidict.cidict):
            from . import map_keys
            directory = cidict(map_keys(lambda s: s.lower(), directory))

        self.directory = deepcopy(directory)
        self.async_results = []
        self.options = {}
        self.tls_enabled = False
        self.bound_as = None

    def _check_valid_dn(self, dn):
        try:
            ldap.dn.str2dn(dn)
        except ldap.DECODING_ERROR:
            raise ldap.INVALID_DN_SYNTAX

    #
    # Begin LDAP methods
    #

    @recorded
    def initialize(self, *args, **kwargs):
        """ This only exists for recording purposes. """
        pass

    @recorded
    def get_option(self, option):
        """
        """
        return self.options[option]

    @recorded
    def set_option(self, option, invalue):
        """
        """
        self.options[option] = invalue

    @recorded
    def simple_bind_s(self, who='', cred=''):
        """
        """
        success = False

        try:
            if(who == '' and cred == ''):
                success = True
            elif self._compare_s(who, 'userPassword', cred):
                success = True
        except ldap.NO_SUCH_OBJECT:
            pass

        if success:
            self.bound_as = who
            return (97, [])
        else:
            raise ldap.INVALID_CREDENTIALS('%s:%s' % (who, cred))

    @recorded
    def search(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
        """
        See :meth:`~mockldap.LDAPObject.search_s`.
        """
        value = self._search_s(base, scope, filterstr, attrlist, attrsonly)

        return self._add_async_result(value)

    @recorded
    def result(self, msgid, all=1, timeout=None):
        """
        """
        return ldap.RES_SEARCH_RESULT, self._pop_async_result(msgid)

    @recorded
    def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
        """
        Supports many, but not all, filter strings.

        Tests of the form ``'(foo=bar)'`` and ``'(foo=\*)'`` are supported, as
        are the &, \|, and !  operators. attrlist and attrsonly are also
        supported. Beyond that, this method must be seeded.
        """
        return self._search_s(base, scope, filterstr, attrlist, attrsonly)

    @recorded
    def start_tls_s(self):
        """
        """
        self.tls_enabled = True

    @recorded
    def compare_s(self, dn, attr, value):
        """
        """
        return self._compare_s(dn, attr, value)

    @recorded
    def modify_s(self, dn, mod_attrs):
        """
        """
        return self._modify_s(dn, mod_attrs)

    @recorded
    def add_s(self, dn, record):
        """
        """
        return self._add_s(dn, record)

    @recorded
    def rename_s(self, dn, newrdn, newsuperior=None):
        """
        """
        return self._rename_s(dn, newrdn, newsuperior)

    @recorded
    def delete_s(self, dn):
        """
        """
        return self._delete_s(dn)

    @recorded
    def unbind(self):
        """
        """
        self.bound_as = None

    @recorded
    def unbind_s(self):
        """
        """
        self.bound_as = None

    @recorded
    def whoami_s(self):
        """
        """
        return "dn:" + self.bound_as

    #
    # Internal implementations
    #

    def _compare_s(self, dn, attr, value):
        self._check_valid_dn(dn)

        try:
            values = self.directory[dn].get(attr, [])
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        if attr == 'userPassword':
            result = 1 if any(self._compare_password(value, candidate) for candidate in values) else 0
        else:
            result = (1 if (value in values) else 0)

        return result

    PASSWORD_RE = re.compile(r'^{(.*?)}(.*)')

    def _compare_password(self, password, value):
        """
        Compare a password to a (possibly hashed) attribute value.

        This returns True iff ``password`` matches the given attribute value. A
        limited set of LDAP password hashing schemes is supported; feel free to
        add more if you need them.

        """
        match = self.PASSWORD_RE.match(value)

        if match is not None:
            scheme, raw = match.groups()

            if crypt is not None and scheme == 'CRYPT':
                matches = (crypt(password, raw[:4]) == raw)
            elif scheme == 'SSHA':
                decoded = base64.b64decode(raw)
                h = hashlib.sha1(password.encode('utf-8'))
                h.update(decoded[-4:])
                matches = (h.digest() == decoded[:-4])
            else:
                matches = False
        else:
            matches = (value == password)

        return matches

    def _search_s(self, base, scope, filterstr, attrlist, attrsonly):
        from .filter import parse, UnsupportedOp

        self._check_valid_dn(base)

        if base not in self.directory:
            raise ldap.NO_SUCH_OBJECT

        # Find directory entries within the requested scope
        base_parts = ldap.dn.explode_dn(base.lower())
        base_len = len(base_parts)
        dn_parts = dict((dn, ldap.dn.explode_dn(dn)) for dn in self.directory.keys())

        if scope == ldap.SCOPE_BASE:
            dns = (dn for dn, parts in dn_parts.items() if parts == base_parts)
        elif scope == ldap.SCOPE_ONELEVEL:
            dns = (dn for dn, parts in dn_parts.items() if parts[1:] == base_parts)
        elif scope == ldap.SCOPE_SUBTREE:
            dns = (dn for dn, parts in dn_parts.items() if parts[-base_len:] == base_parts)
        else:
            raise ValueError(u"Unrecognized scope: {0}".format(scope))

        # Apply the filter expression
        try:
            filter_expr = parse(filterstr)
        except UnsupportedOp as e:
            raise SeedRequired(e)

        results = ((dn, self.directory[dn]) for dn in dns
                   if filter_expr.matches(dn, self.directory[dn]))

        # Apply attribute filtering, if any
        if attrlist is not None:
            results = ((dn, dict((attr, values) for attr, values in attrs.items() if attr in attrlist))
                       for dn, attrs in results)

        if attrsonly:
            results = ((dn, dict((attr, []) for attr in attrs.keys()))
                       for dn, attrs in results)

        return list(results)

    def _modify_s(self, dn, mod_attrs):
        self._check_valid_dn(dn)

        for item in mod_attrs:
            op, key, value = item

            try:
                entry = self.directory[dn]
            except KeyError:
                raise ldap.NO_SUCH_OBJECT

            if value is None:
                value = []
            elif type(value) is str:
                value = [value]

            if op == ldap.MOD_ADD:
                if value == []:
                    raise ldap.PROTOCOL_ERROR

                if key not in entry:
                    entry[key] = value
                else:
                    for subvalue in value:
                        if subvalue not in entry[key]:
                            entry[key].append(subvalue)
            elif op == ldap.MOD_DELETE:
                if key not in entry:
                    pass
                elif value == []:
                    del entry[key]
                else:
                    entry[key] = [v for v in entry[key] if v not in value]
                    if entry[key] == []:
                        del entry[key]
            elif op == ldap.MOD_REPLACE:
                if value == []:
                    if key in entry:
                        del entry[key]
                else:
                    entry[key] = value

        return (103, [])

    def _add_s(self, dn, record):
        self._check_valid_dn(dn)

        entry = {}
        dn = str(dn)
        for item in record:
            entry[item[0]] = list(item[1])
        try:
            self.directory[dn]
            raise ldap.ALREADY_EXISTS
        except KeyError:
            self.directory[dn] = entry
            return (105, [], len(self.methods_called()), [])

    def _rename_s(self, dn, newrdn, newsuperior):
        self._check_valid_dn(dn)
        self._check_valid_dn(newrdn)
        if newsuperior:
            self._check_valid_dn(newsuperior)

        try:
            entry = self.directory[dn]
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        if newsuperior:
            superior = newsuperior
        else:
            superior = ','.join(dn.split(',')[1:])

        newfulldn = '%s,%s' % (newrdn, superior)
        if newfulldn in self.directory:
            raise ldap.ALREADY_EXISTS

        oldattr, oldvalue = dn.split(',')[0].split('=')
        newattr, newvalue = newrdn.split('=')

        try:
            if newvalue not in entry[newattr]:
                entry[newattr].append(newvalue)
        except KeyError:
            entry[newattr] = [newvalue]

        if oldattr == newattr or len(entry[oldattr]) > 1:
            entry[oldattr].remove(oldvalue)
        else:
            del entry[oldattr]

        self.directory[newfulldn] = entry
        del self.directory[dn]

        return (109, [])

    def _delete_s(self, dn):
        self._check_valid_dn(dn)

        try:
            del self.directory[dn]
        except KeyError:
            raise ldap.NO_SUCH_OBJECT

        return (107, [])

    #
    # Async
    #

    def _add_async_result(self, value):
        self.async_results.append(value)

        return len(self.async_results) - 1

    def _pop_async_result(self, msgid):
        if msgid in range(len(self.async_results)):
            value = self.async_results[msgid]
            self.async_results[msgid] = None
        else:
            value = None

        return value
<|endoftext|>