<commit_before>import os
from os.path import join as pjoin, normpath, exists as pexists, dirname
import subprocess
from shutil import rmtree, move as shmove
import re
from zipfile import ZipFile

BUILD_MSI = False
SRC_ROOT = normpath(pjoin(os.getcwd(), os.pardir, os.pardir, os.pardir))
BUILD_ROOT = os.getcwd()

PYVER = '2.5'
ARCH = 'nosse'

PYEXECS = {"2.5" : "C:\python25\python.exe",
        "2.4" : "C:\python24\python24.exe",
        "2.3" : "C:\python23\python23.exe"}

_SSE3_CFG = r"""[atlas]
library_dirs = C:\local\lib\yop\sse3"""
_SSE2_CFG = r"""[atlas]
library_dirs = C:\local\lib\yop\sse2"""
_NOSSE_CFG = r"""[atlas]
library_dirs = fakedirectorywhichhopefullydoesnotexist
[DEFAULT]
library_dirs = C:\local\lib\yop\nosse"""

SITECFG = {"sse2" : _SSE2_CFG, "sse3" : _SSE3_CFG, "nosse" : _NOSSE_CFG}

options(
    clean=Bunch(
        src_dir = SRC_ROOT,
        pyver = PYVER
    ),
    clean_bootstrap=Bunch(
        src_dir = SRC_ROOT,
        pyver = PYVER
    ),
    build_sdist=Bunch(
        src_dir = SRC_ROOT
    ),
    build_binary=Bunch(
        pyver = PYVER,
        arch = ARCH,
        src_root = SRC_ROOT
    ),
    bootstrap=Bunch(
        pyver = PYVER,
        src_root = SRC_ROOT
    ),
    bootstrap_arch=Bunch(
        pyver = PYVER,
        arch = ARCH
    ),
    bootstrap_nsis=Bunch(
        pyver = PYVER,
        src_root = SRC_ROOT
    )
)

# Clean everything, including bootstrap source tree
@task
def clean():
    raw_clean(options.src_dir, options.pyver)

# Clean the bootstrap source tree for a clean build from scratch
@task
def clean_bootstrap():
    bdir = bootstrap_dir(options.pyver)
    for d in ["build", "dist"]:
        if pexists(pjoin(bdir, d)):
            rmtree(pjoin(bdir, d))

    if pexists(pjoin(bdir, "site.cfg")):
        os.remove(pjoin(bdir, "site.cfg"))

@task
def build_sdist():
    cmd = ["python", "setup.py", "sdist", "--format=zip"]
    st = subprocess.call(cmd, cwd=options.src_dir)

@task
@needs('build_sdist')
def bootstrap():
    bdir = bootstrap_dir(options.pyver)
    prepare_scipy_sources(options.src_dir, bdir)

@task
def bootstrap_arch():
    pyver = options.pyver
    arch = options.arch
    set_bootstrap_sources(arch, pyver)

@task
def bootstrap_nsis():
    pyver = options.pyver
    bdir = bootstrap_dir(options.pyver)
    prepare_nsis_script(bdir, pyver, get_scipy_version(options.src_root))

@task
def build_binary():
    pyver = options.pyver
    arch = options.arch
    scipy_verstr = get_scipy_version(options.src_root)
    bdir = bootstrap_dir(pyver)
    print "Building scipy (version %s) binary for python %s, arch is %s" % \
          (scipy_verstr, get_python_exec(pyver), arch)

    if BUILD_MSI:
        cmd = [get_python_exec(pyver), "setup.py", "build", "-c", "mingw32",
               "bdist_msi"]
    else:
        cmd = [get_python_exec(pyver), "setup.py", "build", "-c", "mingw32",
               "bdist_wininst"]
    build_log = "build-%s-%s.log" % (arch, pyver)
    f = open(build_log, 'w')

    try:
        try:
            st = subprocess.call(cmd, #shell = True,
                            stderr = subprocess.STDOUT, stdout = f,
                            cwd=bdir)
            if st:
                raise RuntimeError("The cmd failed with status %d" % st)
        finally:
            f.close()
    except (subprocess.CalledProcessError, RuntimeError), e:
        print e
        msg = """
There was an error while executing the following command:

    %s

Error was : %s

Look at the build log (%s).""" % (cmd, str(e), build_log)
        raise Exception(msg)

    move_binary(arch, pyver, bdir, scipy_verstr)

@task
@needs('build_binary')
@needs('bootstrap_arch')
@needs('bootstrap_nsis')
@needs('bootstrap')
@needs('clean')
def build_all():
    pass
# Helpers
def set_bootstrap_sources(arch, pyver):
    bdir = bootstrap_dir(pyver)
    write_site_cfg(arch, cwd=bdir)

def get_sdist_tarball(src_root):
    """Return the name of the installer built by sdist command."""
    # Yeah, the name logic is harcoded in distutils. We have to reproduce it
    # here
    name = "scipy-%s.zip" % get_scipy_version(src_root)
    return name

def prepare_scipy_sources(src_root, bootstrap):
    zid = ZipFile(pjoin(src_root, 'dist', get_sdist_tarball(src_root)))
    root = 'scipy-%s' % get_scipy_version(src_root)

    # From the sdist-built tarball, extract all files into bootstrap directory,
    # but removing the scipy-VERSION head path
    for name in zid.namelist():
        cnt = zid.read(name)
        if name.startswith(root):
            # XXX: even on windows, the path sep in zip is '/' ?
            name = name.split('/', 1)[1]
        newname = pjoin(bootstrap, name)

        if not pexists(dirname(newname)):
            os.makedirs(dirname(newname))
        fid = open(newname, 'wb')
        fid.write(cnt)

def prepare_nsis_script(bdir, pyver, numver):
    tpl = pjoin('nsis_scripts', 'scipy-superinstaller.nsi.in')
    source = open(tpl, 'r')
    target = open(pjoin(bdir, 'scipy-superinstaller.nsi'), 'w')

    installer_name = 'scipy-%s-win32-superpack-python%s.exe' % (numver, pyver)
    cnt = "".join(source.readlines())
    cnt = cnt.replace('@SCIPY_INSTALLER_NAME@', installer_name)
    for arch in ['nosse', 'sse2', 'sse3']:
        cnt = cnt.replace('@%s_BINARY@' % arch.upper(),
                          get_binary_name(arch, numver))

    target.write(cnt)

def bootstrap_dir(pyver):
    return pjoin(BUILD_ROOT, "bootstrap-%s" % pyver)

def get_scipy_version(src_root):
    version_file = pjoin(src_root, "scipy", "version.py")
    if not pexists(version_file):
        raise IOError("file %s not found" % version_file)

    fid = open(version_file, "r")
    vregex = re.compile("version\s*=\s*'(\d+)\.(\d+)\.(\d+)'")
    isrelregex = re.compile("release\s*=\s*True")
    isdevregex = re.compile("release\s*=\s*False")
    isdev = None
    version = None
    for line in fid.readlines():
        m = vregex.match(line)
        if m:
            version = [int(i) for i in m.groups()]
        if isrelregex.match(line):
            if isdev is None:
                isdev = False
            else:
                raise RuntimeError("isdev already set ?")
        if isdevregex.match(line):
            if isdev is None:
                isdev = True
            else:
                raise RuntimeError("isdev already set ?")

    verstr = ".".join([str(i) for i in version])
    if isdev:
        verstr += ".dev"
        verstr += get_svn_version(src_root)
    return verstr

def get_svn_version(chdir):
    out = subprocess.Popen(['svn', 'info'],
                           stdout = subprocess.PIPE,
                           cwd = chdir).communicate()[0]
    r = re.compile('Revision: ([0-9]+)')
    svnver = None
    for line in out.split('\n'):
        m = r.match(line)
        if m:
            svnver = m.group(1)

    if not svnver:
        raise ValueError("Error while parsing svn version ?")

    return svnver

def get_python_exec(ver):
    """Return the executable of python for the given version."""
    # XXX Check that the file actually exists
    try:
        return PYEXECS[ver]
    except KeyError:
        raise ValueError("Version %s not supported/recognized" % ver)

def write_site_cfg(arch, cwd=None):
    if not cwd:
        cwd = os.getcwd()

    scfg = pjoin(cwd, "site.cfg")
    if pexists(scfg):
        os.remove(scfg)
    f = open(scfg, 'w')
    f.writelines(SITECFG[arch])
    f.close()

def move_binary(arch, pyver, cwd, scipy_verstr):
    if not pexists(pjoin(cwd, "binaries")):
        os.makedirs(pjoin(cwd, "binaries"))

    shmove(pjoin(cwd, 'dist', get_windist_exec(pyver, scipy_verstr)),
           pjoin(cwd, 'binaries', get_binary_name(arch, scipy_verstr)))

def get_binary_name(arch, scipy_verstr):
    if BUILD_MSI:
        ext = '.msi'
    else:
        ext = '.exe'
    return "scipy-%s-%s%s" % (scipy_verstr, arch, ext)

def get_windist_exec(pyver, scipy_verstr):
    """Return the name of the installer built by wininst command."""
    # Yeah, the name logic is harcoded in distutils. We have to reproduce it
    # here
    if BUILD_MSI:
        ext = '.msi'
    else:
        ext = '.exe'
    name = "scipy-%s.win32-py%s%s" % (scipy_verstr, pyver, ext)
    return name

