<commit_before>import os
import io
from contextlib import contextmanager
from copy import copy
from importlib.machinery import SourceFileLoader

from homely._errors import HelperError
from homely._engine2 import getengine, Helper, Cleaner, Engine, getrepoinfo
from homely._utils import (
    filereplacer, _repopath2real, _homepath2real, isnecessarypath,
    NoChangesNeeded
)
from homely._ui import entersection, warn

# allow importing from outside
from homely._utils import haveexecutable  # noqa

def run(updatehelper):
    getengine().run(updatehelper)

_include_num = 0

def include(pyscript):
    path = _repopath2real(pyscript, getrepoinfo().localrepo)
    if not os.path.exists(path):
        warn("{} not found at {}".format(pyscript, path))
        return

    global _include_num
    _include_num += 1

    source = SourceFileLoader('__imported_by_homely_{}'.format(_include_num),
                              path)
    try:
        with entersection("/" + pyscript):
            source.load_module()
    except Exception as err:
        warn("Error while including {}: {}".format(pyscript, str(err)))

def section(func):
    name = func.__name__
    engine = getengine()
    try:
        with entersection(":" + name + "()"):
            if engine.pushsection(name):
                func()
    finally:
        engine.popsection(name)

def mkdir(path):
    path = _homepath2real(path)
    getengine().run(MakeDir(path))

def download(url, dest, expiry=None):
    getengine().run(Download(url, _homepath2real(dest), expiry))

def lineinfile(filename, contents, where=None):
    filename = _homepath2real(filename)
    obj = LineInFile(filename, contents, where)
    getengine().run(obj)

def blockinfile(filename, lines, prefix, suffix, where=None):
    filename = _homepath2real(filename)
    obj = BlockInFile(filename, lines, prefix, suffix, where)
    getengine().run(obj)

def symlink(target, linkname=None):
    # expand <target> to a path relative to the current repo
    target = _repopath2real(target, getrepoinfo().localrepo)

    # if [linkname] is omited, assume the symlink goes into $HOME/ at the top
    # level
    if linkname is None:
        linkname = os.path.join(os.environ.get('HOME'),
                                os.path.basename(target))
    else:
        linkname = _homepath2real(linkname)
    getengine().run(MakeSymlink(target, linkname))

@contextmanager
def writefile(filename):
    stream = None
    try:
        stream = io.StringIO('foo')
        yield stream
        stream.seek(0)
        getengine().run(WriteFile(_homepath2real(filename), stream.read()))
    finally:
        if stream:
            stream.close()

class MakeDir(Helper):
    _path = None

    def __init__(self, path):
        super(MakeDir, self).__init__()
        self._path = path

    @property
    def description(self):
        return "Create dir %s" % self._path

    def getcleaner(self):
        return

    def isdone(self):
        return os.path.exists(self._path) and os.path.isdir(self._path)

    def makechanges(self):
        os.mkdir(self._path)

    def affectspath(self, path):
        return path == self._path

    def pathsownable(self):
        return {self._path: Engine.TYPE_FOLDER_ONLY}

    def getclaims(self):
        return []

class MakeSymlink(Helper):
    def __init__(self, target, linkname):
        assert target.startswith('/')
        assert linkname.startswith('/')
        self._target = target
        self._linkname = linkname
        assert self._target != self._linkname

    def getclaims(self):
        return []

    def getcleaner(self):
        return

    def isdone(self):
        return (os.path.islink(self._linkname) and
                os.readlink(self._linkname) == self._target)

    @property
    def description(self):
        return "Create symlink %s -> %s" % (self._linkname, self._target)

    def makechanges(self):
        assert not os.path.exists(self._linkname)
        os.symlink(self._target, self._linkname)

    def affectspath(self, path):
        return path == self._linkname

    def pathsownable(self):
        return {self._linkname: Engine.TYPE_LINK}

class Download(Helper):
    def __init__(self, url, dest, expiry=None):
        assert dest.startswith('/')
        self._url = url
        self._dest = dest
        self._expiry = expiry

    def getclaims(self):
        return []

    def getcleaner(self):
        return

    def isdone(self):
        # TODO: make it so that we retry after the file is <self._expiry>
        # seconds old
        return os.path.exists(self._dest)

    @property
    def description(self):
        return "Download %s to %s" % (self._url, self._dest)

    def makechanges(self):
        import requests
        r = requests.get(self._url)
        if r.status_code != 200:
            raise HelperError("Download of %s failed: %s"
                              % (self._url, r.status_code))
        with open(self._dest, 'w') as f:
            f.write(r.text)

    def affectspath(self, path):
        return path == self._dest

    def pathsownable(self):
        return {self._dest: Engine.TYPE_FILE_ALL}

WHERE_TOP = "__TOP__"
WHERE_END = "__END__"
WHERE_ANY = "__ANY__"

class LineInFile(Helper):
    def __init__(self, filename, contents, where=None):
        super(LineInFile, self).__init__()
        self._filename = filename
        self._contents = contents
        if not len(contents):
            raise Exception("LineInFile cannot work with empty contents")
        if "\n" in contents or "\r" in contents:
            raise Exception("LineInFile contents cannot include linebreaks")
        if contents.strip() != contents:
            raise Exception(
                "LineInFile contents cannot start or end with whitespace")
        if where is None:
            where = WHERE_ANY
        self._where = where

    @property
    def description(self):
        pos = ""
        if self._where == WHERE_TOP:
            pos = " (at top)"
        elif self._where == WHERE_END:
            pos = " (at end)"
        return "Add line to %s: %r%s" % (self._filename, self._contents, pos)

    def getcleaner(self):
        return CleanLineInFile(self._filename, self._contents)

    def isdone(self):
        if not os.path.exists(self._filename):
            return False
        foundat = []
        linecount = 0
        with open(self._filename) as f:
            for line in f.readlines():
                linecount += 1
                if line.rstrip('\r\n') == self._contents:
                    foundat.append(linecount)
        # if the line appears multiple times, we count it as NOT done
        if len(foundat) != 1:
            return False
        if self._where == WHERE_TOP:
            return foundat[0] == 1
        if self._where == WHERE_END:
            return foundat[0] == linecount
        return True

    def makechanges(self):
        # the content wasn't found in the file, so we'll have to add it
        with filereplacer(self._filename) as (tmp, origlines, NL):
            seen = False
            # if the new line goes at the top, write it out first
            if self._where == WHERE_TOP:
                tmp.write(self._contents)
                tmp.write(NL)
                seen = True
            # read through the original file and look for a line to replace
            if origlines is not None:
                for line in origlines:
                    if line == self._contents:
                        # skip the line if it's already been seen before, or it
                        # needs to move to the end of the file
                        if seen or self._where == WHERE_END:
                            continue
                        seen = True
                    tmp.write(line)
                    tmp.write(NL)
            if not seen:
                assert self._where in (WHERE_END, WHERE_ANY)
                tmp.write(self._contents)
                tmp.write(NL)

    def pathsownable(self):
        return {self._filename: Engine.TYPE_FILE_PART}

    def affectspath(self, path):
        return path == self._filename

    def getclaims(self):
        return []

class CleanLineInFile(Cleaner):
    def __init__(self, filename, contents):
        self._filename = filename
        self._contents = contents

    @property
    def description(self):
        return "Remove line from %s: %r" % (self._filename, self._contents)

    def asdict(self):
        return dict(filename=self._filename, contents=self._contents)

    @classmethod
    def fromdict(class_, data):
        return class_(data["filename"], data["contents"])

    def __eq__(self, other):
        return (other._filename == self._filename and
                other._contents == self._contents)

    def isneeded(self):
        if not os.path.exists(self._filename):
            return False
        with open(self._filename) as f:
            for line in f:
                if line.rstrip("\r\n") == self._contents:
                    return True
        return False

    def wantspath(self, path):
        return path == self._filename or isnecessarypath(path, self._filename)

    def makechanges(self):
        # if the file doesn't exist, we don't need to make changes
        assert os.path.exists(self._filename)

        with filereplacer(self._filename) as (tmp, origlines, NL):
            # if the file doesn't exist any more, then no changes are needed
            changed = False
            if origlines is not None:
                for line in origlines:
                    if line == self._contents:
                        changed = True
                    else:
                        tmp.write(line)
                        tmp.write(NL)
            if not changed:
                raise NoChangesNeeded()

        return [self._filename] if changed else []

    def needsclaims(self):
        return []

