<commit_before>#TODO Add intepreter here
#@Author Sean McLaughlin
desc ='''
This module is the first I'm writing for my project for the winter quarter of 2016 in Aaron Roodman's group.
This is built on top of previous work conducted by Aaron and his graduate student Chris Davis. They have
developed WavefrontPSF, which estimates the optical contribution of the PSF with a relatively simple model.
It was found that the optical portion was not as signifcant a fraction of the total PSF as hoped,
so some sort of afterburner is going to need to be added. After the optical portion has been deconvolved
from the observed stars (using Richardson-Lucy deconvolution), the remainder will be treated as the "atmospheric"
portion of the psf. This module load in preprocessed observed stars, run WavefrontPSF on them, deconvolve
the optical PSF, then run PSFEX (a packaged PSF modeler) on the residual.

#TODO Details on actually running the module.
'''
from argparse import ArgumentParser
parser = ArgumentParser(description = desc)

parser.add_argument('expid', metavar = 'expid', type = int, help =\
                    'ID of the exposure to analyze')
#May want to rename to tmp, since that's what the files really are.
parser.add_argument('outputDir', metavar = 'outputDir', type = str, help =\
                    'Directory to store outputs.')

args = vars(parser.parse_args())

#Ensure provided dir exists
from os import path
if not path.isdir(args['outputDir']):
    raise IOError("The directory %s does not exist."%args['outputDir'])

if args['outputDir'][-1]  != '/':
    args['outputDir']+='/'

import numpy as np
from itertools import izip
from optical_model import get_optical_psf
from lucy import deconvolve, convolve
from subprocess import call
from psfex import PSFEx
from glob import glob
import cPickle as pickle

#For identifying "bad stars"
import warnings
warnings.filterwarnings('error')

print 'Starting.'

#get optical PSF
optpsf_stamps, meta_hdulist = get_optical_psf(args['expid'])

print 'Opts Calculated.'

vignettes = np.zeros((optpsf_stamps.shape[0], 32,32))

#extract star vignettes from the hdulists
vig_idx=0
hdu_lengths = np.zeros((62,))
for ccd_num, hdulist in enumerate(meta_hdulist):
    #TODO Check for off by one errors and centering.
    #TODO Turn sliced off pixels into background estimate

    list_len = hdulist[2].data.shape[0]
    sliced_vig  = hdulist[2].data['VIGNET'][:, 15:47, 15:47] #slice to same size as stamps
    sliced_vig[sliced_vig<-1000] = 0 #set really negative values to 0
    sliced_vig = sliced_vig/sliced_vig.sum((1,2))[:, None, None] #normalize
    vignettes[vig_idx:vig_idx+list_len] = sliced_vig
    vig_idx+=list_len
    vig_shape = hdulist[2].data['VIGNET'][0].shape
    print 'CCD: %d\tVignette Shape:(%d, %d)'%(ccd_num+1, vig_shape[0], vig_shape[1] )
    hdu_lengths[ccd_num] = list_len
'''
#Calculate the atmospheric portion of the psf
atmpsf_list = []
for idx, (optpsf, vignette) in enumerate(izip(optpsf_stamps, vignettes)):
    #atmpsf_small,diffs,psiByIter,chi2ByIter = deconvolve(optpsf,vignette,psi_0=None,mask=None,mu0=6e3,convergence=1e-3,chi2Level=0.,niterations=50, extra= True)
    atmpsf = np.zeros((63,63))
    try:
        atmpsf_small = deconvolve(optpsf,vignette,psi_0=None,mask=None,mu0=6e3,convergence=1e-3,chi2Level=0.,niterations=50, extra= False)
        atmpsf[15:47, 15:47] = atmpsf_small
    except RuntimeWarning:
       #TODO What should I do on a failure?
        print 'Failed on %d'%idx
        pass

    atmpsf_list.append(atmpsf)

atmpsf_list =  np.array(atmpsf_list)
'''
print 'Deconv done.'
'''
#now, insert the atmospheric portion back into the hdulists, and write them to disk
#PSFEx needs the information in those lists to run correctly.
atmpsf_idx =0
for hdulist in meta_hdulist:
    list_len = hdulist[2].data.shape[0]
    hdulist[2].data['VIGNET'] = atmpsf_list[atmpsf_idx:atmpsf_idx+list_len]
    atmpsf_idx+=list_len

    #Make new filename from old one.
    original_fname = hdulist.filename().split('/')[-1]#just get the filename, not the path
    original_fname_split = original_fname.split('_')
    original_fname_split[-1] = 'seldeconv.fits'
    hdulist.writeto(args['outputDir']+'_'.join(original_fname_split), clobber = True)
'''
print 'Copy and write done.'

'''
#call psfex
psfex_path = '/nfs/slac/g/ki/ki22/roodman/EUPS_DESDM/eups/packages/Linux64/psfex/3.17.3+0/bin/psfex'
psfex_config = '/afs/slac.stanford.edu/u/ec/roodman/Astrophysics/PSF/desdm-plus.psfex'
#TODO This is gonna run on all *.fits in the outputdir. If the user doesn't want that uh... then what?
#TODO Actaully want to call on just the ones with this expid
command_list = [psfex_path, args['outputDir']+'*.fits', "-c", psfex_config]
#command_list = [psfex_path, args['outputDir']+'DECam_00180489_01_seldeconv.fits', "-c", psfex_config]

#If shell != True, the wildcard won't work
psfex_return= call(' '.join(command_list), shell = True)
psfex_success = True if psfex_return==0 else False
print 'PSFEx Call Successful: %s'%psfex_success

#TODO Clear temporary files?

#no use continuing if the psfex call failed.
if not psfex_success:
    from sys import exit
    exit(1)
'''
#Now, load in psfex's work, and reconolve with the optics portion.
psf_files = glob(args['outputDir']+'*.psf')
atmpsf_list = []
#TODO Check that files are in the same order as the hdulist
for file, hdulist in  izip(psf_files, meta_hdulist):
    pex = PSFEx(file)
    for yimage, ximage in izip(hdulist[2].data['Y_IMAGE'], hdulist[2].data['X_IMAGE']):
        atmpsf_list.append(pex.get_rec(yimage, ximage))

#TODO np.array(atmpsf_list)?
#TODO what to do with these?
stars = []
for idx, (optpsf, atmpsf) in enumerate(izip(optpsf_stamps, atmpsf_list)):
    print optpsf.shape, atmpsf.shape
    try:
        stars.append(convolve(optpsf[1:30, 1:30], atmpsf))
    except ValueError:
        for ccd_num, hdu_len in enumerate( hdu_lengths) :
            if hdu_len > idx:
                print 'Failed on CCD %d Image %d'%(ccd_num+1, idx)
                break
            else:
                idx-=hdu_len
        raise

pickle.dump(np.array(stars), open(args['outputDir']+'%s_stars.pkl'%args['expid'], 'w'))
from matplotlib import pyplot as plt
for star in stars:
    im = plt.imshow(star, cmap = plt.get_cmap('afmhot'), interpolation = 'none')
    plt.colorbar(im)
    plt.savefig(args['outputDir']+'%s_star.png'%args['expid'])

print 'Done'
<commit_msg>Fixed an issue with PSFEx returning odd sizes.<commit_after>#TODO Add intepreter here
#@Author Sean McLaughlin
desc ='''
This module is the first I'm writing for my project for the winter quarter of 2016 in Aaron Roodman's group.
This is built on top of previous work conducted by Aaron and his graduate student Chris Davis. They have
developed WavefrontPSF, which estimates the optical contribution of the PSF with a relatively simple model.
It was found that the optical portion was not as signifcant a fraction of the total PSF as hoped,
so some sort of afterburner is going to need to be added. After the optical portion has been deconvolved
from the observed stars (using Richardson-Lucy deconvolution), the remainder will be treated as the "atmospheric"
portion of the psf. This module load in preprocessed observed stars, run WavefrontPSF on them, deconvolve
the optical PSF, then run PSFEX (a packaged PSF modeler) on the residual.

#TODO Details on actually running the module.
'''
from argparse import ArgumentParser
parser = ArgumentParser(description = desc)

parser.add_argument('expid', metavar = 'expid', type = int, help =\
                    'ID of the exposure to analyze')
#May want to rename to tmp, since that's what the files really are.
parser.add_argument('outputDir', metavar = 'outputDir', type = str, help =\
                    'Directory to store outputs.')

args = vars(parser.parse_args())

#Ensure provided dir exists
from os import path
if not path.isdir(args['outputDir']):
    raise IOError("The directory %s does not exist."%args['outputDir'])

if args['outputDir'][-1]  != '/':
    args['outputDir']+='/'

import numpy as np
from itertools import izip
from optical_model import get_optical_psf
from lucy import deconvolve, convolve
from subprocess import call
from psfex import PSFEx
from glob import glob
import cPickle as pickle

#For identifying "bad stars"
import warnings
warnings.filterwarnings('error')

print 'Starting.'

#get optical PSF
optpsf_stamps, meta_hdulist = get_optical_psf(args['expid'])

print 'Opts Calculated.'

vignettes = np.zeros((optpsf_stamps.shape[0], 32,32))

#extract star vignettes from the hdulists
vig_idx=0
hdu_lengths = np.zeros((62,))
for ccd_num, hdulist in enumerate(meta_hdulist):
    #TODO Check for off by one errors and centering.
    #TODO Turn sliced off pixels into background estimate

    list_len = hdulist[2].data.shape[0]
    sliced_vig  = hdulist[2].data['VIGNET'][:, 15:47, 15:47] #slice to same size as stamps
    sliced_vig[sliced_vig<-1000] = 0 #set really negative values to 0
    sliced_vig = sliced_vig/sliced_vig.sum((1,2))[:, None, None] #normalize
    vignettes[vig_idx:vig_idx+list_len] = sliced_vig
    vig_idx+=list_len
    vig_shape = hdulist[2].data['VIGNET'][0].shape
    print 'CCD: %d\tVignette Shape:(%d, %d)'%(ccd_num+1, vig_shape[0], vig_shape[1] )
    hdu_lengths[ccd_num] = list_len
