<commit_before>#- Code for trimming desimodel/data into smaller files

from __future__ import absolute_import, division, print_function
from astropy.io.fits import HDUList, PrimaryHDU, ImageHDU, BinTableHDU
from astropy.io import fits
import numpy as np
import os.path
import shutil

def trim_data(indir, outdir, clobber=False):
    '''
    Trim a $DESIMODEL/data directory into a lightweight version for testing

    Args:
        indir : a $DESIMODEL/data directory from svn
        outdir : output data directory location

    Optional:
        clobber : if True, remove outdir if it already exists
    '''
    assert os.path.abspath(indir) != os.path.abspath(outdir)
    if os.path.exists(outdir) and clobber:
        shutil.rmtree(outdir)

    os.makedirs(outdir)

    #- python note:
    #- *inout(indir, outdir, filename) -> indir/filename, outdir/filename

    shutil.copy(*inout(indir, outdir, 'desi.yaml'))
    trim_focalplane(*inout(indir, outdir, 'focalplane'))
    trim_footprint(*inout(indir, outdir, 'footprint'))
    trim_inputs(*inout(indir, outdir, 'inputs'))
    trim_sky(*inout(indir, outdir, 'sky'))
    trim_specpsf(*inout(indir, outdir, 'specpsf'))
    trim_spectra(*inout(indir, outdir, 'spectra'))
    trim_targets(*inout(indir, outdir, 'targets'))
    trim_throughput(*inout(indir, outdir, 'throughput'))

def inout(indir, outdir, filename):
    '''returns os.path.join(indir, filename) and .join(outdir, filename)'''
    infile = os.path.join(indir, filename)
    outfile = os.path.join(outdir, filename)
    return infile, outfile

def trim_focalplane(indir, outdir):
    '''copy everything in focalplane'''
    assert os.path.basename(indir) == 'focalplane'
    shutil.copytree(indir, outdir)

def trim_footprint(indir, outdir):
    '''Copies subset of desi-tiles.fits but not desi-tiles.par'''
    assert os.path.basename(indir) == 'footprint'
    if not os.path.exists(outdir):
        os.makedirs(outdir)
    infile, outfile = inout(indir, outdir, 'desi-tiles.fits')
    t = fits.getdata(infile)
    ii = (110 < t.RA) & (t.RA < 140) & (-10 < t.DEC) & (t.DEC < 20)
    fits.writeto(outfile, t[ii])

def trim_inputs(indir, outdir):
    '''Don't copy any inputs'''
    pass

def trim_sky(indir, outdir):
    '''don't copy any sky files; maybe they shouldn't be in desimodel anyway'''
    pass

def trim_specpsf(indir, outdir):
    '''trim specpsf files to be much smaller'''
    assert os.path.basename(indir) == 'specpsf'
    if not os.path.exists(outdir):
        os.makedirs(outdir)
    trim_quickpsf(indir, outdir, 'psf-quicksim.fits')
    trim_psf(indir, outdir, 'psf-b.fits')
    trim_psf(indir, outdir, 'psf-r.fits')
    trim_psf(indir, outdir, 'psf-z.fits')

def trim_spectra(indir, outdir):
    '''downsample spectra, and only a few of them'''
    assert os.path.basename(indir) == 'spectra'
    if not os.path.exists(outdir):
        os.makedirs(outdir)
    for filename in (
        'spec-ABmag22.0.dat',
        'spec-elg-o2flux-8e-17-average-line-ratios.dat',
        'spec-lrg-z0.8-zmag20.38.dat',
        'spec-qso-z1.5-rmag22.81.dat',
        'spec-sky.dat',
        'spec-sky-grey.dat',
        'spec-sky-bright.dat',
        ):
        i = 0
        fx = open(os.path.join(outdir, filename), 'w')
        for line in open(os.path.join(indir, filename)):
            if line.startswith('#'):
                fx.write(line)
            elif i%20 == 0:
                fx.write(line)
            i += 1
        fx.close()

def trim_targets(indir, outdir):
    '''copy everything in targets/'''
    assert os.path.basename(indir) == 'targets'
    shutil.copytree(indir, outdir)

def trim_throughput(indir, outdir):
    '''downsample throughput files'''
    assert os.path.basename(indir) == 'throughput'
    if not os.path.exists(outdir):
        os.makedirs(outdir)
    for filename in ['thru-b.fits', 'thru-r.fits', 'thru-z.fits']:
        fx = fits.open(indir+'/'+filename)
        hdus = HDUList()
        hdus.append(fx[0])
        hdus.append(BinTableHDU(fx[1].data[::20], header=fx[1].header))
        hdus.append(BinTableHDU(fx[2].data[::20], header=fx[2].header))
        hdus.writeto(outdir+'/'+filename)
        fx.close()

#-------------------------------------------------------------------------
#- Triming PSF files

def rebin_image(image, n):
    """
    rebin 2D array pix into bins of size n x n

    New binsize must be evenly divisible into original pix image
    """
    assert image.shape[0] % n == 0
    assert image.shape[1] % n == 0

    s = image.shape[0]//n, n, image.shape[1]//n, n
    return image.reshape(s).sum(-1).sum(1)

def trim_psf(indir, outdir, filename):
    assert os.path.abspath(indir) != os.path.abspath(outdir)
    infile = os.path.join(indir, filename)
    outfile = os.path.join(outdir, filename)

    fx = fits.open(infile)
    hdus = HDUList()

    #- HDU 0 XCOEFF - data unchanged but update keywords for less samples
    xcoeff = fx[0].data
    hdr = fx[0].header
    hdr['NWAVE'] = 3    #- down from 11
    hdr['CRPIX1'] = 23  #- 23=45//2+1, down from 113=225//2+1
    hdr['CRPIX1'] = 23
    hdr['CDELT1'] = 0.005   #- 5mm instead of 1mm
    hdr['CDELT2'] = 0.005   #- 5mm instead of 1mm
    hdr['PIXSIZE'] = 0.005   #- 5mm instead of 1mm

    hdus.append(PrimaryHDU(xcoeff, header=hdr))
    hdus.append(fx['YCOEFF'])

    #- subsample spots
    inspots = fx['SPOTS'].data
    spots = np.zeros((3,3,45,45))
    spots[0,0] = rebin_image(inspots[0,0], 5)
    spots[1,0] = rebin_image(inspots[5,0], 5)
    spots[2,0] = rebin_image(inspots[10,0], 5)
    spots[0,1] = rebin_image(inspots[0,5], 5)
    spots[1,1] = rebin_image(inspots[5,5], 5)
    spots[2,1] = rebin_image(inspots[10,5], 5)
    spots[0,2] = rebin_image(inspots[0,10], 5)
    spots[1,2] = rebin_image(inspots[5,10], 5)
    spots[2,2] = rebin_image(inspots[10,10], 5)
    hdus.append(ImageHDU(spots, header=fx['SPOTS'].header))

    #- subsample spots x,y locations
    dx = fx['SPOTX'].data
    hdus.append(ImageHDU(dx[::5, ::5], header=fx['SPOTX'].header))
    dy = fx['SPOTY'].data
    hdus.append(ImageHDU(dy[::5, ::5], header=fx['SPOTY'].header))

    #- Fiberpos unchanged
    hdus.append(fx['FIBERPOS'])

    #- Subsample SPOTPOS and SPOTWAVE
    d = fx['SPOTPOS'].data
    hdus.append(ImageHDU(d[::5], header=fx['SPOTPOS'].header))
    d = fx['SPOTWAVE'].data
    hdus.append(ImageHDU(d[::5], header=fx['SPOTWAVE'].header))

    hdus.writeto(outfile, clobber=True)
    fx.close()

def trim_quickpsf(indir, outdir, filename):
    assert os.path.abspath(indir) != os.path.abspath(outdir)
    infile = os.path.join(indir, filename)
    outfile = os.path.join(outdir, filename)
    fx = fits.open(infile)
    hdus = HDUList()
    hdus.append(fx[0])
    for i in [1,2,3]:
        d = fx[i].data
        hdus.append(BinTableHDU(d[::10], header=fx[i].header))
    hdus.writeto(outfile, clobber=True)
    fx.close()

<commit_msg>add module-level docstring<commit_after># Licensed under a 3-clause BSD style license - see LICENSE.rst
# -*- coding: utf-8 -*-
"""
desimodel.trim
==============

Code for trimming desimodel/data into smaller files.
"""
from __future__ import absolute_import, division, print_function
from astropy.io.fits import HDUList, PrimaryHDU, ImageHDU, BinTableHDU
from astropy.io import fits
import numpy as np
import os.path
import shutil

def trim_data(indir, outdir, clobber=False):
    '''
    Trim a $DESIMODEL/data directory into a lightweight version for testing

    Args:
        indir : a $DESIMODEL/data directory from svn
        outdir : output data directory location

    Optional:
        clobber : if True, remove outdir if it already exists
    '''
    assert os.path.abspath(indir) != os.path.abspath(outdir)
    if os.path.exists(outdir) and clobber:
        shutil.rmtree(outdir)

    os.makedirs(outdir)

    #- python note:
    #- *inout(indir, outdir, filename) -> indir/filename, outdir/filename

    shutil.copy(*inout(indir, outdir, 'desi.yaml'))
    trim_focalplane(*inout(indir, outdir, 'focalplane'))
    trim_footprint(*inout(indir, outdir, 'footprint'))
    trim_inputs(*inout(indir, outdir, 'inputs'))
    trim_sky(*inout(indir, outdir, 'sky'))
    trim_specpsf(*inout(indir, outdir, 'specpsf'))
    trim_spectra(*inout(indir, outdir, 'spectra'))
    trim_targets(*inout(indir, outdir, 'targets'))
    trim_throughput(*inout(indir, outdir, 'throughput'))

def inout(indir, outdir, filename):
    '''returns os.path.join(indir, filename) and .join(outdir, filename)'''
    infile = os.path.join(indir, filename)
    outfile = os.path.join(outdir, filename)
    return infile, outfile

def trim_focalplane(indir, outdir):
    '''copy everything in focalplane'''
    assert os.path.basename(indir) == 'focalplane'
    shutil.copytree(indir, outdir)

def trim_footprint(indir, outdir):
    '''Copies subset of desi-tiles.fits but not desi-tiles.par'''
    assert os.path.basename(indir) == 'footprint'
    if not os.path.exists(outdir):
        os.makedirs(outdir)
    infile, outfile = inout(indir, outdir, 'desi-tiles.fits')
    t = fits.getdata(infile)
    ii = (110 < t.RA) & (t.RA < 140) & (-10 < t.DEC) & (t.DEC < 20)
    fits.writeto(outfile, t[ii])

def trim_inputs(indir, outdir):
    '''Don't copy any inputs'''
    pass

def trim_sky(indir, outdir):
    '''don't copy any sky files; maybe they shouldn't be in desimodel anyway'''
    pass

def trim_specpsf(indir, outdir):
    '''trim specpsf files to be much smaller'''
    assert os.path.basename(indir) == 'specpsf'
    if not os.path.exists(outdir):
        os.makedirs(outdir)
    trim_quickpsf(indir, outdir, 'psf-quicksim.fits')
    trim_psf(indir, outdir, 'psf-b.fits')
    trim_psf(indir, outdir, 'psf-r.fits')
    trim_psf(indir, outdir, 'psf-z.fits')