def raw_clean(src_dir, pyver):
    # Clean sdist
    sdir = pjoin(src_dir, "dist")
    if pexists(sdir):
        rmtree(sdir)
    mani = pjoin(src_dir, "MANIFEST")
    if pexists(mani):
        os.remove(mani)

    # Clean bootstrap directory
    bdir = bootstrap_dir(pyver)
    if pexists(bdir):
        rmtree(bdir)
<commit_msg>Put clean_bootstrap action into separate function.<commit_after>import os
from os.path import join as pjoin, normpath, exists as pexists, dirname
import subprocess
from shutil import rmtree, move as shmove
import re
from zipfile import ZipFile

BUILD_MSI = False
SRC_ROOT = normpath(pjoin(os.getcwd(), os.pardir, os.pardir, os.pardir))
BUILD_ROOT = os.getcwd()

PYVER = '2.5'
ARCH = 'nosse'

PYEXECS = {"2.5" : "C:\python25\python.exe",
        "2.4" : "C:\python24\python24.exe",
        "2.3" : "C:\python23\python23.exe"}

_SSE3_CFG = r"""[atlas]
library_dirs = C:\local\lib\yop\sse3"""
_SSE2_CFG = r"""[atlas]
library_dirs = C:\local\lib\yop\sse2"""
_NOSSE_CFG = r"""[atlas]
library_dirs = fakedirectorywhichhopefullydoesnotexist
[DEFAULT]
library_dirs = C:\local\lib\yop\nosse"""

SITECFG = {"sse2" : _SSE2_CFG, "sse3" : _SSE3_CFG, "nosse" : _NOSSE_CFG}

options(
    clean=Bunch(
        src_dir = SRC_ROOT,
        pyver = PYVER
    ),
    clean_bootstrap=Bunch(
        src_dir = SRC_ROOT,
        pyver = PYVER
    ),
    build_sdist=Bunch(
        src_dir = SRC_ROOT
    ),
    build_binary=Bunch(
        pyver = PYVER,
        arch = ARCH,
        src_root = SRC_ROOT
    ),
    bootstrap=Bunch(
        pyver = PYVER,
        src_root = SRC_ROOT
    ),
    bootstrap_arch=Bunch(
        pyver = PYVER,
        arch = ARCH
    ),
    bootstrap_nsis=Bunch(
        pyver = PYVER,
        src_root = SRC_ROOT
    )
)

# Clean everything, including bootstrap source tree
@task
def clean():
    raw_clean(options.src_dir, options.pyver)

# Clean the bootstrap source tree for a clean build from scratch
@task
def clean_bootstrap():
    raw_clean_bootstrap(options.pyver)

@task
def build_sdist():
    cmd = ["python", "setup.py", "sdist", "--format=zip"]
    st = subprocess.call(cmd, cwd=options.src_dir)

@task
@needs('build_sdist')
def bootstrap():
    bdir = bootstrap_dir(options.pyver)
    prepare_scipy_sources(options.src_dir, bdir)

@task
def bootstrap_arch():
    pyver = options.pyver
    arch = options.arch
    set_bootstrap_sources(arch, pyver)

@task
def bootstrap_nsis():
    pyver = options.pyver
    bdir = bootstrap_dir(options.pyver)
    prepare_nsis_script(bdir, pyver, get_scipy_version(options.src_root))

@task
def build_binary():
    pyver = options.pyver
    arch = options.arch
    scipy_verstr = get_scipy_version(options.src_root)
    bdir = bootstrap_dir(pyver)
    print "Building scipy (version %s) binary for python %s, arch is %s" % \
          (scipy_verstr, get_python_exec(pyver), arch)

    if BUILD_MSI:
        cmd = [get_python_exec(pyver), "setup.py", "build", "-c", "mingw32",
               "bdist_msi"]
    else:
        cmd = [get_python_exec(pyver), "setup.py", "build", "-c", "mingw32",
               "bdist_wininst"]
    build_log = "build-%s-%s.log" % (arch, pyver)
    f = open(build_log, 'w')

    try:
        try:
            st = subprocess.call(cmd, #shell = True,
                            stderr = subprocess.STDOUT, stdout = f,
                            cwd=bdir)
            if st:
                raise RuntimeError("The cmd failed with status %d" % st)
        finally:
            f.close()
    except (subprocess.CalledProcessError, RuntimeError), e:
        print e
        msg = """
There was an error while executing the following command:

    %s

Error was : %s

Look at the build log (%s).""" % (cmd, str(e), build_log)
        raise Exception(msg)

    move_binary(arch, pyver, bdir, scipy_verstr)

@task
@needs('build_binary')
@needs('bootstrap_arch')
@needs('bootstrap_nsis')
@needs('bootstrap')
@needs('clean')
def build_all():
    pass
# Helpers
def set_bootstrap_sources(arch, pyver):
    bdir = bootstrap_dir(pyver)
    write_site_cfg(arch, cwd=bdir)

def get_sdist_tarball(src_root):
    """Return the name of the installer built by sdist command."""
    # Yeah, the name logic is harcoded in distutils. We have to reproduce it
    # here
    name = "scipy-%s.zip" % get_scipy_version(src_root)
    return name

def prepare_scipy_sources(src_root, bootstrap):
    zid = ZipFile(pjoin(src_root, 'dist', get_sdist_tarball(src_root)))
    root = 'scipy-%s' % get_scipy_version(src_root)

    # From the sdist-built tarball, extract all files into bootstrap directory,
    # but removing the scipy-VERSION head path
    for name in zid.namelist():
        cnt = zid.read(name)
        if name.startswith(root):
            # XXX: even on windows, the path sep in zip is '/' ?
            name = name.split('/', 1)[1]
        newname = pjoin(bootstrap, name)

        if not pexists(dirname(newname)):
            os.makedirs(dirname(newname))
        fid = open(newname, 'wb')
        fid.write(cnt)

def prepare_nsis_script(bdir, pyver, numver):
    tpl = pjoin('nsis_scripts', 'scipy-superinstaller.nsi.in')
    source = open(tpl, 'r')
    target = open(pjoin(bdir, 'scipy-superinstaller.nsi'), 'w')

    installer_name = 'scipy-%s-win32-superpack-python%s.exe' % (numver, pyver)
    cnt = "".join(source.readlines())
    cnt = cnt.replace('@SCIPY_INSTALLER_NAME@', installer_name)
    for arch in ['nosse', 'sse2', 'sse3']:
        cnt = cnt.replace('@%s_BINARY@' % arch.upper(),
                          get_binary_name(arch, numver))

    target.write(cnt)

def bootstrap_dir(pyver):
    return pjoin(BUILD_ROOT, "bootstrap-%s" % pyver)

def get_scipy_version(src_root):
    version_file = pjoin(src_root, "scipy", "version.py")
    if not pexists(version_file):
        raise IOError("file %s not found" % version_file)

    fid = open(version_file, "r")
    vregex = re.compile("version\s*=\s*'(\d+)\.(\d+)\.(\d+)'")
    isrelregex = re.compile("release\s*=\s*True")
    isdevregex = re.compile("release\s*=\s*False")
    isdev = None
    version = None
    for line in fid.readlines():
        m = vregex.match(line)
        if m:
            version = [int(i) for i in m.groups()]
        if isrelregex.match(line):
            if isdev is None:
                isdev = False
            else:
                raise RuntimeError("isdev already set ?")
        if isdevregex.match(line):
            if isdev is None:
                isdev = True
            else:
                raise RuntimeError("isdev already set ?")

    verstr = ".".join([str(i) for i in version])
    if isdev:
        verstr += ".dev"
        verstr += get_svn_version(src_root)
    return verstr

def get_svn_version(chdir):
    out = subprocess.Popen(['svn', 'info'],
                           stdout = subprocess.PIPE,
                           cwd = chdir).communicate()[0]
    r = re.compile('Revision: ([0-9]+)')
    svnver = None
    for line in out.split('\n'):
        m = r.match(line)
        if m:
            svnver = m.group(1)

    if not svnver:
        raise ValueError("Error while parsing svn version ?")

    return svnver

def get_python_exec(ver):
    """Return the executable of python for the given version."""
    # XXX Check that the file actually exists
    try:
        return PYEXECS[ver]
    except KeyError:
        raise ValueError("Version %s not supported/recognized" % ver)

def write_site_cfg(arch, cwd=None):
    if not cwd:
        cwd = os.getcwd()

    scfg = pjoin(cwd, "site.cfg")
    if pexists(scfg):
        os.remove(scfg)
    f = open(scfg, 'w')
    f.writelines(SITECFG[arch])
    f.close()

def move_binary(arch, pyver, cwd, scipy_verstr):
    if not pexists(pjoin(cwd, "binaries")):
        os.makedirs(pjoin(cwd, "binaries"))

    shmove(pjoin(cwd, 'dist', get_windist_exec(pyver, scipy_verstr)),
           pjoin(cwd, 'binaries', get_binary_name(arch, scipy_verstr)))

def get_binary_name(arch, scipy_verstr):
    if BUILD_MSI:
        ext = '.msi'
    else:
        ext = '.exe'
    return "scipy-%s-%s%s" % (scipy_verstr, arch, ext)

def get_windist_exec(pyver, scipy_verstr):
    """Return the name of the installer built by wininst command."""
    # Yeah, the name logic is harcoded in distutils. We have to reproduce it
    # here
    if BUILD_MSI:
        ext = '.msi'
    else:
        ext = '.exe'
    name = "scipy-%s.win32-py%s%s" % (scipy_verstr, pyver, ext)
    return name