'''
#Calculate the atmospheric portion of the psf
atmpsf_list = []
for idx, (optpsf, vignette) in enumerate(izip(optpsf_stamps, vignettes)):
    #atmpsf_small,diffs,psiByIter,chi2ByIter = deconvolve(optpsf,vignette,psi_0=None,mask=None,mu0=6e3,convergence=1e-3,chi2Level=0.,niterations=50, extra= True)
    atmpsf = np.zeros((63,63))
    try:
        atmpsf_small = deconvolve(optpsf,vignette,psi_0=None,mask=None,mu0=6e3,convergence=1e-3,chi2Level=0.,niterations=50, extra= False)
        atmpsf[15:47, 15:47] = atmpsf_small
    except RuntimeWarning:
       #TODO What should I do on a failure?
        print 'Failed on %d'%idx
        pass

    atmpsf_list.append(atmpsf)

atmpsf_list =  np.array(atmpsf_list)
'''
print 'Deconv done.'
'''
#now, insert the atmospheric portion back into the hdulists, and write them to disk
#PSFEx needs the information in those lists to run correctly.
atmpsf_idx =0
for hdulist in meta_hdulist:
    list_len = hdulist[2].data.shape[0]
    hdulist[2].data['VIGNET'] = atmpsf_list[atmpsf_idx:atmpsf_idx+list_len]
    atmpsf_idx+=list_len

    #Make new filename from old one.
    original_fname = hdulist.filename().split('/')[-1]#just get the filename, not the path
    original_fname_split = original_fname.split('_')
    original_fname_split[-1] = 'seldeconv.fits'
    hdulist.writeto(args['outputDir']+'_'.join(original_fname_split), clobber = True)
'''
print 'Copy and write done.'

'''
#call psfex
psfex_path = '/nfs/slac/g/ki/ki22/roodman/EUPS_DESDM/eups/packages/Linux64/psfex/3.17.3+0/bin/psfex'
psfex_config = '/afs/slac.stanford.edu/u/ec/roodman/Astrophysics/PSF/desdm-plus.psfex'
#TODO This is gonna run on all *.fits in the outputdir. If the user doesn't want that uh... then what?
#TODO Actaully want to call on just the ones with this expid
command_list = [psfex_path, args['outputDir']+'*.fits', "-c", psfex_config]
#command_list = [psfex_path, args['outputDir']+'DECam_00180489_01_seldeconv.fits', "-c", psfex_config]

#If shell != True, the wildcard won't work
psfex_return= call(' '.join(command_list), shell = True)
psfex_success = True if psfex_return==0 else False
print 'PSFEx Call Successful: %s'%psfex_success

#TODO Clear temporary files?

#no use continuing if the psfex call failed.
if not psfex_success:
    from sys import exit
    exit(1)
'''
#Now, load in psfex's work, and reconolve with the optics portion.
psf_files = glob(args['outputDir']+'*.psf')
atmpsf_list = []
#TODO Check that files are in the same order as the hdulist
for file, hdulist in  izip(psf_files, meta_hdulist):
    pex = PSFEx(file)
    for yimage, ximage in izip(hdulist[2].data['Y_IMAGE'], hdulist[2].data['X_IMAGE']):
        atmpsf = np.zeros((32,32))
        #psfex has a tendency to return images of weird and varying sizes
        #This scheme ensures that they will all be the same 32x32 by zero padding
        #assumes the images are square and smaller than 32x32
        #Proof god is real and hates observational astronomers.
        atmpsf_small = pex.get_rec(yimage, ximage)
        atm_shape = atmpsf_small.shape[0] #assumed to be square
        pad_amount = (32-atmpsf_small.shape[0])/2
        atmpsf[pad_amount:32-(pad_amount+pad_amount%2),pad_amount:32-(pad_amount+pad_amount%2) ] = atmpsf_small
        atmpsf_list.append(atmpsf)

atmpsf_list = np.array(atmpsf_list)

stars = []#TODO what to do with these
for idx, (optpsf, atmpsf) in enumerate(izip(optpsf_stamps, atmpsf_list)):
    try:
        stars.append(convolve(optpsf, atmpsf))
    except ValueError:
        for ccd_num, hdu_len in enumerate( hdu_lengths) :
            if hdu_len > idx:
                print 'Failed on CCD %d Image %d'%(ccd_num+1, idx)
                break
            else:
                idx-=hdu_len
        raise

pickle.dump(np.array(stars), open(args['outputDir']+'%s_stars.pkl'%args['expid'], 'w'))
from matplotlib import pyplot as plt
for star in stars:
    im = plt.imshow(star, cmap = plt.get_cmap('afmhot'), interpolation = 'none')
    plt.colorbar(im)
    plt.savefig(args['outputDir']+'%s_star.png'%args['expid'])

print 'Done'
<|endoftext|>


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

from datetime import datetime

import json
import logging
import sys
import traceback

APP_VERSION = "0.4.0-rc1"
APP_NAME = "clouddq"
LOG_LEVEL = logging._nameToLevel["DEBUG"]

class JsonEncoderStrFallback(json.JSONEncoder):
    def default(self, obj):
        try:
            return super().default(obj)
        except TypeError as exc:
            if "not JSON serializable" in str(exc):
                return str(obj)
            raise

class JsonEncoderDatetime(JsonEncoderStrFallback):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
        else:
            return super().default(obj)

class JSONFormatter(logging.Formatter):
    def __init__(self):
        super().__init__()

    def format(self, record):
        record.msg = json.dumps(
            {
                "severity": record.levelname,
                "time": datetime.utcfromtimestamp(record.created)
                .astimezone()
                .isoformat()
                .replace("+00:00", "Z"),
                "logging.googleapis.com/sourceLocation": {
                    "file": record.pathname or record.filename,
                    "function": record.funcName,
                    "line": record.lineno,
                },
                "exception": record.exc_info,
                "traceback": traceback.format_exception(*record.exc_info)
                if record.exc_info
                else None,
                "message": record.getMessage(),
                "logging.googleapis.com/labels": {
                    "name": APP_NAME,
                    "releaseId": APP_VERSION,
                },
            },
            cls=JsonEncoderDatetime,
        )
        return super().format(record)

def get_json_logger():
    json_logger = logging.getLogger("clouddq-json-logger")
    json_logger.setLevel(LOG_LEVEL)
    logging_stream_handler = logging.StreamHandler(sys.stdout)
    logging_stream_handler.setFormatter(JSONFormatter())
    json_logger.addHandler(logging_stream_handler)
    return json_logger

def get_logger():
    logger = logging.getLogger("clouddq")
    logger.setLevel(LOG_LEVEL)
    logging_stream_handler = logging.StreamHandler(sys.stderr)
    stream_formatter = logging.Formatter(
        "{asctime} {name} {levelname:8s} {message}", style="{"
    )
    logging_stream_handler.setFormatter(stream_formatter)
    logger.addHandler(logging_stream_handler)
    return logger