def trim_spectra(indir, outdir):
    '''downsample spectra, and only a few of them'''
    assert os.path.basename(indir) == 'spectra'
    if not os.path.exists(outdir):
        os.makedirs(outdir)
    for filename in (
        'spec-ABmag22.0.dat',
        'spec-elg-o2flux-8e-17-average-line-ratios.dat',
        'spec-lrg-z0.8-zmag20.38.dat',
        'spec-qso-z1.5-rmag22.81.dat',
        'spec-sky.dat',
        'spec-sky-grey.dat',
        'spec-sky-bright.dat',
        ):
        i = 0
        fx = open(os.path.join(outdir, filename), 'w')
        for line in open(os.path.join(indir, filename)):
            if line.startswith('#'):
                fx.write(line)
            elif i%20 == 0:
                fx.write(line)
            i += 1
        fx.close()

def trim_targets(indir, outdir):
    '''copy everything in targets/'''
    assert os.path.basename(indir) == 'targets'
    shutil.copytree(indir, outdir)

def trim_throughput(indir, outdir):
    '''downsample throughput files'''
    assert os.path.basename(indir) == 'throughput'
    if not os.path.exists(outdir):
        os.makedirs(outdir)
    for filename in ['thru-b.fits', 'thru-r.fits', 'thru-z.fits']:
        fx = fits.open(indir+'/'+filename)
        hdus = HDUList()
        hdus.append(fx[0])
        hdus.append(BinTableHDU(fx[1].data[::20], header=fx[1].header))
        hdus.append(BinTableHDU(fx[2].data[::20], header=fx[2].header))
        hdus.writeto(outdir+'/'+filename)
        fx.close()

#-------------------------------------------------------------------------
#- Triming PSF files

def rebin_image(image, n):
    """
    rebin 2D array pix into bins of size n x n

    New binsize must be evenly divisible into original pix image
    """
    assert image.shape[0] % n == 0
    assert image.shape[1] % n == 0

    s = image.shape[0]//n, n, image.shape[1]//n, n
    return image.reshape(s).sum(-1).sum(1)

def trim_psf(indir, outdir, filename):
    assert os.path.abspath(indir) != os.path.abspath(outdir)
    infile = os.path.join(indir, filename)
    outfile = os.path.join(outdir, filename)

    fx = fits.open(infile)
    hdus = HDUList()

    #- HDU 0 XCOEFF - data unchanged but update keywords for less samples
    xcoeff = fx[0].data
    hdr = fx[0].header
    hdr['NWAVE'] = 3    #- down from 11
    hdr['CRPIX1'] = 23  #- 23=45//2+1, down from 113=225//2+1
    hdr['CRPIX1'] = 23
    hdr['CDELT1'] = 0.005   #- 5mm instead of 1mm
    hdr['CDELT2'] = 0.005   #- 5mm instead of 1mm
    hdr['PIXSIZE'] = 0.005   #- 5mm instead of 1mm

    hdus.append(PrimaryHDU(xcoeff, header=hdr))
    hdus.append(fx['YCOEFF'])

    #- subsample spots
    inspots = fx['SPOTS'].data
    spots = np.zeros((3,3,45,45))
    spots[0,0] = rebin_image(inspots[0,0], 5)
    spots[1,0] = rebin_image(inspots[5,0], 5)
    spots[2,0] = rebin_image(inspots[10,0], 5)
    spots[0,1] = rebin_image(inspots[0,5], 5)
    spots[1,1] = rebin_image(inspots[5,5], 5)
    spots[2,1] = rebin_image(inspots[10,5], 5)
    spots[0,2] = rebin_image(inspots[0,10], 5)
    spots[1,2] = rebin_image(inspots[5,10], 5)
    spots[2,2] = rebin_image(inspots[10,10], 5)
    hdus.append(ImageHDU(spots, header=fx['SPOTS'].header))

    #- subsample spots x,y locations
    dx = fx['SPOTX'].data
    hdus.append(ImageHDU(dx[::5, ::5], header=fx['SPOTX'].header))
    dy = fx['SPOTY'].data
    hdus.append(ImageHDU(dy[::5, ::5], header=fx['SPOTY'].header))

    #- Fiberpos unchanged
    hdus.append(fx['FIBERPOS'])

    #- Subsample SPOTPOS and SPOTWAVE
    d = fx['SPOTPOS'].data
    hdus.append(ImageHDU(d[::5], header=fx['SPOTPOS'].header))
    d = fx['SPOTWAVE'].data
    hdus.append(ImageHDU(d[::5], header=fx['SPOTWAVE'].header))

    hdus.writeto(outfile, clobber=True)
    fx.close()

def trim_quickpsf(indir, outdir, filename):
    assert os.path.abspath(indir) != os.path.abspath(outdir)
    infile = os.path.join(indir, filename)
    outfile = os.path.join(outdir, filename)
    fx = fits.open(infile)
    hdus = HDUList()
    hdus.append(fx[0])
    for i in [1,2,3]:
        d = fx[i].data
        hdus.append(BinTableHDU(d[::10], header=fx[i].header))
    hdus.writeto(outfile, clobber=True)
    fx.close()
<|endoftext|>


<commit_before>"""
desitarget.mtl
==============

Merged target lists.
"""

import numpy as np
import sys
from astropy.table import Table

from desitarget.targetmask import desi_mask, bgs_mask, mws_mask, obsmask, obsconditions
from desitarget.targets import calc_numobs, calc_priority_no_table

def make_mtl(targets, zcat=None, trim=False):
    """Adds NUMOBS, PRIORITY, and OBSCONDITIONS columns to a targets table.

    Parameters
    ----------
    targets : :class:`~numpy.array` or `~astropy.table.Table`
        A numpy rec array or astropy Table with at least the columns
        ``TARGETID``, ``DESI_TARGET``.
    zcat : :class:`~astropy.table.Table`, optional
        Redshift catalog table with columns ``TARGETID``, ``NUMOBS``, ``Z``,
        ``ZWARN``.
    trim : :class:`bool`, optional
        If ``True`` (default), don't include targets that don't need
        any more observations.  If ``False``, include every input target.

    Returns
    -------
    :class:`~astropy.table.Table`
        MTL Table with targets columns plus:

        * NUMOBS_MORE    - number of additional observations requested
        * PRIORITY       - target priority (larger number = higher priority)
        * OBSCONDITIONS  - replaces old GRAYLAYER
    """
    # ADM set up the default logger.
    from desiutil.log import get_logger
    log = get_logger()

    # Trim targets from zcat that aren't in original targets table
    if zcat is not None:
        ok = np.in1d(zcat['TARGETID'], targets['TARGETID'])
        num_extra = np.count_nonzero(~ok)
        if num_extra > 0:
            log.warning("Ignoring {} zcat entries that aren't "
                        "in the input target list".format(num_extra))
            zcat = zcat[ok]

    n = len(targets)
    # ADM if the input target columns were incorrectly called NUMOBS or PRIORITY
    # ADM rename them to NUMOBS_INIT or PRIORITY_INIT.
    # ADM Note that the syntax is slightly different for a Table.
    for name in ['NUMOBS', 'PRIORITY']:
        if isinstance(targets, Table):
            try:
                targets.rename_column(name, name+'_INIT')
            except KeyError:
                pass
        else:
            targets.dtype.names = [name+'_INIT' if col == name else col for col in targets.dtype.names]

    # ADM if a redshift catalog was passed, order it to match the input targets
    # ADM catalog on 'TARGETID'.
    if zcat is not None:
        # ADM there might be a quicker way to do this?
        # ADM set up a dictionary of the indexes of each target id.
        d = dict(tuple(zip(targets["TARGETID"], np.arange(n))))
        # ADM loop through the zcat and look-up the index in the dictionary.
        zmatcher = np.array([d[tid] for tid in zcat["TARGETID"]])
        ztargets = zcat
        if ztargets.masked:
            unobs = ztargets['NUMOBS'].mask
            ztargets['NUMOBS'][unobs] = 0
            unobsz = ztargets['Z'].mask
            ztargets['Z'][unobsz] = -1
            unobszw = ztargets['ZWARN'].mask
            ztargets['ZWARN'][unobszw] = -1
    else:
        ztargets = Table()
        ztargets['TARGETID'] = targets['TARGETID']
        ztargets['NUMOBS'] = np.zeros(n, dtype=np.int32)
        ztargets['Z'] = -1 * np.ones(n, dtype=np.float32)
        ztargets['ZWARN'] = -1 * np.ones(n, dtype=np.int32)
        # ADM if zcat wasn't passed, there is a one-to-one correspondence
        # ADM between the targets and the zcat.
        zmatcher = np.arange(n)

    # ADM extract just the targets that match the input zcat.
    targets_zmatcher = targets[zmatcher]

    # ADM use passed value of NUMOBS_INIT instead of calling the memory-heavy calc_numobs.
    # ztargets['NUMOBS_MORE'] = np.maximum(0, calc_numobs(ztargets) - ztargets['NUMOBS'])
    ztargets['NUMOBS_MORE'] = np.maximum(0, targets_zmatcher['NUMOBS_INIT'] - ztargets['NUMOBS'])

    # ADM we need a minor hack to ensure that BGS targets are observed once (and only once)
    # ADM every time, regardless of how many times they've previously been observed.
    ii = targets_zmatcher["DESI_TARGET"] & desi_mask.BGS_ANY > 0
    ztargets['NUMOBS_MORE'][ii] = 1

    # ADM assign priorities, note that only things in the zcat can have changed priorities.
    # ADM anything else will be assigned PRIORITY_INIT, below.
    priority = calc_priority_no_table(targets_zmatcher, ztargets)

    # If priority went to 0==DONOTOBSERVE or 1==OBS or 2==DONE, then NUMOBS_MORE should also be 0.
    # ## mtl['NUMOBS_MORE'] = ztargets['NUMOBS_MORE']
    ii = (priority <= 2)
    log.info('{:d} of {:d} targets have priority zero, setting N_obs=0.'.format(np.sum(ii), n))
    ztargets['NUMOBS_MORE'][ii] = 0

    # - Set the OBSCONDITIONS mask for each target bit.
    obscon = np.zeros(n, dtype='i4')
    for mask, xxx_target in [
            (desi_mask, 'DESI_TARGET'),
            (mws_mask, 'MWS_TARGET'),
            (bgs_mask, 'BGS_TARGET')]:
        for name in mask.names():
            # - which targets have this bit for this mask set?
            ii = (targets[xxx_target] & mask[name]) != 0
            # - under what conditions can that bit be observed?
            if np.any(ii):
                obscon[ii] |= obsconditions.mask(mask[name].obsconditions)

    # ADM set up the output mtl table.
    mtl = Table(targets)
    mtl.meta['EXTNAME'] = 'MTL'
    # ADM any target that wasn't matched to the ZCAT should retain its
    # ADM original (INIT) value of PRIORITY and NUMOBS.
    mtl['NUMOBS_MORE'] = mtl['NUMOBS_INIT']
    mtl['PRIORITY'] = mtl['PRIORITY_INIT']
    # ADM now populate the new mtl columns with the updated information.
    mtl['OBSCONDITIONS'] = obscon
    mtl['PRIORITY'][zmatcher] = priority
    mtl['NUMOBS_MORE'][zmatcher] = ztargets['NUMOBS_MORE']

    # Filter out any targets marked as done.
    if trim:
        notdone = mtl['NUMOBS_MORE'] > 0
        log.info('{:d} of {:d} targets are done, trimming these'.format(
            len(mtl) - np.sum(notdone), len(mtl))
        )
        mtl = mtl[notdone]

    # Filtering can reset the fill_value, which is just wrong wrong wrong
    # See https://github.com/astropy/astropy/issues/4707
    # and https://github.com/astropy/astropy/issues/4708
    mtl['NUMOBS_MORE'].fill_value = -1

    return mtl