def raw_clean(src_dir, pyver):
    # Clean sdist
    sdir = pjoin(src_dir, "dist")
    if pexists(sdir):
        rmtree(sdir)
    mani = pjoin(src_dir, "MANIFEST")
    if pexists(mani):
        os.remove(mani)

    # Clean bootstrap directory
    bdir = bootstrap_dir(pyver)
    if pexists(bdir):
        rmtree(bdir)

def raw_clean_bootstrap(pyver):
    bdir = bootstrap_dir(pyver)
    for d in ["build", "dist"]:
        if pexists(pjoin(bdir, d)):
            rmtree(pjoin(bdir, d))

    if pexists(pjoin(bdir, "site.cfg")):
        os.remove(pjoin(bdir, "site.cfg"))

<|endoftext|>


<commit_before>import os
from os.path import join as pjoin, normpath, exists as pexists, dirname
import subprocess
from shutil import rmtree, move as shmove
import re
from zipfile import ZipFile

BUILD_MSI = False
SRC_ROOT = normpath(pjoin(os.getcwd(), os.pardir, os.pardir, os.pardir))
BUILD_ROOT = os.getcwd()

PYVER = '2.5'
ARCH = 'nosse'

PYEXECS = {"2.5" : "C:\python25\python.exe",
        "2.4" : "C:\python24\python24.exe",
        "2.3" : "C:\python23\python23.exe"}

_SSE3_CFG = r"""[atlas]
library_dirs = C:\local\lib\yop\sse3"""
_SSE2_CFG = r"""[atlas]
library_dirs = C:\local\lib\yop\sse2"""
_NOSSE_CFG = r"""[atlas]
library_dirs = fakedirectorywhichhopefullydoesnotexist
[DEFAULT]
library_dirs = C:\local\lib\yop\nosse"""

SITECFG = {"sse2" : _SSE2_CFG, "sse3" : _SSE3_CFG, "nosse" : _NOSSE_CFG}

options(
    clean=Bunch(
        src_dir = SRC_ROOT,
        pyver = PYVER
    ),
    clean_bootstrap=Bunch(
        src_dir = SRC_ROOT,
        pyver = PYVER
    ),
    build_sdist=Bunch(
        src_dir = SRC_ROOT
    ),
    build_binary=Bunch(
        pyver = PYVER,
        arch = ARCH,
        src_root = SRC_ROOT
    ),
    bootstrap=Bunch(
        pyver = PYVER,
        src_root = SRC_ROOT
    ),
    bootstrap_arch=Bunch(
        pyver = PYVER,
        arch = ARCH
    )
)

# Clean everything, including bootstrap source tree
@task
def clean():
    # Clean sdist
    sdir = pjoin(options.src_dir, "dist")
    if pexists(sdir):
        rmtree(sdir)
    mani = pjoin(options.src_dir, "MANIFEST")
    if pexists(mani):
        os.remove(mani)

    # Clean bootstrap directory
    bdir = bootstrap_dir(options.pyver)
    if pexists(bdir):
        rmtree(bdir)

# Clean the bootstrap source tree for a clean build from scratch
@task
def clean_bootstrap():
    bdir = bootstrap_dir(options.pyver)
    for d in ["build", "dist"]:
        if pexists(pjoin(bdir, d)):
            rmtree(pjoin(bdir, d))

    if pexists(pjoin(bdir, "site.cfg")):
        os.remove(pjoin(bdir, "site.cfg"))

@task
def build_sdist():
    cmd = ["python", "setup.py", "sdist", "--format=zip"]
    st = subprocess.call(cmd, cwd=options.src_dir)

@task
@needs('build_sdist')
def bootstrap():
    bdir = bootstrap_dir(options.pyver)
    prepare_scipy_sources(options.src_dir, bdir)
    prepare_nsis_script(bootstrap, pyver, get_scipy_version(options.src_root))

@task
def bootstrap_arch():
    pyver = options.pyver
    arch = options.arch
    set_bootstrap_sources(arch, pyver)

@task
@needs('bootstrap_arch')
def build_binary():
    pyver = options.pyver
    arch = options.arch
    scipy_verstr = get_scipy_version(options.src_root)
    bdir = bootstrap_dir(pyver)
    print "Building scipy (version %s) binary for python %s, arch is %s" % \
          (scipy_verstr, get_python_exec(pyver), arch)

    if BUILD_MSI:
        cmd = [get_python_exec(pyver), "setup.py", "build", "-c", "mingw32",
               "bdist_msi"]
    else:
        cmd = [get_python_exec(pyver), "setup.py", "build", "-c", "mingw32",
               "bdist_wininst"]
    build_log = "build-%s-%s.log" % (arch, pyver)
    f = open(build_log, 'w')

    try:
        try:
            subprocess.call(cmd, #shell = True,
                            stderr = subprocess.STDOUT, stdout = f,
                            cwd=bdir)
        finally:
            f.close()
    except subprocess.CalledProcessError, e:
        msg = """
There was an error while executing the following command:

    %s

Error was : %s

Look at the build log (%s).""" % (cmd, str(e), build_log)
        raise Exception(msg)

    move_binary(arch, pyver, bdir, scipy_verstr)

# Helpers
def set_bootstrap_sources(arch, pyver):
    bdir = bootstrap_dir(pyver)
    write_site_cfg(arch, cwd=bdir)

def get_sdist_tarball(src_root):
    """Return the name of the installer built by sdist command."""
    # Yeah, the name logic is harcoded in distutils. We have to reproduce it
    # here
    name = "scipy-%s.zip" % get_scipy_version(src_root)
    return name

def prepare_scipy_sources(src_root, bootstrap):
    zid = ZipFile(pjoin(src_root, 'dist', get_sdist_tarball(src_root)))
    root = 'scipy-%s' % get_scipy_version(src_root)

    # From the sdist-built tarball, extract all files into bootstrap directory,
    # but removing the scipy-VERSION head path
    for name in zid.namelist():
        cnt = zid.read(name)
        if name.startswith(root):
            # XXX: even on windows, the path sep in zip is '/' ?
            name = name.split('/', 1)[1]
        newname = pjoin(bootstrap, name)

        if not pexists(dirname(newname)):
            os.makedirs(dirname(newname))
        fid = open(newname, 'wb')
        fid.write(cnt)

def prepare_nsis_script(bootstrap, pyver, numver):
    tpl = pjoin('nsis_scripts', 'scipy-superinstaller.nsi.in')
    source = open(tpl, 'r')
    target = open(pjoin(bootstrap, 'scipy-superinstaller.nsi'), 'w')

    installer_name = 'scipy-%s-win32-superpack-python%s.exe' % (numver, pyver)
    cnt = "".join(source.readlines())
    cnt = cnt.replace('@SCIPY_INSTALLER_NAME@', installer_name)
    for arch in ['nosse', 'sse2', 'sse3']:
        cnt = cnt.replace('@%s_BINARY@' % arch.upper(),
                          get_binary_name(arch))

    target.write(cnt)

def bootstrap_dir(pyver):
    return pjoin(BUILD_ROOT, "bootstrap-%s" % pyver)

def get_scipy_version(src_root):
    version_file = pjoin(src_root, "scipy", "version.py")
    if not pexists(version_file):
        raise IOError("file %s not found" % version_file)

    fid = open(version_file, "r")
    vregex = re.compile("version\s*=\s*'(\d+)\.(\d+)\.(\d+)'")
    isrelregex = re.compile("release\s*=\s*True")
    isdevregex = re.compile("release\s*=\s*False")
    isdev = None
    version = None
    for line in fid.readlines():
        m = vregex.match(line)
        if m:
            version = [int(i) for i in m.groups()]
        if isrelregex.match(line):
            if isdev is None:
                isdev = False
            else:
                raise RuntimeError("isdev already set ?")
        if isdevregex.match(line):
            if isdev is None:
                isdev = True
            else:
                raise RuntimeError("isdev already set ?")

    verstr = ".".join([str(i) for i in version])
    if isdev:
        verstr += ".dev"
        verstr += get_svn_version(src_root)
    return verstr

def get_svn_version(chdir):
    out = subprocess.Popen(['svn', 'info'],
                           stdout = subprocess.PIPE,
                           cwd = chdir).communicate()[0]
    r = re.compile('Revision: ([0-9]+)')
    svnver = None
    for line in out.split('\n'):
        m = r.match(line)
        if m:
            svnver = m.group(1)

    if not svnver:
        raise ValueError("Error while parsing svn version ?")

    return svnver

def get_python_exec(ver):
    """Return the executable of python for the given version."""
    # XXX Check that the file actually exists
    try:
        return PYEXECS[ver]
    except KeyError:
        raise ValueError("Version %s not supported/recognized" % ver)

def write_site_cfg(arch, cwd=None):
    if not cwd:
        cwd = os.getcwd()

    scfg = pjoin(cwd, "site.cfg")
    if pexists(scfg):
        os.remove(scfg)
    f = open(scfg, 'w')
    f.writelines(SITECFG[arch])
    f.close()

def move_binary(arch, pyver, cwd, scipy_verstr):
    if not pexists(pjoin(cwd, "binaries")):
        os.makedirs(pjoin(cwd, "binaries"))

    shmove(pjoin(cwd, 'dist', get_windist_exec(pyver, scipy_verstr)),
           pjoin(cwd, 'binaries', get_binary_name(arch, scipy_verstr)))

def get_binary_name(arch, scipy_verstr):
    if BUILD_MSI:
        ext = '.msi'
    else:
        ext = '.exe'
    return "scipy-%s-%s%s" % (scipy_verstr, arch, ext)