class BlockInFile(Helper):
    def __init__(self, filename, lines, prefix, suffix, where=None):
        self._filename = filename
        self._lines = lines
        self._prefix = prefix
        self._suffix = suffix
        self._where = WHERE_ANY if where is None else where

    @property
    def description(self):
        pos = {WHERE_TOP: "top of ", WHERE_END: "end of ", WHERE_ANY: ""}
        return "Add %d lines to %s%s: %r...%r" % (
            2 + len(self._lines),
            pos[self._where],
            self._filename,
            self._prefix,
            self._suffix,
        )

    def getcleaner(self):
        return CleanBlockInFile(self._filename,
                                self._prefix,
                                self._suffix,
                                )

    def isdone(self):
        if not os.path.exists(self._filename):
            return False

        # look to see if our contents appear in the file
        with open(self._filename) as f:
            firstline = True
            count = 0
            expect = None
            prev = None
            for line in f:
                stripped = line.rstrip('\r\n')
                if firstline and self._where == WHERE_TOP:
                    if stripped != self._prefix:
                        return False
                if expect is not None:
                    if stripped != expect.pop(0):
                        return False
                    if len(expect):
                        prev = "INNER"
                    else:
                        prev = "SUFFIX"
                        count += 1
                elif stripped == self._prefix:
                    expect = copy(self._lines)
                    expect.extend(self._suffix)
                    prev = "PREFIX"
                else:
                    prev = "OTHER"
        if self._where == WHERE_END and prev != "SUFFIX":
            return False
        return count == 1

    def makechanges(self):
        with filereplacer(self._filename) as (tmp, origlines, NL):
            findsuffix = False
            found = False

            def _writeall():
                nonlocal found
                found = True
                tmp.write(self._prefix)
                tmp.write(NL)
                for line in self._lines:
                    tmp.write(line)
                    tmp.write(NL)
                tmp.write(self._suffix)
                tmp.write(NL)

            if self._where == WHERE_TOP:
                _writeall()
            if origlines is not None:
                for line in origlines:
                    if findsuffix:
                        # while we're looking for the suffix, discard any other
                        # lines we encounter
                        # encountered into a temporary holding area
                        if line != self._suffix:
                            continue

                        # We've found the suffix! If we're allowed to write the
                        # block out anywhere, and we haven't written it out
                        # yet write it out here, now
                        if self._where == WHERE_ANY and not found:
                            _writeall()
                        findsuffix = False
                        continue

                    if line == self._prefix:
                        findsuffix = True
                        continue

                    tmp.write(line)
                    tmp.write(NL)

            if findsuffix:
                # FIXME: some sort of exception that we can handle nicely
                raise Exception(
                    "Error in %s: Found prefix %r but not suffix %r" % (
                        self._filename,
                        self._prefix,
                        self._suffix,
                    ))

            # if we haven't found the block yet, write it out now
            if not found:
                _writeall()

    def affectspath(self, path):
        return path == self._filename

    def pathsownable(self):
        return {self._filename: Engine.TYPE_FILE_PART}

    def getclaims(self):
        return []

class CleanBlockInFile(Cleaner):
    def __init__(self, filename, prefix, suffix):
        self._filename = filename
        self._prefix = prefix
        self._suffix = suffix

    @property
    def description(self):
        return "Remove lines from %s: %r...%r" % (
            self._filename,
            self._prefix,
            self._suffix,
        )

    def asdict(self):
        return dict(filename=self._filename,
                    prefix=self._prefix,
                    suffix=self._suffix)

    @classmethod
    def fromdict(class_, data):
        return class_(data["filename"], data["prefix"], data["suffix"])

    def __eq__(self, other):
        return (other._filename == self._filename and
                other._prefix == self._prefix and
                other._suffix == self._suffix)

    def isneeded(self):
        # the cleaner is needed if both the prefix and the suffix are found in
        # the file, in the correct order
        if not os.path.exists(self._filename):
            return False

        haveprefix = False

        with open(self._filename) as f:
            for line in [l.rstrip("\r\n") for l in f]:
                if line == self._prefix:
                    haveprefix = True
                elif line == self._suffix and haveprefix:
                    return True

        return False

    def wantspath(self, path):
        return path == self._filename or isnecessarypath(path, self._filename)

    def makechanges(self):
        assert os.path.exists(self._filename)

        with filereplacer(self._filename) as (tmp, origlines, NL):
            changed = False
            findsuffix = False
            for line in origlines:
                if findsuffix:
                    if line == self._suffix:
                        findsuffix = False
                        changed = True
                elif line == self._prefix:
                    findsuffix = True
                else:
                    tmp.write(line)
                    tmp.write(NL)
            if findsuffix or not changed:
                # we couldn't find the suffix ... don't change the file
                raise NoChangesNeeded()

        return [self._filename] if changed else []

    def needsclaims(self):
        return []

class WriteFile(Helper):
    def __init__(self, filename, contents, canoverwrite=False):
        self._filename = filename
        self._contents = contents
        self._canoverwrite = canoverwrite

    @property
    def description(self):
        return "Write file %s" % self._filename

    def getcleaner(self):
        # no cleaner needed
        pass

    def isdone(self):
        if os.path.islink(self._filename):
            return False
        try:
            with open(self._filename, 'r') as f:
                if f.read() == self._contents:
                    return True
        except FileNotFoundError:
            pass
        return False

    def makechanges(self):
        assert not os.path.islink(self._filename)
        with open(self._filename, 'w') as f:
            f.write(self._contents)

    def pathsownable(self):
        return {self._filename: Engine.TYPE_FILE_ALL}

    def affectspath(self, path):
        return path == self._filename

    def getclaims(self):
        return []
<commit_msg>Ensure binary downloads are OK<commit_after>import os
import io
from contextlib import contextmanager
from copy import copy
from importlib.machinery import SourceFileLoader

from homely._errors import HelperError
from homely._engine2 import getengine, Helper, Cleaner, Engine, getrepoinfo
from homely._utils import (
    filereplacer, _repopath2real, _homepath2real, isnecessarypath,
    NoChangesNeeded
)
from homely._ui import entersection, warn

# allow importing from outside
from homely._utils import haveexecutable  # noqa

def run(updatehelper):
    getengine().run(updatehelper)

_include_num = 0

def include(pyscript):
    path = _repopath2real(pyscript, getrepoinfo().localrepo)
    if not os.path.exists(path):
        warn("{} not found at {}".format(pyscript, path))
        return

    global _include_num
    _include_num += 1

    source = SourceFileLoader('__imported_by_homely_{}'.format(_include_num),
                              path)
    try:
        with entersection("/" + pyscript):
            source.load_module()
    except Exception as err:
        warn("Error while including {}: {}".format(pyscript, str(err)))

def section(func):
    name = func.__name__
    engine = getengine()
    try:
        with entersection(":" + name + "()"):
            if engine.pushsection(name):
                func()
    finally:
        engine.popsection(name)

def mkdir(path):
    path = _homepath2real(path)
    getengine().run(MakeDir(path))

def download(url, dest, expiry=None):
    getengine().run(Download(url, _homepath2real(dest), expiry))

def lineinfile(filename, contents, where=None):
    filename = _homepath2real(filename)
    obj = LineInFile(filename, contents, where)
    getengine().run(obj)

def blockinfile(filename, lines, prefix, suffix, where=None):
    filename = _homepath2real(filename)
    obj = BlockInFile(filename, lines, prefix, suffix, where)
    getengine().run(obj)

def symlink(target, linkname=None):
    # expand <target> to a path relative to the current repo
    target = _repopath2real(target, getrepoinfo().localrepo)

    # if [linkname] is omited, assume the symlink goes into $HOME/ at the top
    # level
    if linkname is None:
        linkname = os.path.join(os.environ.get('HOME'),
                                os.path.basename(target))
    else:
        linkname = _homepath2real(linkname)
    getengine().run(MakeSymlink(target, linkname))

@contextmanager
def writefile(filename):
    stream = None
    try:
        stream = io.StringIO('foo')
        yield stream
        stream.seek(0)
        getengine().run(WriteFile(_homepath2real(filename), stream.read()))
    finally:
        if stream:
            stream.close()

class MakeDir(Helper):
    _path = None

    def __init__(self, path):
        super(MakeDir, self).__init__()
        self._path = path

    @property
    def description(self):
        return "Create dir %s" % self._path

    def getcleaner(self):
        return

    def isdone(self):
        return os.path.exists(self._path) and os.path.isdir(self._path)

    def makechanges(self):
        os.mkdir(self._path)

    def affectspath(self, path):
        return path == self._path

    def pathsownable(self):
        return {self._path: Engine.TYPE_FOLDER_ONLY}

    def getclaims(self):
        return []

class MakeSymlink(Helper):
    def __init__(self, target, linkname):
        assert target.startswith('/')
        assert linkname.startswith('/')
        self._target = target
        self._linkname = linkname
        assert self._target != self._linkname

    def getclaims(self):
        return []

    def getcleaner(self):
        return

    def isdone(self):
        return (os.path.islink(self._linkname) and
                os.readlink(self._linkname) == self._target)

    @property
    def description(self):
        return "Create symlink %s -> %s" % (self._linkname, self._target)

    def makechanges(self):
        assert not os.path.exists(self._linkname)
        os.symlink(self._target, self._linkname)

    def affectspath(self, path):
        return path == self._linkname

    def pathsownable(self):
        return {self._linkname: Engine.TYPE_LINK}

class Download(Helper):
    def __init__(self, url, dest, expiry=None):
        assert dest.startswith('/')
        self._url = url
        self._dest = dest
        self._expiry = expiry

    def getclaims(self):
        return []

    def getcleaner(self):
        return

    def isdone(self):
        # TODO: make it so that we retry after the file is <self._expiry>
        # seconds old
        return os.path.exists(self._dest)

    @property
    def description(self):
        return "Download %s to %s" % (self._url, self._dest)

    def makechanges(self):
        import requests
        r = requests.get(self._url)
        if r.status_code != 200:
            raise HelperError("Download of %s failed: %s"
                              % (self._url, r.status_code))
        with open(self._dest, 'wb') as f:
            f.write(r.content)

    def affectspath(self, path):
        return path == self._dest

    def pathsownable(self):
        return {self._dest: Engine.TYPE_FILE_ALL}