<commit_msg>call calc_priority instead of calc_priority_no_table in mtl<commit_after>"""
desitarget.mtl
==============

Merged target lists.
"""

import numpy as np
import sys
from astropy.table import Table

from desitarget.targetmask import desi_mask, bgs_mask, mws_mask, obsmask, obsconditions
from desitarget.targets import calc_numobs, calc_priority

def make_mtl(targets, zcat=None, trim=False):
    """Adds NUMOBS, PRIORITY, and OBSCONDITIONS columns to a targets table.

    Parameters
    ----------
    targets : :class:`~numpy.array` or `~astropy.table.Table`
        A numpy rec array or astropy Table with at least the columns
        ``TARGETID``, ``DESI_TARGET``.
    zcat : :class:`~astropy.table.Table`, optional
        Redshift catalog table with columns ``TARGETID``, ``NUMOBS``, ``Z``,
        ``ZWARN``.
    trim : :class:`bool`, optional
        If ``True`` (default), don't include targets that don't need
        any more observations.  If ``False``, include every input target.

    Returns
    -------
    :class:`~astropy.table.Table`
        MTL Table with targets columns plus:

        * NUMOBS_MORE    - number of additional observations requested
        * PRIORITY       - target priority (larger number = higher priority)
        * OBSCONDITIONS  - replaces old GRAYLAYER
    """
    # ADM set up the default logger.
    from desiutil.log import get_logger
    log = get_logger()

    # Trim targets from zcat that aren't in original targets table
    if zcat is not None:
        ok = np.in1d(zcat['TARGETID'], targets['TARGETID'])
        num_extra = np.count_nonzero(~ok)
        if num_extra > 0:
            log.warning("Ignoring {} zcat entries that aren't "
                        "in the input target list".format(num_extra))
            zcat = zcat[ok]

    n = len(targets)
    # ADM if the input target columns were incorrectly called NUMOBS or PRIORITY
    # ADM rename them to NUMOBS_INIT or PRIORITY_INIT.
    # ADM Note that the syntax is slightly different for a Table.
    for name in ['NUMOBS', 'PRIORITY']:
        if isinstance(targets, Table):
            try:
                targets.rename_column(name, name+'_INIT')
            except KeyError:
                pass
        else:
            targets.dtype.names = [name+'_INIT' if col == name else col for col in targets.dtype.names]

    # ADM if a redshift catalog was passed, order it to match the input targets
    # ADM catalog on 'TARGETID'.
    if zcat is not None:
        # ADM there might be a quicker way to do this?
        # ADM set up a dictionary of the indexes of each target id.
        d = dict(tuple(zip(targets["TARGETID"], np.arange(n))))
        # ADM loop through the zcat and look-up the index in the dictionary.
        zmatcher = np.array([d[tid] for tid in zcat["TARGETID"]])
        ztargets = zcat
        if ztargets.masked:
            unobs = ztargets['NUMOBS'].mask
            ztargets['NUMOBS'][unobs] = 0
            unobsz = ztargets['Z'].mask
            ztargets['Z'][unobsz] = -1
            unobszw = ztargets['ZWARN'].mask
            ztargets['ZWARN'][unobszw] = -1
    else:
        ztargets = Table()
        ztargets['TARGETID'] = targets['TARGETID']
        ztargets['NUMOBS'] = np.zeros(n, dtype=np.int32)
        ztargets['Z'] = -1 * np.ones(n, dtype=np.float32)
        ztargets['ZWARN'] = -1 * np.ones(n, dtype=np.int32)
        # ADM if zcat wasn't passed, there is a one-to-one correspondence
        # ADM between the targets and the zcat.
        zmatcher = np.arange(n)

    # ADM extract just the targets that match the input zcat.
    targets_zmatcher = targets[zmatcher]

    # ADM use passed value of NUMOBS_INIT instead of calling the memory-heavy calc_numobs.
    # ztargets['NUMOBS_MORE'] = np.maximum(0, calc_numobs(ztargets) - ztargets['NUMOBS'])
    ztargets['NUMOBS_MORE'] = np.maximum(0, targets_zmatcher['NUMOBS_INIT'] - ztargets['NUMOBS'])

    # ADM we need a minor hack to ensure that BGS targets are observed once (and only once)
    # ADM every time, regardless of how many times they've previously been observed.
    ii = targets_zmatcher["DESI_TARGET"] & desi_mask.BGS_ANY > 0
    ztargets['NUMOBS_MORE'][ii] = 1

    # ADM assign priorities, note that only things in the zcat can have changed priorities.
    # ADM anything else will be assigned PRIORITY_INIT, below.
    priority = calc_priority(targets_zmatcher, ztargets)

    # If priority went to 0==DONOTOBSERVE or 1==OBS or 2==DONE, then NUMOBS_MORE should also be 0.
    # ## mtl['NUMOBS_MORE'] = ztargets['NUMOBS_MORE']
    ii = (priority <= 2)
    log.info('{:d} of {:d} targets have priority zero, setting N_obs=0.'.format(np.sum(ii), n))
    ztargets['NUMOBS_MORE'][ii] = 0

    # - Set the OBSCONDITIONS mask for each target bit.
    obscon = np.zeros(n, dtype='i4')
    for mask, xxx_target in [
            (desi_mask, 'DESI_TARGET'),
            (mws_mask, 'MWS_TARGET'),
            (bgs_mask, 'BGS_TARGET')]:
        for name in mask.names():
            # - which targets have this bit for this mask set?
            ii = (targets[xxx_target] & mask[name]) != 0
            # - under what conditions can that bit be observed?
            if np.any(ii):
                obscon[ii] |= obsconditions.mask(mask[name].obsconditions)

    # ADM set up the output mtl table.
    mtl = Table(targets)
    mtl.meta['EXTNAME'] = 'MTL'
    # ADM any target that wasn't matched to the ZCAT should retain its
    # ADM original (INIT) value of PRIORITY and NUMOBS.
    mtl['NUMOBS_MORE'] = mtl['NUMOBS_INIT']
    mtl['PRIORITY'] = mtl['PRIORITY_INIT']
    # ADM now populate the new mtl columns with the updated information.
    mtl['OBSCONDITIONS'] = obscon
    mtl['PRIORITY'][zmatcher] = priority
    mtl['NUMOBS_MORE'][zmatcher] = ztargets['NUMOBS_MORE']

    # Filter out any targets marked as done.
    if trim:
        notdone = mtl['NUMOBS_MORE'] > 0
        log.info('{:d} of {:d} targets are done, trimming these'.format(
            len(mtl) - np.sum(notdone), len(mtl))
        )
        mtl = mtl[notdone]

    # Filtering can reset the fill_value, which is just wrong wrong wrong
    # See https://github.com/astropy/astropy/issues/4707
    # and https://github.com/astropy/astropy/issues/4708
    mtl['NUMOBS_MORE'].fill_value = -1

    return mtl
<|endoftext|>


<commit_before># Licensed under a 3-clause BSD style license - see LICENSE.rst
# -*- coding: utf-8 -*-
"""
==================
desiutils.funcfits
==================

Module for fitting simple functions to 1D arrays

J. Xavier Prochaska, UC Santa Cruz
Fall 2015

.. _desispec: http://desispec.readthedocs.org
"""
from __future__ import print_function, absolute_import, division, unicode_literals

import numpy as np
#from scipy.interpolate import interp1d
#import pdb

def perc(x, per=68.2):
    """ Calculate the percentile bounds of a distribution,
    i.e. for per=0.68, the code returns the upper and lower bounds
    that encompass 68percent of the distribution.

    Uses simple interpolation

    Parameters
    ----------
      x : float
        numpy array of values
      per : float, optional
          Percentile for the calculation

    Returns
    -------
      xper : array
        Value at lower, value at upper
    """
    #
    return np.percentile(x, [50-per/2.0, 50+per/2.0])

<commit_msg>tweak docs to clarify [0,100] percentile range<commit_after># Licensed under a 3-clause BSD style license - see LICENSE.rst
# -*- coding: utf-8 -*-
"""
==================
desiutils.funcfits
==================

Module for fitting simple functions to 1D arrays

J. Xavier Prochaska, UC Santa Cruz
Fall 2015

.. _desispec: http://desispec.readthedocs.org
"""
from __future__ import print_function, absolute_import, division, unicode_literals

import numpy as np
#import pdb

def perc(x, per=68.2):
    """ Calculate the percentile bounds of a distribution,
    i.e. for per=68, the code returns the upper and lower bounds
    that encompass 68percent of the distribution.

    Uses simple interpolation

    Parameters
    ----------
      x : float
        numpy array of values
      per : float, optional
        Percentile for the calculation [0-100]

    Returns
    -------
      xper : array
        Value at lower, value at upper
    """
    #
    return np.percentile(x, [50-per/2.0, 50+per/2.0])

<|endoftext|>


<commit_before>"""
Main program for 2to3.
"""

from __future__ import print_function

from __future__ import with_statement

import sys
import os
import difflib
import logging
import shutil
import optparse

from . import refactor

def diff_texts(a, b, filename):
    """Return a unified diff of two strings."""
    a = a.splitlines()
    b = b.splitlines()
    return difflib.unified_diff(a, b, filename, filename,
                                "(original)", "(refactored)",
                                lineterm="")