def get_windist_exec(pyver, scipy_verstr):
    """Return the name of the installer built by wininst command."""
    # Yeah, the name logic is harcoded in distutils. We have to reproduce it
    # here
    if BUILD_MSI:
        ext = '.msi'
    else:
        ext = '.exe'
    name = "scipy-%s.win32-py%s%s" % (scipy_verstr, pyver, ext)
    return name
<commit_msg>Fix nsis script generation.<commit_after>import os
from os.path import join as pjoin, normpath, exists as pexists, dirname
import subprocess
from shutil import rmtree, move as shmove
import re
from zipfile import ZipFile

BUILD_MSI = False
SRC_ROOT = normpath(pjoin(os.getcwd(), os.pardir, os.pardir, os.pardir))
BUILD_ROOT = os.getcwd()

PYVER = '2.5'
ARCH = 'nosse'

PYEXECS = {"2.5" : "C:\python25\python.exe",
        "2.4" : "C:\python24\python24.exe",
        "2.3" : "C:\python23\python23.exe"}

_SSE3_CFG = r"""[atlas]
library_dirs = C:\local\lib\yop\sse3"""
_SSE2_CFG = r"""[atlas]
library_dirs = C:\local\lib\yop\sse2"""
_NOSSE_CFG = r"""[atlas]
library_dirs = fakedirectorywhichhopefullydoesnotexist
[DEFAULT]
library_dirs = C:\local\lib\yop\nosse"""

SITECFG = {"sse2" : _SSE2_CFG, "sse3" : _SSE3_CFG, "nosse" : _NOSSE_CFG}

options(
    clean=Bunch(
        src_dir = SRC_ROOT,
        pyver = PYVER
    ),
    clean_bootstrap=Bunch(
        src_dir = SRC_ROOT,
        pyver = PYVER
    ),
    build_sdist=Bunch(
        src_dir = SRC_ROOT
    ),
    build_binary=Bunch(
        pyver = PYVER,
        arch = ARCH,
        src_root = SRC_ROOT
    ),
    bootstrap=Bunch(
        pyver = PYVER,
        src_root = SRC_ROOT
    ),
    bootstrap_arch=Bunch(
        pyver = PYVER,
        arch = ARCH
    ),
    bootstrap_nsis=Bunch(
        pyver = PYVER,
        src_root = SRC_ROOT
    )
)

# Clean everything, including bootstrap source tree
@task
def clean():
    # Clean sdist
    sdir = pjoin(options.src_dir, "dist")
    if pexists(sdir):
        rmtree(sdir)
    mani = pjoin(options.src_dir, "MANIFEST")
    if pexists(mani):
        os.remove(mani)

    # Clean bootstrap directory
    bdir = bootstrap_dir(options.pyver)
    if pexists(bdir):
        rmtree(bdir)

# Clean the bootstrap source tree for a clean build from scratch
@task
def clean_bootstrap():
    bdir = bootstrap_dir(options.pyver)
    for d in ["build", "dist"]:
        if pexists(pjoin(bdir, d)):
            rmtree(pjoin(bdir, d))

    if pexists(pjoin(bdir, "site.cfg")):
        os.remove(pjoin(bdir, "site.cfg"))

@task
def build_sdist():
    cmd = ["python", "setup.py", "sdist", "--format=zip"]
    st = subprocess.call(cmd, cwd=options.src_dir)

@task
@needs('build_sdist')
def bootstrap():
    bdir = bootstrap_dir(options.pyver)
    prepare_scipy_sources(options.src_dir, bdir)

@task
def bootstrap_arch():
    pyver = options.pyver
    arch = options.arch
    set_bootstrap_sources(arch, pyver)

@task
def bootstrap_nsis():
    pyver = options.pyver
    bdir = bootstrap_dir(options.pyver)
    prepare_nsis_script(bdir, pyver, get_scipy_version(options.src_root))

@task
@needs('bootstrap_arch')
@needs('bootstrap_nsis')
@needs('clean_bootstrap')
def build_binary():
    pyver = options.pyver
    arch = options.arch
    scipy_verstr = get_scipy_version(options.src_root)
    bdir = bootstrap_dir(pyver)
    print "Building scipy (version %s) binary for python %s, arch is %s" % \
          (scipy_verstr, get_python_exec(pyver), arch)

    if BUILD_MSI:
        cmd = [get_python_exec(pyver), "setup.py", "build", "-c", "mingw32",
               "bdist_msi"]
    else:
        cmd = [get_python_exec(pyver), "setup.py", "build", "-c", "mingw32",
               "bdist_wininst"]
    build_log = "build-%s-%s.log" % (arch, pyver)
    f = open(build_log, 'w')

    try:
        try:
            subprocess.call(cmd, #shell = True,
                            stderr = subprocess.STDOUT, stdout = f,
                            cwd=bdir)
        finally:
            f.close()
    except subprocess.CalledProcessError, e:
        msg = """
There was an error while executing the following command:

    %s

Error was : %s

Look at the build log (%s).""" % (cmd, str(e), build_log)
        raise Exception(msg)

    move_binary(arch, pyver, bdir, scipy_verstr)

# Helpers
def set_bootstrap_sources(arch, pyver):
    bdir = bootstrap_dir(pyver)
    write_site_cfg(arch, cwd=bdir)

def get_sdist_tarball(src_root):
    """Return the name of the installer built by sdist command."""
    # Yeah, the name logic is harcoded in distutils. We have to reproduce it
    # here
    name = "scipy-%s.zip" % get_scipy_version(src_root)
    return name

def prepare_scipy_sources(src_root, bootstrap):
    zid = ZipFile(pjoin(src_root, 'dist', get_sdist_tarball(src_root)))
    root = 'scipy-%s' % get_scipy_version(src_root)

    # From the sdist-built tarball, extract all files into bootstrap directory,
    # but removing the scipy-VERSION head path
    for name in zid.namelist():
        cnt = zid.read(name)
        if name.startswith(root):
            # XXX: even on windows, the path sep in zip is '/' ?
            name = name.split('/', 1)[1]
        newname = pjoin(bootstrap, name)

        if not pexists(dirname(newname)):
            os.makedirs(dirname(newname))
        fid = open(newname, 'wb')
        fid.write(cnt)

def prepare_nsis_script(bdir, pyver, numver):
    tpl = pjoin('nsis_scripts', 'scipy-superinstaller.nsi.in')
    source = open(tpl, 'r')
    target = open(pjoin(bdir, 'scipy-superinstaller.nsi'), 'w')

    installer_name = 'scipy-%s-win32-superpack-python%s.exe' % (numver, pyver)
    cnt = "".join(source.readlines())
    cnt = cnt.replace('@SCIPY_INSTALLER_NAME@', installer_name)
    for arch in ['nosse', 'sse2', 'sse3']:
        cnt = cnt.replace('@%s_BINARY@' % arch.upper(),
                          get_binary_name(arch, numver))

    target.write(cnt)

def bootstrap_dir(pyver):
    return pjoin(BUILD_ROOT, "bootstrap-%s" % pyver)

def get_scipy_version(src_root):
    version_file = pjoin(src_root, "scipy", "version.py")
    if not pexists(version_file):
        raise IOError("file %s not found" % version_file)

    fid = open(version_file, "r")
    vregex = re.compile("version\s*=\s*'(\d+)\.(\d+)\.(\d+)'")
    isrelregex = re.compile("release\s*=\s*True")
    isdevregex = re.compile("release\s*=\s*False")
    isdev = None
    version = None
    for line in fid.readlines():
        m = vregex.match(line)
        if m:
            version = [int(i) for i in m.groups()]
        if isrelregex.match(line):
            if isdev is None:
                isdev = False
            else:
                raise RuntimeError("isdev already set ?")
        if isdevregex.match(line):
            if isdev is None:
                isdev = True
            else:
                raise RuntimeError("isdev already set ?")

    verstr = ".".join([str(i) for i in version])
    if isdev:
        verstr += ".dev"
        verstr += get_svn_version(src_root)
    return verstr

def get_svn_version(chdir):
    out = subprocess.Popen(['svn', 'info'],
                           stdout = subprocess.PIPE,
                           cwd = chdir).communicate()[0]
    r = re.compile('Revision: ([0-9]+)')
    svnver = None
    for line in out.split('\n'):
        m = r.match(line)
        if m:
            svnver = m.group(1)

    if not svnver:
        raise ValueError("Error while parsing svn version ?")

    return svnver

def get_python_exec(ver):
    """Return the executable of python for the given version."""
    # XXX Check that the file actually exists
    try:
        return PYEXECS[ver]
    except KeyError:
        raise ValueError("Version %s not supported/recognized" % ver)

def write_site_cfg(arch, cwd=None):
    if not cwd:
        cwd = os.getcwd()

    scfg = pjoin(cwd, "site.cfg")
    if pexists(scfg):
        os.remove(scfg)
    f = open(scfg, 'w')
    f.writelines(SITECFG[arch])
    f.close()

def move_binary(arch, pyver, cwd, scipy_verstr):
    if not pexists(pjoin(cwd, "binaries")):
        os.makedirs(pjoin(cwd, "binaries"))

    shmove(pjoin(cwd, 'dist', get_windist_exec(pyver, scipy_verstr)),
           pjoin(cwd, 'binaries', get_binary_name(arch, scipy_verstr)))

def get_binary_name(arch, scipy_verstr):
    if BUILD_MSI:
        ext = '.msi'
    else:
        ext = '.exe'
    return "scipy-%s-%s%s" % (scipy_verstr, arch, ext)