<commit_msg>Do not add multiple handlers following multiple calls to get logger (#95)<commit_after># Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from datetime import datetime

import json
import logging
import sys
import traceback

APP_VERSION = "0.4.0-rc1"
APP_NAME = "clouddq"
LOG_LEVEL = logging._nameToLevel["DEBUG"]

class JsonEncoderStrFallback(json.JSONEncoder):
    def default(self, obj):
        try:
            return super().default(obj)
        except TypeError as exc:
            if "not JSON serializable" in str(exc):
                return str(obj)
            raise

class JsonEncoderDatetime(JsonEncoderStrFallback):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
        else:
            return super().default(obj)

class JSONFormatter(logging.Formatter):
    def __init__(self):
        super().__init__()

    def format(self, record):
        record.msg = json.dumps(
            {
                "severity": record.levelname,
                "time": datetime.utcfromtimestamp(record.created)
                .astimezone()
                .isoformat()
                .replace("+00:00", "Z"),
                "logging.googleapis.com/sourceLocation": {
                    "file": record.pathname or record.filename,
                    "function": record.funcName,
                    "line": record.lineno,
                },
                "exception": record.exc_info,
                "traceback": traceback.format_exception(*record.exc_info)
                if record.exc_info
                else None,
                "message": record.getMessage(),
                "logging.googleapis.com/labels": {
                    "name": APP_NAME,
                    "releaseId": APP_VERSION,
                },
            },
            cls=JsonEncoderDatetime,
        )
        return super().format(record)

def get_json_logger():
    json_logger = logging.getLogger("clouddq-json-logger")
    if not json_logger.hasHandlers():
        json_logger.setLevel(LOG_LEVEL)
        logging_stream_handler = logging.StreamHandler(sys.stdout)
        logging_stream_handler.setFormatter(JSONFormatter())
        json_logger.addHandler(logging_stream_handler)
    return json_logger

def get_logger():
    logger = logging.getLogger("clouddq")
    if not logger.hasHandlers():
        logger.setLevel(LOG_LEVEL)
        logging_stream_handler = logging.StreamHandler(sys.stderr)
        stream_formatter = logging.Formatter(
            "{asctime} {name} {levelname:8s} {message}", style="{"
        )
        logging_stream_handler.setFormatter(stream_formatter)
        logger.addHandler(logging_stream_handler)
    return logger
<|endoftext|>


<commit_before>import inotify.adapters

from django.conf import settings
from django.core.management.base import BaseCommand

from photonix.photos.utils.db import record_photo
from photonix.photos.models import LibraryPath

class Command(BaseCommand):
    help = 'Watches photo directories and creates relevant database records for all photos that are added or modified.'

    def watch_photos(self):
        library_paths = LibraryPath.objects.filter(type='St', backend_type='Lo')

        for library_path in library_paths:
            print(f'Watching path for changes {library_path.path}')

            # TODO: Work out how to watch multiple paths at once
            i = inotify.adapters.InotifyTree(library_path.path)

            for event in i.event_gen():
                if event is not None:
                    (header, type_names, watch_path, filename) = event
                    # if set(type_names).intersection(['IN_CLOSE_WRITE', 'IN_DELETE', 'IN_MOVED_FROM', 'IN_MOVED_TO']):  # TODO: Make moving photos really efficient by using the 'from' path
                    if set(type_names).intersection(['IN_CLOSE_WRITE', 'IN_DELETE', 'IN_MOVED_TO']):
                        photo_path = '{}/{}'.format(watch_path, filename)
                        print(f'Recording photo "{photo_path}" to library "{library_path.library}"')
                        record_photo(photo_path, library_path.library)

    def handle(self, *args, **options):
        try:
            self.watch_photos()
        except KeyboardInterrupt:
            exit(0)
<commit_msg>Fixes photo import error<commit_after>import inotify.adapters

from django.conf import settings
from django.core.management.base import BaseCommand

from photonix.photos.utils.db import record_photo
from photonix.photos.models import LibraryPath

class Command(BaseCommand):
    help = 'Watches photo directories and creates relevant database records for all photos that are added or modified.'

    def watch_photos(self):
        library_paths = LibraryPath.objects.filter(type='St', backend_type='Lo')

        for library_path in library_paths:
            print(f'Watching path for changes {library_path.path}')

            # TODO: Work out how to watch multiple paths at once
            i = inotify.adapters.InotifyTree(library_path.path)

            for event in i.event_gen():
                if event is not None:
                    (header, type_names, watch_path, filename) = event
                    # if set(type_names).intersection(['IN_CLOSE_WRITE', 'IN_DELETE', 'IN_MOVED_FROM', 'IN_MOVED_TO']):  # TODO: Make moving photos really efficient by using the 'from' path
                    if set(type_names).intersection(['IN_CLOSE_WRITE', 'IN_DELETE', 'IN_MOVED_TO']):
                        photo_path = '{}{}'.format(watch_path, filename)
                        print(f'Recording photo "{photo_path}" to library "{library_path.library}"')
                        record_photo(photo_path, library_path.library)

    def handle(self, *args, **options):
        try:
            self.watch_photos()
        except KeyboardInterrupt:
            exit(0)
<|endoftext|>


<commit_before>import sys
import os
sys.path.append(os.path.dirname(__file__))
import wsgi_server

def application(environ, start_response):
    return wsgi_server.application(environ,start_response)
<commit_msg>Switch to Werkzeug middleware<commit_after>import sys
import os
sys.path.append(os.path.dirname(__file__))
import wsgi_static

def application(environ, start_response):
    return wsgi_static.application(environ,start_response)
<|endoftext|>


<commit_before>"""
Numba optimized functions for computing similarities and distances.

These routines are only neccesary for computing thousands of distances
between sequences or short peptides.

For single distances or smaller distances these functions may
effectively be slower due to conversions from strings to vector
representations.

Templates:

d = nb_metric(seqVec1, seqVec2, substMat, **kwargs)

pwdist = nb_dist_rect(seqMat1, seqMat2, substMat, **kwargs)

"""

"""NOTE: The default kwargs used here will not jit with numba as of version 0.18.2"""

import numpy as np

"""Can assume NB_SUCCESS is True if this module is imported at all."""
from . import NB_SUCCESS
from . import nb

__all__ = ['nb_hamming_distance',
           'nb_seq_similarity',
           'nb_seq_distance',
           'nb_coverage_distance']

@nb.jit(nb.int8(nb.char[:],nb.char[:]), nopython = True)
def nb_hamming_distance(str1,str2):
    assert str1.shape[0] == str2.shape[0]

    tot = 0
    for s1,s2 in zip(str1,str2):
        if s1 != s2:
            tot += 1
    return tot

#@nb.jit(nb.float64(nb.int8[:],nb.int8[:],nb.float64[:,:],nb.boolean,nb.boolean), nopython = True)
@nb.jit(nopython = True)
def nb_seq_similarity(seq1, seq2, substMat, normed = True, asDistance = False):
    """Computes sequence similarity based on the substitution matrix."""
    assert seq1.shape[0] == seq2.shape[0]

    if normed:
        sim12 = 0.
        site12N = 0.
        sim11 = 0.
        sim22 = 0.
        for i in range(seq1.shape[0]):
            cur12 = substMat[seq1[i],seq2[i]]
            cur11 = substMat[seq1[i],seq1[i]]
            cur22 = substMat[seq2[i],seq2[i]]
            if not np.isnan(cur12):
                sim12 += cur12
                site12N += 1.
            if not np.isnan(cur11):
                sim11 += cur11
            if not np.isnan(cur22):
                sim22 += cur22
        sim12 = 2*sim12/((sim11/site12N) + (sim22/site12N))
    else:
        sim12 = 0.
        site12N = 0.
        for i in range(seq1.shape[0]):
            if not np.isnan(substMat[seq1[i],seq2[i]]):
                sim12 += substMat[seq1[i],seq2[i]]
                site12N += 1.

    if asDistance:
        if normed:
            sim12 = (site12N - sim12)/site12N
        else:
            sim12 = site12N - sim12
    return sim12

@nb.jit(nopython = True)
def nb_seq_distance(seq1, seq2, substMat, normed = False):
    """Compare two sequences and return the distance from one to the other
    If the seqs are of different length then it raises an exception

    Returns a scalar [0, siteN] where siteN ignores nan similarities which may depend on gaps
    Optionally returns normed = True distance:
        [0, 1]

    Note that either way the distance is "normed", its either per site (True) or total normed (False):
        [0, siteN]"""
    return nb_seq_similarity(seq1, seq2, substMat, normed, True)

#@nb.jit(nb.float64(nb.int8[:],nb.int8[:],nb.int8), nopython = True)
@nb.jit(nopython = True)
def nb_coverage_distance(epitope, peptide, mmTolerance = 0):
    """Determines whether pepitide covers epitope
    and can handle epitopes and peptides of different lengths.

    To be a consistent distance matrix:
        covered = 0
        not-covered = 1

    If epitope is longer than peptide it is not covered.
    Otherwise coverage is determined based on a mmTolerance

    Parameters
    ----------
    epitope : np.array
    peptide : np.array
    mmTolerance : int
        Number of mismatches tolerated
        If dist <= mmTolerance then it is covered

    Returns
    -------
    covered : int
        Covered (0) or not-covered (1)"""

    LEpitope, LPeptide = len(epitope), len(peptide)
    if LEpitope > LPeptide:
        return 1

    for starti in range(LPeptide-LEpitope+1):
        mm = 0
        for k in range(LEpitope):
            if epitope[k] != peptide[starti + k]:
                mm = mm + 1
                if mm > mmTolerance:
                    """If this peptide is already over the tolerance then goto next one"""
                    break
        if mm <= mmTolerance:
            """If this peptide is below tolerance then return covered (0)"""
            return 0
    """If no peptides meet mmTolerance then return not covered"""
    return 1<commit_msg>Fixed nb hamming return type bug.<commit_after>"""
Numba optimized functions for computing similarities and distances.

These routines are only neccesary for computing thousands of distances
between sequences or short peptides.

For single distances or smaller distances these functions may
effectively be slower due to conversions from strings to vector
representations.

Templates:

d = nb_metric(seqVec1, seqVec2, substMat, **kwargs)

pwdist = nb_dist_rect(seqMat1, seqMat2, substMat, **kwargs)

"""

"""NOTE: The default kwargs used here will not jit with numba as of version 0.18.2"""

import numpy as np

"""Can assume NB_SUCCESS is True if this module is imported at all."""
from . import NB_SUCCESS
from . import nb

__all__ = ['nb_hamming_distance',
           'nb_seq_similarity',
           'nb_seq_distance',
           'nb_coverage_distance']

@nb.jit(nb.float64(nb.char[:],nb.char[:]), nopython = True)
def nb_hamming_distance(str1,str2):
    assert str1.shape[0] == str2.shape[0]

    tot = 0
    for s1,s2 in zip(str1,str2):
        if s1 != s2:
            tot += 1
    return tot

@nb.jit(nb.float64(nb.int8[:],nb.int8[:],nb.float64[:,:],nb.boolean,nb.boolean), nopython = True)
def nb_seq_similarity(seq1, seq2, substMat, normed = True, asDistance = False):
    """Computes sequence similarity based on the substitution matrix."""
    assert seq1.shape[0] == seq2.shape[0]

    if normed:
        sim12 = 0.
        site12N = 0.
        sim11 = 0.
        sim22 = 0.
        for i in range(seq1.shape[0]):
            cur12 = substMat[seq1[i],seq2[i]]
            cur11 = substMat[seq1[i],seq1[i]]
            cur22 = substMat[seq2[i],seq2[i]]
            if not np.isnan(cur12):
                sim12 += cur12
                site12N += 1.
            if not np.isnan(cur11):
                sim11 += cur11
            if not np.isnan(cur22):
                sim22 += cur22
        sim12 = 2*sim12/((sim11/site12N) + (sim22/site12N))
    else:
        sim12 = 0.
        site12N = 0.
        for i in range(seq1.shape[0]):
            if not np.isnan(substMat[seq1[i],seq2[i]]):
                sim12 += substMat[seq1[i],seq2[i]]
                site12N += 1.

    if asDistance:
        if normed:
            sim12 = (site12N - sim12)/site12N
        else:
            sim12 = site12N - sim12
    return sim12
@nb.jit(nb.float64(nb.int8[:],nb.int8[:],nb.float64[:,:],nb.boolean), nopython = True)
def nb_seq_distance(seq1, seq2, substMat, normed = False):
    """Compare two sequences and return the distance from one to the other
    If the seqs are of different length then it raises an exception

    Returns a scalar [0, siteN] where siteN ignores nan similarities which may depend on gaps
    Optionally returns normed = True distance:
        [0, 1]

    Note that either way the distance is "normed", its either per site (True) or total normed (False):
        [0, siteN]"""
    return nb_seq_similarity(seq1, seq2, substMat, normed, True)

@nb.jit(nb.float64(nb.int8[:],nb.int8[:],nb.int8), nopython = True)
def nb_coverage_distance(epitope, peptide, mmTolerance = 0):
    """Determines whether pepitide covers epitope
    and can handle epitopes and peptides of different lengths.

    To be a consistent distance matrix:
        covered = 0
        not-covered = 1

    If epitope is longer than peptide it is not covered.
    Otherwise coverage is determined based on a mmTolerance

    Parameters
    ----------
    epitope : np.array
    peptide : np.array
    mmTolerance : int
        Number of mismatches tolerated
        If dist <= mmTolerance then it is covered

    Returns
    -------
    covered : int
        Covered (0) or not-covered (1)"""

    LEpitope, LPeptide = len(epitope), len(peptide)
    if LEpitope > LPeptide:
        return 1

    for starti in range(LPeptide-LEpitope+1):
        mm = 0
        for k in range(LEpitope):
            if epitope[k] != peptide[starti + k]:
                mm = mm + 1
                if mm > mmTolerance:
                    """If this peptide is already over the tolerance then goto next one"""
                    break
        if mm <= mmTolerance:
            """If this peptide is below tolerance then return covered (0)"""
            return 0
    """If no peptides meet mmTolerance then return not covered"""
    return 1<|endoftext|>


<commit_before>"""Handle a world folder, containing .mcr or .mca region files"""

import os, glob, re
from . import region
from . import chunk

class UnknownWorldFormat(Exception):
	"""Unknown or invalid world folder"""
	def __init__(self, msg):
		self.msg = msg

class WorldFolder(object):
	"""Abstract class, representing either a McRegion or Anvil Chunk."""
	type = "Generic"
	# Preferred subclasses to use (in this order)
	# this is defined as (AnvilWorldFolder, McRegionWorldFolder) AFTER the
	# definition of those subclasses (it can't be done here, because Python insists
	# that objects are defined before they are referenced.
	subclasses = ()

	def __new__(cls, world_folder, *args, **kwargs):
		"""Python trickery to return a AnvilWorldFolder or McRegionWorldFolder
		instance, or raise a UnknownWorldFormat."""
		if cls == WorldFolder: # Format unspecified. Check which world format to use.
			for cls in cls.subclasses:
				wf = cls(world_folder, *args, **kwargs)
				if wf.valid(): # Check if the world is non-empty
					return wf
			raise UnknownWorldFormat("Empty world or unknown format: %r" % world_folder)
		else:
			return object.__new__(cls, world_folder, *args, **kwargs)

	def __init__(self, world_folder):
		"""Initialize a WorldFolder. To check if the world is valid (=non-empty), use self.valid()"""
		self.worldfolder = world_folder
		self.format = format
		self.regionfiles = {}
		self.regions     = {}
		self.chunks      = None
		# os.listdir triggers an OSError for non-existant directories or permission errors.
		# This is needed, because glob.glob silently returns no files.
		os.listdir(world_folder)
		filenames = None
		if self.format == None:
			# may raise UnknownWorldFormat
			self.format, filenames = self.guessformat()
		else:
			filenames = self.get_filenames()
		for filename in filenames:
			# Assume that filenames have the name r.<x-digit>.<z-digit>.<extension>
			m = re.match(r"r.(\-?\d+).(\-?\d+)."+self.extension, os.path.basename(filename))
			if m:
				x = int(m.group(1))
				z = int(m.group(2))
			else:
				# Only raised if a .mca of .mcr file exists which does not comply to the
				#  r.<x-digit>.<z-digit>.<extension> filename format. This may raise false
				# errors if a copy is made, e.g. "r.0.-1 copy.mca". If this is an issue, override
				# get_filenames(). In most cases, it is an error, and we like to raise that.
				raise UnknownWorldFormat("Unrecognized filename format %s" % os.path.basename(filename))
			self.regionfiles[(x,z)] = filename

	def get_filenames(self):
		# Warning: glob returns a empty list if the directory is unreadable, without raising an Exception
		return list(glob.glob(os.path.join(self.worldfolder,'region','r.*.*.'+self.extension)))

	def valid(self):
		"""Return True is the world is non-empty"""
		return len(self.regionfiles) > 0

	def get_regionfiles(self):
		"""return a list of full path with region files"""
		return list(self.regionfiles.values())

	def get_region(self, x,z):
		"""Get a region using x,z coordinates of a region. Cache results."""
		if (x,z) not in self.regions:
			if (x,z) in self.regionfiles:
				self.regions[(x,z)] = region.RegionFile(self.regionfiles[(x,z)])
			else:
				# Return an empty RegionFile object
				# TODO: this does not yet allow for saving of the region file
				self.regions[(x,z)] = region.RegionFile()
		return self.regions[(x,z)]

	def iter_regions(self):
		for x,z in self.regionfiles.keys():
			yield self.get_region(x,z)

	def iter_nbt(self):
		"""Returns an iterable list of all NBT. Use this function if you only
		want to loop through the chunks once, and don't need the block or data arrays.
		Use """
		# TODO: Implement BoundingBox
		# TODO: Implement sort order
		for region in self.iter_regions():
			for c in region.iter_chunks():
				yield c

	def iter_chunks(self):
		"""Returns an iterable list of all chunks. Use this function if you only
		want to loop through the chunks once or have a very large world.
		Use get_chunks() if you access the chunk list frequently and want to cache
		the results. Use iter_nbt() if you are concerned about speed and don't want
		to parse the block data."""
		# TODO: Implement BoundingBox
		# TODO: Implement sort order
		for c in self.iter_nbt():
			yield chunk.Chunk(c)

	def get_chunk(self,x,z):
		"""Return a chunk specified by the chunk coordinates x,z."""
		# TODO: Implement (calculate region filename from x,z, see if file exists.)
		rx,x = divmod(x,32)
		rz,z = divmod(z,32)
		return self.chunkclass(self.get_region(rx,rz).get_chunk(x,z))

	def get_chunks(self, boundingbox=None):
		"""Returns a list of all chunks. Use this function if you access the chunk
		list frequently and want to cache the result.
		Use iter_chunks() if you only want to loop through the chunks once or have a
		very large world."""
		if self.chunks == None:
			self.chunks = list(self.iter_chunks())
		return self.chunks

	def chunk_count(self):
		c = 0
		for r in self.iter_regions():
			c += r.chunk_count()
		return c

	def get_boundingbox(self):
		"""Return minimum and maximum x and z coordinates of the chunks."""
		b = BoundingBox()
		for rx,rz in self.regionfiles.keys():
			region = self.get_region(rx,rz)
			rx,rz = 32*rx,32*rz
			for cc in region.get_chunk_coords():
				x,z = (rx+cc['x'],rz+cc['z'])
				b.expand(x,None,z)
		return b

	def cache_test(self):
		"""Debug routine: loop through all chunks, fetch them again by coordinates, and check if the same object is returned."""
		# TODO: make sure this test succeeds (at least True,True,False, preferable True,True,True)
		# TODO: Move this function to test class.
		for rx,rz in self.regionfiles.keys():
			region = self.get_region(rx,rz)
			rx,rz = 32*rx,32*rz
			for cc in region.get_chunk_coords():
				x,z = (rx+cc['x'],rz+cc['z'])
				c1 = self.chunkclass(region.get_chunk(cc['x'],cc['z']))
				c2 = self.get_chunk(x,z)
				correct_coords = (c2.get_coords() == (x,z))
				is_comparable = (c1 == c2) # test __eq__ function
				is_equal = (id(c1) == id(c2)) # test if they point to the same memory location
				# DEBUG (prints a tuple)
				print((x,z,c1,c2,correct_coords,is_comparable,is_equal))

	def __repr__(self):
		return "%s(%s)" % (self.__class__.__name__,self.worldfolder)

class McRegionWorldFolder(WorldFolder):
	type = "McRegion"
	extension = 'mcr'
	chunkclass = chunk.Chunk
	# chunkclass = chunk.McRegionChunk  # TODO: change to McRegionChunk when done

class AnvilWorldFolder(WorldFolder):
	type = "Anvil"
	extension = 'mca'
	chunkclass = chunk.Chunk
	# chunkclass = chunk.AnvilChunk  # TODO: change to AnvilChunk when done

WorldFolder.subclasses = (AnvilWorldFolder, McRegionWorldFolder)

class BoundingBox(object):
	"""A bounding box of x,y,z coordinates"""
	def __init__(self,minx=None, maxx=None, miny=None, maxy=None, minz=None, maxz=None):
		self.minx,self.maxx = minx, maxx
		self.miny,self.maxy = miny, maxy
		self.minz,self.maxz = minz, maxz
	def expand(self,x,y,z):
		if x != None:
			if self.minx is None or x < self.minx:
				self.minx = x
			if self.maxx is None or x > self.maxx:
				self.maxx = x
		if y != None:
			if self.miny is None or y < self.miny:
				self.miny = y
			if self.maxy is None or y > self.maxy:
				self.maxy = y
		if z != None:
			if self.minz is None or z < self.minz:
				self.minz = z
			if self.maxz is None or z > self.maxz:
				self.maxz = z
	def lenx(self):
		return self.maxx-self.minx+1
	def leny(self):
		return self.maxy-self.miny+1
	def lenz(self):
		return self.maxz-self.minz+1
	def __str__(self):
		return "%s(%s,%s,%s,%s,%s,%s)" % (self.__class__.__name__,self.minx,self.maxx,
				self.miny,self.maxy,self.minz,self.maxz)
<commit_msg>get_chunk raises exception for non-existing Chunks<commit_after>"""Handle a world folder, containing .mcr or .mca region files"""

import os, glob, re
from . import region
from . import chunk

class UnknownWorldFormat(Exception):
	"""Unknown or invalid world folder"""
	def __init__(self, msg):
		self.msg = msg

class InconceivedChunk(LookupError):
	"""Specified does not exist in world file"""

class WorldFolder(object):
	"""Abstract class, representing either a McRegion or Anvil Chunk."""
	type = "Generic"
	# Preferred subclasses to use (in this order)
	# this is defined as (AnvilWorldFolder, McRegionWorldFolder) AFTER the
	# definition of those subclasses (it can't be done here, because Python insists
	# that objects are defined before they are referenced.
	subclasses = ()

	def __new__(cls, world_folder, *args, **kwargs):
		"""Python trickery to return a AnvilWorldFolder or McRegionWorldFolder
		instance, or raise a UnknownWorldFormat."""
		if cls == WorldFolder: # Format unspecified. Check which world format to use.
			for cls in cls.subclasses:
				wf = cls(world_folder, *args, **kwargs)
				if wf.valid(): # Check if the world is non-empty
					return wf
			raise UnknownWorldFormat("Empty world or unknown format: %r" % world_folder)
		else:
			return object.__new__(cls, world_folder, *args, **kwargs)

	def __init__(self, world_folder):
		"""Initialize a WorldFolder. To check if the world is valid (=non-empty), use self.valid()"""
		self.worldfolder = world_folder
		self.format = format
		self.regionfiles = {}
		self.regions     = {}
		self.chunks      = None
		# os.listdir triggers an OSError for non-existant directories or permission errors.
		# This is needed, because glob.glob silently returns no files.
		os.listdir(world_folder)
		filenames = None
		if self.format == None:
			# may raise UnknownWorldFormat
			self.format, filenames = self.guessformat()
		else:
			filenames = self.get_filenames()
		for filename in filenames:
			# Assume that filenames have the name r.<x-digit>.<z-digit>.<extension>
			m = re.match(r"r.(\-?\d+).(\-?\d+)."+self.extension, os.path.basename(filename))
			if m:
				x = int(m.group(1))
				z = int(m.group(2))
			else:
				# Only raised if a .mca of .mcr file exists which does not comply to the
				#  r.<x-digit>.<z-digit>.<extension> filename format. This may raise false
				# errors if a copy is made, e.g. "r.0.-1 copy.mca". If this is an issue, override
				# get_filenames(). In most cases, it is an error, and we like to raise that.
				raise UnknownWorldFormat("Unrecognized filename format %s" % os.path.basename(filename))
			self.regionfiles[(x,z)] = filename

	def get_filenames(self):
		# Warning: glob returns a empty list if the directory is unreadable, without raising an Exception
		return list(glob.glob(os.path.join(self.worldfolder,'region','r.*.*.'+self.extension)))

	def valid(self):
		"""Return True is the world is non-empty"""
		return len(self.regionfiles) > 0

	def get_regionfiles(self):
		"""return a list of full path with region files"""
		return list(self.regionfiles.values())

	def get_region(self, x,z):
		"""Get a region using x,z coordinates of a region. Cache results."""
		if (x,z) not in self.regions:
			if (x,z) in self.regionfiles:
				self.regions[(x,z)] = region.RegionFile(self.regionfiles[(x,z)])
			else:
				# Return an empty RegionFile object
				# TODO: this does not yet allow for saving of the region file
				self.regions[(x,z)] = region.RegionFile()
		return self.regions[(x,z)]

	def iter_regions(self):
		for x,z in self.regionfiles.keys():
			yield self.get_region(x,z)

	def iter_nbt(self):
		"""Returns an iterable list of all NBT. Use this function if you only
		want to loop through the chunks once, and don't need the block or data arrays.
		Use """
		# TODO: Implement BoundingBox
		# TODO: Implement sort order
		for region in self.iter_regions():
			for c in region.iter_chunks():
				yield c

	def iter_chunks(self):
		"""Returns an iterable list of all chunks. Use this function if you only
		want to loop through the chunks once or have a very large world.
		Use get_chunks() if you access the chunk list frequently and want to cache
		the results. Use iter_nbt() if you are concerned about speed and don't want
		to parse the block data."""
		# TODO: Implement BoundingBox
		# TODO: Implement sort order
		for c in self.iter_nbt():
			yield chunk.Chunk(c)

	def get_chunk(self,x,z):
		"""Return a chunk specified by the chunk coordinates x,z."""
		# TODO: Implement (calculate region filename from x,z, see if file exists.)
		rx,x = divmod(x,32)
		rz,z = divmod(z,32)
		nbt = self.get_region(rx,rz).get_chunk(x,z)
		if nbt == None:
			raise InconceivedChunk("Chunk %s,%s not present in world" % (32*rx+x,32*rz+z))
		return self.chunkclass(nbt)

	def get_chunks(self, boundingbox=None):
		"""Returns a list of all chunks. Use this function if you access the chunk
		list frequently and want to cache the result.
		Use iter_chunks() if you only want to loop through the chunks once or have a
		very large world."""
		if self.chunks == None:
			self.chunks = list(self.iter_chunks())
		return self.chunks

	def chunk_count(self):
		c = 0
		for r in self.iter_regions():
			c += r.chunk_count()
		return c

	def get_boundingbox(self):
		"""Return minimum and maximum x and z coordinates of the chunks."""
		b = BoundingBox()
		for rx,rz in self.regionfiles.keys():
			region = self.get_region(rx,rz)
			rx,rz = 32*rx,32*rz
			for cc in region.get_chunk_coords():
				x,z = (rx+cc['x'],rz+cc['z'])
				b.expand(x,None,z)
		return b

	def cache_test(self):
		"""Debug routine: loop through all chunks, fetch them again by coordinates, and check if the same object is returned."""
		# TODO: make sure this test succeeds (at least True,True,False, preferable True,True,True)
		# TODO: Move this function to test class.
		for rx,rz in self.regionfiles.keys():
			region = self.get_region(rx,rz)
			rx,rz = 32*rx,32*rz
			for cc in region.get_chunk_coords():
				x,z = (rx+cc['x'],rz+cc['z'])
				c1 = self.chunkclass(region.get_chunk(cc['x'],cc['z']))
				c2 = self.get_chunk(x,z)
				correct_coords = (c2.get_coords() == (x,z))
				is_comparable = (c1 == c2) # test __eq__ function
				is_equal = (id(c1) == id(c2)) # test if they point to the same memory location
				# DEBUG (prints a tuple)
				print((x,z,c1,c2,correct_coords,is_comparable,is_equal))

	def __repr__(self):
		return "%s(%s)" % (self.__class__.__name__,self.worldfolder)

class McRegionWorldFolder(WorldFolder):
	type = "McRegion"
	extension = 'mcr'
	chunkclass = chunk.Chunk
	# chunkclass = chunk.McRegionChunk  # TODO: change to McRegionChunk when done

class AnvilWorldFolder(WorldFolder):
	type = "Anvil"
	extension = 'mca'
	chunkclass = chunk.Chunk
	# chunkclass = chunk.AnvilChunk  # TODO: change to AnvilChunk when done

WorldFolder.subclasses = (AnvilWorldFolder, McRegionWorldFolder)

class BoundingBox(object):
	"""A bounding box of x,y,z coordinates"""
	def __init__(self,minx=None, maxx=None, miny=None, maxy=None, minz=None, maxz=None):
		self.minx,self.maxx = minx, maxx
		self.miny,self.maxy = miny, maxy
		self.minz,self.maxz = minz, maxz
	def expand(self,x,y,z):
		if x != None:
			if self.minx is None or x < self.minx:
				self.minx = x
			if self.maxx is None or x > self.maxx:
				self.maxx = x
		if y != None:
			if self.miny is None or y < self.miny:
				self.miny = y
			if self.maxy is None or y > self.maxy:
				self.maxy = y
		if z != None:
			if self.minz is None or z < self.minz:
				self.minz = z
			if self.maxz is None or z > self.maxz:
				self.maxz = z
	def lenx(self):
		return self.maxx-self.minx+1
	def leny(self):
		return self.maxy-self.miny+1
	def lenz(self):
		return self.maxz-self.minz+1
	def __str__(self):
		return "%s(%s,%s,%s,%s,%s,%s)" % (self.__class__.__name__,self.minx,self.maxx,
				self.miny,self.maxy,self.minz,self.maxz)
<|endoftext|>


<commit_before>from __future__ import print_function, division
from time import sleep
import matplotlib.pyplot as plt
import signal

__all__ = ["sig_int_handler", "publish_pose", "publish_trajectory",
           "publish_time_indexed_trajectory", "plot"]

def sig_int_handler(signal, frame):
    raise KeyboardInterrupt

def publish_pose(q, problem, t=0.0):
    problem.get_scene().update(q, t)
    problem.get_scene().get_kinematic_tree().publish_frames()

def publish_trajectory(traj, T, problem, once=False):
    if len(traj) == 0:
        print("Trajectory has zero elements")
        raise
    signal.signal(signal.SIGINT, sig_int_handler)
    print('Playing back trajectory ' + str(T) + 's')
    dt = float(T) / float(len(traj))
    t = 0
    while True:
        try:
            publish_pose(traj[t], problem, float(t) * dt)
            sleep(dt)
            if t >= len(traj) - 1 and once:
                return
            t = (t + 1) % len(traj)
        except KeyboardInterrupt:
            return False

def publish_time_indexed_trajectory(traj, Ts, problem, once=False):
    if len(traj) == 0:
        print("Trajectory has zero elements")
        raise
    signal.signal(signal.SIGINT, sig_int_handler)
    print('Playing back trajectory ' + str(len(Ts)) +
          ' states in ' + str(Ts[len(Ts) - 1]))

    while True:
        try:
            for i in range(1, len(Ts) - 1):
                publish_pose(traj[i], problem, Ts[i])
                sleep(Ts[i] - Ts[i-1])
            if once:
                break
        except KeyboardInterrupt:
            return False
    return True

def plot(solution):
    print('Plotting the solution')
    plt.plot(solution, '.-')
    plt.show()
<commit_msg>[exotica_python] Allow labels/legend in plot()<commit_after>from __future__ import print_function, division
from time import sleep
import matplotlib.pyplot as plt
import signal

__all__ = ["sig_int_handler", "publish_pose", "publish_trajectory",
           "publish_time_indexed_trajectory", "plot"]

def sig_int_handler(signal, frame):
    raise KeyboardInterrupt

def publish_pose(q, problem, t=0.0):
    problem.get_scene().update(q, t)
    problem.get_scene().get_kinematic_tree().publish_frames()

def publish_trajectory(traj, T, problem, once=False):
    if len(traj) == 0:
        print("Trajectory has zero elements")
        raise
    signal.signal(signal.SIGINT, sig_int_handler)
    print('Playing back trajectory ' + str(T) + 's')
    dt = float(T) / float(len(traj))
    t = 0
    while True:
        try:
            publish_pose(traj[t], problem, float(t) * dt)
            sleep(dt)
            if t >= len(traj) - 1 and once:
                return
            t = (t + 1) % len(traj)
        except KeyboardInterrupt:
            return False

def publish_time_indexed_trajectory(traj, Ts, problem, once=False):
    if len(traj) == 0:
        print("Trajectory has zero elements")
        raise
    signal.signal(signal.SIGINT, sig_int_handler)
    print('Playing back trajectory ' + str(len(Ts)) +
          ' states in ' + str(Ts[len(Ts) - 1]))

    while True:
        try:
            for i in range(1, len(Ts) - 1):
                publish_pose(traj[i], problem, Ts[i])
                sleep(Ts[i] - Ts[i-1])
            if once:
                break
        except KeyboardInterrupt:
            return False
    return True

def plot(solution, labels=None):
    print('Plotting the solution')
    plt.plot(solution, '.-')
    if labels is not None:
        plt.legend(labels)
    plt.show()
<|endoftext|>


<commit_before>from __future__ import unicode_literals

import re

class LineBuffer(object):
    r"""
    Buffer bytes read in from a connection and serve complete lines back.

    >>> b = LineBuffer()
    >>> len(b)
    0

    >>> b.feed(b'foo\nbar')
    >>> len(b)
    7
    >>> list(b.lines()) == [b'foo']
    True
    >>> len(b)
    3

    >>> b.feed(b'bar\r\nbaz\n')
    >>> list(b.lines()) == [b'barbar', b'baz']
    True
    >>> len(b)
    0

    The buffer will not perform any decoding.

    >>> b.feed(b'Ol\xe9\n')
    >>> list(b.lines()) == [b'Ol\xe9']
    True

    The LineBuffer should also act as an iterable.

    >>> b.feed(b'iterate\nthis\n')
    >>> for line, expected in zip(b, [b'iterate', b'this']):
    ...    assert line == expected
    """
    line_sep_exp = re.compile(b'\r?\n')

    def __init__(self):
        self.buffer = b''

    def feed(self, bytes):
        self.buffer += bytes

    def lines(self):
        lines = self.line_sep_exp.split(self.buffer)
        # save the last, unfinished, possibly empty line
        self.buffer = lines.pop()
        return iter(lines)

    def __iter__(self):
        return self.lines()

    def __len__(self):
        return len(self.buffer)

class DecodingLineBuffer(LineBuffer):
    r"""
    Like LineBuffer, but decode the output (default assumes UTF-8).

    >>> utf8_word = b'Ol\xc3\xa9'
    >>> b = DecodingLineBuffer()
    >>> b.feed(b'bar\r\nbaz\n' + utf8_word + b'\n')
    >>> list(b.lines()) == ['bar', 'baz', utf8_word.decode('utf-8')]
    True
    >>> len(b)
    0

    Some clients will feed latin-1 or other encodings. If your client should
    support docoding from these clients (and not raise a UnicodeDecodeError),
    set errors='replace':

    >>> b = DecodingLineBuffer()
    >>> b.errors = 'replace'
    >>> b.feed(b'Ol\xe9\n')
    >>> list(b.lines()) == ['Ol\ufffd']
    True

    >>> b = DecodingLineBuffer()
    >>> b.feed(b'Ol\xe9\n')
    >>> list(b.lines())
    Traceback (most recent call last):
    ...
    UnicodeDecodeError: ...
    """
    encoding = 'utf-8'
    errors = 'strict'

    def lines(self):
        for line in super(DecodingLineBuffer, self).lines():
            try:
                yield line.decode(self.encoding, self.errors)
            except UnicodeDecodeError:
                self.handle_exception()

    def handle_exception(self):
        raise

class LenientDecodingLineBuffer(LineBuffer):
    r"""
    Like LineBuffer, but decode the output. First try UTF-8 and if that
    fails, use latin-1, which decodes all byte strings.

    >>> b = LenientDecodingLineBuffer()
    >>> utf8_word = b'Ol\xc3\xa9'
    >>> b.feed(utf8_word + b'\n')
    >>> b.feed(b'Ol\xe9\n')
    >>> list(b.lines()) == [utf8_word.decode('utf-8')]*2
    True
    """

    def lines(self):
        for line in super(LenientDecodingLineBuffer, self).lines():
            try:
                yield line.decode('utf-8', 'strict')
            except UnicodeDecodeError:
                yield line.decode('latin-1')
<commit_msg>Log a warning when DecodingLineBuffer encounters undecodable input. Fixes #54.<commit_after>from __future__ import unicode_literals

import re
import textwrap
import logging

log = logging.getLogger(__name__)

class LineBuffer(object):
    r"""
    Buffer bytes read in from a connection and serve complete lines back.

    >>> b = LineBuffer()
    >>> len(b)
    0

    >>> b.feed(b'foo\nbar')
    >>> len(b)
    7
    >>> list(b.lines()) == [b'foo']
    True
    >>> len(b)
    3

    >>> b.feed(b'bar\r\nbaz\n')
    >>> list(b.lines()) == [b'barbar', b'baz']
    True
    >>> len(b)
    0

    The buffer will not perform any decoding.

    >>> b.feed(b'Ol\xe9\n')
    >>> list(b.lines()) == [b'Ol\xe9']
    True

    The LineBuffer should also act as an iterable.

    >>> b.feed(b'iterate\nthis\n')
    >>> for line, expected in zip(b, [b'iterate', b'this']):
    ...    assert line == expected
    """
    line_sep_exp = re.compile(b'\r?\n')

    def __init__(self):
        self.buffer = b''

    def feed(self, bytes):
        self.buffer += bytes

    def lines(self):
        lines = self.line_sep_exp.split(self.buffer)
        # save the last, unfinished, possibly empty line
        self.buffer = lines.pop()
        return iter(lines)

    def __iter__(self):
        return self.lines()

    def __len__(self):
        return len(self.buffer)

class DecodingLineBuffer(LineBuffer):
    r"""
    Like LineBuffer, but decode the output (default assumes UTF-8).

    >>> utf8_word = b'Ol\xc3\xa9'
    >>> b = DecodingLineBuffer()
    >>> b.feed(b'bar\r\nbaz\n' + utf8_word + b'\n')
    >>> list(b.lines()) == ['bar', 'baz', utf8_word.decode('utf-8')]
    True
    >>> len(b)
    0

    Some clients will feed latin-1 or other encodings. If your client should
    support docoding from these clients (and not raise a UnicodeDecodeError),
    set errors='replace':

    >>> b = DecodingLineBuffer()
    >>> b.errors = 'replace'
    >>> b.feed(b'Ol\xe9\n')
    >>> list(b.lines()) == ['Ol\ufffd']
    True

    >>> b = DecodingLineBuffer()
    >>> b.feed(b'Ol\xe9\n')
    >>> list(b.lines())
    Traceback (most recent call last):
    ...
    UnicodeDecodeError: ...
    """
    encoding = 'utf-8'
    errors = 'strict'

    def lines(self):
        for line in super(DecodingLineBuffer, self).lines():
            try:
                yield line.decode(self.encoding, self.errors)
            except UnicodeDecodeError:
                self.handle_exception()

    def handle_exception(self):
        msg = textwrap.dedent("""
            Unknown encoding encountered. See 'Decoding Input'
            in https://pypi.python.org/pypi/irc for details.
            """)
        log.warning(msg)
        raise

class LenientDecodingLineBuffer(LineBuffer):
    r"""
    Like LineBuffer, but decode the output. First try UTF-8 and if that
    fails, use latin-1, which decodes all byte strings.

    >>> b = LenientDecodingLineBuffer()
    >>> utf8_word = b'Ol\xc3\xa9'
    >>> b.feed(utf8_word + b'\n')
    >>> b.feed(b'Ol\xe9\n')
    >>> list(b.lines()) == [utf8_word.decode('utf-8')]*2
    True
    """

    def lines(self):
        for line in super(LenientDecodingLineBuffer, self).lines():
            try:
                yield line.decode('utf-8', 'strict')
            except UnicodeDecodeError:
                yield line.decode('latin-1')
<|endoftext|>


<commit_before># Copyright (c) 2009 Dusty Phillips <dusty@linux.ca>
#
# 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 optparse import OptionParser
import inspect
import sys

__version__ = "0.1"

def opterate(func):
    '''A decorator for a main function entry point to a script. It tries to
    automatically generate the options for the main entry point based on the
    arguments, keyword arguments, and docstring.

    All keyword arguments in the function definition are options. Positional
    arguments are mandatory arguments.  Varargs become a variable length (zero
    allowed) list of positional arguments. Varkwargs are currently not
    supported/translated.

    The default value assigned to a keyword argument helps determine the type
    of option and action. The defalut value is assigned directly to the option
    parser's default for that option. In addition, it determines the
    OptionParser action -- a default value of False implies store_true, while
    True implies store_false. If the default value is a list, the action is
    append (multiple instances of that option are permitted). Strings or None
    imply a store action.

    Options are further defined in the docstring. The top part of the docstring
    becomes the usage message for the app. Below that, @param lines in the
    following format describe the option:

    @param variable_name -v --verbose the help_text for the variable
    @param variable_name -v the help_text no long option
    @param variable_name --verbose the help_text no short option

    the format is:
    @param name [short option and/or long option] help text

    Variable_name is the name of the variable in the function specification and
    must refer to a keyword argument. All options must have an @param line like
    this. If you can have an arbitrary length of positional arguments, add a
    *arglist variable; It can be named with any valid python identifier.

    See opterator_test.py for some examples.'''
    argnames, varargs, varkw, defaults = inspect.getargspec(func)

    if defaults:
        positional_params = argnames[:-1*len(defaults)]
        kw_params = argnames[-1*len(defaults):]
    else:
        positional_params = argnames
        kw_params = []

    usage_text = ''
    parameters = []
    if func.func_doc:
        parameters = func.func_doc.split('@param')
        usage_text = parameters.pop(0).strip()

    usage = "%prog [options]"
    if positional_params:
        usage += " " + " ".join(positional_params)
    if varargs:
        usage += " [%s]" % varargs
    usage += "\n\n%s" % usage_text

    option_names = []
    parser = OptionParser(usage)
    for param in parameters:
        option_strings = []
        param_args = param.split()
        variable_name = param_args.pop(0)
        option_names.append(variable_name)
        long_name = short_name = None
        while param_args[0].startswith('-'):
            option_strings.append(param_args.pop(0))
        help_text = ' '.join(param_args)

        if variable_name not in kw_params:
            raise ValueError('%s is not a valid @param name.'
                    '@params must match keyword argumentnames in the'
                    'function signature.' % variable_name)

        default = None
        if variable_name in kw_params:
            default = defaults[kw_params.index(variable_name)]

        if default == False:
            action = 'store_true'
        elif default == True:
            action = 'store_false'
        elif type(default) in (list, tuple):
            action = 'append'
        else:
            action = 'store'

        parser.add_option(action=action, default=default, help=help_text,
                dest=variable_name, *option_strings)

    def wrapper(argv=None):
        options, positional = parser.parse_args(argv)
        processed_args = []

        for arg_name in argnames:
            if arg_name in option_names:
                option_value = getattr(options, arg_name)
                if not option_value and arg_name not in kw_params:
                    parser.error('%s is required.' % arg_name)

                processed_args.append(option_value)
            else:
                if positional:
                    processed_args.append(positional.pop(0))
                else:
                    parser.error('Not enough arguments.')

        processed_args += positional

        if len(processed_args) > len(argnames) and not varargs:
            parser.error('Too many arguments.')

        func(*processed_args)
    return wrapper
<commit_msg>func_doc is not python 3<commit_after># Copyright (c) 2009 Dusty Phillips <dusty@linux.ca>
#
# 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 optparse import OptionParser
import inspect
import sys

__version__ = "0.1"

def opterate(func):
    '''A decorator for a main function entry point to a script. It tries to
    automatically generate the options for the main entry point based on the
    arguments, keyword arguments, and docstring.

    All keyword arguments in the function definition are options. Positional
    arguments are mandatory arguments.  Varargs become a variable length (zero
    allowed) list of positional arguments. Varkwargs are currently not
    supported/translated.

    The default value assigned to a keyword argument helps determine the type
    of option and action. The defalut value is assigned directly to the option
    parser's default for that option. In addition, it determines the
    OptionParser action -- a default value of False implies store_true, while
    True implies store_false. If the default value is a list, the action is
    append (multiple instances of that option are permitted). Strings or None
    imply a store action.

    Options are further defined in the docstring. The top part of the docstring
    becomes the usage message for the app. Below that, @param lines in the
    following format describe the option:

    @param variable_name -v --verbose the help_text for the variable
    @param variable_name -v the help_text no long option
    @param variable_name --verbose the help_text no short option

    the format is:
    @param name [short option and/or long option] help text

    Variable_name is the name of the variable in the function specification and
    must refer to a keyword argument. All options must have an @param line like
    this. If you can have an arbitrary length of positional arguments, add a
    *arglist variable; It can be named with any valid python identifier.

    See opterator_test.py for some examples.'''
    argnames, varargs, varkw, defaults = inspect.getargspec(func)

    if defaults:
        positional_params = argnames[:-1*len(defaults)]
        kw_params = argnames[-1*len(defaults):]
    else:
        positional_params = argnames
        kw_params = []

    usage_text = ''
    parameters = []
    if func.__doc__:
        parameters = func.__doc__.split('@param')
        usage_text = parameters.pop(0).strip()

    usage = "%prog [options]"
    if positional_params:
        usage += " " + " ".join(positional_params)
    if varargs:
        usage += " [%s]" % varargs
    usage += "\n\n%s" % usage_text

    option_names = []
    parser = OptionParser(usage)
    for param in parameters:
        option_strings = []
        param_args = param.split()
        variable_name = param_args.pop(0)
        option_names.append(variable_name)
        long_name = short_name = None
        while param_args[0].startswith('-'):
            option_strings.append(param_args.pop(0))
        help_text = ' '.join(param_args)

        if variable_name not in kw_params:
            raise ValueError('%s is not a valid @param name.'
                    '@params must match keyword argumentnames in the'
                    'function signature.' % variable_name)

        default = None
        if variable_name in kw_params:
            default = defaults[kw_params.index(variable_name)]

        if default == False:
            action = 'store_true'
        elif default == True:
            action = 'store_false'
        elif type(default) in (list, tuple):
            action = 'append'
        else:
            action = 'store'

        parser.add_option(action=action, default=default, help=help_text,
                dest=variable_name, *option_strings)

    def wrapper(argv=None):
        options, positional = parser.parse_args(argv)
        processed_args = []

        for arg_name in argnames:
            if arg_name in option_names:
                option_value = getattr(options, arg_name)
                if not option_value and arg_name not in kw_params:
                    parser.error('%s is required.' % arg_name)

                processed_args.append(option_value)
            else:
                if positional:
                    processed_args.append(positional.pop(0))
                else:
                    parser.error('Not enough arguments.')

        processed_args += positional

        if len(processed_args) > len(argnames) and not varargs:
            parser.error('Too many arguments.')

        func(*processed_args)
    return wrapper
<|endoftext|>


<commit_before># Copyright (c) 2009, 2012, 2014 Dusty Phillips <dusty@buchuki.com>
#
# 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 argparse import ArgumentParser
import inspect

__version__ = "0.5"

def opterate(func):
    '''A decorator for a main function entry point to a script. It
    automatically generates the options for the main entry point based on the
    arguments, keyword arguments, and docstring.

    All keyword arguments in the function definition are options. Positional
    arguments are mandatory arguments that store a string value.  Varargs
    become a variable length (zero allowed) list of positional arguments.
    Varkwargs are currently ignored.

    The default value assigned to a keyword argument helps determine the type
    of option and action. The defalut value is assigned directly to the
    parser's default for that option. In addition, it determines the
    ArgumentParser action -- a default value of False implies store_true, while
    True implies store_false. If the default value is a list, the action is
    append (multiple instances of that option are permitted). Strings or None
    imply a store action.

    Options are further defined in the docstring. The top part of the docstring
    becomes the usage message for the app. Below that, ReST-style :param: lines
    in the following format describe the option:

    :param variable_name: -v --verbose the help_text for the variable
    :param variable_name: -v the help_text no long option
    :param variable_name: --verbose the help_text no short option

    the format is:
    :param name: [short option and/or long option] help text

    Variable_name is the name of the variable in the function specification and
    must refer to a keyword argument. All options must have a :param: line like
    this. If you can have an arbitrary length of positional arguments, add a
    *arglist variable; It can be named with any valid python identifier.

    See opterator_test.py and examples/ for some examples.'''
    argnames, varargs, varkw, defaults = inspect.getargspec(func)

    kw_boundary = len(argnames) - len(defaults) if defaults else len(argnames)
    positional_params = argnames[:kw_boundary]
    kw_params = argnames[kw_boundary:]

    description = ''
    param_docs = {}
    if func.__doc__:
        param_doc = func.__doc__.split(':param')
        description = param_doc.pop(0).strip()
        for param in param_doc:
            param_args = param.split()
            variable_name = param_args.pop(0)[:-1]
            param_docs[variable_name] = param_args

    parser = ArgumentParser(description=description)

    for param in positional_params:
        parser.add_argument(param, help=" ".join(param_docs.get(param, [])))
    for param in kw_params:
        default = defaults[kw_params.index(param)]
        names = []
        if param in param_docs:
            param_doc = param_docs.get(param, [])
            while param_doc and param_doc[0].startswith('-'):
                names.append(param_doc.pop(0))
        names = names if names else ['-'+param[0], '--' + param]

        option_kwargs = {
            'action': 'store',
            'help': ' '.join(param_doc),
            'dest': param,
            'default': default
        }
        if default is False:
            option_kwargs['action'] = 'store_true'
        elif default is True:
            option_kwargs['action'] = 'store_false'
        elif type(default) in (list, tuple):
            if default:
                option_kwargs['choices'] = default
            else:
                option_kwargs['action'] = 'append'

        parser.add_argument(*names, **option_kwargs)
    if varargs:
        parser.add_argument(varargs, nargs='*')

    def wrapper(argv=None):
        args = vars(parser.parse_args(argv))
        processed_args = [args[p] for p in argnames]
        if varargs:
            processed_args.extend(args[varargs])
        func(*processed_args)
    return wrapper
<commit_msg>Reusing variables is dangerous...<commit_after># Copyright (c) 2009, 2012, 2014 Dusty Phillips <dusty@buchuki.com>
#
# 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 argparse import ArgumentParser
import inspect

__version__ = "0.5"

def opterate(func):
    '''A decorator for a main function entry point to a script. It
    automatically generates the options for the main entry point based on the
    arguments, keyword arguments, and docstring.

    All keyword arguments in the function definition are options. Positional
    arguments are mandatory arguments that store a string value.  Varargs
    become a variable length (zero allowed) list of positional arguments.
    Varkwargs are currently ignored.

    The default value assigned to a keyword argument helps determine the type
    of option and action. The defalut value is assigned directly to the
    parser's default for that option. In addition, it determines the
    ArgumentParser action -- a default value of False implies store_true, while
    True implies store_false. If the default value is a list, the action is
    append (multiple instances of that option are permitted). Strings or None
    imply a store action.

    Options are further defined in the docstring. The top part of the docstring
    becomes the usage message for the app. Below that, ReST-style :param: lines
    in the following format describe the option:

    :param variable_name: -v --verbose the help_text for the variable
    :param variable_name: -v the help_text no long option
    :param variable_name: --verbose the help_text no short option

    the format is:
    :param name: [short option and/or long option] help text

    Variable_name is the name of the variable in the function specification and
    must refer to a keyword argument. All options must have a :param: line like
    this. If you can have an arbitrary length of positional arguments, add a
    *arglist variable; It can be named with any valid python identifier.

    See opterator_test.py and examples/ for some examples.'''
    argnames, varargs, varkw, defaults = inspect.getargspec(func)

    kw_boundary = len(argnames) - len(defaults) if defaults else len(argnames)
    positional_params = argnames[:kw_boundary]
    kw_params = argnames[kw_boundary:]

    description = ''
    param_docs = {}
    if func.__doc__:
        param_doc = func.__doc__.split(':param')
        description = param_doc.pop(0).strip()
        for param in param_doc:
            param_args = param.split()
            variable_name = param_args.pop(0)[:-1]
            param_docs[variable_name] = param_args

    parser = ArgumentParser(description=description)

    for param in positional_params:
        parser.add_argument(param, help=" ".join(param_docs.get(param, [])))
    for param in kw_params:
        default = defaults[kw_params.index(param)]
        names = []
        param_doc = []
        if param in param_docs:
            param_doc = param_docs.get(param, [])
            while param_doc and param_doc[0].startswith('-'):
                names.append(param_doc.pop(0))
        names = names if names else ['-' + param[0], '--' + param]

        option_kwargs = {
            'action': 'store',
            'help': ' '.join(param_doc),
            'dest': param,
            'default': default
        }
        if default is False:
            option_kwargs['action'] = 'store_true'
        elif default is True:
            option_kwargs['action'] = 'store_false'
        elif type(default) in (list, tuple):
            if default:
                option_kwargs['choices'] = default
            else:
                option_kwargs['action'] = 'append'

        parser.add_argument(*names, **option_kwargs)
    if varargs:
        parser.add_argument(varargs, nargs='*')

    def wrapper(argv=None):
        args = vars(parser.parse_args(argv))
        processed_args = [args[p] for p in argnames]
        if varargs:
            processed_args.extend(args[varargs])
        func(*processed_args)
    return wrapper
<|endoftext|>


<commit_before>import traceback, sys
import json
import configparser
import os.path
import collections

from UM.Settings.SettingsCategory import SettingsCategory
from UM.Settings.Setting import Setting
from UM.Signal import Signal, SignalEmitter
from PyQt5.QtCore import QCoreApplication
from UM.Logger import Logger
from UM.Resources import Resources
from UM.i18n import i18nCatalog

class MachineSettings(SignalEmitter):
    def __init__(self):
        super().__init__()
        self._categories = []
        self._platformMesh = None
        self._platform_texture = None
        self._name = "Unknown Machine",
        self._type_name = 'Unknown'
        self._type_id = 'unknown'
        self._icon = "unknown.png",
        self._machine_settings = []   ## Settings that don't have a category are 'fixed' (eg; they can not be changed by the user, unless they change the json)
        self._i18n_catalog = None

    ##  Load settings from JSON file. Used to load tree structure & default values etc from file.
    #   /param file_name String
    def loadSettingsFromFile(self, file_name):
        with open(file_name, 'rt', -1, 'utf-8') as f:
            data = json.load(f, object_pairs_hook=collections.OrderedDict)

        self._i18n_catalog = i18nCatalog(os.path.basename(file_name))

        if "id" in data:
            self._type_id = data["id"]

        if "platform" in data:
            self._platformMesh = data["platform"]

        if "platform_texture" in data:
            self._platform_texture = data["platform_texture"]

        if "name" in data:
            self._type_name = data["name"]

        if "icon" in data:
            self._icon = data["icon"]

        if "inherits" in data:
            inherits_from = MachineSettings()
            inherits_from.loadSettingsFromFile(os.path.dirname(file_name) + '/' + data['inherits'])
            self._machine_settings = inherits_from._machine_settings
            self._categories = inherits_from._categories

        if "machine_settings" in data:
            for key, value in data["machine_settings"].items():
                setting = self.getSettingByKey(key)
                if not setting:
                    setting = Setting(key, self._i18n_catalog)
                    self.addSetting(setting)
                setting.fillByDict(value)

        if "categories" in data:
            for key, value in data["categories"].items():
                category = self.getSettingsCategory(key)
                if not category:
                    category = SettingsCategory(key, self._i18n_catalog, self)
                    self.addSettingsCategory(category)
                category.fillByDict(value)

        for setting in self.getAllSettings():
            setting.valueChanged.connect(self.settingChanged)

        self.settingsLoaded.emit() #Emit signal that all settings are loaded (some setting stuff can only be done when all settings are loaded (eg; the conditional stuff)
    settingsLoaded = Signal()

    ##  Load values of settings from file.
    def loadValuesFromFile(self, file_name):
        config = configparser.ConfigParser()
        config.read(file_name)

        if not self._categories:
            self.loadSettingsFromFile(Resources.getPath(Resources.SettingsLocation, config['General']['type'] + '.json'))

        self._name = config.get('General', 'name', fallback = 'Unknown Machine')

        visibility = config.get('General', 'visibility', fallback = None)
        if visibility:
            values = visibility.split(',')
            for setting in self.getAllSettings():
                if setting.getKey() in values:
                    setting.setVisible(True)
                else:
                    setting.setVisible(False)

        for name, section in config.items():
            for key in section:
                setting = self.getSettingByKey(key)
                if setting is not None:
                    setting.setValue(section[key])

                    if not setting.isVisible():
                        setting.setVisible(True)

    ##  Save setting values to file
    def saveValuesToFile(self, file_name):
        config = configparser.ConfigParser()

        config.add_section('General')
        config['General']['type'] = self._type_id
        config['General']['name'] = self._name
        config['General']['visibility'] = self._getVisibleSettings()

        for category in self._categories:
            configData = {}
            for setting in category.getAllSettings():
                if setting.isVisible():
                    configData[setting.getKey()] = setting.getValue()
            config[category.getKey()] = configData

        with open(file_name, 'w') as f:
            config.write(f)

    ##  Add a category of settings
    def addSettingsCategory(self, category):
        self._categories.append(category)

    ##  Get setting category by key
    #   \param key Category key to get.
    #   \return category or None if key was not found.
    def getSettingsCategory(self, key):
        for category in self._categories:
            if category.getKey() == key:
                return category
        return None

    ##  Get all categories.
    #   \returns list of categories
    def getAllCategories(self):
        return self._categories

    ##  Get all settings of this machine
    #   \param kwargs Keyword arguments.
    #                 Possible values are:
    #                 * include_machine: boolean, true if machine settings should be included. Default false.
    #   \return list of settings
    def getAllSettings(self, **kwargs):
        all_settings = []
        if kwargs.get('include_machine', False):
            all_settings.extend(self._machine_settings)

        for category in self._categories:
            all_settings.extend(category.getAllSettings())
        return all_settings

    ##  Get setting by key.
    #   \param key Key to select setting by (string)
    #   \return Setting or none if no setting was found.
    def getSettingByKey(self, key):
        for category in self._categories:
            setting = category.getSettingByKey(key)
            if setting is not None:
                return setting
        for setting in self._machine_settings:
            setting = setting.getSettingByKey(key)
            if setting is not None:
                return setting
        return None #No setting found

    ##  Add (machine) setting to machine.
    def addSetting(self, setting):
        self._machine_settings.append(setting)
        setting.valueChanged.connect(self.settingChanged)

    ##  Set the value of a setting by key.
    #   \param key Key of setting to change.
    #   \param value value to set.
    def setSettingValueByKey(self, key, value):
        setting = self.getSettingByKey(key)
        if setting is not None:
            setting.setValue(value)

    ##  Get the value of setting by key.
    #   \param key Key of the setting to get value from
    #   \return value (or none)
    def getSettingValueByKey(self, key):
        setting = self.getSettingByKey(key)
        if setting is not None:
            return setting.getValue()
        return None

    settingChanged = Signal()

    ##  Get the machine mesh (in most cases platform)
    #   Todo: Might need to rename this to get machine mesh?
    def getPlatformMesh(self):
        return self._platformMesh

    def getPlatformTexture(self):
        return self._platform_texture

    ##  Return the machine name.
    def getName(self):
        return self._name

    ##  Set the machine name
    #
    #   \param name The name of the machine.
    def setName(self, name):
        self._name = name

    ##  Returns the machine's icon.
    def getIcon(self):
        return self._icon

    def _getVisibleSettings(self):
        visible = []
        settings = self.getAllSettings()
        for setting in settings:
            if setting.isVisible():
                visible.append(setting.getKey())

        return ','.join(visible)
<commit_msg>Do not save values of settings that are not active<commit_after>import traceback, sys
import json
import configparser
import os.path
import collections

from UM.Settings.SettingsCategory import SettingsCategory
from UM.Settings.Setting import Setting
from UM.Signal import Signal, SignalEmitter
from PyQt5.QtCore import QCoreApplication
from UM.Logger import Logger
from UM.Resources import Resources
from UM.i18n import i18nCatalog

class MachineSettings(SignalEmitter):
    def __init__(self):
        super().__init__()
        self._categories = []
        self._platformMesh = None
        self._platform_texture = None
        self._name = "Unknown Machine",
        self._type_name = 'Unknown'
        self._type_id = 'unknown'
        self._icon = "unknown.png",
        self._machine_settings = []   ## Settings that don't have a category are 'fixed' (eg; they can not be changed by the user, unless they change the json)
        self._i18n_catalog = None

    ##  Load settings from JSON file. Used to load tree structure & default values etc from file.
    #   /param file_name String
    def loadSettingsFromFile(self, file_name):
        with open(file_name, 'rt', -1, 'utf-8') as f:
            data = json.load(f, object_pairs_hook=collections.OrderedDict)

        self._i18n_catalog = i18nCatalog(os.path.basename(file_name))

        if "id" in data:
            self._type_id = data["id"]

        if "platform" in data:
            self._platformMesh = data["platform"]

        if "platform_texture" in data:
            self._platform_texture = data["platform_texture"]

        if "name" in data:
            self._type_name = data["name"]

        if "icon" in data:
            self._icon = data["icon"]

        if "inherits" in data:
            inherits_from = MachineSettings()
            inherits_from.loadSettingsFromFile(os.path.dirname(file_name) + '/' + data['inherits'])
            self._machine_settings = inherits_from._machine_settings
            self._categories = inherits_from._categories

        if "machine_settings" in data:
            for key, value in data["machine_settings"].items():
                setting = self.getSettingByKey(key)
                if not setting:
                    setting = Setting(key, self._i18n_catalog)
                    self.addSetting(setting)
                setting.fillByDict(value)

        if "categories" in data:
            for key, value in data["categories"].items():
                category = self.getSettingsCategory(key)
                if not category:
                    category = SettingsCategory(key, self._i18n_catalog, self)
                    self.addSettingsCategory(category)
                category.fillByDict(value)

        for setting in self.getAllSettings():
            setting.valueChanged.connect(self.settingChanged)

        self.settingsLoaded.emit() #Emit signal that all settings are loaded (some setting stuff can only be done when all settings are loaded (eg; the conditional stuff)
    settingsLoaded = Signal()

    ##  Load values of settings from file.
    def loadValuesFromFile(self, file_name):
        config = configparser.ConfigParser()
        config.read(file_name)

        if not self._categories:
            self.loadSettingsFromFile(Resources.getPath(Resources.SettingsLocation, config['General']['type'] + '.json'))

        self._name = config.get('General', 'name', fallback = 'Unknown Machine')

        visibility = config.get('General', 'visibility', fallback = None)
        if visibility:
            values = visibility.split(',')
            for setting in self.getAllSettings():
                if setting.getKey() in values:
                    setting.setVisible(True)
                else:
                    setting.setVisible(False)

        for name, section in config.items():
            for key in section:
                setting = self.getSettingByKey(key)
                if setting is not None:
                    setting.setValue(section[key])

    ##  Save setting values to file
    def saveValuesToFile(self, file_name):
        config = configparser.ConfigParser()

        config.add_section('General')
        config['General']['type'] = self._type_id
        config['General']['name'] = self._name
        config['General']['visibility'] = self._getVisibleSettings()

        for category in self._categories:
            configData = {}
            for setting in category.getAllSettings():
                if setting.isVisible() and setting.isActive():
                    configData[setting.getKey()] = setting.getValue()
            config[category.getKey()] = configData

        with open(file_name, 'w') as f:
            config.write(f)

    ##  Add a category of settings
    def addSettingsCategory(self, category):
        self._categories.append(category)

    ##  Get setting category by key
    #   \param key Category key to get.
    #   \return category or None if key was not found.
    def getSettingsCategory(self, key):
        for category in self._categories:
            if category.getKey() == key:
                return category
        return None

    ##  Get all categories.
    #   \returns list of categories
    def getAllCategories(self):
        return self._categories

    ##  Get all settings of this machine
    #   \param kwargs Keyword arguments.
    #                 Possible values are:
    #                 * include_machine: boolean, true if machine settings should be included. Default false.
    #   \return list of settings
    def getAllSettings(self, **kwargs):
        all_settings = []
        if kwargs.get('include_machine', False):
            all_settings.extend(self._machine_settings)

        for category in self._categories:
            all_settings.extend(category.getAllSettings())
        return all_settings

    ##  Get setting by key.
    #   \param key Key to select setting by (string)
    #   \return Setting or none if no setting was found.
    def getSettingByKey(self, key):
        for category in self._categories:
            setting = category.getSettingByKey(key)
            if setting is not None:
                return setting
        for setting in self._machine_settings:
            setting = setting.getSettingByKey(key)
            if setting is not None:
                return setting
        return None #No setting found

    ##  Add (machine) setting to machine.
    def addSetting(self, setting):
        self._machine_settings.append(setting)
        setting.valueChanged.connect(self.settingChanged)

    ##  Set the value of a setting by key.
    #   \param key Key of setting to change.
    #   \param value value to set.
    def setSettingValueByKey(self, key, value):
        setting = self.getSettingByKey(key)
        if setting is not None:
            setting.setValue(value)

    ##  Get the value of setting by key.
    #   \param key Key of the setting to get value from
    #   \return value (or none)
    def getSettingValueByKey(self, key):
        setting = self.getSettingByKey(key)
        if setting is not None:
            return setting.getValue()
        return None

    settingChanged = Signal()

    ##  Get the machine mesh (in most cases platform)
    #   Todo: Might need to rename this to get machine mesh?
    def getPlatformMesh(self):
        return self._platformMesh

    def getPlatformTexture(self):
        return self._platform_texture

    ##  Return the machine name.
    def getName(self):
        return self._name

    ##  Set the machine name
    #
    #   \param name The name of the machine.
    def setName(self, name):
        self._name = name

    ##  Returns the machine's icon.
    def getIcon(self):
        return self._icon

    def _getVisibleSettings(self):
        visible = []
        settings = self.getAllSettings()
        for setting in settings:
            if setting.isVisible():
                visible.append(setting.getKey())

        return ','.join(visible)
<|endoftext|>