class StdoutRefactoringTool(refactor.MultiprocessRefactoringTool):
    """
    A refactoring tool that can avoid overwriting its input files.
    Prints output to stdout.

    Output files can optionally be written to a different directory and or
    have an extra file suffix appended to their name for use in situations
    where you do not want to replace the input files.
    """

    def __init__(self, fixers, options, explicit, nobackups, show_diffs,
                 input_base_dir='', output_dir='', append_suffix=''):
        """
        Args:
            fixers: A list of fixers to import.
            options: A dict with RefactoringTool configuration.
            explicit: A list of fixers to run even if they are explicit.
            nobackups: If true no backup '.bak' files will be created for those
                files that are being refactored.
            show_diffs: Should diffs of the refactoring be printed to stdout?
            input_base_dir: The base directory for all input files.  This class
                will strip this path prefix off of filenames before substituting
                it with output_dir.  Only meaningful if output_dir is supplied.
                All files processed by refactor() must start with this path.
            output_dir: If supplied, all converted files will be written into
                this directory tree instead of input_base_dir.
            append_suffix: If supplied, all files output by this tool will have
                this appended to their filename.  Useful for changing .py to
                .py3 for example by passing append_suffix='3'.
        """
        self.nobackups = nobackups
        self.show_diffs = show_diffs
        if input_base_dir and not input_base_dir.endswith(os.sep):
            input_base_dir += os.sep
        self._input_base_dir = input_base_dir
        self._output_dir = output_dir
        self._append_suffix = append_suffix
        super(StdoutRefactoringTool, self).__init__(fixers, options, explicit)

    def log_error(self, msg, *args, **kwargs):
        self.errors.append((msg, args, kwargs))
        self.logger.error(msg, *args, **kwargs)

    def write_file(self, new_text, filename, old_text, encoding):
        orig_filename = filename
        if self._output_dir:
            if filename.startswith(self._input_base_dir):
                filename = os.path.join(self._output_dir,
                                        filename[len(self._input_base_dir):])
            else:
                raise ValueError('filename %s does not start with the '
                                 'input_base_dir %s' % (
                                         filename, self._input_base_dir))
        if self._append_suffix:
            filename += self._append_suffix
        if orig_filename != filename:
            output_dir = os.path.dirname(filename)
            if not os.path.isdir(output_dir):
                os.makedirs(output_dir)
            self.log_message('Writing converted %s to %s.', orig_filename,
                             filename)
        if not self.nobackups:
            # Make backup
            backup = filename + ".bak"
            if os.path.lexists(backup):
                try:
                    os.remove(backup)
                except os.error:
                    self.log_message("Can't remove backup %s", backup)
            try:
                os.rename(filename, backup)
            except os.error:
                self.log_message("Can't rename %s to %s", filename, backup)
        # Actually write the new file
        write = super(StdoutRefactoringTool, self).write_file
        write(new_text, filename, old_text, encoding)
        if not self.nobackups:
            shutil.copymode(backup, filename)
        if orig_filename != filename:
            # Preserve the file mode in the new output directory.
            shutil.copymode(orig_filename, filename)

    def print_output(self, old, new, filename, equal):
        if equal:
            self.log_message("No changes to %s", filename)
        else:
            self.log_message("Refactored %s", filename)
            if self.show_diffs:
                diff_lines = diff_texts(old, new, filename)
                try:
                    if self.output_lock is not None:
                        with self.output_lock:
                            for line in diff_lines:
                                print(line)
                            sys.stdout.flush()
                    else:
                        for line in diff_lines:
                            print(line)
                except UnicodeEncodeError:
                    warn("couldn't encode %s's diff for your terminal" %
                         (filename,))
                    return

def warn(msg):
    print("WARNING: %s" % (msg,), file=sys.stderr)

def main(fixer_pkg='py3kwarn2to3.fixes', args=None):
    """Main program.

    Args:
        fixer_pkg: the name of a package where the fixers are located.
        args: optional; a list of command line arguments. If omitted,
              sys.argv[1:] is used.

    Returns a suggested exit status (0, 1, 2).

    """
    # Set up option parser
    parser = optparse.OptionParser(usage="2to3 [options] file|dir ...")
    parser.add_option("-d", "--doctests-only", action="store_true",
                      help="Fix up doctests only")
    parser.add_option("-f", "--fix", action="append", default=[],
                      help="Each FIX specifies a transformation; default: all")
    parser.add_option("-j", "--processes", action="store", default=1,
                      type="int", help="Run 2to3 concurrently")
    parser.add_option("-x", "--nofix", action="append", default=[],
                      help="Prevent a transformation from being run")
    parser.add_option("-l", "--list-fixes", action="store_true",
                      help="List available transformations")
    parser.add_option("-p", "--print-function", action="store_true",
                      help="Modify the grammar so that print() is a function")
    parser.add_option("-v", "--verbose", action="store_true",
                      help="More verbose logging")
    parser.add_option("--no-diffs", action="store_true",
                      help="Don't show diffs of the refactoring")
    parser.add_option("-w", "--write", action="store_true",
                      help="Write back modified files")
    parser.add_option("-n", "--nobackups", action="store_true", default=False,
                      help="Don't write backups for modified files")
    parser.add_option("-o", "--output-dir", action="store", type="str",
                      default="", help="Put output files in this directory "
                      "instead of overwriting the input files.  Requires -n.")
    parser.add_option("-W", "--write-unchanged-files", action="store_true",
                      help="Also write files even if no changes were required"
                      " (useful with --output-dir); implies -w.")
    parser.add_option("--add-suffix", action="store", type="str", default="",
                      help="Append this string to all output filenames."
                      " Requires -n if non-empty.  "
                      "ex: --add-suffix='3' will generate .py3 files.")

    # Parse command line arguments
    refactor_stdin = False
    flags = {}
    options, args = parser.parse_args(args)
    if options.write_unchanged_files:
        flags["write_unchanged_files"] = True
        if not options.write:
            warn("--write-unchanged-files/-W implies -w.")
        options.write = True
    # If we allowed these, the original files would be renamed to backup names
    # but not replaced.
    if options.output_dir and not options.nobackups:
        parser.error("Can't use --output-dir/-o without -n.")
    if options.add_suffix and not options.nobackups:
        parser.error("Can't use --add-suffix without -n.")

    if not options.write and options.no_diffs:
        warn("not writing files and not printing diffs; that's not very useful")
    if not options.write and options.nobackups:
        parser.error("Can't use -n without -w")
    if options.list_fixes:
        print("Available transformations for the -f/--fix option:")
        for fixname in refactor.get_all_fix_names(fixer_pkg):
            print(fixname)
        if not args:
            return 0
    if not args:
        print("At least one file or directory argument required.", file=sys.stderr)
        print("Use --help to show usage.", file=sys.stderr)
        return 2
    if "-" in args:
        refactor_stdin = True
        if options.write:
            print("Can't write to stdin.", file=sys.stderr)
            return 2
    if options.print_function:
        flags["print_function"] = True

    # Set up logging handler
    level = logging.DEBUG if options.verbose else logging.INFO
    logging.basicConfig(format='%(name)s: %(message)s', level=level)
    logger = logging.getLogger('py3kwarn2to3.main')

    # Initialize the refactoring tool
    avail_fixes = set(refactor.get_fixers_from_package(fixer_pkg))
    unwanted_fixes = set(fixer_pkg + ".fix_" + fix for fix in options.nofix)
    explicit = set()
    if options.fix:
        all_present = False
        for fix in options.fix:
            if fix == "all":
                all_present = True
            else:
                explicit.add(fixer_pkg + ".fix_" + fix)
        requested = avail_fixes.union(explicit) if all_present else explicit
    else:
        requested = avail_fixes.union(explicit)
    fixer_names = requested.difference(unwanted_fixes)
    input_base_dir = os.path.commonprefix(args)
    if (input_base_dir and not input_base_dir.endswith(os.sep)
        and not os.path.isdir(input_base_dir)):
        # One or more similar names were passed, their directory is the base.
        # os.path.commonprefix() is ignorant of path elements, this corrects
        # for that weird API.
        input_base_dir = os.path.dirname(input_base_dir)
    if options.output_dir:
        input_base_dir = input_base_dir.rstrip(os.sep)
        logger.info('Output in %r will mirror the input directory %r layout.',
                    options.output_dir, input_base_dir)
    rt = StdoutRefactoringTool(
            sorted(fixer_names), flags, sorted(explicit),
            options.nobackups, not options.no_diffs,
            input_base_dir=input_base_dir,
            output_dir=options.output_dir,
            append_suffix=options.add_suffix)

    # Refactor all files and directories passed as arguments
    if not rt.errors:
        if refactor_stdin:
            rt.refactor_stdin()
        else:
            try:
                rt.refactor(args, options.write, options.doctests_only,
                            options.processes)
            except refactor.MultiprocessingUnsupported:
                assert options.processes > 1
                print("Sorry, -j isn't "
                      "supported on this platform.", file=sys.stderr)
                return 1
        rt.summarize()

    # Return error status (0 if rt.errors is zero)
    return int(bool(rt.errors))
<commit_msg>Use a better name<commit_after>"""
Main program for 2to3.
"""

from __future__ import print_function

from __future__ import with_statement

import sys
import os
import difflib
import logging
import shutil
import optparse

from . import refactor

def diff_texts(a, b, filename):
    """Return a unified diff of two strings."""
    a = a.splitlines()
    b = b.splitlines()
    return difflib.unified_diff(a, b, filename, filename,
                                "(original)", "(refactored)",
                                lineterm="")

class StdoutRefactoringTool(refactor.MultiprocessRefactoringTool):
    """
    A refactoring tool that can avoid overwriting its input files.
    Prints output to stdout.

    Output files can optionally be written to a different directory and or
    have an extra file suffix appended to their name for use in situations
    where you do not want to replace the input files.
    """

    def __init__(self, fixers, options, explicit, nobackups, show_diffs,
                 input_base_dir='', output_dir='', append_suffix=''):
        """
        Args:
            fixers: A list of fixers to import.
            options: A dict with RefactoringTool configuration.
            explicit: A list of fixers to run even if they are explicit.
            nobackups: If true no backup '.bak' files will be created for those
                files that are being refactored.
            show_diffs: Should diffs of the refactoring be printed to stdout?
            input_base_dir: The base directory for all input files.  This class
                will strip this path prefix off of filenames before substituting
                it with output_dir.  Only meaningful if output_dir is supplied.
                All files processed by refactor() must start with this path.
            output_dir: If supplied, all converted files will be written into
                this directory tree instead of input_base_dir.
            append_suffix: If supplied, all files output by this tool will have
                this appended to their filename.  Useful for changing .py to
                .py3 for example by passing append_suffix='3'.
        """
        self.nobackups = nobackups
        self.show_diffs = show_diffs
        if input_base_dir and not input_base_dir.endswith(os.sep):
            input_base_dir += os.sep
        self._input_base_dir = input_base_dir
        self._output_dir = output_dir
        self._append_suffix = append_suffix
        super(StdoutRefactoringTool, self).__init__(fixers, options, explicit)

    def log_error(self, msg, *args, **kwargs):
        self.errors.append((msg, args, kwargs))
        self.logger.error(msg, *args, **kwargs)

    def write_file(self, new_text, filename, old_text, encoding):
        orig_filename = filename
        if self._output_dir:
            if filename.startswith(self._input_base_dir):
                filename = os.path.join(self._output_dir,
                                        filename[len(self._input_base_dir):])
            else:
                raise ValueError('filename %s does not start with the '
                                 'input_base_dir %s' % (
                                         filename, self._input_base_dir))
        if self._append_suffix:
            filename += self._append_suffix
        if orig_filename != filename:
            output_dir = os.path.dirname(filename)
            if not os.path.isdir(output_dir):
                os.makedirs(output_dir)
            self.log_message('Writing converted %s to %s.', orig_filename,
                             filename)
        if not self.nobackups:
            # Make backup
            backup = filename + ".bak"
            if os.path.lexists(backup):
                try:
                    os.remove(backup)
                except os.error:
                    self.log_message("Can't remove backup %s", backup)
            try:
                os.rename(filename, backup)
            except os.error:
                self.log_message("Can't rename %s to %s", filename, backup)
        # Actually write the new file
        write = super(StdoutRefactoringTool, self).write_file
        write(new_text, filename, old_text, encoding)
        if not self.nobackups:
            shutil.copymode(backup, filename)
        if orig_filename != filename:
            # Preserve the file mode in the new output directory.
            shutil.copymode(orig_filename, filename)

    def print_output(self, old, new, filename, equal):
        if equal:
            self.log_message("No changes to %s", filename)
        else:
            self.log_message("Refactored %s", filename)
            if self.show_diffs:
                diff_lines = diff_texts(old, new, filename)
                try:
                    if self.output_lock is not None:
                        with self.output_lock:
                            for line in diff_lines:
                                print(line)
                            sys.stdout.flush()
                    else:
                        for line in diff_lines:
                            print(line)
                except UnicodeEncodeError:
                    warn("couldn't encode %s's diff for your terminal" %
                         (filename,))
                    return