WHERE_TOP = "__TOP__"
WHERE_END = "__END__"
WHERE_ANY = "__ANY__"

class LineInFile(Helper):
    def __init__(self, filename, contents, where=None):
        super(LineInFile, self).__init__()
        self._filename = filename
        self._contents = contents
        if not len(contents):
            raise Exception("LineInFile cannot work with empty contents")
        if "\n" in contents or "\r" in contents:
            raise Exception("LineInFile contents cannot include linebreaks")
        if contents.strip() != contents:
            raise Exception(
                "LineInFile contents cannot start or end with whitespace")
        if where is None:
            where = WHERE_ANY
        self._where = where

    @property
    def description(self):
        pos = ""
        if self._where == WHERE_TOP:
            pos = " (at top)"
        elif self._where == WHERE_END:
            pos = " (at end)"
        return "Add line to %s: %r%s" % (self._filename, self._contents, pos)

    def getcleaner(self):
        return CleanLineInFile(self._filename, self._contents)

    def isdone(self):
        if not os.path.exists(self._filename):
            return False
        foundat = []
        linecount = 0
        with open(self._filename) as f:
            for line in f.readlines():
                linecount += 1
                if line.rstrip('\r\n') == self._contents:
                    foundat.append(linecount)
        # if the line appears multiple times, we count it as NOT done
        if len(foundat) != 1:
            return False
        if self._where == WHERE_TOP:
            return foundat[0] == 1
        if self._where == WHERE_END:
            return foundat[0] == linecount
        return True

    def makechanges(self):
        # the content wasn't found in the file, so we'll have to add it
        with filereplacer(self._filename) as (tmp, origlines, NL):
            seen = False
            # if the new line goes at the top, write it out first
            if self._where == WHERE_TOP:
                tmp.write(self._contents)
                tmp.write(NL)
                seen = True
            # read through the original file and look for a line to replace
            if origlines is not None:
                for line in origlines:
                    if line == self._contents:
                        # skip the line if it's already been seen before, or it
                        # needs to move to the end of the file
                        if seen or self._where == WHERE_END:
                            continue
                        seen = True
                    tmp.write(line)
                    tmp.write(NL)
            if not seen:
                assert self._where in (WHERE_END, WHERE_ANY)
                tmp.write(self._contents)
                tmp.write(NL)

    def pathsownable(self):
        return {self._filename: Engine.TYPE_FILE_PART}

    def affectspath(self, path):
        return path == self._filename

    def getclaims(self):
        return []

class CleanLineInFile(Cleaner):
    def __init__(self, filename, contents):
        self._filename = filename
        self._contents = contents

    @property
    def description(self):
        return "Remove line from %s: %r" % (self._filename, self._contents)

    def asdict(self):
        return dict(filename=self._filename, contents=self._contents)

    @classmethod
    def fromdict(class_, data):
        return class_(data["filename"], data["contents"])

    def __eq__(self, other):
        return (other._filename == self._filename and
                other._contents == self._contents)

    def isneeded(self):
        if not os.path.exists(self._filename):
            return False
        with open(self._filename) as f:
            for line in f:
                if line.rstrip("\r\n") == self._contents:
                    return True
        return False

    def wantspath(self, path):
        return path == self._filename or isnecessarypath(path, self._filename)

    def makechanges(self):
        # if the file doesn't exist, we don't need to make changes
        assert os.path.exists(self._filename)

        with filereplacer(self._filename) as (tmp, origlines, NL):
            # if the file doesn't exist any more, then no changes are needed
            changed = False
            if origlines is not None:
                for line in origlines:
                    if line == self._contents:
                        changed = True
                    else:
                        tmp.write(line)
                        tmp.write(NL)
            if not changed:
                raise NoChangesNeeded()

        return [self._filename] if changed else []

    def needsclaims(self):
        return []

class BlockInFile(Helper):
    def __init__(self, filename, lines, prefix, suffix, where=None):
        self._filename = filename
        self._lines = lines
        self._prefix = prefix
        self._suffix = suffix
        self._where = WHERE_ANY if where is None else where

    @property
    def description(self):
        pos = {WHERE_TOP: "top of ", WHERE_END: "end of ", WHERE_ANY: ""}
        return "Add %d lines to %s%s: %r...%r" % (
            2 + len(self._lines),
            pos[self._where],
            self._filename,
            self._prefix,
            self._suffix,
        )

    def getcleaner(self):
        return CleanBlockInFile(self._filename,
                                self._prefix,
                                self._suffix,
                                )

    def isdone(self):
        if not os.path.exists(self._filename):
            return False

        # look to see if our contents appear in the file
        with open(self._filename) as f:
            firstline = True
            count = 0
            expect = None
            prev = None
            for line in f:
                stripped = line.rstrip('\r\n')
                if firstline and self._where == WHERE_TOP:
                    if stripped != self._prefix:
                        return False
                if expect is not None:
                    if stripped != expect.pop(0):
                        return False
                    if len(expect):
                        prev = "INNER"
                    else:
                        prev = "SUFFIX"
                        count += 1
                elif stripped == self._prefix:
                    expect = copy(self._lines)
                    expect.extend(self._suffix)
                    prev = "PREFIX"
                else:
                    prev = "OTHER"
        if self._where == WHERE_END and prev != "SUFFIX":
            return False
        return count == 1

    def makechanges(self):
        with filereplacer(self._filename) as (tmp, origlines, NL):
            findsuffix = False
            found = False

            def _writeall():
                nonlocal found
                found = True
                tmp.write(self._prefix)
                tmp.write(NL)
                for line in self._lines:
                    tmp.write(line)
                    tmp.write(NL)
                tmp.write(self._suffix)
                tmp.write(NL)

            if self._where == WHERE_TOP:
                _writeall()
            if origlines is not None:
                for line in origlines:
                    if findsuffix:
                        # while we're looking for the suffix, discard any other
                        # lines we encounter
                        # encountered into a temporary holding area
                        if line != self._suffix:
                            continue

                        # We've found the suffix! If we're allowed to write the
                        # block out anywhere, and we haven't written it out
                        # yet write it out here, now
                        if self._where == WHERE_ANY and not found:
                            _writeall()
                        findsuffix = False
                        continue

                    if line == self._prefix:
                        findsuffix = True
                        continue

                    tmp.write(line)
                    tmp.write(NL)

            if findsuffix:
                # FIXME: some sort of exception that we can handle nicely
                raise Exception(
                    "Error in %s: Found prefix %r but not suffix %r" % (
                        self._filename,
                        self._prefix,
                        self._suffix,
                    ))

            # if we haven't found the block yet, write it out now
            if not found:
                _writeall()

    def affectspath(self, path):
        return path == self._filename

    def pathsownable(self):
        return {self._filename: Engine.TYPE_FILE_PART}

    def getclaims(self):
        return []

class CleanBlockInFile(Cleaner):
    def __init__(self, filename, prefix, suffix):
        self._filename = filename
        self._prefix = prefix
        self._suffix = suffix

    @property
    def description(self):
        return "Remove lines from %s: %r...%r" % (
            self._filename,
            self._prefix,
            self._suffix,
        )

    def asdict(self):
        return dict(filename=self._filename,
                    prefix=self._prefix,
                    suffix=self._suffix)

    @classmethod
    def fromdict(class_, data):
        return class_(data["filename"], data["prefix"], data["suffix"])

    def __eq__(self, other):
        return (other._filename == self._filename and
                other._prefix == self._prefix and
                other._suffix == self._suffix)

    def isneeded(self):
        # the cleaner is needed if both the prefix and the suffix are found in
        # the file, in the correct order
        if not os.path.exists(self._filename):
            return False

        haveprefix = False

        with open(self._filename) as f:
            for line in [l.rstrip("\r\n") for l in f]:
                if line == self._prefix:
                    haveprefix = True
                elif line == self._suffix and haveprefix:
                    return True

        return False

    def wantspath(self, path):
        return path == self._filename or isnecessarypath(path, self._filename)

    def makechanges(self):
        assert os.path.exists(self._filename)

        with filereplacer(self._filename) as (tmp, origlines, NL):
            changed = False
            findsuffix = False
            for line in origlines:
                if findsuffix:
                    if line == self._suffix:
                        findsuffix = False
                        changed = True
                elif line == self._prefix:
                    findsuffix = True
                else:
                    tmp.write(line)
                    tmp.write(NL)
            if findsuffix or not changed:
                # we couldn't find the suffix ... don't change the file
                raise NoChangesNeeded()

        return [self._filename] if changed else []

    def needsclaims(self):
        return []

class WriteFile(Helper):
    def __init__(self, filename, contents, canoverwrite=False):
        self._filename = filename
        self._contents = contents
        self._canoverwrite = canoverwrite

    @property
    def description(self):
        return "Write file %s" % self._filename

    def getcleaner(self):
        # no cleaner needed
        pass

    def isdone(self):
        if os.path.islink(self._filename):
            return False
        try:
            with open(self._filename, 'r') as f:
                if f.read() == self._contents:
                    return True
        except FileNotFoundError:
            pass
        return False

    def makechanges(self):
        assert not os.path.islink(self._filename)
        with open(self._filename, 'w') as f:
            f.write(self._contents)

    def pathsownable(self):
        return {self._filename: Engine.TYPE_FILE_ALL}

    def affectspath(self, path):
        return path == self._filename

    def getclaims(self):
        return []