def get_windist_exec(pyver, scipy_verstr):
    """Return the name of the installer built by wininst command."""
    # Yeah, the name logic is harcoded in distutils. We have to reproduce it
    # here
    if BUILD_MSI:
        ext = '.msi'
    else:
        ext = '.exe'
    name = "scipy-%s.win32-py%s%s" % (scipy_verstr, pyver, ext)
    return name
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero 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 Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import os
import tools

import zipfile
from StringIO import StringIO
import base64
from tools.translate import _
from osv import osv, fields

class base_module_import(osv.osv_memory):
    """ Import Module """

    _name = "base.module.import"
    _inherit = "ir.wizard.screen"
    _description = "Import Module"

    _columns = {
          'module_file': fields.binary('Module .ZIP file', required=True),
          'state':fields.selection([('init','init'),('done','done')], 'state', readonly=True),
          'module_name': fields.char('Module Name', size=128),
    }

    _defaults = {
        'state': 'init',
    }

    def importzip(self, cr, uid, ids, context):
        (data,) = self.browse(cr, uid, ids , context=context)
        module_data = data.module_file

        val = base64.decodestring(module_data)
        fp = StringIO()
        fp.write(val)
        fdata = zipfile.ZipFile(fp, 'r')
        fname = fdata.namelist()[0]
        module_name = os.path.split(fname)[0]

        ad = tools.config['addons_path'].split(",")[-1]

        fname = os.path.join(ad, module_name+'.zip')
        try:
            fp = file(fname, 'wb')
            fp.write(val)
            fp.close()
        except IOError:
            raise osv.except_osv(_('Error !'), _('Can not create the module file: %s !') % (fname,) )

        self.pool.get('ir.module.module').update_list(cr, uid, {'module_name': module_name,})
        self.write(cr, uid, ids, {'state':'done', 'module_name': module_name}, context)
        return False

    def action_module_open(self, cr, uid, ids, context):
        (data,) = self.browse(cr, uid, ids , context=context)
        return {
            'domain': str([('name', '=', data.module_name)]),
            'name': 'Modules',
            'view_type': 'form',
            'view_mode': 'tree,form',
            'res_model': 'ir.module.module',
            'type': 'ir.actions.act_window',
        }

base_module_import()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:<commit_msg>[MERGE] zipped module import wizard improvements, courtesy of Boris Timokhin<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero 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 Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import os
import tools

import zipfile
from StringIO import StringIO
import base64
from tools.translate import _
from osv import osv, fields

ADDONS_PATH = tools.config['addons_path'].split(",")[-1]

class base_module_import(osv.osv_memory):
    """ Import Module """

    _name = "base.module.import"
    _inherit = "ir.wizard.screen"
    _description = "Import Module"

    _columns = {
          'module_file': fields.binary('Module .ZIP file', required=True),
          'state':fields.selection([('init','init'),('done','done')],
                                   'state', readonly=True),
          'module_name': fields.char('Module Name', size=128),
    }

    _defaults = {
        'state': 'init',
    }

    def importzip(self, cr, uid, ids, context):
        (data,) = self.browse(cr, uid, ids , context=context)
        module_data = data.module_file
        zip_data = base64.decodestring(module_data)
        fp = StringIO()
        fp.write(zip_data)
        try:
            file_data = zipfile.ZipFile(fp, 'r')
        except zipfile.BadZipfile:
            raise osv.except_osv(_('Error !'), _('File is not a zip file!'))
        init_file_name = sorted(file_data.namelist())[0]
        module_name = os.path.split(init_file_name)[0]

        file_path = os.path.join(ADDONS_PATH, '%s.zip' % module_name)
        try:
            zip_file = open(file_path, 'wb')
        except IOError:
            raise osv.except_osv(_('Error !'),
                                 _('Can not create the module file: %s !') % \
                                 (file_path,) )
        zip_file.write(zip_data)
        zip_file.close()

        self.pool.get('ir.module.module').update_list(cr, uid,
                                                  {'module_name': module_name,})
        self.write(cr, uid, ids, {'state':'done', 'module_name': module_name},
                   context)
        return False

    def action_module_open(self, cr, uid, ids, context):
        (data,) = self.browse(cr, uid, ids , context=context)
        return {
            'domain': str([('name', '=', data.module_name)]),
            'name': 'Modules',
            'view_type': 'form',
            'view_mode': 'tree,form',
            'res_model': 'ir.module.module',
            'type': 'ir.actions.act_window',
        }

base_module_import()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Atmosphere service exceptions.
"""

from ansible.errors import AnsibleError
from socket import error as socket_error
from rtwo.exceptions import ConnectionFailure
from libcloud.common.types import InvalidCredsError, MalformedResponseError

class ServiceException(Exception):
    """
    Base Service exception class
    """

class ActionNotAllowed(ServiceException):

    def __init__(self, message):
        self.message = message
        self.status_code = 409
        super(ActionNotAllowed, self).__init__()

class InstanceDoesNotExist(ServiceException):

    def __init__(self, instance_id):
        self.message = instance_id
        self.status_code = 404
        super(UnderThresholdError, self).__init__()

class InstanceDoesNotExist(ServiceException):

    def __init__(self, instance_id):
        self.message = instance_id
        self.status_code = 404
        super(UnderThresholdError, self).__init__()

class UnderThresholdError(ServiceException):

    def __init__(self, message):
        self.message = message
        self.status_code = 400
        super(UnderThresholdError, self).__init__()

class SecurityGroupNotCreated(ServiceException):

    def __init__(self):
        self.message = ("Gateway Timeout! Security Group(s) could not be "
                        "created. Please try again later")
        self.status_code = 504
        super(SecurityGroupNotCreated, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class HypervisorCapacityError(ServiceException):

    def __init__(self, hypervisor, message):
        self.hypervisor = hypervisor
        self.message = message
        super(HypervisorCapacityError, self).__init__(self.message)

class OverAllocationError(ServiceException):

    def __init__(self, amount_exceeded):
        self.overage = amount_exceeded
        self.message = "Time allocation exceeded: Instance usage is over by "\
            "%s."\
            % (self.overage,)
        super(OverAllocationError, self).__init__(self.message)

    def __str__(self):
        return "%s" % (self.message, )

class OverQuotaError(ServiceException):

    def __init__(self, resource=None, requested=None,
                 used=None, limit=None, message=None):
        if not message:
            self.message = "Quota exceeded: Requested %s %s but already used "\
                           "%s/%s %s."\
                           % (requested, resource, used, limit, resource)
        else:
            self.message = message
        super(OverQuotaError, self).__init__(self.message)

    def __str__(self):
        return "%s" % (self.message, )

class DeviceBusyException(ServiceException):

    def __init__(self, mount_loc, process_list):
        proc_str = ''
        for proc_name, pid in process_list:
            proc_str += '\nProcess name:%s process id:%s' % (proc_name, pid)
        message = "Volume mount location is: %s\nRunning processes that"\
                  " are accessing that directory must be closed before "\
                  "unmounting. All offending processes names and IDs are "\
                  "listed below:%s" % (mount_loc, proc_str)
        self.message = message
        super(DeviceBusyException, self).__init__(mount_loc, process_list)

    def __str__(self):
        return "%s:\n%s" % (self.message, repr(self.process_list))

class SizeNotAvailable(ServiceException):

    def __init__(self):
        self.message = "Size Not Available."
        super(SizeNotAvailable, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class VolumeDetachConflict(ServiceException):

    def __init__(self, message):
        self.message = message
        super(VolumeDetachConflict, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class VolumeAttachConflict(ServiceException):

    def __init__(self, instance_id=None, volume_id=None, message=None):
        if not message:
            message = "Volume %s is still attached to instance %s"\
            % (volume_id, instance_id)
        self.message = message
        super(VolumeAttachConflict, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class VolumeError(ServiceException):
    """
    Errors encountered during volume creation.
    """

class NotFound(ServiceException):
    """
    Exception raised when a resource cannot be found.
    """

class VolumeMountConflict(ServiceException):

    def __init__(self, instance_id, volume_id, extra=None):
        self.message = "Volume %s could not be auto-mounted to %s. %s"\
            " See Available Volumes -> Mounting a Volume "\
            " to learn how to mount the device manually"\
            % (volume_id, instance_id, "Reason:%s" % extra)
        super(VolumeMountConflict, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class AnsibleDeployException(AnsibleError, ServiceException):
    pass
<commit_msg>send proper object when calling super()<commit_after># -*- coding: utf-8 -*-
"""
Atmosphere service exceptions.
"""

from ansible.errors import AnsibleError
from socket import error as socket_error
from rtwo.exceptions import ConnectionFailure
from libcloud.common.types import InvalidCredsError, MalformedResponseError

class ServiceException(Exception):
    """
    Base Service exception class
    """

class ActionNotAllowed(ServiceException):

    def __init__(self, message):
        self.message = message
        self.status_code = 409
        super(ActionNotAllowed, self).__init__()

class InstanceDoesNotExist(ServiceException):

    def __init__(self, instance_id):
        self.message = instance_id
        self.status_code = 404
        super(InstanceDoesNotExist, self).__init__()

class UnderThresholdError(ServiceException):

    def __init__(self, message):
        self.message = message
        self.status_code = 400
        super(UnderThresholdError, self).__init__()

class SecurityGroupNotCreated(ServiceException):

    def __init__(self):
        self.message = ("Gateway Timeout! Security Group(s) could not be "
                        "created. Please try again later")
        self.status_code = 504
        super(SecurityGroupNotCreated, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class HypervisorCapacityError(ServiceException):

    def __init__(self, hypervisor, message):
        self.hypervisor = hypervisor
        self.message = message
        super(HypervisorCapacityError, self).__init__(self.message)

class OverAllocationError(ServiceException):

    def __init__(self, amount_exceeded):
        self.overage = amount_exceeded
        self.message = "Time allocation exceeded: Instance usage is over by "\
            "%s."\
            % (self.overage,)
        super(OverAllocationError, self).__init__(self.message)

    def __str__(self):
        return "%s" % (self.message, )

class OverQuotaError(ServiceException):

    def __init__(self, resource=None, requested=None,
                 used=None, limit=None, message=None):
        if not message:
            self.message = "Quota exceeded: Requested %s %s but already used "\
                           "%s/%s %s."\
                           % (requested, resource, used, limit, resource)
        else:
            self.message = message
        super(OverQuotaError, self).__init__(self.message)

    def __str__(self):
        return "%s" % (self.message, )

class DeviceBusyException(ServiceException):

    def __init__(self, mount_loc, process_list):
        proc_str = ''
        for proc_name, pid in process_list:
            proc_str += '\nProcess name:%s process id:%s' % (proc_name, pid)
        message = "Volume mount location is: %s\nRunning processes that"\
                  " are accessing that directory must be closed before "\
                  "unmounting. All offending processes names and IDs are "\
                  "listed below:%s" % (mount_loc, proc_str)
        self.message = message
        super(DeviceBusyException, self).__init__(mount_loc, process_list)

    def __str__(self):
        return "%s:\n%s" % (self.message, repr(self.process_list))

class SizeNotAvailable(ServiceException):

    def __init__(self):
        self.message = "Size Not Available."
        super(SizeNotAvailable, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class VolumeDetachConflict(ServiceException):

    def __init__(self, message):
        self.message = message
        super(VolumeDetachConflict, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class VolumeAttachConflict(ServiceException):

    def __init__(self, instance_id=None, volume_id=None, message=None):
        if not message:
            message = "Volume %s is still attached to instance %s"\
            % (volume_id, instance_id)
        self.message = message
        super(VolumeAttachConflict, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class VolumeError(ServiceException):
    """
    Errors encountered during volume creation.
    """

class NotFound(ServiceException):
    """
    Exception raised when a resource cannot be found.
    """

class VolumeMountConflict(ServiceException):

    def __init__(self, instance_id, volume_id, extra=None):
        self.message = "Volume %s could not be auto-mounted to %s. %s"\
            " See Available Volumes -> Mounting a Volume "\
            " to learn how to mount the device manually"\
            % (volume_id, instance_id, "Reason:%s" % extra)
        super(VolumeMountConflict, self).__init__()

    def __str__(self):
        return "%s" % (self.message, )

class AnsibleDeployException(AnsibleError, ServiceException):
    pass
<|endoftext|>


<commit_before>"""
blockchain