def warn(msg):
    print("WARNING: %s" % (msg,), file=sys.stderr)

def main(fixer_pkg='py3kwarn2to3.fixes', args=None):
    """Main program.

    Args:
        fixer_pkg: the name of a package where the fixers are located.
        args: optional; a list of command line arguments. If omitted,
              sys.argv[1:] is used.

    Returns a suggested exit status (0, 1, 2).

    """
    # Set up option parser
    parser = optparse.OptionParser(usage="2to3 [options] file|dir ...")
    parser.add_option("-d", "--doctests", action="store_true",
                      help="Fix up doctests only")
    parser.add_option("-f", "--fix", action="append", default=[],
                      help="Each FIX specifies a transformation; default: all")
    parser.add_option("-j", "--processes", action="store", default=1,
                      type="int", help="Run 2to3 concurrently")
    parser.add_option("-x", "--nofix", action="append", default=[],
                      help="Prevent a transformation from being run")
    parser.add_option("-l", "--list-fixes", action="store_true",
                      help="List available transformations")
    parser.add_option("-p", "--print-function", action="store_true",
                      help="Modify the grammar so that print() is a function")
    parser.add_option("-v", "--verbose", action="store_true",
                      help="More verbose logging")
    parser.add_option("--no-diffs", action="store_true",
                      help="Don't show diffs of the refactoring")
    parser.add_option("-w", "--write", action="store_true",
                      help="Write back modified files")
    parser.add_option("-n", "--nobackups", action="store_true", default=False,
                      help="Don't write backups for modified files")
    parser.add_option("-o", "--output-dir", action="store", type="str",
                      default="", help="Put output files in this directory "
                      "instead of overwriting the input files.  Requires -n.")
    parser.add_option("-W", "--write-unchanged-files", action="store_true",
                      help="Also write files even if no changes were required"
                      " (useful with --output-dir); implies -w.")
    parser.add_option("--add-suffix", action="store", type="str", default="",
                      help="Append this string to all output filenames."
                      " Requires -n if non-empty.  "
                      "ex: --add-suffix='3' will generate .py3 files.")

    # Parse command line arguments
    refactor_stdin = False
    flags = {}
    options, args = parser.parse_args(args)
    if options.write_unchanged_files:
        flags["write_unchanged_files"] = True
        if not options.write:
            warn("--write-unchanged-files/-W implies -w.")
        options.write = True
    # If we allowed these, the original files would be renamed to backup names
    # but not replaced.
    if options.output_dir and not options.nobackups:
        parser.error("Can't use --output-dir/-o without -n.")
    if options.add_suffix and not options.nobackups:
        parser.error("Can't use --add-suffix without -n.")

    if not options.write and options.no_diffs:
        warn("not writing files and not printing diffs; that's not very useful")
    if not options.write and options.nobackups:
        parser.error("Can't use -n without -w")
    if options.list_fixes:
        print("Available transformations for the -f/--fix option:")
        for fixname in refactor.get_all_fix_names(fixer_pkg):
            print(fixname)
        if not args:
            return 0
    if not args:
        print("At least one file or directory argument required.", file=sys.stderr)
        print("Use --help to show usage.", file=sys.stderr)
        return 2
    if "-" in args:
        refactor_stdin = True
        if options.write:
            print("Can't write to stdin.", file=sys.stderr)
            return 2
    if options.print_function:
        flags["print_function"] = True

    # Set up logging handler
    level = logging.DEBUG if options.verbose else logging.INFO
    logging.basicConfig(format='%(name)s: %(message)s', level=level)
    logger = logging.getLogger('py3kwarn2to3.main')

    # Initialize the refactoring tool
    avail_fixes = set(refactor.get_fixers_from_package(fixer_pkg))
    unwanted_fixes = set(fixer_pkg + ".fix_" + fix for fix in options.nofix)
    explicit = set()
    if options.fix:
        all_present = False
        for fix in options.fix:
            if fix == "all":
                all_present = True
            else:
                explicit.add(fixer_pkg + ".fix_" + fix)
        requested = avail_fixes.union(explicit) if all_present else explicit
    else:
        requested = avail_fixes.union(explicit)
    fixer_names = requested.difference(unwanted_fixes)
    input_base_dir = os.path.commonprefix(args)
    if (input_base_dir and not input_base_dir.endswith(os.sep)
        and not os.path.isdir(input_base_dir)):
        # One or more similar names were passed, their directory is the base.
        # os.path.commonprefix() is ignorant of path elements, this corrects
        # for that weird API.
        input_base_dir = os.path.dirname(input_base_dir)
    if options.output_dir:
        input_base_dir = input_base_dir.rstrip(os.sep)
        logger.info('Output in %r will mirror the input directory %r layout.',
                    options.output_dir, input_base_dir)
    rt = StdoutRefactoringTool(
            sorted(fixer_names), flags, sorted(explicit),
            options.nobackups, not options.no_diffs,
            input_base_dir=input_base_dir,
            output_dir=options.output_dir,
            append_suffix=options.add_suffix)

    # Refactor all files and directories passed as arguments
    if not rt.errors:
        if refactor_stdin:
            rt.refactor_stdin()
        else:
            try:
                rt.refactor(args, options.write, options.doctests,
                            options.processes)
            except refactor.MultiprocessingUnsupported:
                assert options.processes > 1
                print("Sorry, -j isn't "
                      "supported on this platform.", file=sys.stderr)
                return 1
        rt.summarize()

    # Return error status (0 if rt.errors is zero)
    return int(bool(rt.errors))
<|endoftext|>


<commit_before># py3status module for Spotify using playerctl

import re
import sys
import subprocess

# In the absence of a GVariant parser, do some rudimentary parsing.
# This certainly will fail for arbitrary valid input, and will accept some
# invalid input, but it is resilient enough in practice to not be worth
# improving until it causes a problem.

# Key value pairs from GVariant data
gvariant_re   = r"'(?P<key>[\w:]+)': <(?P<value>.*?)>"
# Strings with potentially mismatched quote marks
string_re     = r"('|\")(?P<value>.*)('|\")"
# First value in a list (must be a string)
first_list_re = r"\[('|\")(?P<value>.*?)('|\")(,|\]).*"

def run(*cmdlist):
    return subprocess.run(cmdlist, stdout=subprocess.PIPE).stdout.decode()

def get_status():
    return run('playerctl', 'status')[:-1]

def get_metadata():
    gvariant_data = run('playerctl', 'metadata')
    return dict(re.findall(gvariant_re, gvariant_data))

def get_value(metadata, key, regex):
    return re.match(regex, metadata[key]).group('value')

class Py3status:
    def spotbar(self):
        status = get_status()
        if status == 'Playing':
            metadata = get_metadata()
            title = get_value(metadata, 'xesam:title', string_re)
            artist = get_value(metadata, 'xesam:artist', first_list_re)
            full_text = ' {} - {} '.format(title, artist)
        elif status == 'Paused':
            full_text = ' Paused '
        else:
            full_text = ''
        return {
            'full_text': full_text,
            'cached_until': self.py3.time_in(seconds=1)
        }
<commit_msg>Remove whitespace<commit_after># py3status module for Spotify using playerctl

import re
import sys
import subprocess

# In the absence of a GVariant parser, do some rudimentary parsing.
# This certainly will fail for arbitrary valid input, and will accept some
# invalid input, but it is resilient enough in practice to not be worth
# improving until it causes a problem.

# Key value pairs from GVariant data
gvariant_re = r"'(?P<key>[\w:]+)': <(?P<value>.*?)>"
# Strings with potentially mismatched quote marks
string_re = r"('|\")(?P<value>.*)('|\")"
# First value in a list (must be a string)
first_list_re = r"\[('|\")(?P<value>.*?)('|\")(,|\]).*"

def run(*cmdlist):
    return subprocess.run(cmdlist, stdout=subprocess.PIPE).stdout.decode()

def get_status():
    return run('playerctl', 'status')[:-1]

def get_metadata():
    gvariant_data = run('playerctl', 'metadata')
    return dict(re.findall(gvariant_re, gvariant_data))

def get_value(metadata, key, regex):
    return re.match(regex, metadata[key]).group('value')

class Py3status:
    def spotbar(self):
        status = get_status()
        if status == 'Playing':
            metadata = get_metadata()
            title = get_value(metadata, 'xesam:title', string_re)
            artist = get_value(metadata, 'xesam:artist', first_list_re)
            full_text = ' {} - {} '.format(title, artist)
        elif status == 'Paused':
            full_text = ' Paused '
        else:
            full_text = ''
        return {
            'full_text': full_text,
            'cached_until': self.py3.time_in(seconds=1)
        }
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals

import datetime

from pyathena.error import *  # noqa

__version__ = '1.10.0'

# Globals https://www.python.org/dev/peps/pep-0249/#globals
apilevel = '2.0'
threadsafety = 3
paramstyle = 'pyformat'

class DBAPITypeObject:
    """Type Objects and Constructors

    https://www.python.org/dev/peps/pep-0249/#type-objects-and-constructors
    """
    def __init__(self, *values):
        self.values = values

    def __cmp__(self, other):
        if other in self.values:
            return 0
        if other < self.values:
            return 1
        else:
            return -1

    def __eq__(self, other):
        return other in self.values

# https://docs.aws.amazon.com/athena/latest/ug/data-types.html
STRING = DBAPITypeObject('char', 'varchar', 'map', 'array', 'row')
BINARY = DBAPITypeObject('varbinary')
BOOLEAN = DBAPITypeObject('boolean')
NUMBER = DBAPITypeObject('tinyint', 'smallint', 'bigint', 'integer',
                         'real', 'double', 'float', 'decimal')
DATE = DBAPITypeObject('date')
TIME = DBAPITypeObject('time', 'time with time zone')
DATETIME = DBAPITypeObject('timestamp', 'timestamp with time zone')
JSON = DBAPITypeObject('json')

Date = datetime.date
Time = datetime.time
Timestamp = datetime.datetime

def connect(*args, **kwargs):
    from pyathena.connection import Connection
    return Connection(*args, **kwargs)
<commit_msg>Bump version to 1.10.1<commit_after># -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals

import datetime

from pyathena.error import *  # noqa

__version__ = '1.10.1'

# Globals https://www.python.org/dev/peps/pep-0249/#globals
apilevel = '2.0'
threadsafety = 3
paramstyle = 'pyformat'

class DBAPITypeObject:
    """Type Objects and Constructors

    https://www.python.org/dev/peps/pep-0249/#type-objects-and-constructors
    """
    def __init__(self, *values):
        self.values = values

    def __cmp__(self, other):
        if other in self.values:
            return 0
        if other < self.values:
            return 1
        else:
            return -1

    def __eq__(self, other):
        return other in self.values