<|endoftext|>


<commit_before>from browbeat_run import browbeat_run
from dtree_classifier import classify_value
import numpy
import psycopg2

def insert_values_db(config, uuid, test, osp_name, avg_runtime, grade):
    db_name = config['database']
    user_name = config['user_name']
    host_ip = config['host']
    conn = psycopg2.connect(database=db_name, user=user_name, host=host_ip, port=26257)  # noqa
    conn.set_session(autocommit=True)
    cur = conn.cursor()
    cur.execute("CREATE TABLE IF NOT EXISTS grades (uuid STRING, test STRING, osp_name STRING, avg_runtime FLOAT, grade INT)")  # noqa
    cur.execute("INSERT INTO grades (uuid, test, osp_name, avg_runtime, grade) VALUES (%s, %s, %s, %s, %s)" , (str(uuid), str(test), str(osp_name), float(avg_runtime), int(grade)))  # noqa

def longest_test_name(config):
    val = 0
    for test in config['tests']:
        if len(test['test']) > val:
            val = len(test['test'])
    return val

def time_summary(config, es_backend, time_period, osp_version):
    if osp_version is not None:
        uuids = es_backend.grab_uuids_by_date(osp_version, time_period)
    else:
        uuids = []
        for version in config['watched_versions']:
            uuids.extend(es_backend.grab_uuids_by_date(version, time_period))
    for uuid in uuids:
        val = print_run_details(config, es_backend, uuid)
        if val is not False:
            print(val)

def summary_uuid(es_backend, config, uuid):
    val = print_run_details(config, es_backend, uuid)
    if val is not False:
        print(val)

def data_summary(data):
    std_dev = "{:.4f}".format(numpy.std(data)).ljust(10)
    avg = "{:.4f}".format(numpy.mean(data)).ljust(10)
    median = "{:.4f}".format(numpy.median(data)).ljust(10)
    summary = [avg, std_dev, median]
    return(summary)

# Used for an easy way to look at run data
def print_run_details(config, es_backend, uuid):
    brun = browbeat_run(es_backend, uuid, caching=True)
    output_string = ""
    osp_version = ""
    padding = longest_test_name(config)
    '''
    conn=sqlite3.connect('database.sqlite')
    c=conn.cursor()
    '''
    for test_type in config['tests']:
        test_name = test_type['test']
        data = []
        for test_run in brun.get_tests(test_search=test_name):
            data.extend(test_run.raw)
            osp_version = test_run.version
        statistics_uuid = data_summary(data)
        average_runtime = statistics_uuid[0]
        output_string += test_name.ljust(padding) + \
            " " + str(average_runtime) + " " + str(statistics_uuid[1])
        if float(average_runtime) > 0.0 and test_run.errortype == "result" \
           and test_name != "nova.boot_server" and \
           test_run.cloud_name in config['clouds'] and \
           'pipeline' not in test_run.dlrn_hash and \
           'trunk' not in test_run.dlrn_hash:  # noqa
            if str(test_name) in config['test_with_scenario_list']:
                test_name = str(test_run.scenario_name) + "." + str(test_name)
            output_prediction = classify_value(config, average_runtime, test_name, osp_version)  # noqa
            insert_values_db(uuid, test_name, osp_version, average_runtime, output_prediction)  # noqa
            if str(output_prediction[0]) == "1":
                print("ALERT!!!!")
                print(uuid, test_name, osp_version, average_runtime)
                exit(1)
            output_string = output_string + str(output_prediction) + "\n"
        else:
            output_string += "\n"
    '''
    conn.commit()
    conn.close()
    '''
    header = ("Browbeat UUID: " + uuid + " OSP_version: " + osp_version + "\n")
    header += ("".ljust(80, "-")) + "\n"
    output_string = header + output_string
    return output_string
<commit_msg>added config<commit_after>from browbeat_run import browbeat_run
from dtree_classifier import classify_value
import numpy
import psycopg2

def insert_values_db(config, uuid, test, osp_name, avg_runtime, grade):
    db_name = config['database']
    user_name = config['user_name']
    host_ip = config['host']
    conn = psycopg2.connect(database=db_name, user=user_name, host=host_ip, port=26257)  # noqa
    conn.set_session(autocommit=True)
    cur = conn.cursor()
    cur.execute("CREATE TABLE IF NOT EXISTS grades (uuid STRING, test STRING, osp_name STRING, avg_runtime FLOAT, grade INT)")  # noqa
    cur.execute("INSERT INTO grades (uuid, test, osp_name, avg_runtime, grade) VALUES (%s, %s, %s, %s, %s)" , (str(uuid), str(test), str(osp_name), float(avg_runtime), int(grade)))  # noqa

def longest_test_name(config):
    val = 0
    for test in config['tests']:
        if len(test['test']) > val:
            val = len(test['test'])
    return val

def time_summary(config, es_backend, time_period, osp_version):
    if osp_version is not None:
        uuids = es_backend.grab_uuids_by_date(osp_version, time_period)
    else:
        uuids = []
        for version in config['watched_versions']:
            uuids.extend(es_backend.grab_uuids_by_date(version, time_period))
    for uuid in uuids:
        val = print_run_details(config, es_backend, uuid)
        if val is not False:
            print(val)

def summary_uuid(es_backend, config, uuid):
    val = print_run_details(config, es_backend, uuid)
    if val is not False:
        print(val)

def data_summary(data):
    std_dev = "{:.4f}".format(numpy.std(data)).ljust(10)
    avg = "{:.4f}".format(numpy.mean(data)).ljust(10)
    median = "{:.4f}".format(numpy.median(data)).ljust(10)
    summary = [avg, std_dev, median]
    return(summary)

# Used for an easy way to look at run data
def print_run_details(config, es_backend, uuid):
    brun = browbeat_run(es_backend, uuid, caching=True)
    output_string = ""
    osp_version = ""
    padding = longest_test_name(config)
    '''
    conn=sqlite3.connect('database.sqlite')
    c=conn.cursor()
    '''
    for test_type in config['tests']:
        test_name = test_type['test']
        data = []
        for test_run in brun.get_tests(test_search=test_name):
            data.extend(test_run.raw)
            osp_version = test_run.version
        statistics_uuid = data_summary(data)
        average_runtime = statistics_uuid[0]
        output_string += test_name.ljust(padding) + \
            " " + str(average_runtime) + " " + str(statistics_uuid[1])
        if float(average_runtime) > 0.0 and test_run.errortype == "result" \
           and test_name != "nova.boot_server" and \
           test_run.cloud_name in config['clouds'] and \
           'pipeline' not in test_run.dlrn_hash and \
           'trunk' not in test_run.dlrn_hash:  # noqa
            if str(test_name) in config['test_with_scenario_list']:
                test_name = str(test_run.scenario_name) + "." + str(test_name)
            output_prediction = classify_value(config, average_runtime, test_name, osp_version)  # noqa
            insert_values_db(config, uuid, test_name, osp_version, average_runtime, output_prediction)  # noqa
            if str(output_prediction[0]) == "1":
                print("ALERT!!!!")
                print(uuid, test_name, osp_version, average_runtime)
                exit(1)
            output_string = output_string + str(output_prediction) + "\n"
        else:
            output_string += "\n"
    '''
    conn.commit()
    conn.close()
    '''
    header = ("Browbeat UUID: " + uuid + " OSP_version: " + osp_version + "\n")
    header += ("".ljust(80, "-")) + "\n"
    output_string = header + output_string
    return output_string
<|endoftext|>


<commit_before># -*- encoding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
#    $Id$
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import pooler
import tools

_uid_cache = {}

def login(db, login, password):
    cr = pooler.get_db(db).cursor()
    if password:
        cr.execute('select id from res_users where login=%s and password=%s and active', (tools.ustr(login), tools.ustr(password)))
    else:
        cr.execute('select id from res_users where login=%s and password is null and active', (tools.ustr(login),))
    res = cr.fetchone()
    cr.close()
    if res:
        return res[0]
    else:
        return False

def check_super(passwd):
    if passwd == tools.config['admin_passwd']:
        return True
    else:
        raise Exception('AccessDenied')

def check(db, uid, passwd):
    if _uid_cache.get(db, {}).get(uid) == passwd:
        return True
    cr = pooler.get_db(db).cursor()
    if passwd:
        cr.execute('select count(*) from res_users where id=%s and password=%s', (int(uid), passwd))
    else:
        cr.execute('select count(*) from res_users where id=%s and password is null', (int(uid),))
    res = cr.fetchone()[0]
    cr.close()
    if not bool(res):
        raise Exception('AccessDenied')
    if res:
        if _uid_cache.has_key(db):
            ulist = _uid_cache[db]
            ulist[uid] = passwd
        else:
            _uid_cache[db] = {uid:passwd}
    return bool(res)