An API for traversing the Bitcoin block chain

"""

__author__ = 'Bernhard Haslhofer (bernhard.haslhofer@ait.ac.at)'
__copyright__ = 'Copyright 2015, Bernhard Haslhofer'
__license__ = "MIT"

import json
import datetime as dt

from bitcoingraph.rpc import JSONRPCException

def to_json(raw_data):
    """
    Pretty-prints JSON data

    :param str raw_data: raw JSON data
    """
    return json.dumps(raw_data, sort_keys=True,
                      indent=4, separators=(',', ': '))

def to_time(numeric_string):
    """
    Converts UTC timestamp to string-formatted data time.

    :param int numeric_string: UTC timestamp
    """
    time_as_date = dt.datetime.utcfromtimestamp(int(numeric_string))
    return time_as_date.strftime('%Y-%m-%d %H:%M:%S')

class BlockchainException(Exception):
    """
    Exception raised when accessing or navigating the block chain.
    """

    def __init__(self, msg, inner_exc):
        self.msg = msg
        self.inner_exc = inner_exc

    def __str__(self):
        return repr(self.msg)

class BlockchainObject(object):
    """
    Generic block chain object.
    """

    def __init__(self, raw_data, blockchain):
        """
        Creates a generic block chain object.

        :param str raw_data: raw JSON data extracted from block chain
        :param BlockChain blockchain: instantiated blockchain object
        :return: block chain object
        :rtype: BlockchainObject
        """
        self._raw_data = raw_data
        self._blockchain = blockchain

    @property
    def time(self):
        """
        UTC timestamp in UNIX-format denoting approximate creation time.

        :return: approximate creation time as UTC timestamp
        :rtype: str
        """
        return self._raw_data['time']

    @property
    def time_as_dt(self):
        """
        String-formatted approximate creation time.

        :return: approximate creation time
        :rtype: str
        """
        return to_time(self.time)

    def __str__(self):
        return to_json(self._raw_data)

    def __repr__(self):
        return to_json(self._raw_data)

class Block(BlockchainObject):

    """
    Bitcoin block recording transactions.
    """

    def __init__(self, raw_data, blockchain):
        """
        Creates a block object.

        :param str raw_data: raw JSON data extracted from block chain
        :param BlockChain blockchain: instantiated blockchain object
        :return: block object
        :rtype: Block
        """
        super().__init__(raw_data, blockchain)

    @property
    def height(self):
        """
        Number of blocks between it and genesis block (height 0).

        :return: block height
        :rtype: int
        """
        return int(self._raw_data['height'])

    @property
    def hash(self):
        """
        256-bit hash based on all of the transactions in the block.

        :return: block hash
        :rtype: str
        """
        return self._raw_data['hash']

    @property
    def nextblockhash(self):
        """
        Reference to the next block.

        :return: next block hash
        :rtype: str
        """
        return self._raw_data['nextblockhash']

    @property
    def hasnextblock(self):
        """
        Returns `True` if block references next block.
        """
        return 'nextblockhash' in self._raw_data

    @property
    def nextblock(self):
        """
        Returns next block.

        :return: next block or `None` if there is none
        :rtype: Block
        """
        if self.hasnextblock:
            block_hash = self.nextblockhash
            return self._blockchain.get_block_by_hash(block_hash)
        else:
            return None

    @property
    def tx_count(self):
        """
        Returns number of transactions in block.

        :return: number of transactions
        :rtype: int
        """
        return len(self._raw_data['tx'])

    @property
    def tx_ids(self):
        """
        Returns ids of transactions in block.

        :return: list of transaction ids
        :rtype: array
        """
        return self._raw_data['tx']

    @property
    def transactions(self):
        """
        Generates Transaction objects for each transaction in block.

        :yield: transaction
        :rtype: Transaction
        """
        for tx_id in self.tx_ids:
            yield self._blockchain.get_transaction(tx_id)

class TxInput(object):

    """
    Transaction input.
    """

    def __init__(self, raw_data, blockchain):
        """
        Creates a transaction input object.

        :param str raw_data: raw JSON data extracted from block chain
        :param BlockChain blockchain: instantiated blockchain object
        :return: transaction input object
        :rtype: TxInput
        """
        self._raw_data = raw_data
        self._blockchain = blockchain

    @property
    def is_coinbase(self):
        """
        Returns `True` if input refers to coinbase.
        """
        return 'coinbase' in self._raw_data

    @property
    def prev_tx_hash(self):
        """
        Returns hash of previous transaction or `None` if transaction
        is coinbase transaction.

        :return: previous transaction hash
        :rtype: str
        """
        if self.is_coinbase:
            return None
        else:
            return self._raw_data['txid']

    @property
    def prev_tx_output_index(self):
        """
        Returns index of output in previous transaction or `None` if
        transaction is coinbase transaction.

        :return: previous transaction output index
        :rtype: str
        """
        if self.is_coinbase:
            return None
        else:
            return self._raw_data['vout']

    @property
    def prev_tx_output(self):
        """
        Returns ouput in previous transaction or `None` if transaction
        is coinbase transaction.

        :return: previous transaction output
        :rtype: TxOutput
        """
        if self.is_coinbase:
            return None
        else:
            prev_tx = self._blockchain.get_transaction(self.prev_tx_hash)
            return prev_tx.get_output_by_index(self.prev_tx_output_index)

    @property
    def addresses(self):
        """
        Returns addresses in output in previous transaction or `None` if
        transaction is coinbase transaction.

        :return: previous transaction output addresses
        :rtype: array
        """
        if self.is_coinbase:
            return None
        else:
            return self.prev_tx_output.addresses

class TxOutput(object):

    """
    Transaction output.
    """

    def __init__(self, raw_data):
        """
        Creates a transaction output object.

        :param str raw_data: raw JSON data extracted from block chain
        :return: transaction output object
        :rtype: TxOutput
        """
        self._raw_data = raw_data

    @property
    def index(self):
        """
        Returns index of transaction output.

        :return: transaction output index
        :rtype: str
        """
        return self._raw_data['n']

    @property
    def value(self):
        """
        Returns amount of Bitcoins.

        :return: amount of Bitcoins
        :rtype: float
        """
        return float(self._raw_data['value'])

    @property
    def addresses(self):
        """
        Returns (possibly empty) transaction output addresses list.

        :return: output addresses
        :rtype: array
        """
        addresses = []

        scriptPubKey = self._raw_data.get('scriptPubKey')
        if scriptPubKey is None:
            return addresses
        output_addresses = scriptPubKey.get('addresses')
        if output_addresses is None:
            return addresses
        else:
            addresses += [address for address in output_addresses]

        return addresses

class Transaction(BlockchainObject):

    """
    Bitcoin transaction.
    """

    def __init__(self, raw_data, blockchain):
        """
        Creates a transaction object.

        :param str raw_data: raw JSON data extracted from block chain
        :param BlockChain blockchain: instantiated blockchain object
        :return: transaction object
        :rtype: Transaction
        """
        super().__init__(raw_data, blockchain)

    # Explicit transaction properties

    @property
    def blocktime(self):
        """
        Returns blocktime as UTC timestamp.

        :return: blocktime as UTC timestamp
        :rtype: str
        """
        return self._raw_data['blocktime']

    @property
    def id(self):
        """
        Returns transaction id (hash)

        :return: transaction id (hash)
        :rtype: str
        """
        return self._raw_data['txid']

    @property
    def blockhash(self):
        """
        Returns hash of included block.

        :return: block hash
        :rtype: str
        """
        return self._raw_data['blockhash']

    # Included block properties

    def get_included_block_height(self):
        """
        Returns height of block this transaction is included in.

        :return: block height
        :rtype: int
        """
        block = self._blockchain.get_block_by_hash(self.blockhash)
        return block.height

    # Input properties

    def get_inputs(self):
        """
        Generates transaction input objects for listed inputs.

        :yield: transaction input objects
        :rtype: TxInput
        """
        for tx_input in self._raw_data['vin']:
            yield TxInput(tx_input, self._blockchain)

    def get_input_count(self):
        """
        Returns number of listed transaction inputs.

        :return: transaction input count
        :rtype: int
        """
        if 'vin' in self._raw_data:
            return len(self._raw_data['vin'])
        else:
            return 0

    @property
    def is_coinbase_tx(self):
        """
        Returns `True` if transaction is coinbase transaction.
        """
        for tx_input in self.get_inputs():
            if tx_input.is_coinbase:
                return True
        else:
            return False

    # Output properties

    def get_output_count(self):
        """
        Returns number of listed transaction outputs.

        :return: transaction output count
        :rtype: int
        """
        if 'vout' in self._raw_data:
            return len(self._raw_data['vout'])
        else:
            return 0

    def get_outputs(self):
        """
        Generates transaction output objects for listed inputs.

        :yield: transaction output objects
        :rtype: TxOutput
        """
        for tx_output in self._raw_data['vout']:
            yield TxOutput(tx_output)

    def get_output_by_index(self, index):
        """
        Returns transaction output by id or `None` if there is none.

        :param int index: transaction output index
        :return: transaction output object
        :rtype: TxOutput
        """
        for output in self.get_outputs():
            if output.index == index:
                return output
        return None

    # Bitcoin flow properties

    @property
    def bc_flows(self):
        """
        Returns flows of Bitcoins between source and target addresses.

        :return: bitcoin flows ([src1, src2, ...], [tgt1, tgt2, ...], value)
        :rtype: array
        """
        bc_flows = []
        # collect input addresses
        if self.is_coinbase_tx:
            src_list = ['COINBASE']
        else:
            src_list = []
            for tx_input in self.get_inputs():
                src_list += [address for address in tx_input.addresses
                                     if address not in src_list]
        # collect output addresses
        for tx_output in self.get_outputs():
            tgt_list = [address for address in tx_output.addresses]
            flow = {'src_list': src_list, 'tgt_list': tgt_list,
                    'value': tx_output.value}
            bc_flows += [flow]

        return bc_flows

    @property
    def flow_sum(self):
        """
        Returns sum of BTC transferred
        """
        return sum([tx_output.value for tx_output in self.get_outputs()])

class BlockChain(object):

    """
    Bitcoin block chain.
    """

    def __init__(self, bitcoin_proxy):
        """
        Creates a block chain object.

        :param BitcoinProxy bitcoin_proxy: reference to Bitcoin proxy
        :return: block chain object
        :rtype: BlockChain
        """
        self._bitcoin_proxy = bitcoin_proxy

    def get_block_by_hash(self, block_hash):
        """
        Returns a block by given block hash.

        :param str block_hash: hash of block to be returned
        :return: the requested block
        :rtype: Block
        :raises BlockchainException: if block cannot be retrieved
        """
        # Returns block by hash
        try:
            raw_block_data = self._bitcoin_proxy.getblock(block_hash)
            return Block(raw_block_data, self)
        except JSONRPCException as exc:
            raise BlockchainException("Cannot retrieve block %s"
                                      % (block_hash), exc)

    def get_block_by_height(self, block_height):
        """
        Returns a block by given block height.

        :param str block_height: height of block to be returned
        :return: the requested block
        :rtype: Block
        :raises BlockchainException: if block cannot be retrieved
        """
        # Returns block by height
        try:
            block_hash = self._bitcoin_proxy.getblockhash(block_height)
            return self.get_block_by_hash(block_hash)
        except JSONRPCException as exc:
            raise BlockchainException("Cannot retrieve block with height %s"
                                      % (block_height), exc)

    def get_blocks_in_range(self, start_height=0, end_height=0):
        """
        Generates blocks in a given range.

        :param int start_height: first block height in range
        :param int end_height: last block height in range
        :yield: the requested blocks
        :rtype: Block
        """
        block = self.get_block_by_height(start_height)
        while (block.height <= end_height):
            yield block
            if not block.hasnextblock:
                break
            else:
                block = block.nextblock

    def get_transaction(self, tx_id):
        """
        Returns a transaction by given transaction id.

        :param str tx_id: transaction id
        :return: the requested transaction
        :rtype: Transaction
        """
        try:
            raw_tx_data = self._bitcoin_proxy.getrawtransaction(tx_id)
            return Transaction(raw_tx_data, self)
        except JSONRPCException as exc:
            raise BlockchainException("Cannot retrieve transaction with id %s"
                                      % (tx_id), exc)

    def get_max_blockheight(self):
        """
        Returns maximum known block height.

        :return: maximum block height
        :rtype: int
        """
        try:
            max_height = self._bitcoin_proxy.getblockcount()
            return max_height
        except JSONRPCException as exc:
            raise BlockchainException("Error when retrieving maximum\
                block height", exc)
<commit_msg>added blockchain accessors<commit_after>"""
blockchain