# https://docs.aws.amazon.com/athena/latest/ug/data-types.html
STRING = DBAPITypeObject('char', 'varchar', 'map', 'array', 'row')
BINARY = DBAPITypeObject('varbinary')
BOOLEAN = DBAPITypeObject('boolean')
NUMBER = DBAPITypeObject('tinyint', 'smallint', 'bigint', 'integer',
                         'real', 'double', 'float', 'decimal')
DATE = DBAPITypeObject('date')
TIME = DBAPITypeObject('time', 'time with time zone')
DATETIME = DBAPITypeObject('timestamp', 'timestamp with time zone')
JSON = DBAPITypeObject('json')

Date = datetime.date
Time = datetime.time
Timestamp = datetime.datetime

def connect(*args, **kwargs):
    from pyathena.connection import Connection
    return Connection(*args, **kwargs)
<|endoftext|>


<commit_before>__version__ = '0.2.0'
<commit_msg>Bump version to 0.2.1<commit_after>__version__ = '0.2.1'
<|endoftext|>


<commit_before># Copyright (c) 2007, 2008, 2009, 2010, 2011  Andrey Golovizin
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from pyparsing import (Suppress, QuotedString, Word,
        alphas, printables, Regex, lineno, line, col,
        Forward, ZeroOrMore, OneOrMore, Group,
        restOfLine, StringEnd, ParserElement, downcaseTokens)
from pybtex.bibtex.interpreter import (Integer, String, QuotedVar,
        Identifier, FunctionLiteral, BibTeXError)
import pybtex.io

#ParserElement.enablePackrat()

def process_int_literal(s, loc, toks):
    try:
        return Integer(int(toks[0][1:]))
    except ValueError:
        raise BibTeXError('%i:%i invalid integer literal\n%s' %
                (lineno(loc, s), col(loc, s), line(loc, s)))

def process_string_literal(toks):
    return String(toks[0])

def process_identifier(toks):
    name = toks[0].lower()
    if name[0] == "'":
        return QuotedVar(name[1:])
    else:
        return Identifier(name)

def process_function(toks):
    return FunctionLiteral(toks[0])

comment = '%' + restOfLine
lbrace = Suppress('{')
rbrace = Suppress('}')
intLiteral = Regex(r'#-?\d+').setParseAction(process_int_literal)
stringLiteral = QuotedString('"').setParseAction(process_string_literal)
restrictedPrintables = ''.join(c for c in printables if not c in '#%^&{}~\\')
nonnums = ''.join(c for c in restrictedPrintables if not c.isdigit())
identifier = Word(nonnums, restrictedPrintables).setParseAction(process_identifier)
token = stringLiteral | intLiteral | identifier
tokenList = Forward()
tokenList.setParseAction(process_function)
tokenList << Group(lbrace + ZeroOrMore(token | tokenList) + rbrace)
commandName = Word(alphas).setParseAction(downcaseTokens)
arg = Group(lbrace + ZeroOrMore(token | tokenList) + rbrace)
command = commandName + ZeroOrMore(arg)
bstGrammar = OneOrMore(command) + StringEnd()

# sloooooow
# bstGrammar.ignore(comment)

# somewhat faster
def strip_comment(line):
    """Strip the commented part of the line."

    >>> print strip_comment('a normal line')
    a normal line
    >>> print strip_comment('a normal line% and a comment')
    a normal line
    >>> print strip_comment('"100% compatibility" is a myth')
    "100% compatibility" is a myth
    >>> print strip_comment('"100% compatibility" is a myth% or not?')
    "100% compatibility" is a myth

    """
    quotes = 0
    pos = 0
    for char in line:
        if char == '"':
            quotes += 1
        elif char == '%':
            if quotes % 2 == 0:
                break
        pos += 1
    return line[:pos]

def parse_file(filename, encoding):
    bst_file = pybtex.io.open_unicode(filename, encoding=encoding)
    bst = ''.join(strip_comment(line) for line in bst_file)
    return bstGrammar.parseString(bst)

if __name__ == '__main__':
    import sys
    print parse_file(sys.argv[1])
<commit_msg>Make the encoding argument of pybtex.bibtex.bst.parse_file() optional.<commit_after># Copyright (c) 2007, 2008, 2009, 2010, 2011  Andrey Golovizin
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from pyparsing import (Suppress, QuotedString, Word,
        alphas, printables, Regex, lineno, line, col,
        Forward, ZeroOrMore, OneOrMore, Group,
        restOfLine, StringEnd, ParserElement, downcaseTokens)
from pybtex.bibtex.interpreter import (Integer, String, QuotedVar,
        Identifier, FunctionLiteral, BibTeXError)
import pybtex.io

#ParserElement.enablePackrat()

def process_int_literal(s, loc, toks):
    try:
        return Integer(int(toks[0][1:]))
    except ValueError:
        raise BibTeXError('%i:%i invalid integer literal\n%s' %
                (lineno(loc, s), col(loc, s), line(loc, s)))

def process_string_literal(toks):
    return String(toks[0])

def process_identifier(toks):
    name = toks[0].lower()
    if name[0] == "'":
        return QuotedVar(name[1:])
    else:
        return Identifier(name)

def process_function(toks):
    return FunctionLiteral(toks[0])

comment = '%' + restOfLine
lbrace = Suppress('{')
rbrace = Suppress('}')
intLiteral = Regex(r'#-?\d+').setParseAction(process_int_literal)
stringLiteral = QuotedString('"').setParseAction(process_string_literal)
restrictedPrintables = ''.join(c for c in printables if not c in '#%^&{}~\\')
nonnums = ''.join(c for c in restrictedPrintables if not c.isdigit())
identifier = Word(nonnums, restrictedPrintables).setParseAction(process_identifier)
token = stringLiteral | intLiteral | identifier
tokenList = Forward()
tokenList.setParseAction(process_function)
tokenList << Group(lbrace + ZeroOrMore(token | tokenList) + rbrace)
commandName = Word(alphas).setParseAction(downcaseTokens)
arg = Group(lbrace + ZeroOrMore(token | tokenList) + rbrace)
command = commandName + ZeroOrMore(arg)
bstGrammar = OneOrMore(command) + StringEnd()

# sloooooow
# bstGrammar.ignore(comment)

# somewhat faster
def strip_comment(line):
    """Strip the commented part of the line."

    >>> print strip_comment('a normal line')
    a normal line
    >>> print strip_comment('a normal line% and a comment')
    a normal line
    >>> print strip_comment('"100% compatibility" is a myth')
    "100% compatibility" is a myth
    >>> print strip_comment('"100% compatibility" is a myth% or not?')
    "100% compatibility" is a myth

    """
    quotes = 0
    pos = 0
    for char in line:
        if char == '"':
            quotes += 1
        elif char == '%':
            if quotes % 2 == 0:
                break
        pos += 1
    return line[:pos]

def parse_file(filename, encoding=None):
    bst_file = pybtex.io.open_unicode(filename, encoding=encoding)
    bst = ''.join(strip_comment(line) for line in bst_file)
    return bstGrammar.parseString(bst)

if __name__ == '__main__':
    import sys
    print parse_file(sys.argv[1])
<|endoftext|>


<commit_before># Copyright 2007 Andrey Golovizin
#
# This file is part of pybtex.
#
# pybtex is free software; you can redistribute it and/or modify
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# pybtex 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 pybtex; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
# USA

import re
from pyparsing import (Literal, Suppress, QuotedString, Word,
        alphas, alphanums, printables, Regex, lineno, line, col,
        Keyword, Forward, ZeroOrMore, OneOrMore, Group,
        restOfLine, StringEnd, ParserElement, downcaseTokens)
from pybtex.bibtex.interpreter import (Integer, String, QuotedVar,
        Identifier, FunctionLiteral, BibTeXError)

#ParserElement.enablePackrat()

def process_int_literal(s, loc, toks):
    try:
        return Integer(int(toks[0][1:]))
    except ValueError:
        raise BibTeXError('%i:%i invalid integer literal\n%s' %
                (lineno(loc, s), col(loc, s), line(loc, s)))

def process_string_literal(toks):
    return String(toks[0])

def process_identifier(toks):
    name = toks[0].lower()
    if name[0] == "'":
        return QuotedVar(name[1:])
    else:
        return Identifier(name)

def process_function(toks):
    return FunctionLiteral(toks[0])

comment = '%' + restOfLine
lbrace = Suppress('{')
rbrace = Suppress('}')
intLiteral = Regex(r'#-?\d+').setParseAction(process_int_literal)
stringLiteral = QuotedString('"').setParseAction(process_string_literal)
restrictedPrintables = ''.join(c for c in printables if not c in '#%^&{}_~\\')
nonnums = ''.join(c for c in restrictedPrintables if not c.isdigit())
identifier = Word(nonnums, restrictedPrintables).setParseAction(process_identifier)
token = stringLiteral | intLiteral | identifier
tokenList = Forward()
tokenList.setParseAction(process_function)
tokenList << Group(lbrace + ZeroOrMore(token | tokenList) + rbrace)
commandName = Word(alphas).setParseAction(downcaseTokens)
arg = Group(lbrace + ZeroOrMore(token | tokenList) + rbrace)
command = commandName + ZeroOrMore(arg)
bstGrammar = OneOrMore(command) + StringEnd()
bstGrammar.ignore(comment)

parse_file = bstGrammar.parseFile

if __name__ == '__main__':
    import sys
    print bstGrammar.parseFile(sys.argv[1])
<commit_msg>strip comments manually - faster<commit_after># Copyright 2007 Andrey Golovizin
#
# This file is part of pybtex.
#
# pybtex is free software; you can redistribute it and/or modify
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# pybtex 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 pybtex; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
# USA

import re
from pyparsing import (Literal, Suppress, QuotedString, Word,
        alphas, alphanums, printables, Regex, lineno, line, col,
        Keyword, Forward, ZeroOrMore, OneOrMore, Group,
        restOfLine, StringEnd, ParserElement, downcaseTokens)
from pybtex.bibtex.interpreter import (Integer, String, QuotedVar,
        Identifier, FunctionLiteral, BibTeXError)

#ParserElement.enablePackrat()

def process_int_literal(s, loc, toks):
    try:
        return Integer(int(toks[0][1:]))
    except ValueError:
        raise BibTeXError('%i:%i invalid integer literal\n%s' %
                (lineno(loc, s), col(loc, s), line(loc, s)))

def process_string_literal(toks):
    return String(toks[0])

def process_identifier(toks):
    name = toks[0].lower()
    if name[0] == "'":
        return QuotedVar(name[1:])
    else:
        return Identifier(name)

def process_function(toks):
    return FunctionLiteral(toks[0])

comment = '%' + restOfLine
lbrace = Suppress('{')
rbrace = Suppress('}')
intLiteral = Regex(r'#-?\d+').setParseAction(process_int_literal)
stringLiteral = QuotedString('"').setParseAction(process_string_literal)
restrictedPrintables = ''.join(c for c in printables if not c in '#%^&{}_~\\')
nonnums = ''.join(c for c in restrictedPrintables if not c.isdigit())
identifier = Word(nonnums, restrictedPrintables).setParseAction(process_identifier)
token = stringLiteral | intLiteral | identifier
tokenList = Forward()
tokenList.setParseAction(process_function)
tokenList << Group(lbrace + ZeroOrMore(token | tokenList) + rbrace)
commandName = Word(alphas).setParseAction(downcaseTokens)
arg = Group(lbrace + ZeroOrMore(token | tokenList) + rbrace)
command = commandName + ZeroOrMore(arg)
bstGrammar = OneOrMore(command) + StringEnd()