def access(db, uid, passwd, sec_level, ids):
    cr = pooler.get_db(db).cursor()
    if passwd:
        cr.execute('select id from res_users where id=%s and password=%s', (uid, passwd))
    else:
        cr.execute('select id from res_users where id=%s and password is null', (uid,))
    res = cr.fetchone()
    cr.close()
    if not res:
        raise Exception('Bad username or password')
    return res[0]

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<commit_msg>[FIX] security issue: avoid access to inactive users [FIX] security issue: avoid access with 'None' password (Thanks to P. Christeas for the bug report)<commit_after># -*- encoding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
#    $Id$
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import pooler
import tools

_uid_cache = {}

def login(db, login, password):
    cr = pooler.get_db(db).cursor()
    if password:
        cr.execute('select id from res_users where login=%s and password=%s and active', (tools.ustr(login), tools.ustr(password)))
    else:
        cr.execute('select id from res_users where login=%s and password is null and active', (tools.ustr(login),))
    res = cr.fetchone()
    cr.close()
    if res:
        return res[0]
    else:
        return False

def check_super(passwd):
    if passwd == tools.config['admin_passwd']:
        return True
    else:
        raise Exception('AccessDenied')

def check(db, uid, passwd):
    cached_pass = _uid_cache.get(db, {}).get(uid)
    if (cached_pass is not None) and cached_pass == passwd:
        return True
    cr = pooler.get_db(db).cursor()
    if passwd:
        cr.execute('select count(1) from res_users where id=%s and password=%s and active=%s', (int(uid), passwd, True))
    else:
        cr.execute('select count(1) from res_users where id=%s and password is null and active=%s', (int(uid), True))
    res = cr.fetchone()[0]
    cr.close()
    if not bool(res):
        raise Exception('AccessDenied')
    if res:
        if _uid_cache.has_key(db):
            ulist = _uid_cache[db]
            ulist[uid] = passwd
        else:
            _uid_cache[db] = {uid:passwd}
    return bool(res)

def access(db, uid, passwd, sec_level, ids):
    cr = pooler.get_db(db).cursor()
    if passwd:
        cr.execute('select id from res_users where id=%s and password=%s', (uid, passwd))
    else:
        cr.execute('select id from res_users where id=%s and password is null', (uid,))
    res = cr.fetchone()
    cr.close()
    if not res:
        raise Exception('Bad username or password')
    return res[0]

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<|endoftext|>


<commit_before># -*-coding:Utf-8 -*

# Copyright (c) 2015 LE GOFF Vincent
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the copyright holder nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""Fichier contenant la classe CommandeDynamique détaillée plus bas."""

from abstraits.obase import BaseObj
from primaires.format.description import Description
from primaires.interpreteur.commande.commande import Commande
from primaires.interpreteur.masque.parametre import Parametre
from primaires.scripting.script import Script
from primaires.scripting import types

class CommandeDynamique(BaseObj):

    """Classe décrivant les commandes dynamiques du crafting.

    Il existe deux types de commandes dynamiques : celles définies
    dans le scripting, qui font systématiquement référence à
    des éléments observables, et celles définies dans le crafting,
    qui sont plus configurables et permettent presque autant de
    liberté que les commandes standard.

    Ces dernières sont intégralement scriptables et permettent
    de configurer un véritable schéma, ce qui rend leur puissance
    encore plus importante. Le schéma est généralement convertit
    en paramètres scripting, ce qui permet une plus grande
    flexibilité.

    """

    nom_scripting = "la commande dynamique"

    def __init__(self, guilde, parent, nom_francais, nom_anglais):
        """Constructeur d'une commande dynamique."""
        BaseObj.__init__(self)
        self.guilde = guilde
        self.parent = parent
        self.nom_francais = nom_francais
        self.nom_anglais = nom_anglais
        self.commande = None # commande statique liée
        self._utilisable = False
        self.doit_etre_membre = True
        self._groupe = "pnj"
        self._nom_categorie = "divers"
        self._aide_courte = "à renseigner..."
        self.aide_longue = Description(parent=self, scriptable=False,
                callback="maj")
        self._schema = ""
        self.etats = {}
        self.script = ScriptCommande(self)
        self._construire()

    def __getnewargs__(self):
        return (None, "", "", "")

    def __repr__(self):
        parent = "{}:".format(self.parent) if self.parent else ""
        return "<CommandeDynamique '{}{}/{}'>".format(
                parent, self.nom_francais, self.nom_anglais)

    def __str__(self):
        parent = "{}:".format(self.parent) if self.parent else ""
        return parent + self.nom_francais + "/" + self.nom_anglais

    def __getstate__(self):
        dico_attr = self.__dict__.copy()
        if "commande" in dico_attr:
            del dico_attr["commande"]

        return dico_attr

    @property
    def nom_complet(self):
        """Retourne le nom complet (<parent>:)<fr>/<an>."""
        parent = "{}:".format(self.parent) if self.parent else ""
        return parent + self.nom_francais + "/" + self.nom_anglais

    @property
    def nom_francais_complet(self):
        """Retourne le nom français complet (parent:nom_francais)."""
        parent = "{}:".format(self.parent) if self.parent else ""
        return parent + self.nom_francais

    def _get_aide_courte(self):
        return self._aide_courte
    def _set_aide_courte(self, aide):
        if len(aide) > 70:
            aide = aide[:70]
        self._aide_courte = aide
        self.maj()
    aide_courte = property(_get_aide_courte, _set_aide_courte)

    def _get_nom_categorie(self):
        return self._nom_categorie
    def _set_nom_categorie(self, categorie):
        self._nom_categorie = categorie
        self.maj()
    nom_categorie = property(_get_nom_categorie, _set_nom_categorie)

    def _get_groupe(self):
        return self._groupe
    def _set_groupe(self, groupe):
        self._groupe = groupe
        if self.commande:
            importeur.interpreteur.groupes.changer_groupe_commande(
                    self.commande.adresse, groupe)
    groupe = property(_get_groupe, _set_groupe)

    def _get_utilisable(self):
        return self._utilisable
    def _set_utilisable(self, utilisable):
        self._utilisable = utilisable
        if utilisable and self.commande is None:
            self.ajouter()
    utilisable = property(_get_utilisable, _set_utilisable)

    def _get_schema(self):
        return self._schema
    def _set_schema(self, schema):
        self._schema = schema
        if self.commande:
            self.commande.noeud.construire_arborescence(schema)
            self.maj_variables()
    schema = property(_get_schema, _set_schema)

    def maj_variables(self):
        """Met à jour les variables en fonction du schéma."""
        # Supprime les variables scripting
        evt = self.script["exécute"]
        for nom in list(evt.variables.keys()):
            if nom not in ("personnage", "commande"):
                evt.supprimer_variable(nom)

        noeud = self.commande.noeud
        masques = noeud.extraire_masques()

        # Parcourt des masques
        for masque in masques.values():
            n_type = getattr(type(masque), "nom", "")
            if n_type == "message":
                var = evt.ajouter_variable(masque.nom, "str")
                var.aide = "le message entré"
            elif n_type == "texte_libre":
                nom = masque.nom
                if nom == "texte_libre":
                    nom = "texte"

                var = evt.ajouter_variable(nom, "str")
                var.aide = "le texte libre entré"
            elif n_type == "nom_joueur":
                nom = masque.nom
                if nom == "nom_joueur":
                    nom = "joueur"

                var = evt.ajouter_variable(nom, "Personnage")
                var.aide = "le joueur dont le nom a été entré"
            elif n_type == "nombre":
                var = evt.ajouter_variable(masque.nom, "Fraction")
                var.aide = "le nombre entré"
            elif n_type in ("objet_equipe", "objet_inventaire", "objet_sol"):
                nom = masque.nom
                if nom in ("objet_equipe", "objet_inventaire", "objet_sol"):
                    nom = "objets"

                var = evt.ajouter_variable(nom, "list")
                var.aide = "la liste des objets entrée"

    def ajouter(self):
        """Ajoute la commande dans l'interpréteur.

        Il est préférable de faire cela après l'insertion des commandes
        statiques dans l'interpréteur, c'est-à-dire durant la phase de
        préparation du module.

        Si la commande a un parent, on va créer à la place son
        paramètre.

        """
        parent = None
        if self.parent:
            parent = importeur.interpreteur.trouver_commande(self.parent)
            commande = Parametre(self.nom_francais, self.nom_anglais)
        else:
            commande = Commande(self.nom_francais, self.nom_anglais)

        commande.schema = self.schema
        commande.groupe = self._groupe
        commande.nom_categorie = self.nom_categorie
        commande.aide_courte = self.aide_courte
        commande.aide_longue = str(self.aide_longue)
        commande.peut_executer = self.peut_executer
        commande.interpreter = self.interpreter
        if parent:
            parent.ajouter_parametre(commande)
        else:
            importeur.interpreteur.ajouter_commande(commande)

        self.commande = commande
        return commande

    def peut_executer(self, personnage):
        """Retourne True si le personnage peut ecuter la commande."""
        if self.doit_etre_membre:
            guilde = self.guilde
            if personnage not in guilde.membres:
                return False

        return True

    def interpreter(self, personnage, dic_masques):
        """Méthode outre-passant l'interprétation de la commande statique.

        On exécute le script de la commande, évènement 'exécute'.
        Les masques interprétés se retrouvent en partie dans les
        variables du script.

        """
        variables = {}

        # On parcourt tous les masques
        for masque in dic_masques.values():
            n_type = getattr(type(masque), "nom", "")
            if n_type == "message":
                variables[masque.nom] = masque.message
            elif n_type == "texte_libre":
                nom = masque.nom
                if nom == "texte_libre":
                    nom = "texte"

                variables[nom] = masque.texte
            elif n_type == "nom_joueur":
                nom = masque.nom
                if nom == "nom_joueur":
                    nom = "joueur"

                variables[nom] = masque.joueur
            elif n_type == "nombre":
                variables[masque.nom] = masque.nombre
            elif n_type in ("objet_equipe", "objet_inventaire", "objet_sol"):
                nom = masque.nom
                if nom in ("objet_equipe", "objet_inventaire", "objet_sol"):
                    nom = "objets"

                variables[nom] = masque.objets

        evenement = self.script["exécute"]
        for nom in evenement.variables:
            if nom in ("personnage", "commande"):
                continue

            if nom not in variables:
                variables[nom] = None

        evenement.executer(personnage=personnage, commande=self,
                **variables)

    def maj(self):
        """Mise à jour de la commande dynamique."""
        if self.commande:
            self.commande.nom_categorie = self.nom_categorie
            self.commande.aide_courte = self._aide_courte
            self.commande.aide_longue = str(self.aide_longue)

        # Ajout des états
        for cle, couple in self.etats.items():
            refus = couple[0]
            visible = couple[1]
            actions = couple[2]
            if cle in importeur.perso.etats:
                etat = importeur.perso.etats[cle]
            else:
                etat = importeur.perso.ajouter_etat(cle)

            etat.msg_refus = refus.capitalize()
            if not etat.msg_refus.endswith((".", "?", "!")):
                etat.msg_refus += "."

            etat.msg_visible = visible.lower().strip(" .?!")
            etat.act_autorisees = actions.split(" ")