An API for traversing the Bitcoin block chain

"""

__author__ = 'Bernhard Haslhofer (bernhard.haslhofer@ait.ac.at)'
__copyright__ = 'Copyright 2015, Bernhard Haslhofer'
__license__ = "MIT"

import json
import datetime as dt

from bitcoingraph.rpc import JSONRPCException

def to_json(raw_data):
    """
    Pretty-prints JSON data

    :param str raw_data: raw JSON data
    """
    return json.dumps(raw_data, sort_keys=True,
                      indent=4, separators=(',', ': '))

def to_time(numeric_string):
    """
    Converts UTC timestamp to string-formatted data time.

    :param int numeric_string: UTC timestamp
    """
    time_as_date = dt.datetime.utcfromtimestamp(int(numeric_string))
    return time_as_date.strftime('%Y-%m-%d %H:%M:%S')

class BlockchainException(Exception):
    """
    Exception raised when accessing or navigating the block chain.
    """

    def __init__(self, msg, inner_exc):
        self.msg = msg
        self.inner_exc = inner_exc

    def __str__(self):
        return repr(self.msg)

class BlockchainObject(object):
    """
    Generic block chain object.
    """

    def __init__(self, raw_data, blockchain):
        """
        Creates a generic block chain object.

        :param str raw_data: raw JSON data extracted from block chain
        :param BlockChain blockchain: instantiated blockchain object
        :return: block chain object
        :rtype: BlockchainObject
        """
        self._raw_data = raw_data
        self._blockchain = blockchain

    @property
    def time(self):
        """
        UTC timestamp in UNIX-format denoting approximate creation time.

        :return: approximate creation time as UTC timestamp
        :rtype: str
        """
        return self._raw_data['time']

    @property
    def time_as_dt(self):
        """
        String-formatted approximate creation time.

        :return: approximate creation time
        :rtype: str
        """
        return to_time(self.time)

    def __str__(self):
        return to_json(self._raw_data)

    def __repr__(self):
        return to_json(self._raw_data)

class Block(BlockchainObject):

    """
    Bitcoin block recording transactions.
    """

    def __init__(self, raw_data, blockchain):
        """
        Creates a block object.

        :param str raw_data: raw JSON data extracted from block chain
        :param BlockChain blockchain: instantiated blockchain object
        :return: block object
        :rtype: Block
        """
        super().__init__(raw_data, blockchain)

    @property
    def height(self):
        """
        Number of blocks between it and genesis block (height 0).

        :return: block height
        :rtype: int
        """
        return int(self._raw_data['height'])

    @property
    def hash(self):
        """
        256-bit hash based on all of the transactions in the block.

        :return: block hash
        :rtype: str
        """
        return self._raw_data['hash']

    @property
    def previousblockhash(self):
        """
        Reference to the previous block.

        :return: previous block hash
        :rtype: str
        """
        return self._raw_data['previousblockhash']

    @property
    def nextblockhash(self):
        """
        Reference to the next block.

        :return: next block hash
        :rtype: str
        """
        return self._raw_data['nextblockhash']

    @property
    def hasnextblock(self):
        """
        Returns `True` if block references next block.
        """
        return 'nextblockhash' in self._raw_data

    @property
    def nextblock(self):
        """
        Returns next block.

        :return: next block or `None` if there is none
        :rtype: Block
        """
        if self.hasnextblock:
            block_hash = self.nextblockhash
            return self._blockchain.get_block_by_hash(block_hash)
        else:
            return None

    @property
    def tx_count(self):
        """
        Returns number of transactions in block.

        :return: number of transactions
        :rtype: int
        """
        return len(self._raw_data['tx'])

    @property
    def tx_ids(self):
        """
        Returns ids of transactions in block.

        :return: list of transaction ids
        :rtype: array
        """
        return self._raw_data['tx']

    @property
    def transactions(self):
        """
        Generates Transaction objects for each transaction in block.

        :yield: transaction
        :rtype: Transaction
        """
        for tx_id in self.tx_ids:
            yield self._blockchain.get_transaction(tx_id)

class TxInput(object):

    """
    Transaction input.
    """

    def __init__(self, raw_data, blockchain):
        """
        Creates a transaction input object.

        :param str raw_data: raw JSON data extracted from block chain
        :param BlockChain blockchain: instantiated blockchain object
        :return: transaction input object
        :rtype: TxInput
        """
        self._raw_data = raw_data
        self._blockchain = blockchain

    @property
    def is_coinbase(self):
        """
        Returns `True` if input refers to coinbase.
        """
        return 'coinbase' in self._raw_data

    @property
    def prev_tx_hash(self):
        """
        Returns hash of previous transaction or `None` if transaction
        is coinbase transaction.

        :return: previous transaction hash
        :rtype: str
        """
        if self.is_coinbase:
            return None
        else:
            return self._raw_data['txid']

    @property
    def prev_tx_output_index(self):
        """
        Returns index of output in previous transaction or `None` if
        transaction is coinbase transaction.

        :return: previous transaction output index
        :rtype: str
        """
        if self.is_coinbase:
            return None
        else:
            return self._raw_data['vout']

    @property
    def prev_tx_output(self):
        """
        Returns ouput in previous transaction or `None` if transaction
        is coinbase transaction.

        :return: previous transaction output
        :rtype: TxOutput
        """
        if self.is_coinbase:
            return None
        else:
            prev_tx = self._blockchain.get_transaction(self.prev_tx_hash)
            return prev_tx.get_output_by_index(self.prev_tx_output_index)

    @property
    def addresses(self):
        """
        Returns addresses in output in previous transaction or `None` if
        transaction is coinbase transaction.

        :return: previous transaction output addresses
        :rtype: array
        """
        if self.is_coinbase:
            return None
        else:
            return self.prev_tx_output.addresses

class TxOutput(object):

    """
    Transaction output.
    """

    def __init__(self, raw_data):
        """
        Creates a transaction output object.

        :param str raw_data: raw JSON data extracted from block chain
        :return: transaction output object
        :rtype: TxOutput
        """
        self._raw_data = raw_data

    @property
    def index(self):
        """
        Returns index of transaction output.

        :return: transaction output index
        :rtype: str
        """
        return self._raw_data['n']

    @property
    def value(self):
        """
        Returns amount of Bitcoins.

        :return: amount of Bitcoins
        :rtype: float
        """
        return float(self._raw_data['value'])

    @property
    def addresses(self):
        """
        Returns (possibly empty) transaction output addresses list.

        :return: output addresses
        :rtype: array
        """
        addresses = []

        scriptPubKey = self._raw_data.get('scriptPubKey')
        if scriptPubKey is None:
            return addresses
        output_addresses = scriptPubKey.get('addresses')
        if output_addresses is None:
            return addresses
        else:
            addresses += [address for address in output_addresses]

        return addresses

class Transaction(BlockchainObject):

    """
    Bitcoin transaction.
    """

    def __init__(self, raw_data, blockchain):
        """
        Creates a transaction object.

        :param str raw_data: raw JSON data extracted from block chain
        :param BlockChain blockchain: instantiated blockchain object
        :return: transaction object
        :rtype: Transaction
        """
        super().__init__(raw_data, blockchain)

    # Explicit transaction properties

    @property
    def blocktime(self):
        """
        Returns blocktime as UTC timestamp.

        :return: blocktime as UTC timestamp
        :rtype: str
        """
        return self._raw_data['blocktime']

    @property
    def id(self):
        """
        Returns transaction id (hash)

        :return: transaction id (hash)
        :rtype: str
        """
        return self._raw_data['txid']

    @property
    def blockhash(self):
        """
        Returns hash of included block.

        :return: block hash
        :rtype: str
        """
        return self._raw_data['blockhash']

    # Included block properties

    def get_included_block_height(self):
        """
        Returns height of block this transaction is included in.

        :return: block height
        :rtype: int
        """
        block = self._blockchain.get_block_by_hash(self.blockhash)
        return block.height

    # Input properties

    def get_inputs(self):
        """
        Generates transaction input objects for listed inputs.

        :yield: transaction input objects
        :rtype: TxInput
        """
        for tx_input in self._raw_data['vin']:
            yield TxInput(tx_input, self._blockchain)

    def get_input_count(self):
        """
        Returns number of listed transaction inputs.

        :return: transaction input count
        :rtype: int
        """
        if 'vin' in self._raw_data:
            return len(self._raw_data['vin'])
        else:
            return 0

    @property
    def is_coinbase_tx(self):
        """
        Returns `True` if transaction is coinbase transaction.
        """
        for tx_input in self.get_inputs():
            if tx_input.is_coinbase:
                return True
        else:
            return False

    # Output properties

    def get_output_count(self):
        """
        Returns number of listed transaction outputs.

        :return: transaction output count
        :rtype: int
        """
        if 'vout' in self._raw_data:
            return len(self._raw_data['vout'])
        else:
            return 0

    def get_outputs(self):
        """
        Generates transaction output objects for listed inputs.

        :yield: transaction output objects
        :rtype: TxOutput
        """
        for tx_output in self._raw_data['vout']:
            yield TxOutput(tx_output)

    def get_output_by_index(self, index):
        """
        Returns transaction output by id or `None` if there is none.

        :param int index: transaction output index
        :return: transaction output object
        :rtype: TxOutput
        """
        for output in self.get_outputs():
            if output.index == index:
                return output
        return None

    # Bitcoin flow properties

    @property
    def bc_flows(self):
        """
        Returns flows of Bitcoins between source and target addresses.

        :return: bitcoin flows ([src1, src2, ...], [tgt1, tgt2, ...], value)
        :rtype: array
        """
        bc_flows = []
        # collect input addresses
        if self.is_coinbase_tx:
            src_list = ['COINBASE']
        else:
            src_list = []
            for tx_input in self.get_inputs():
                src_list += [address for address in tx_input.addresses
                                     if address not in src_list]
        # collect output addresses
        for tx_output in self.get_outputs():
            tgt_list = [address for address in tx_output.addresses]
            flow = {'src_list': src_list, 'tgt_list': tgt_list,
                    'value': tx_output.value}
            bc_flows += [flow]

        return bc_flows

    @property
    def flow_sum(self):
        """
        Returns sum of BTC transferred
        """
        return sum([tx_output.value for tx_output in self.get_outputs()])

class BlockChain(object):

    """
    Bitcoin block chain.
    """

    def __init__(self, bitcoin_proxy):
        """
        Creates a block chain object.

        :param BitcoinProxy bitcoin_proxy: reference to Bitcoin proxy
        :return: block chain object
        :rtype: BlockChain
        """
        self._bitcoin_proxy = bitcoin_proxy

    def get_block_by_hash(self, block_hash):
        """
        Returns a block by given block hash.

        :param str block_hash: hash of block to be returned
        :return: the requested block
        :rtype: Block
        :raises BlockchainException: if block cannot be retrieved
        """
        # Returns block by hash
        try:
            raw_block_data = self._bitcoin_proxy.getblock(block_hash)
            return Block(raw_block_data, self)
        except JSONRPCException as exc:
            raise BlockchainException("Cannot retrieve block %s"
                                      % (block_hash), exc)

    def get_block_by_height(self, block_height):
        """
        Returns a block by given block height.

        :param str block_height: height of block to be returned
        :return: the requested block
        :rtype: Block
        :raises BlockchainException: if block cannot be retrieved
        """
        # Returns block by height
        try:
            block_hash = self._bitcoin_proxy.getblockhash(block_height)
            return self.get_block_by_hash(block_hash)
        except JSONRPCException as exc:
            raise BlockchainException("Cannot retrieve block with height %s"
                                      % (block_height), exc)

    def get_blocks_in_range(self, start_height=0, end_height=0):
        """
        Generates blocks in a given range.

        :param int start_height: first block height in range
        :param int end_height: last block height in range
        :yield: the requested blocks
        :rtype: Block
        """
        block = self.get_block_by_height(start_height)
        while (block.height <= end_height):
            yield block
            if not block.hasnextblock:
                break
            else:
                block = block.nextblock

    def get_transaction(self, tx_id):
        """
        Returns a transaction by given transaction id.

        :param str tx_id: transaction id
        :return: the requested transaction
        :rtype: Transaction
        """
        try:
            raw_tx_data = self._bitcoin_proxy.getrawtransaction(tx_id)
            return Transaction(raw_tx_data, self)
        except JSONRPCException as exc:
            raise BlockchainException("Cannot retrieve transaction with id %s"
                                      % (tx_id), exc)

    def get_max_blockheight(self):
        """
        Returns maximum known block height.

        :return: maximum block height
        :rtype: int
        """
        try:
            max_height = self._bitcoin_proxy.getblockcount()
            return max_height
        except JSONRPCException as exc:
            raise BlockchainException("Error when retrieving maximum\
                block height", exc)
<|endoftext|>