# sloooooow
# bstGrammar.ignore(comment)

# somewhat faster
def strip_comment(s):
    quotes = 0
    pos = 0
    for char in s:
        if char == '"':
            quotes += 1
        elif char == '%':
            if quotes % 2 == 0:
                break
        pos += 1
    return s[:pos]

def parse_file(filename):
    l = []
    f = file(filename)
    s = ''.join(strip_comment(line) for line in f)
    return bstGrammar.parseString(s)

if __name__ == '__main__':
    import sys
    print parse_file(sys.argv[1])
<|endoftext|>


<commit_before>import wx
import wx.grid as grid
import view
import util
import app

class MemoryTable(grid.PyGridTableBase):

    def __init__(self, base, size, stride, update_callable=None):
        grid.PyGridTableBase.__init__(self)
        self.base = base
        self.size = size
        self.stride = stride
        self.set_cols(1)
        self.cached_range = (-1,-1)
        self.cached_data = []
        self.update_callable=update_callable

    def set_stride(self, stride):
        self.stride = int(stride)
        self.ResetView()

    def update(self, base_addr, values):
        self.cached_range =  (base_addr, base_addr + (len(values)*self.stride))
        self.cached_data = list(values)

    def is_in_cache(self, addr):
        min_addr, max_addr = self.cached_range
        return addr >= min_addr and addr <= max_addr

    def set_cols(self, cols):
        self.cols = cols
        self.rows = self.size / (self.stride*cols)
        self.ResetView()

    def __address_from_coordinate(self, row, col):
        return row*self.stride*self.cols + col*self.stride

    def __coordinate_from_address(self, address):
        row = address / (self.stride*self.cols)
        col = (address % (self.cols))/self.stride
        return row, col

    def address_from_coordinate(self, row, col):
        return self.__address_from_coordinate(row, col)

    def address_from_row(self, row):
        return self.__address_from_coordinate(row, 0)

    def coordinate_from_address(self, address):
        return self.__coordinate_from_address(address)

    def GetAttr(self, row, col, kind):
        addr = self.__address_from_coordinate(row, col)
        attr = grid.GridCellAttr()
        if not self.is_in_cache(addr):
            attr.SetTextColour("darkgray")
        else:
            attr.SetTextColour(wx.BLACK)
        attr.IncRef()
        return attr

    def GetRowLabelValue(self, row):
        return "0x%08x" % int(self.address_from_row(row))

    def GetNumberRows(self):
        return self.rows

    def GetNumberCols(self):
        return self.cols

    def IsEmptyCell(self, row,col):
        return False

    def GetValue(self, row,col):
        addr = self.__address_from_coordinate(row ,col)
        if addr >= self.cached_range[0] and addr < self.cached_range[1]:
            fmt = "%%0%dx" % (self.stride*2)
            try:
                return fmt % self.cached_data[(addr-self.cached_range[0])/self.stride]
            except:
                return "??"*self.stride
        else:
            return "??"*self.stride

    def SetValue(self, row, col, value):
        addr = self.__address_from_coordinate(row,col)
        if self.update_callable:
            self.update_callable(addr, value)

    def clear_cache(self):
        self.cached_range = (-1,-1)
        self.cached_data = []

    def ResetView(self):
            """Trim/extend the control's rows and update all values"""
            #print "Table rows, cols = %s" % ((self.rows, self.cols),)
            if self.GetView() is None:
                return
            self.clear_cache()
            self.GetView().BeginBatch()
            for current, new, delmsg, addmsg in [
                    (self.GetView().GetNumberRows(), self.GetNumberRows(), grid.GRIDTABLE_NOTIFY_ROWS_DELETED, grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
                    (self.GetView().GetNumberCols(), self.GetNumberCols(), grid.GRIDTABLE_NOTIFY_COLS_DELETED, grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
            ]:
                    if new < current:
                            msg = grid.GridTableMessage(
                                    self,
                                    delmsg,
                                    new,    # position
                                    current-new,
                            )
                            self.GetView().ProcessTableMessage(msg)
                    elif new > current:
                            msg = grid.GridTableMessage(
                                    self,
                                    addmsg,
                                    new-current
                            )
                            self.GetView().ProcessTableMessage(msg)
            self.GetView().EndBatch()

class MemoryGridControl(grid.Grid):

    def __init__(self, parent, id=-1, style=0, size=1048576, stride=4, update_callable=None):
        grid.Grid.__init__(self, parent, id=id, style=style)
        self.SetColLabelSize(0)
        table = MemoryTable(base=0, size=size, stride=stride, update_callable=update_callable)
        self.hover_cell = (None,None)
        self.hover_address = None
        self.update_callable=update_callable
        self.SetTable(table,True)
        self.set_font("Courier New")
        self.EnableGridLines(False)
        self.AutoSizeLabels()
        self.resize()
        self.Bind(wx.EVT_SIZE, self.on_size)
        self.Bind(grid.EVT_GRID_CELL_LEFT_CLICK, self.on_cell_clicked)

    def on_size(self, evt):
        self.resize()

    def visible_address_range(self):
        x,y1 = self.CalcScrolledPosition(0,0)
        y1 = abs(y1)
        x,y = self.GetClientSize()
        y2 = y1 + y

        row1 = abs(y1)/self.GetDefaultRowSize()
        row2 = abs(y2)/self.GetDefaultRowSize()
        start_addr = self.GetTable().address_from_row(row1-1 if row1-1 >= 0 else 0)
        end_addr = self.GetTable().address_from_row(row2+1) + self.GetNumberCols()*self.GetTable().stride
        #print "getting visible address range: %s" % ((start_addr, end_addr),)
        return (start_addr, end_addr)

    def on_cell_clicked(self, evt):
        #print evt.GetPosition()
        r,c = evt.GetRow(), evt.GetCol()
        #print "Row=%d, Col=%d, Addr =0x%08x" % (r,c,self.Table.address_from_coordinate(r,c))
        #print "Computed Row and Column: %d,%d" % self.Table.coordinate_from_address(self.Table.address_from_coordinate(r,c))
        evt.Skip()

    def set_font(self, face, size=9):
        font = self.GetDefaultCellFont()
        font.SetFaceName(face)
        font.SetPointSize(size)
        self.SetDefaultCellFont(font)
        self.SetLabelFont(font)
        self.SetDefaultCellAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)

    def update(self, base_addr, values):
        self.GetTable().update(base_addr,values)
        self.ForceRefresh()

    def clear_cache(self):
        self.GetTable().clear_cache()

    def set_size(self, size):
        self.size = size

    def resize(self):
        table = self.GetTable()
        grid_width,grid_height = self.GetClientSize()
        grid_width -= self.GetRowLabelSize()
        grid_width -= wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X)
        dc = wx.ScreenDC()
        dc.SetFont(self.GetDefaultCellFont())

        cell_width,cell_height = dc.GetTextExtent("M"*((table.stride*2)+2))
        columns = grid_width/cell_width or 1
        cell_width = grid_width/columns
        table.set_cols(columns)
        self.SetDefaultColSize(cell_width)
        self.ForceRefresh()

    def AutoSizeLabels(self):
            dc = wx.ScreenDC()
            dc.SetFont(self.GetLabelFont())
            width = dc.GetTextExtent("0x00000000 ")[0]
            self.SetRowLabelSize(width)

class MemoryView(view.View):

    def __init__(self, parent, controller, size=1048576, stride=4):
        super(MemoryView, self).__init__(parent, -1, style=wx.BORDER_STATIC, size=(300, 800), controller=controller)
        self.grid = MemoryGridControl(self, -1, style=wx.BORDER_NONE, stride=stride, update_callable=self.on_cell_update)
        self.grid.Bind(wx.EVT_SCROLLWIN, self.on_scrolled)
        #self.grid.Bind(wx.EVT_SIZE, self.on_size)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.fetching = False

        #sizer.Add(self.create_toolbar(), 0, wx.EXPAND)
        sizer.Add(self.grid,1, wx.EXPAND)

        self.SetSizer(sizer)
        self.controller.Bind(app.EVT_APP_TARGET_HALTED, self.on_target_halted)
        self.controller.Bind(app.EVT_APP_TARGET_RUNNING, self.on_target_running)
        self.controller.Bind(app.EVT_APP_TARGET_CONNECTED, self.on_target_connected)

        self.grid.Bind(grid.EVT_GRID_LABEL_LEFT_DCLICK, self.on_label_dclick)
        self.grid.GetGridWindow().Bind(wx.EVT_MOTION, self.on_mouse_motion)

    def create_toolbar(self):
        pnl_toolbar = wx.Panel(self)
        combo_address = wx.ComboBox(pnl_toolbar)
        toolbar_sizer = wx.BoxSizer(wx.HORIZONTAL)
        toolbar_sizer.Add(combo_address, border=5, flag=wx.ALL)
        pnl_toolbar.SetSizer(toolbar_sizer)
        return pnl_toolbar

    def on_target_connected(self, evt):
        self._fetch_data()

    def on_target_halted(self, evt):
        self._fetch_data()

    def on_target_running(self, evt):
        pass

    def _fetch_data(self):
        if not self.fetching and self.controller.gdb:
            start, end = self.grid.visible_address_range()

            #print "Fetching data for 0x%08x -> 0x%08x" % (start, end)
            self.controller.gdb.read_memory(start, self.grid.GetTable().stride, end-start, callback=self._on_data_fetched)
            self.fetching = True

    def _on_data_fetched(self, result):
        self.fetching = False
        if hasattr(result, 'memory'):
                memtable = []
                for entry in result.memory:
                    memtable.append(int(entry.data[0]))
                    wx.CallAfter(self.update,int(result.addr,16), memtable)

    def update(self, base_addr, values):
        self.grid.update(base_addr, values)

    def on_cell_update(self, addr, value):
        print hex(addr) + "=" + str(value)

    def on_scrolled(self, evt):
        #print "scrolled"
        #print evt.GetPosition()
        wx.CallAfter(self._fetch_data)
        #self._fetch_data()
        evt.Skip()

    def on_size(self, evt):
        self._fetch_data()
        evt.Skip()

    def on_label_dclick(self, evt):
        start, end = self.grid.visible_address_range()
        #print "Visible Address Range: 0x%08x -> 0x%08x" % (start, end)
        #print "Stride: ", self.grid.GetTable().stride
        self._fetch_data()

    def on_mouse_motion(self, evt):
        x,y = self.grid.CalcUnscrolledPosition(evt.GetPosition())
        row = self.grid.YToRow(y)
        col = self.grid.XToCol(x)
        if (row,col) != self.grid.hover_cell:
            self.grid.hover_cell = (row, col)
            self.grid.hover_address = self.grid.GetTable().address_from_coordinate(row, col)
            #print "Changed cell", self.grid.hover_cell, "0x%08x" % self.grid.hover_address
            v = int(self.grid.GetCellValue(row, col),16)
            self.controller.gdb.command('info symbol 0x%08x' % v, callback=self.on_symbol_lookup)
        evt.Skip()

    def on_symbol_lookup(self, data):
        pass
        #print data