class ScriptCommande(Script):

    """Script et évènements propres aux commandes dynamiques."""

    def init(self):
        """Initialisation du script"""
        # Événement exécute
        evt_execute = self.creer_evenement("exécute")
        evt_execute.aide_courte = "un personnage exécute la commande"
        evt_execute.aide_longue = \
            "Cet évènement est appelé quand un personnage exécute " \
            "la commande. Il doit en avoir le droit (être membre " \
            "de la guilde si la commande est limitée aux membres, " \
            "par exemple). Le schéma de la commande est interprété " \
            "et disponible grâce à des variables, listées ci-dessous."

        # Configuration des variables de l'évènement exécute
        var_perso = evt_execute.ajouter_variable("personnage", "Personnage")
        var_perso.aide = "le personnage exécutant la commande"
        var_commande = evt_execute.ajouter_variable("commande", "Commande")
        var_commande.aide = "la commande-même (utile pour les blocs)"

types.Commande = CommandeDynamique
<commit_msg>Ajout du masque <cle> aux commandes dynamiques<commit_after># -*-coding:Utf-8 -*

# Copyright (c) 2015 LE GOFF Vincent
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the copyright holder nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""Fichier contenant la classe CommandeDynamique détaillée plus bas."""

from abstraits.obase import BaseObj
from primaires.format.description import Description
from primaires.interpreteur.commande.commande import Commande
from primaires.interpreteur.masque.parametre import Parametre
from primaires.scripting.script import Script
from primaires.scripting import types

class CommandeDynamique(BaseObj):

    """Classe décrivant les commandes dynamiques du crafting.

    Il existe deux types de commandes dynamiques : celles définies
    dans le scripting, qui font systématiquement référence à
    des éléments observables, et celles définies dans le crafting,
    qui sont plus configurables et permettent presque autant de
    liberté que les commandes standard.

    Ces dernières sont intégralement scriptables et permettent
    de configurer un véritable schéma, ce qui rend leur puissance
    encore plus importante. Le schéma est généralement convertit
    en paramètres scripting, ce qui permet une plus grande
    flexibilité.

    """

    nom_scripting = "la commande dynamique"

    def __init__(self, guilde, parent, nom_francais, nom_anglais):
        """Constructeur d'une commande dynamique."""
        BaseObj.__init__(self)
        self.guilde = guilde
        self.parent = parent
        self.nom_francais = nom_francais
        self.nom_anglais = nom_anglais
        self.commande = None # commande statique liée
        self._utilisable = False
        self.doit_etre_membre = True
        self._groupe = "pnj"
        self._nom_categorie = "divers"
        self._aide_courte = "à renseigner..."
        self.aide_longue = Description(parent=self, scriptable=False,
                callback="maj")
        self._schema = ""
        self.etats = {}
        self.script = ScriptCommande(self)
        self._construire()

    def __getnewargs__(self):
        return (None, "", "", "")

    def __repr__(self):
        parent = "{}:".format(self.parent) if self.parent else ""
        return "<CommandeDynamique '{}{}/{}'>".format(
                parent, self.nom_francais, self.nom_anglais)

    def __str__(self):
        parent = "{}:".format(self.parent) if self.parent else ""
        return parent + self.nom_francais + "/" + self.nom_anglais

    def __getstate__(self):
        dico_attr = self.__dict__.copy()
        if "commande" in dico_attr:
            del dico_attr["commande"]

        return dico_attr

    @property
    def nom_complet(self):
        """Retourne le nom complet (<parent>:)<fr>/<an>."""
        parent = "{}:".format(self.parent) if self.parent else ""
        return parent + self.nom_francais + "/" + self.nom_anglais

    @property
    def nom_francais_complet(self):
        """Retourne le nom français complet (parent:nom_francais)."""
        parent = "{}:".format(self.parent) if self.parent else ""
        return parent + self.nom_francais

    def _get_aide_courte(self):
        return self._aide_courte
    def _set_aide_courte(self, aide):
        if len(aide) > 70:
            aide = aide[:70]
        self._aide_courte = aide
        self.maj()
    aide_courte = property(_get_aide_courte, _set_aide_courte)

    def _get_nom_categorie(self):
        return self._nom_categorie
    def _set_nom_categorie(self, categorie):
        self._nom_categorie = categorie
        self.maj()
    nom_categorie = property(_get_nom_categorie, _set_nom_categorie)

    def _get_groupe(self):
        return self._groupe
    def _set_groupe(self, groupe):
        self._groupe = groupe
        if self.commande:
            importeur.interpreteur.groupes.changer_groupe_commande(
                    self.commande.adresse, groupe)
    groupe = property(_get_groupe, _set_groupe)

    def _get_utilisable(self):
        return self._utilisable
    def _set_utilisable(self, utilisable):
        self._utilisable = utilisable
        if utilisable and self.commande is None:
            self.ajouter()
    utilisable = property(_get_utilisable, _set_utilisable)

    def _get_schema(self):
        return self._schema
    def _set_schema(self, schema):
        self._schema = schema
        if self.commande:
            self.commande.noeud.construire_arborescence(schema)
            self.maj_variables()
    schema = property(_get_schema, _set_schema)

    def maj_variables(self):
        """Met à jour les variables en fonction du schéma."""
        # Supprime les variables scripting
        evt = self.script["exécute"]
        for nom in list(evt.variables.keys()):
            if nom not in ("personnage", "commande"):
                evt.supprimer_variable(nom)

        noeud = self.commande.noeud
        masques = noeud.extraire_masques()

        # Parcourt des masques
        for masque in masques.values():
            n_type = getattr(type(masque), "nom", "")
            if n_type == "message":
                var = evt.ajouter_variable(masque.nom, "str")
                var.aide = "le message entré"
            elif n_type == "texte_libre":
                nom = masque.nom
                if nom == "texte_libre":
                    nom = "texte"

                var = evt.ajouter_variable(nom, "str")
                var.aide = "le texte libre entré"
            elif n_type == "nom_joueur":
                nom = masque.nom
                if nom == "nom_joueur":
                    nom = "joueur"

                var = evt.ajouter_variable(nom, "Personnage")
                var.aide = "le joueur dont le nom a été entré"
            elif n_type == "cle":
                var = evt.ajouter_variable(masque.nom, "str")
                var.aide = "la clé entrée (une chaîne)"
            elif n_type == "nombre":
                var = evt.ajouter_variable(masque.nom, "Fraction")
                var.aide = "le nombre entré"
            elif n_type in ("objet_equipe", "objet_inventaire", "objet_sol"):
                nom = masque.nom
                if nom in ("objet_equipe", "objet_inventaire", "objet_sol"):
                    nom = "objets"

                var = evt.ajouter_variable(nom, "list")
                var.aide = "la liste des objets entrée"

    def ajouter(self):
        """Ajoute la commande dans l'interpréteur.

        Il est préférable de faire cela après l'insertion des commandes
        statiques dans l'interpréteur, c'est-à-dire durant la phase de
        préparation du module.

        Si la commande a un parent, on va créer à la place son
        paramètre.

        """
        parent = None
        if self.parent:
            parent = importeur.interpreteur.trouver_commande(self.parent)
            commande = Parametre(self.nom_francais, self.nom_anglais)
        else:
            commande = Commande(self.nom_francais, self.nom_anglais)

        commande.schema = self.schema
        commande.groupe = self._groupe
        commande.nom_categorie = self.nom_categorie
        commande.aide_courte = self.aide_courte
        commande.aide_longue = str(self.aide_longue)
        commande.peut_executer = self.peut_executer
        commande.interpreter = self.interpreter
        if parent:
            parent.ajouter_parametre(commande)
        else:
            importeur.interpreteur.ajouter_commande(commande)

        self.commande = commande
        return commande

    def peut_executer(self, personnage):
        """Retourne True si le personnage peut ecuter la commande."""
        if self.doit_etre_membre:
            guilde = self.guilde
            if personnage not in guilde.membres:
                return False

        return True

    def interpreter(self, personnage, dic_masques):
        """Méthode outre-passant l'interprétation de la commande statique.

        On exécute le script de la commande, évènement 'exécute'.
        Les masques interprétés se retrouvent en partie dans les
        variables du script.

        """
        variables = {}

        # On parcourt tous les masques
        for masque in dic_masques.values():
            n_type = getattr(type(masque), "nom", "")
            if n_type == "message":
                variables[masque.nom] = masque.message
            elif n_type == "texte_libre":
                nom = masque.nom
                if nom == "texte_libre":
                    nom = "texte"

                variables[nom] = masque.texte
            elif n_type == "nom_joueur":
                nom = masque.nom
                if nom == "nom_joueur":
                    nom = "joueur"

                variables[nom] = masque.joueur
            elif n_type == "cle":
                variables[masque.nom] = masque.cle
            elif n_type == "nombre":
                variables[masque.nom] = masque.nombre
            elif n_type in ("objet_equipe", "objet_inventaire", "objet_sol"):
                nom = masque.nom
                if nom in ("objet_equipe", "objet_inventaire", "objet_sol"):
                    nom = "objets"

                variables[nom] = masque.objets

        evenement = self.script["exécute"]
        for nom in evenement.variables:
            if nom in ("personnage", "commande"):
                continue

            if nom not in variables:
                variables[nom] = None

        evenement.executer(personnage=personnage, commande=self,
                **variables)

    def maj(self):
        """Mise à jour de la commande dynamique."""
        if self.commande:
            self.commande.nom_categorie = self.nom_categorie
            self.commande.aide_courte = self._aide_courte
            self.commande.aide_longue = str(self.aide_longue)

        # Ajout des états
        for cle, couple in self.etats.items():
            refus = couple[0]
            visible = couple[1]
            actions = couple[2]
            if cle in importeur.perso.etats:
                etat = importeur.perso.etats[cle]
            else:
                etat = importeur.perso.ajouter_etat(cle)

            etat.msg_refus = refus.capitalize()
            if not etat.msg_refus.endswith((".", "?", "!")):
                etat.msg_refus += "."

            etat.msg_visible = visible.lower().strip(" .?!")
            etat.act_autorisees = actions.split(" ")