if __name__ == "__main__":
    pass<commit_msg>Memory view is relocatable now.  You can enter an address in the toobar combobox and it will relocate the memory table to that region in memory.  (Be careful, you guys!)<commit_after>import wx
import wx.grid as grid
import view
import util
import app

class MemoryTable(grid.PyGridTableBase):

    def __init__(self, base, size, stride, update_callable=None):
        grid.PyGridTableBase.__init__(self)
        self.base = base
        self.size = size
        self.stride = stride
        self.set_cols(1)
        self.cached_range = (-1,-1)
        self.cached_data = []
        self.update_callable=update_callable

    def set_stride(self, stride):
        self.stride = int(stride)
        self.ResetView()

    def set_base(self, base):
        self.base = int(base)
        self.ResetView()

    def update(self, base_addr, values):
        self.cached_range =  (base_addr, base_addr + (len(values)*self.stride))
        self.cached_data = list(values)

    def is_in_cache(self, addr):
        min_addr, max_addr = self.cached_range
        return addr >= min_addr and addr <= max_addr

    def set_cols(self, cols):
        self.cols = cols
        self.rows = self.size / (self.stride*cols)
        self.ResetView()

    def address_from_coordinate(self, row, col):
        return self.base + row*self.stride*self.cols + col*self.stride

    def coordinate_from_address(self, address):
        row = (address-self.base) / (self.stride*self.cols)
        col = ((address-self.base) % (self.cols))/self.stride
        return row, col

    def address_from_row(self, row):
        return self.address_from_coordinate(row, 0)

    def GetAttr(self, row, col, kind):
        addr = self.address_from_coordinate(row, col)
        attr = grid.GridCellAttr()
        if not self.is_in_cache(addr):
            attr.SetTextColour("darkgray")
        else:
            attr.SetTextColour(wx.BLACK)
        attr.IncRef()
        return attr

    def GetRowLabelValue(self, row):
        return "0x%08x" % int(self.address_from_row(row))

    def GetNumberRows(self):
        return self.rows

    def GetNumberCols(self):
        return self.cols

    def IsEmptyCell(self, row,col):
        return False

    def GetValue(self, row,col):
        addr = self.address_from_coordinate(row ,col)
        if addr >= self.cached_range[0] and addr < self.cached_range[1]:
            fmt = "%%0%dx" % (self.stride*2)
            try:
                return fmt % self.cached_data[(addr-self.cached_range[0])/self.stride]
            except:
                return "??"*self.stride
        else:
            return "??"*self.stride

    def SetValue(self, row, col, value):
        addr = self.address_from_coordinate(row,col)
        if self.update_callable:
            self.update_callable(addr, value)

    def clear_cache(self):
        self.cached_range = (-1,-1)
        self.cached_data = []

    def ResetView(self):
            """Trim/extend the control's rows and update all values"""
            if self.GetView() is None:
                return
            self.clear_cache()
            self.GetView().BeginBatch()
            for current, new, delmsg, addmsg in [
                    (self.GetView().GetNumberRows(), self.GetNumberRows(), grid.GRIDTABLE_NOTIFY_ROWS_DELETED, grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
                    (self.GetView().GetNumberCols(), self.GetNumberCols(), grid.GRIDTABLE_NOTIFY_COLS_DELETED, grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
            ]:
                    if new < current:
                            msg = grid.GridTableMessage(
                                    self,
                                    delmsg,
                                    new,    # position
                                    current-new,
                            )
                            self.GetView().ProcessTableMessage(msg)
                    elif new > current:
                            msg = grid.GridTableMessage(
                                    self,
                                    addmsg,
                                    new-current
                            )
                            self.GetView().ProcessTableMessage(msg)
            self.GetView().EndBatch()

class MemoryGridControl(grid.Grid):

    def __init__(self, parent, id=-1, style=0, size=1048576, stride=4, update_callable=None):
        grid.Grid.__init__(self, parent, id=id, style=style)
        self.SetColLabelSize(0)
        table = MemoryTable(base=0, size=size, stride=stride, update_callable=update_callable)
        self.hover_cell = (None,None)
        self.hover_address = None
        self.update_callable=update_callable
        self.SetTable(table,True)
        self.set_font("Courier New")
        self.EnableGridLines(False)
        self.AutoSizeLabels()
        self.resize()
        self.Bind(wx.EVT_SIZE, self.on_size)
        self.Bind(grid.EVT_GRID_CELL_LEFT_CLICK, self.on_cell_clicked)

    def on_size(self, evt):
        self.resize()

    def visible_address_range(self):
        x,y1 = self.CalcScrolledPosition(0,0)
        y1 = abs(y1)
        x,y = self.GetClientSize()
        y2 = y1 + y

        row1 = abs(y1)/self.GetDefaultRowSize()
        row2 = abs(y2)/self.GetDefaultRowSize()
        start_addr = self.GetTable().address_from_row(row1-1 if row1-1 >= 0 else 0)
        end_addr = self.GetTable().address_from_row(row2+1) + self.GetNumberCols()*self.GetTable().stride
        #print "getting visible address range: %s" % ((start_addr, end_addr),)
        return (start_addr, end_addr)

    def on_cell_clicked(self, evt):
        #print evt.GetPosition()
        r,c = evt.GetRow(), evt.GetCol()
        #print "Row=%d, Col=%d, Addr =0x%08x" % (r,c,self.Table.address_from_coordinate(r,c))
        #print "Computed Row and Column: %d,%d" % self.Table.coordinate_from_address(self.Table.address_from_coordinate(r,c))
        evt.Skip()

    def set_font(self, face, size=9):
        font = self.GetDefaultCellFont()
        font.SetFaceName(face)
        font.SetPointSize(size)
        self.SetDefaultCellFont(font)
        self.SetLabelFont(font)
        self.SetDefaultCellAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)

    def update(self, base_addr, values):
        self.GetTable().update(base_addr,values)
        self.ForceRefresh()

    def clear_cache(self):
        self.GetTable().clear_cache()

    def set_size(self, size):
        self.size = size

    def resize(self):
        table = self.GetTable()
        grid_width,grid_height = self.GetClientSize()
        grid_width -= self.GetRowLabelSize()
        grid_width -= wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X)
        dc = wx.ScreenDC()
        dc.SetFont(self.GetDefaultCellFont())

        cell_width,cell_height = dc.GetTextExtent("M"*((table.stride*2)+2))
        columns = grid_width/cell_width or 1
        cell_width = grid_width/columns
        table.set_cols(columns)
        self.SetDefaultColSize(cell_width)
        self.ForceRefresh()

    def AutoSizeLabels(self):
            dc = wx.ScreenDC()
            dc.SetFont(self.GetLabelFont())
            width = dc.GetTextExtent("0x00000000 ")[0]
            self.SetRowLabelSize(width)

class MemoryView(view.View):

    def __init__(self, parent, controller, size=1048576, stride=4):
        super(MemoryView, self).__init__(parent, -1, style=wx.BORDER_STATIC, size=(300, 800), controller=controller)
        self.grid = MemoryGridControl(self, -1, style=wx.BORDER_NONE, stride=stride, update_callable=self.on_cell_update)
        self.grid.Bind(wx.EVT_SCROLLWIN, self.on_scrolled)
        #self.grid.Bind(wx.EVT_SIZE, self.on_size)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.fetching = False

        sizer.Add(self.create_toolbar(), 0, wx.EXPAND)
        sizer.Add(self.grid,1, wx.EXPAND)

        self.SetSizer(sizer)
        self.controller.Bind(app.EVT_APP_TARGET_HALTED, self.on_target_halted)
        self.controller.Bind(app.EVT_APP_TARGET_RUNNING, self.on_target_running)
        self.controller.Bind(app.EVT_APP_TARGET_CONNECTED, self.on_target_connected)

        self.grid.Bind(grid.EVT_GRID_LABEL_LEFT_DCLICK, self.on_label_dclick)
        self.grid.GetGridWindow().Bind(wx.EVT_MOTION, self.on_mouse_motion)

    def create_toolbar(self):
        pnl_toolbar = wx.Panel(self)
        combo_address = wx.ComboBox(pnl_toolbar, style=wx.TE_PROCESS_ENTER)
        combo_address.Bind(wx.EVT_TEXT_ENTER, self.on_addr_changed)
        combo_address.Bind(wx.EVT_COMBOBOX, self.on_addr_changed)

        self.combo_address = combo_address
        toolbar_sizer = wx.BoxSizer(wx.HORIZONTAL)
        toolbar_sizer.Add(combo_address, border=5, flag=wx.ALL)
        pnl_toolbar.SetSizer(toolbar_sizer)
        return pnl_toolbar

    def on_addr_changed(self, evt):
        cb = evt.GetEventObject()
        try:
            s = cb.GetValue()
            new_addr = util.str2int(s)
            self.grid.Table.set_base(new_addr)
            self.refresh()
            self.last_set_address = s
            if cb.FindString(s) < 0:
                cb.Append(s)
        except:
            cb.SetValue(self.last_set_address)

    def on_target_connected(self, evt):
        self._fetch_data()

    def on_target_halted(self, evt):
        self._fetch_data()

    def on_target_running(self, evt):
        pass

    def refresh(self):
        self._fetch_data()
    def _fetch_data(self):
        if not self.fetching and self.controller.gdb:
            start, end = self.grid.visible_address_range()

            #print "Fetching data for 0x%08x -> 0x%08x" % (start, end)
            self.controller.gdb.read_memory(start, self.grid.GetTable().stride, end-start, callback=self._on_data_fetched)
            self.fetching = True

    def _on_data_fetched(self, result):
        self.fetching = False
        if hasattr(result, 'memory'):
                memtable = []
                for entry in result.memory:
                    memtable.append(int(entry.data[0]))
                    wx.CallAfter(self.update,int(result.addr,16), memtable)

    def update(self, base_addr, values):
        self.grid.update(base_addr, values)

    def on_cell_update(self, addr, value):
        print hex(addr) + "=" + str(value)

    def on_scrolled(self, evt):
        #print "scrolled"
        #print evt.GetPosition()
        wx.CallAfter(self._fetch_data)
        #self._fetch_data()
        evt.Skip()

    def on_size(self, evt):
        self._fetch_data()
        evt.Skip()

    def on_label_dclick(self, evt):
        start, end = self.grid.visible_address_range()
        #print "Visible Address Range: 0x%08x -> 0x%08x" % (start, end)
        #print "Stride: ", self.grid.GetTable().stride
        self._fetch_data()

    def on_mouse_motion(self, evt):
        x,y = self.grid.CalcUnscrolledPosition(evt.GetPosition())
        row = self.grid.YToRow(y)
        col = self.grid.XToCol(x)
        if (row,col) != self.grid.hover_cell:
            self.grid.hover_cell = (row, col)
            self.grid.hover_address = self.grid.GetTable().address_from_coordinate(row, col)
            #print "Changed cell", self.grid.hover_cell, "0x%08x" % self.grid.hover_address
            v = int(self.grid.GetCellValue(row, col),16)
            self.controller.gdb.command('info symbol 0x%08x' % v, callback=self.on_symbol_lookup)
        evt.Skip()

    def on_symbol_lookup(self, data):
        pass
        #print data

if __name__ == "__main__":
    pass<|endoftext|>