class ScriptCommande(Script):

    """Script et évènements propres aux commandes dynamiques."""

    def init(self):
        """Initialisation du script"""
        # Événement exécute
        evt_execute = self.creer_evenement("exécute")
        evt_execute.aide_courte = "un personnage exécute la commande"
        evt_execute.aide_longue = \
            "Cet évènement est appelé quand un personnage exécute " \
            "la commande. Il doit en avoir le droit (être membre " \
            "de la guilde si la commande est limitée aux membres, " \
            "par exemple). Le schéma de la commande est interprété " \
            "et disponible grâce à des variables, listées ci-dessous."

        # Configuration des variables de l'évènement exécute
        var_perso = evt_execute.ajouter_variable("personnage", "Personnage")
        var_perso.aide = "le personnage exécutant la commande"
        var_commande = evt_execute.ajouter_variable("commande", "Commande")
        var_commande.aide = "la commande-même (utile pour les blocs)"

types.Commande = CommandeDynamique
<|endoftext|>


<commit_before># boink/tests/utils.py
# Copyright (C) 2018 Camille Scott
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the MIT license.  See the LICENSE file for details.

import pytest

from debruijnal_enhance_o_tron.fixtures.subgraphs import *
from debruijnal_enhance_o_tron.fixtures.sequence import *

from boink.dbg import get_dbg_type

@pytest.fixture(params=['BitStorage', 'ByteStorage', 'NibbleStorage'])
def graph_type(request, ksize):

    class BoinkAdapter(get_dbg_type(storage=request.param)):
        '''Basic adapter for boink dBG.
        '''

        def __init__(self, ksize, starting_size, n_tables):
            self.ksize = ksize
            self.size = starting_size
            self.n_tables = n_tables
            self.storage = request.param
            pass

        def __new__(cls, ksize, starting_size, n_tables, *args, **kwargs):
            return super().__new__(cls, ksize, starting_size, n_tables,
                                   *args, **kwargs)

        def add(self, item):
            if not isinstance(item, int) and len(item) < self.ksize:
                raise ValueError()
            elif isinstance(item, int) or len(item) == self.ksize:
                return super().add(item)
            else:
                return self.add_sequence(item)

        def __repr__(self):
            return '<BoinkAdapter[{0}] K={1} X={2} N={3}>'.format(self.storage,
                                                                  self.ksize,
                                                                  self.size,
                                                                  self.n_tables)

    return BoinkAdapter

@pytest.fixture
def graph(ksize, graph_type):
    return graph_type(ksize, 1000000, 4)

@pytest.fixture(params=[50000, 500000, 50000000],
                ids=['small', 'medium', 'large'])
def capacity_args(request):
    return optimal_fp(request.param, 0.0001)

@pytest.fixture
def fastx_writer(tmpdir):

    def write(sequences):
        filepath = tmpdir.join('tmp.fasta')
        with open(filepath, 'w') as fp:
            for n, sequence in enumerate(sequences):
                fp.write('>{0}\n{1}\n'.format(n, sequence))
        return filepath

    return write
<commit_msg>Have BoinkAdapter keep params as attrs, return type<commit_after># boink/tests/utils.py
# Copyright (C) 2018 Camille Scott
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the MIT license.  See the LICENSE file for details.

import pytest

from debruijnal_enhance_o_tron.fixtures.subgraphs import *
from debruijnal_enhance_o_tron.fixtures.sequence import *

from boink.dbg import get_dbg_type

@pytest.fixture(params=['BitStorage', 'ByteStorage', 'NibbleStorage'])
def graph_type(request, ksize):

    _graph_type = get_dbg_type(storage=request.param)

    class BoinkAdapter(_graph_type):
        '''Basic adapter for boink dBG.
        '''

        def __init__(self, ksize, starting_size, n_tables):
            self.ksize = ksize
            self.size = starting_size
            self.n_tables = n_tables
            self.storage = request.param
            pass

        def __new__(cls, ksize, starting_size, n_tables, *args, **kwargs):
            return super().__new__(cls, ksize, starting_size, n_tables,
                                   *args, **kwargs)

        def add(self, item):
            if not isinstance(item, int) and len(item) < self.ksize:
                raise ValueError()
            elif isinstance(item, int) or len(item) == self.ksize:
                return super().add(item)
            else:
                return self.add_sequence(item)

        def __repr__(self):
            return '<BoinkAdapter[{0}] K={1} X={2} N={3}>'.format(self.storage,
                                                                  self.ksize,
                                                                  self.size,
                                                                  self.n_tables)

    return _graph_type, BoinkAdapter

@pytest.fixture
def graph(ksize, graph_type):
    _graph_type, Adapter = graph_type
    return Adapter(ksize, 1000000, 4)

@pytest.fixture(params=[50000, 500000, 50000000],
                ids=['small', 'medium', 'large'])
def capacity_args(request):
    return optimal_fp(request.param, 0.0001)

@pytest.fixture
def fastx_writer(tmpdir):

    def write(sequences):
        filepath = tmpdir.join('tmp.fasta')
        with open(filepath, 'w') as fp:
            for n, sequence in enumerate(sequences):
                fp.write('>{0}\n{1}\n'.format(n, sequence))
        return filepath

    return write
<|endoftext|>


<commit_before>
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
# Copyright 2013 Red Hat, 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.

import inspect

from oslo.config import cfg

from openstack.common.gettextutils import _
from openstack.common import local
from openstack.common import log as logging
from openstack.common.messaging import target
from openstack.common.messaging import _utils as utils

_client_opts = [
    cfg.IntOpt('rpc_response_timeout',
               default=60,
               help='Seconds to wait for a response from a call'),
]

_LOG = logging.getLogger(__name__)

class RpcVersionCapError(Exception):

    def __init__(self, version, version_cap):
        self.version = version
        self.version_cap = version_cap

    def __str__(self):
        return (_("Specified RPC version cap, %(version_cap)s, is too low. "
                  "Needs to be higher than %(version)s.") %
                dict(version=self.version, version_cap=self.version_cap))

class _CallContext(object):

    def __init__(self, transport, target,
                 timeout=None, check_for_lock=None, version_cap=None):
        self.conf = transport.conf

        self.transport = transport
        self.target = target
        self.timeout = timeout
        self.check_for_lock = check_for_lock
        self.version_cap = version_cap

        super(_CallContext, self).__init__()

    def _make_message(self, method, args):
        msg = dict(method=method, args=args)
        if self.target.namespace is not None:
            msg['namespace'] = self.target.namespace
        if self.target.version is not None:
            msg['version'] = self.target.version
        return msg

    def cast(self, ctxt, method, **kwargs):
        msg = self._make_message(method, kwargs)
        self.transport._send(target, ctxt, msg)

    def _check_for_lock(self):
        if not self.conf.debug:
            return None

        if (hasattr(local.strong_store, 'locks_held') and
            local.strong_store.locks_held):
            stack = ' :: '.join([frame[3] for frame in inspect.stack()])
            _LOG.warn(_('An RPC is being made while holding a lock. The locks '
                        'currently held are %(locks)s. This is probably a bug. '
                        'Please report it. Include the following: [%(stack)s].'),
                      {'locks': local.strong_store.locks_held, 'stack': stack})

    def _check_version_cap(self, version):
        if not utils.version_is_compatible(self.version_cap, v):
            raise RpcVersionCapError(version=version,
                                     version_cap=self.version_cap)

    def call(self, ctxt, method, **kwargs):
        msg = self._make_message(method, kwargs)

        timeout = self.timeout
        if self.timeout is None:
            timeout = self.conf.rpc_response_timeout

        if self.check_for_lock:
            self._check_for_lock()
        if self.version_cap:
            self._check_version_cap(msg.get('version'))

        return self.transport._send(self.target, ctxt, msg,
                                    wait_for_reply=True, timeout=timeout)

class RPCClient(object):

    def __init__(self, transport, target,
                 timeout=None, check_for_lock=None, version_cap=None):
        self.conf = transport.conf
        self.conf.register_opts(_client_opts)

        self.transport = transport
        self.target = target
        self.timeout = timeout
        self.check_for_lock = check_for_lock
        self.version_cap = version_cap

        super(RPCClient, self).__init__()

    def prepare(self, server=None, version=None,
                timeout=None, check_for_lock=None, version_cap=None):
        target = self.target(server=server, version=version)

        if timeout is None:
            timeout = self.timeout
        if check_for_lock is None:
            check_for_lock = self.check_for_lock
        if version_cap is None:
            version_cap = self.version_cap

        return _CallContext(self.transport, target,
                            timeout, check_for_lock, version_cap)

    def cast(self, ctxt, method, **kwargs):
        self.prepare().cast(ctxt, method, **kwargs)

    def call(self, ctxt, method, **kwargs):
        return self.prepare().call(ctxt, method, **kwargs)
<commit_msg>Support namespace in RPCClient.prepare()<commit_after>
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
# Copyright 2013 Red Hat, 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.

import inspect

from oslo.config import cfg

from openstack.common.gettextutils import _
from openstack.common import local
from openstack.common import log as logging
from openstack.common.messaging import target
from openstack.common.messaging import _utils as utils

_client_opts = [
    cfg.IntOpt('rpc_response_timeout',
               default=60,
               help='Seconds to wait for a response from a call'),
]

_LOG = logging.getLogger(__name__)

class RpcVersionCapError(Exception):

    def __init__(self, version, version_cap):
        self.version = version
        self.version_cap = version_cap

    def __str__(self):
        return (_("Specified RPC version cap, %(version_cap)s, is too low. "
                  "Needs to be higher than %(version)s.") %
                dict(version=self.version, version_cap=self.version_cap))

class _CallContext(object):

    def __init__(self, transport, target,
                 timeout=None, check_for_lock=None, version_cap=None):
        self.conf = transport.conf

        self.transport = transport
        self.target = target
        self.timeout = timeout
        self.check_for_lock = check_for_lock
        self.version_cap = version_cap

        super(_CallContext, self).__init__()

    def _make_message(self, method, args):
        msg = dict(method=method, args=args)
        if self.target.namespace is not None:
            msg['namespace'] = self.target.namespace
        if self.target.version is not None:
            msg['version'] = self.target.version
        return msg

    def cast(self, ctxt, method, **kwargs):
        msg = self._make_message(method, kwargs)
        self.transport._send(target, ctxt, msg)

    def _check_for_lock(self):
        if not self.conf.debug:
            return None

        if (hasattr(local.strong_store, 'locks_held') and
            local.strong_store.locks_held):
            stack = ' :: '.join([frame[3] for frame in inspect.stack()])
            _LOG.warn(_('An RPC is being made while holding a lock. The locks '
                        'currently held are %(locks)s. This is probably a bug. '
                        'Please report it. Include the following: [%(stack)s].'),
                      {'locks': local.strong_store.locks_held, 'stack': stack})

    def _check_version_cap(self, version):
        if not utils.version_is_compatible(self.version_cap, v):
            raise RpcVersionCapError(version=version,
                                     version_cap=self.version_cap)

    def call(self, ctxt, method, **kwargs):
        msg = self._make_message(method, kwargs)

        timeout = self.timeout
        if self.timeout is None:
            timeout = self.conf.rpc_response_timeout

        if self.check_for_lock:
            self._check_for_lock()
        if self.version_cap:
            self._check_version_cap(msg.get('version'))

        return self.transport._send(self.target, ctxt, msg,
                                    wait_for_reply=True, timeout=timeout)

class RPCClient(object):

    def __init__(self, transport, target,
                 timeout=None, check_for_lock=None, version_cap=None):
        self.conf = transport.conf
        self.conf.register_opts(_client_opts)

        self.transport = transport
        self.target = target
        self.timeout = timeout
        self.check_for_lock = check_for_lock
        self.version_cap = version_cap

        super(RPCClient, self).__init__()

    def prepare(self, namespace=None, version=None, server=None,
                timeout=None, check_for_lock=None, version_cap=None):
        target = self.target(namespace=namespace,
                             version=version,
                             server=server)

        if timeout is None:
            timeout = self.timeout
        if check_for_lock is None:
            check_for_lock = self.check_for_lock
        if version_cap is None:
            version_cap = self.version_cap

        return _CallContext(self.transport, target,
                            timeout, check_for_lock, version_cap)

    def cast(self, ctxt, method, **kwargs):
        self.prepare().cast(ctxt, method, **kwargs)

    def call(self, ctxt, method, **kwargs):
        return self.prepare().call(ctxt, method, **kwargs)
<|endoftext|>


<commit_before># -*- encoding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
#    $Id$
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import pooler
import tools

_uid_cache = {}

def login(db, login, password):
    cr = pooler.get_db(db).cursor()
    if password:
        cr.execute('select id from res_users where login=%s and password=%s and active', (login.encode('utf-8'), password.encode('utf-8')))
    else:
        cr.execute('select id from res_users where login=%s and password is null and active', (login.encode('utf-8'),))
    res = cr.fetchone()
    cr.close()
    if res:
        return res[0]
    else:
        return False

def check_super(passwd):
    if passwd == tools.config['admin_passwd']:
        return True
    else:
        raise Exception('AccessDenied')

def check(db, uid, passwd):
    if _uid_cache.get(db, {}).get(uid) == passwd:
        return True
    cr = pooler.get_db(db).cursor()
    if passwd:
        cr.execute('select count(*) from res_users where id=%s and password=%s', (int(uid), passwd))
    else:
        cr.execute('select count(*) from res_users where id=%s and password is null', (int(uid),))
    res = cr.fetchone()[0]
    cr.close()
    if not bool(res):
        raise Exception('AccessDenied')
    if res:
        if _uid_cache.has_key(db):
            ulist = _uid_cache[db]
            ulist[uid] = passwd
        else:
            _uid_cache[db] = {uid:passwd}
    return bool(res)

def access(db, uid, passwd, sec_level, ids):
    cr = pooler.get_db(db).cursor()
    if passwd:
        cr.execute('select id from res_users where id=%s and password=%s', (uid, passwd))
    else:
        cr.execute('select id from res_users where id=%s and password is null', (uid,))
    res = cr.fetchone()
    cr.close()
    if not res:
        raise Exception('Bad username or password')
    return res[0]

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<commit_msg>[FIX] Login : Login and password accepting accented characters<commit_after># -*- encoding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
#    $Id$
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import pooler
import tools

_uid_cache = {}

def login(db, login, password):
    cr = pooler.get_db(db).cursor()
    if password:
        cr.execute('select id from res_users where login=%s and password=%s and active', (tools.ustr(login), tools.ustr(password)))
    else:
        cr.execute('select id from res_users where login=%s and password is null and active', (tools.ustr(login),))
    res = cr.fetchone()
    cr.close()
    if res:
        return res[0]
    else:
        return False

def check_super(passwd):
    if passwd == tools.config['admin_passwd']:
        return True
    else:
        raise Exception('AccessDenied')

def check(db, uid, passwd):
    if _uid_cache.get(db, {}).get(uid) == passwd:
        return True
    cr = pooler.get_db(db).cursor()
    if passwd:
        cr.execute('select count(*) from res_users where id=%s and password=%s', (int(uid), passwd))
    else:
        cr.execute('select count(*) from res_users where id=%s and password is null', (int(uid),))
    res = cr.fetchone()[0]
    cr.close()
    if not bool(res):
        raise Exception('AccessDenied')
    if res:
        if _uid_cache.has_key(db):
            ulist = _uid_cache[db]
            ulist[uid] = passwd
        else:
            _uid_cache[db] = {uid:passwd}
    return bool(res)

def access(db, uid, passwd, sec_level, ids):
    cr = pooler.get_db(db).cursor()
    if passwd:
        cr.execute('select id from res_users where id=%s and password=%s', (uid, passwd))
    else:
        cr.execute('select id from res_users where id=%s and password is null', (uid,))
    res = cr.fetchone()
    cr.close()
    if not res:
        raise Exception('Bad username or password')
    return res[0]

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<|endoftext|>