<commit_before>from __future__ import print_function
from time import strftime

from keras.layers.recurrent import GRU, LSTM
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.models import Sequential
from keras.layers import Embedding
from keras.layers.core import Dense, Dropout
import keras.utils.np_utils
import keras.models
import numpy as np

from cervantes.language import OneLevelEmbedding, TwoLevelsEmbedding

class LanguageClassifierException(Exception):
    pass

class LanguageClassifier(object):

    def __init__(self, model, optimizer=None):
        self.model = model
        self.binary = "unk"  # unknown until training

        self.ttime_start = None
        self.ttime_stop = None      # for training logging purposes
        self.optimizer = optimizer  # used for saving / loading models

    def save_model(self, model_spec_file, model_weights_file):
        from json import dump, loads

        dump({
                'model': loads(self.model.to_json()),
                'binary': self.binary,
                'optimizer': self.optimizer
            },
            open(model_spec_file, 'w'))

        self.model.save_weights(model_weights_file, overwrite=True)

    @staticmethod
    def load_model(model_spec_file, model_weights_file):
        from json import dumps, load

        params = load(open(model_spec_file, "r"))

        model = keras.models.model_from_json(dumps(params['model']))
        binary = params['binary']
        optimizer = params['optimizer']

        model.load_weights(model_weights_file)
        if binary:
            model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=["accuracy"])
        else:
            model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=["accuracy"])

        lc = LanguageClassifier(model)
        lc.binary = binary
        return lc

    def train(self, X, y, nb_epoch=20, validation_split=0.15, batch_size=32,
              model_weights_file=None, model_spec_file=None, **kwargs):

        # Provide some default values as training options
        fit_params = {
            "batch_size": batch_size,
            "nb_epoch": nb_epoch,
            "verbose": 1,
            "validation_split": validation_split,
            "callbacks": [EarlyStopping(verbose=True, patience=5, monitor='val_acc')]
        }

        # Add a callback for saving temporal status of the model while training
        if model_weights_file is not None:
            fit_params["callbacks"].append(ModelCheckpoint(model_weights_file, monitor='val_acc',
                                                           verbose=True, save_best_only=True))

        # Override any params provided by the user for Keras training. This allows overriding
        # default Cervantes behavior when training
        fit_params.update(kwargs)

        # in Keras 1.0, a shape of tuple => single in/out model
        if type(self.model.output_shape) is tuple:
            if max(y) > 1:
                Y = keras.utils.np_utils.to_categorical(y)
                self.binary = False
            else:
                Y = np.array(y)
                self.binary = True
        else:
            raise LanguageClassifierException('Mult-output models are not supported yet')

        # if we don't need 3d inputs...
        if type(self.model.input_shape) is tuple:
            X = np.array(X)
            if len(self.model.input_shape) == 2:
                X = X.reshape((X.shape[0], -1))
        else:
            raise LanguageClassifierException('Mult-input models are not supported yet')

        try:
            print("Fitting! Hit CTRL-C to stop early...")
            self.ttime_start = strftime("%Y-%m-%d %H:%M:%S")
            self.model.fit(X, Y, **fit_params)
            print("Training finished")
        except KeyboardInterrupt:
            print("Training stopped early!")
        self.ttime_stop = strftime("%Y-%m-%d %H:%M:%S")

        # If weights file was specified, we load the best model computed up to the moment
        # (otherwise the last epoch model would still be working)
        if model_weights_file is not None:
            print("Loading best model found...")
            self.model.load_weights(model_weights_file)

        # Save the full model (spec + weights) if all the information was provided
        if model_weights_file is not None and model_spec_file is not None:
            print("Saving full model...")
            self.save_model(model_spec_file, model_weights_file)

    def predict(self, X):
        predictions = self.model.predict(X, verbose=True, batch_size=32)
        if (len(predictions.shape) > 1) and (1 not in predictions.shape):
            predictions = predictions.argmax(axis=-1)
        else:
            predictions = 1 * (predictions > 0.5).ravel()
        return predictions

    def predict_proba(self, X):
        return self.model.predict(X, verbose=True, batch_size=32)

    def test(self, X, y, verbose=True):
        # if we don't need 3d inputs...
        if type(self.model.input_shape) is tuple:
            X = np.array(X)
            if len(self.model.input_shape) == 2:
                X = X.reshape((X.shape[0], -1))
        else:
            raise LanguageClassifierException('Mult-input models are not supported yet')

        if verbose:
            print("Getting predictions on the test set")
        predictions = self.predict(X)

        if len(predictions) != len(y):
            raise LanguageClassifierException("Non comparable arrays")

        if self.binary:
            acc = (predictions == y).mean()
            prec = np.sum(np.bitwise_and(predictions, y)) * 1.0 / np.sum(predictions)
            recall = np.sum(np.bitwise_and(predictions, y)) * 1.0 / np.sum(y)
            if verbose:
                print("Test set accuracy of {0:.3f}%".format(acc * 100.0))
                print("Test set error of {0:.3f}%".format((1 - acc) * 100.0))
                print("Precision for class=1: {0:.3f}".format(prec))
                print("Recall for class=1: {0:.3f}".format(recall))

            return (acc, prec, recall)
        else:
            # TODO: Obtain more metrics for the multiclass problem
            acc = (predictions == y).mean()
            if verbose:
                print("Test set accuracy of {0:.3f}%".format(acc * 100.0))
                print("Test set error of {0:.3f}%".format((1 - acc) * 100.0))
            return acc

    def log_results(self, logfile, X_test, y_test):

        def print_history(history_obj, f):
            for (i, (loss, val_loss, acc, val_acc)) in enumerate(zip(history_obj.history['loss'],
                                                       history_obj.history['val_loss'],
                                                       history_obj.history['acc'],
                                                       history_obj.history['val_acc'])):
                print("Epoch %d: loss: %f, val_loss: %f, acc: %f, val_acc: %f" %
                      (i+1, loss, val_loss, acc, val_acc), file=f)

        with open(logfile, "w") as f:
            print("Started training: " + self.ttime_start, file=f)
            print("Stopped training: " + self.ttime_stop, file=f)

            print("Obtaining test error...")
            results = self.test(X_test, y_test, verbose=False)
            if self.binary:
                (acc, prec, recall) = results
                print("Test set accuracy of {0:.3f}%".format(acc * 100.0), file=f)
                print("Test set error of {0:.3f}%".format((1 - acc) * 100.0), file=f)
                print("Precision for class=1: {0:.3f}".format(prec), file=f)
                print("Recall for class=1: {0:.3f}".format(recall), file=f)
            else:
                acc = results
                print("Test set accuracy of {0:.3f}%".format(acc * 100.0), file=f)
                print("Test set error of {0:.3f}%".format((1 - acc) * 100.0), file=f)

            print("==" * 40, file=f)
            print("Model: ", file=f)
            print(self.model.to_json(), file=f)
            print("==" * 40, file=f)
            print("Training history:", file=f)
            if self.model.history is not None:
                print_history(self.model.history, f)
            else:
                print("Training history not available in loaded models", file=f)

class RNNClassifier(LanguageClassifier):

    def __init__(self, lembedding, num_classes=2, unit='gru', rnn_size=128, train_vectors=True,
                 optimizer=None):

        if not isinstance(lembedding, OneLevelEmbedding):
            raise LanguageClassifierException("The model only accepts one-level language embeddings")
        if num_classes < 2:
            raise LanguageClassifierException("Classes must be 2 or more")

        self.optimizer = optimizer
        model = self._generate_model(lembedding, num_classes, unit,
                                     rnn_size, train_vectors)
        super(RNNClassifier, self).__init__(model, LanguageClassifier.SEQUENTIAL, self.optimizer)

    def _generate_model(self, lembedding, num_classes=2, unit='gru', rnn_size=128, train_vectors=True):

        model = Sequential()
        if lembedding.vector_box.W is None:
            emb = Embedding(lembedding.vector_box.size,
                            lembedding.vector_box.vector_dim,
                            W_constraint=None)
        else:
            emb = Embedding(lembedding.vector_box.size,
                            lembedding.vector_box.vector_dim,
                            weights=[lembedding.vector_box.W], W_constraint=None)
        emb.trainable = train_vectors
        model.add(emb)
        if unit == 'gru':
            model.add(GRU(rnn_size))
        else:
            model.add(LSTM(rnn_size))
        model.add(Dropout(0.2))
        if num_classes == 2:
            model.add(Dense(1, activation='sigmoid'))
            if self.optimizer is None:
                self.optimizer = 'rmsprop'
            model.compile(loss='binary_crossentropy', optimizer=self.optimizer, metrics=["accuracy"])
        else:
            if self.optimizer is None:
                self.optimizer = 'adam'
            model.add(Dense(num_classes, activation='softmax'))
            model.compile(loss='categorical_crossentropy', optimizer=self.optimizer, metrics=["accuracy"])

        return model
<commit_msg>changes for history compatibility<commit_after>from __future__ import print_function
from time import strftime

from keras.layers.recurrent import GRU, LSTM
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.models import Sequential
from keras.layers import Embedding
from keras.layers.core import Dense, Dropout
import keras.utils.np_utils
import keras.models
import numpy as np

from cervantes.language import OneLevelEmbedding, TwoLevelsEmbedding

class LanguageClassifierException(Exception):
    pass

class LanguageClassifier(object):

    def __init__(self, model, optimizer=None):
        self.model = model
        self.binary = "unk"  # unknown until training

        self.ttime_start = None
        self.ttime_stop = None      # for training logging purposes
        self.optimizer = optimizer  # used for saving / loading models

    def save_model(self, model_spec_file, model_weights_file):
        from json import dump, loads

        dump({
                'model': loads(self.model.to_json()),
                'binary': self.binary,
                'optimizer': self.optimizer
            },
            open(model_spec_file, 'w'))

        self.model.save_weights(model_weights_file, overwrite=True)

    @staticmethod
    def load_model(model_spec_file, model_weights_file):
        from json import dumps, load

        params = load(open(model_spec_file, "r"))

        model = keras.models.model_from_json(dumps(params['model']))
        binary = params['binary']
        optimizer = params['optimizer']

        model.load_weights(model_weights_file)
        if binary:
            model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=["accuracy"])
        else:
            model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=["accuracy"])

        lc = LanguageClassifier(model)
        lc.binary = binary
        return lc

    def train(self, X, y, nb_epoch=20, validation_split=0.15, batch_size=32,
              model_weights_file=None, model_spec_file=None, **kwargs):

        # Provide some default values as training options
        fit_params = {
            "batch_size": batch_size,
            "nb_epoch": nb_epoch,
            "verbose": 1,
            "validation_split": validation_split,
            "callbacks": [EarlyStopping(verbose=True, patience=5, monitor='val_acc')]
        }

        # Add a callback for saving temporal status of the model while training
        if model_weights_file is not None:
            fit_params["callbacks"].append(ModelCheckpoint(model_weights_file, monitor='val_acc',
                                                           verbose=True, save_best_only=True))

        # Override any params provided by the user for Keras training. This allows overriding
        # default Cervantes behavior when training
        fit_params.update(kwargs)

        # in Keras 1.0, a shape of tuple => single in/out model
        if type(self.model.output_shape) is tuple:
            if max(y) > 1:
                Y = keras.utils.np_utils.to_categorical(y)
                self.binary = False
            else:
                Y = np.array(y)
                self.binary = True
        else:
            raise LanguageClassifierException('Mult-output models are not supported yet')

        # if we don't need 3d inputs...
        if type(self.model.input_shape) is tuple:
            X = np.array(X)
            if len(self.model.input_shape) == 2:
                X = X.reshape((X.shape[0], -1))
        else:
            raise LanguageClassifierException('Mult-input models are not supported yet')

        try:
            print("Fitting! Hit CTRL-C to stop early...")
            self.ttime_start = strftime("%Y-%m-%d %H:%M:%S")
            self.model.fit(X, Y, **fit_params)
            print("Training finished")
        except KeyboardInterrupt:
            print("Training stopped early!")
        self.ttime_stop = strftime("%Y-%m-%d %H:%M:%S")

        # If weights file was specified, we load the best model computed up to the moment
        # (otherwise the last epoch model would still be working)
        if model_weights_file is not None:
            print("Loading best model found...")
            self.model.load_weights(model_weights_file)

        # Save the full model (spec + weights) if all the information was provided
        if model_weights_file is not None and model_spec_file is not None:
            print("Saving full model...")
            self.save_model(model_spec_file, model_weights_file)

    def predict(self, X):
        if type(self.model.input_shape) is tuple:
            X = np.array(X)
            if len(self.model.input_shape) == 2:
                X = X.reshape((X.shape[0], -1))
        else:
            raise LanguageClassifierException('Mult-input models are not supported yet')

        predictions = self.model.predict(X, verbose=True, batch_size=32)
        if (len(predictions.shape) > 1) and (1 not in predictions.shape):
            predictions = predictions.argmax(axis=-1)
        else:
            predictions = 1 * (predictions > 0.5).ravel()
        return predictions

    def predict_proba(self, X):
        if type(self.model.input_shape) is tuple:
            X = np.array(X)
            if len(self.model.input_shape) == 2:
                X = X.reshape((X.shape[0], -1))
        else:
            raise LanguageClassifierException('Mult-input models are not supported yet')
        return self.model.predict(X, verbose=True, batch_size=32)

    def test(self, X, y, verbose=True):
        # if we don't need 3d inputs...
        if type(self.model.input_shape) is tuple:
            X = np.array(X)
            if len(self.model.input_shape) == 2:
                X = X.reshape((X.shape[0], -1))
        else:
            raise LanguageClassifierException('Mult-input models are not supported yet')

        if verbose:
            print("Getting predictions on the test set")
        predictions = self.predict(X)

        if len(predictions) != len(y):
            raise LanguageClassifierException("Non comparable arrays")

        if self.binary:
            acc = (predictions == y).mean()
            prec = np.sum(np.bitwise_and(predictions, y)) * 1.0 / np.sum(predictions)
            recall = np.sum(np.bitwise_and(predictions, y)) * 1.0 / np.sum(y)
            if verbose:
                print("Test set accuracy of {0:.3f}%".format(acc * 100.0))
                print("Test set error of {0:.3f}%".format((1 - acc) * 100.0))
                print("Precision for class=1: {0:.3f}".format(prec))
                print("Recall for class=1: {0:.3f}".format(recall))

            return (acc, prec, recall)
        else:
            # TODO: Obtain more metrics for the multiclass problem
            acc = (predictions == y).mean()
            if verbose:
                print("Test set accuracy of {0:.3f}%".format(acc * 100.0))
                print("Test set error of {0:.3f}%".format((1 - acc) * 100.0))
            return acc

    def log_results(self, logfile, X_test, y_test):

        def print_history(history_obj, f):
            for (i, (loss, val_loss, acc, val_acc)) in enumerate(zip(history_obj.history['loss'],
                                                       history_obj.history['val_loss'],
                                                       history_obj.history['acc'],
                                                       history_obj.history['val_acc'])):
                print("Epoch %d: loss: %f, val_loss: %f, acc: %f, val_acc: %f" %
                      (i+1, loss, val_loss, acc, val_acc), file=f)

        with open(logfile, "w") as f:
            print("Started training: " + self.ttime_start, file=f)
            print("Stopped training: " + self.ttime_stop, file=f)

            print("Obtaining test error...")
            results = self.test(X_test, y_test, verbose=False)
            if self.binary:
                (acc, prec, recall) = results
                print("Test set accuracy of {0:.3f}%".format(acc * 100.0), file=f)
                print("Test set error of {0:.3f}%".format((1 - acc) * 100.0), file=f)
                print("Precision for class=1: {0:.3f}".format(prec), file=f)
                print("Recall for class=1: {0:.3f}".format(recall), file=f)
            else:
                acc = results
                print("Test set accuracy of {0:.3f}%".format(acc * 100.0), file=f)
                print("Test set error of {0:.3f}%".format((1 - acc) * 100.0), file=f)

            print("==" * 40, file=f)
            print("Model: ", file=f)
            print(self.model.to_json(), file=f)
            print("==" * 40, file=f)
            print("Training history:", file=f)
            hist = self.model.history \
                   if hasattr(self.model, 'history') \
                   else \
                   self.model.model.history

            if hist is not None:
                print_history(hist, f)
            else:
                print("Training history not available in loaded models", file=f)

class RNNClassifier(LanguageClassifier):

    def __init__(self, lembedding, num_classes=2, unit='gru', rnn_size=128, train_vectors=True,
                 optimizer=None):

        if not isinstance(lembedding, OneLevelEmbedding):
            raise LanguageClassifierException("The model only accepts one-level language embeddings")
        if num_classes < 2:
            raise LanguageClassifierException("Classes must be 2 or more")

        self.optimizer = optimizer
        model = self._generate_model(lembedding, num_classes, unit,
                                     rnn_size, train_vectors)
        super(RNNClassifier, self).__init__(model, LanguageClassifier.SEQUENTIAL, self.optimizer)

    def _generate_model(self, lembedding, num_classes=2, unit='gru', rnn_size=128, train_vectors=True):

        model = Sequential()
        if lembedding.vector_box.W is None:
            emb = Embedding(lembedding.vector_box.size,
                            lembedding.vector_box.vector_dim,
                            W_constraint=None)
        else:
            emb = Embedding(lembedding.vector_box.size,
                            lembedding.vector_box.vector_dim,
                            weights=[lembedding.vector_box.W], W_constraint=None)
        emb.trainable = train_vectors
        model.add(emb)
        if unit == 'gru':
            model.add(GRU(rnn_size))
        else:
            model.add(LSTM(rnn_size))
        model.add(Dropout(0.2))
        if num_classes == 2:
            model.add(Dense(1, activation='sigmoid'))
            if self.optimizer is None:
                self.optimizer = 'rmsprop'
            model.compile(loss='binary_crossentropy', optimizer=self.optimizer, metrics=["accuracy"])
        else:
            if self.optimizer is None:
                self.optimizer = 'adam'
            model.add(Dense(num_classes, activation='softmax'))
            model.compile(loss='categorical_crossentropy', optimizer=self.optimizer, metrics=["accuracy"])

        return model
<|endoftext|>


<commit_before>import string
_complement = string.maketrans('GATCRYgatcry','CTAGYRctagyr')

class Fasta:
    ''' iterates through a fasta or fastq file, returning dnaobj objects '''
    def __init__(self, handle, filetype='fastq'):
        self.filetype = filetype
        self.handle = handle
        self.county = 0
    def __iter__(self):
        if self.filetype == 'fastq':
            counter = 0
            rec = { 0: '', 1: '', 2: '', 3: '' }
            for line in self.handle:
                if counter < 3:
                    rec[counter] = line.strip()
                    counter += 1
                elif counter == 3:
                    rec[counter] = line.strip()
                    counter = 0
                    yield Dna(rec[0], rec[1], rec[3])

class Dna:
    ''' An object representing either a FASTA or FASTQ record '''
    def __init__(self, header, sequence, quality = False):
        self.header = header[1:-1]
        self.seq = sequence
        self.qual = quality
        if quality:
            self.type = 'fastq'
        else:
            self.type = 'fasta'

        if len(self.seq) != len(self.qual):
            raise IOError, \
                'Seq length and qual length do not agree: %s' % (self.header)

    def __str__(self):
        ''' returns a FASTA/Q formatted string '''
        if not self.qual:
            return ('>%s\nself.sequence\n') % \
                (self.header, self.seq)
        else:
            return('\n@%s\n%s\n+%s\n%s') % \
                (self.header, self.seq, self.header, self.qual)
    def __len__(self):
        return len(self.seq)
    def __repr__(self):
        return '<dnaobj.%s instance: %s>' % (self.type, self.header)
    @property
    def complement(self):
        ''' returns complement of sequence '''
        return self.seq.translate(_complement)
    @property
    def revcomp(self):
        ''' returns reverse complement of sequence '''
        return self.complement[::-1]
    @property
    def rqual(self):
        ''' returns reverse quality'''
        return self.qual[::-1]<commit_msg>fasta is now fastq only - should I make this program work for fasta files as well?<commit_after>import string
_complement = string.maketrans('GATCRYgatcry','CTAGYRctagyr')

class Fasta:
    ''' iterates through a fastq file, returning dnaobj objects '''
    def __init__(self, handle, filetype='fastq'):
        self.filetype = filetype
        self.handle = handle
        self.county = 0
    def __iter__(self):
        if self.filetype == 'fastq':
            counter = 0
            rec = { 0: '', 1: '', 2: '', 3: '' }
            for line in self.handle:
                if counter < 3:
                    rec[counter] = line.strip()
                    counter += 1
                elif counter == 3:
                    rec[counter] = line.strip()
                    counter = 0
                    yield Dna(rec[0], rec[1], rec[3])

class Dna:
    ''' An object representing either a FASTA or FASTQ record '''
    def __init__(self, header, sequence, quality = False):
        self.header = header[1:-1]
        self.seq = sequence
        self.qual = quality
        if quality:
            self.type = 'fastq'
        else:
            self.type = 'fasta'

        if len(self.seq) != len(self.qual):
            raise IOError, \
                'Seq length and qual length do not agree: %s' % (self.header)

    def __str__(self):
        ''' returns a FASTA/Q formatted string '''
        if not self.qual:
            return ('>%s\nself.sequence\n') % \
                (self.header, self.seq)
        else:
            return('\n@%s\n%s\n+%s\n%s') % \
                (self.header, self.seq, self.header, self.qual)
    def __len__(self):
        return len(self.seq)
    def __repr__(self):
        return '<dnaobj.%s instance: %s>' % (self.type, self.header)
    @property
    def complement(self):
        ''' returns complement of sequence '''
        return self.seq.translate(_complement)
    @property
    def revcomp(self):
        ''' returns reverse complement of sequence '''
        return self.complement[::-1]
    @property
    def rqual(self):
        ''' returns reverse quality'''
        return self.qual[::-1]<|endoftext|>


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

AUTHOR = 'Jed Brown'
SITENAME = 'Jed Brown'
SITEURL = 'http://jedbrown.org'

TIMEZONE = 'America/Denver'

DEFAULT_LANG = 'en'

# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None

DISPLAY_CATEGORIES_ON_MENU = False
MENUITEMS = [('Publications', '/pubs/'),
             ('Presentations', '/presentations/')]

PAGE_URL = '{slug}'
PAGE_SAVE_AS = '{slug}/index.html'
ARTICLE_URL = '{slug}'
ARTICLE_SAVE_AS = '{slug}/index.html'

# Blogroll
LINKS =  (('Pelican', 'http://getpelican.com/'),
          ('Python.org', 'http://python.org/'),
          ('Jinja2', 'http://jinja.pocoo.org/'),
          ('You can modify those links in your config file', '#'),)

PORTRAIT_IMG = 'images/jed.jpg'
PORTRAIT_ALT = "Jed's head"

SOFTWARE = [dict(name='PETSc', url='http://mcs.anl.gov/petsc',
                 description='Portable Extensible Toolkit for Scientific computing: an library of parallel algebraic solvers and time integrators'),
            dict(name='PISM', url='http://pism-docs.org',
                 description='Parallel Ice Sheet Model.'),
            dict(name='Dohp', url='https://github.com/jedbrown/dohp',
                 description='Dual-order h- and p-version finite element library.'),
            dict(name='git-fat', url='https://github.com/jedbrown/git-fat',
                 description='A simple approach to large file support for Git.'),]
#PROJECTS = SOFTWARE             # Some themes use the other name

# Social widget
SOCIAL = [('github', 'https://github.com/jedbrown'),
          ('bitbucket', 'https://bitbucket.org/jedbrown'),
          ('scicomp.stackexchange', 'http://scicomp.stackexchange.com/users/119/jed-brown'),
          ('diaspora', 'https://diasp.org/u/jed'),
          ('scholar', 'http://scholar.google.com/citations?hl=en&user=x_9_NGwAAAAJ')]

DEFAULT_PAGINATION = False

PLUGINS = ['render_math',
           'pelican_bibtex',
           'extract_toc']
PLUGIN_PATHS = ['./pelican-plugins', './pelican-bibtex']
MD_EXTENSIONS = ['codehilite(css_class=highlight)', 'extra', 'headerid', 'toc']
STATIC_PATHS = ['theme/images', 'images', 'files']

LANDING_PAGE_ABOUT = dict(title='Jed Brown',
                          details=
"""

  jed at jedbrown.org

  Assistant Professor

  Department of Computer Science

  University of Colorado Boulder

  430 UCB, Boulder, CO 80309

Awards

2015 SIAM/ACM Prize in Computational Science and Engineering (PETSc team)

2014 IEEE TCSC Young Achiever

2014 SIAG/SC Junior Scientist Prize

2010 Piolet d'Or for the first ascent of Xuelian West (6422 m, China) via "The Great White Jade Heist" (with Kyle Dempster and Bruce Normand)

CV (pdf)

Research

Teaching

High-Performance Linear Algebra: CSCI-4830/7000, Spring 2017

Numerical Computation: CSCI-3656, Fall 2016

Topics in Computational Science and Engineering: CSCI-4830-008/7000-014, Fall 2015

HPC Performance Analysis: CSCI-4830-014/7000-018, Spring 2015

Identity

PGP Public Key to verify my signed mail or encrypt to me.

SSH Public Key to grant me login access, git push, etc.

""" % dict(SITEURL=SITEURL))

THEME = 'theme'
BOOTSTRAP_THEME = 'spacelab'
PYGMENTS_STYLE = 'default'

PUBLICATIONS_SRC = 'content/jedpub.bib'
DIRECT_TEMPLATES = ('index', 'pubs/index', 'tags', 'archives')

def latex_decode(latexsrc):
    import codecs
    import latexcodec
    utf8src = codecs.decode(latexsrc.encode(), 'latex+utf-8')
    s = utf8src.replace('{','').replace('}','')
    return s
JINJA_FILTERS = dict(latex_decode=latex_decode, strip=str.strip)

# Uncomment following line if you want document-relative URLs when developing
RELATIVE_URLS = True
<commit_msg>Use my new public ssh key<commit_after># -*- coding: utf-8 -*- #
from __future__ import unicode_literals

AUTHOR = 'Jed Brown'
SITENAME = 'Jed Brown'
SITEURL = 'http://jedbrown.org'

TIMEZONE = 'America/Denver'

DEFAULT_LANG = 'en'

# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None

DISPLAY_CATEGORIES_ON_MENU = False
MENUITEMS = [('Publications', '/pubs/'),
             ('Presentations', '/presentations/')]

PAGE_URL = '{slug}'
PAGE_SAVE_AS = '{slug}/index.html'
ARTICLE_URL = '{slug}'
ARTICLE_SAVE_AS = '{slug}/index.html'

# Blogroll
LINKS =  (('Pelican', 'http://getpelican.com/'),
          ('Python.org', 'http://python.org/'),
          ('Jinja2', 'http://jinja.pocoo.org/'),
          ('You can modify those links in your config file', '#'),)

PORTRAIT_IMG = 'images/jed.jpg'
PORTRAIT_ALT = "Jed's head"

SOFTWARE = [dict(name='PETSc', url='http://mcs.anl.gov/petsc',
                 description='Portable Extensible Toolkit for Scientific computing: an library of parallel algebraic solvers and time integrators'),
            dict(name='PISM', url='http://pism-docs.org',
                 description='Parallel Ice Sheet Model.'),
            dict(name='Dohp', url='https://github.com/jedbrown/dohp',
                 description='Dual-order h- and p-version finite element library.'),
            dict(name='git-fat', url='https://github.com/jedbrown/git-fat',
                 description='A simple approach to large file support for Git.'),]
#PROJECTS = SOFTWARE             # Some themes use the other name

# Social widget
SOCIAL = [('github', 'https://github.com/jedbrown'),
          ('bitbucket', 'https://bitbucket.org/jedbrown'),
          ('scicomp.stackexchange', 'http://scicomp.stackexchange.com/users/119/jed-brown'),
          ('diaspora', 'https://diasp.org/u/jed'),
          ('scholar', 'http://scholar.google.com/citations?hl=en&user=x_9_NGwAAAAJ')]

DEFAULT_PAGINATION = False

PLUGINS = ['render_math',
           'pelican_bibtex',
           'extract_toc']
PLUGIN_PATHS = ['./pelican-plugins', './pelican-bibtex']
MD_EXTENSIONS = ['codehilite(css_class=highlight)', 'extra', 'headerid', 'toc']
STATIC_PATHS = ['theme/images', 'images', 'files']

LANDING_PAGE_ABOUT = dict(title='Jed Brown',
                          details=
"""

  jed at jedbrown.org

  Assistant Professor

  Department of Computer Science

  University of Colorado Boulder

  430 UCB, Boulder, CO 80309

Awards

2015 SIAM/ACM Prize in Computational Science and Engineering (PETSc team)

2014 IEEE TCSC Young Achiever

2014 SIAG/SC Junior Scientist Prize

2010 Piolet d'Or for the first ascent of Xuelian West (6422 m, China) via "The Great White Jade Heist" (with Kyle Dempster and Bruce Normand)

CV (pdf)

Research

Teaching

High-Performance Linear Algebra: CSCI-4830/7000, Spring 2017

Numerical Computation: CSCI-3656, Fall 2016

Topics in Computational Science and Engineering: CSCI-4830-008/7000-014, Fall 2015

HPC Performance Analysis: CSCI-4830-014/7000-018, Spring 2015

Identity

PGP Public Key to verify my signed mail or encrypt to me.

SSH Public Key to grant me login access, git push, etc.

""" % dict(SITEURL=SITEURL))

THEME = 'theme'
BOOTSTRAP_THEME = 'spacelab'
PYGMENTS_STYLE = 'default'

PUBLICATIONS_SRC = 'content/jedpub.bib'
DIRECT_TEMPLATES = ('index', 'pubs/index', 'tags', 'archives')

def latex_decode(latexsrc):
    import codecs
    import latexcodec
    utf8src = codecs.decode(latexsrc.encode(), 'latex+utf-8')
    s = utf8src.replace('{','').replace('}','')
    return s
JINJA_FILTERS = dict(latex_decode=latex_decode, strip=str.strip)

# Uncomment following line if you want document-relative URLs when developing
RELATIVE_URLS = True
<|endoftext|>


<commit_before>#
# Copyright 2017-2018 European Centre for Medium-Range Weather Forecasts (ECMWF).
#
# 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.
#
# Authors:
#   Alessandro Amici - B-Open - https://bopen.eu
#

from __future__ import absolute_import, division, print_function, unicode_literals

import collections
import itertools
import logging
import typing as T

import attr
import xarray as xr
from xarray import Variable
from xarray.core import indexing
from xarray.core.utils import FrozenOrderedDict
from xarray.backends.api import open_dataset as _open_dataset, _validate_dataset_names, _validate_attrs
from xarray.backends.common import AbstractDataStore, BackendArray

import cfgrib

LOGGER = logging.getLogger(__name__)

class WrapGrib(BackendArray):
    def __init__(self, backend_array):
        self.backend_array = backend_array

    def __getattr__(self, item):
        return getattr(self.backend_array, item)

    def __getitem__(self, item):
        key, np_inds = indexing.decompose_indexer(
            item, self.shape, indexing.IndexingSupport.OUTER_1VECTOR)

        array = self.backend_array[key.tuple]

        if len(np_inds.tuple) > 0:
            array = indexing.NumpyIndexingAdapter(array)[np_inds]

        return array

FLAVOURS = {
    'eccodes': {
        'dataset': {
            'encode_time': False,
            'encode_vertical': False,
            'encode_geography': False,
        },
    },
    'ecmwf': {
        'variable_map': {
            'forecast_reference_time': 'time',
            'forecast_period': 'step',
            'time': 'valid_time',
            'air_pressure': 'level',
            'topLevel': 'level',
        },
        'type_of_level_map': {
            'hybrid': lambda attrs: 'L%d' % ((attrs['GRIB_NV'] - 2) // 2),
        },
    },
    'cds': {
        'variable_map': {
            'number': 'realization',
            'forecast_period': 'leadtime',
            'air_pressure': 'plev',
            'latitude': 'lat',
            'longitude': 'lon',
            'topLevel': 'level',
        },
        'type_of_level_map': {
            'hybrid': lambda attrs: 'L%d' % ((attrs['GRIB_NV'] - 2) // 2),
        },
    },
}

@attr.attrs()
class GribDataStore(AbstractDataStore):
    ds = attr.attrib()
    variable_map = attr.attrib(default={})
    type_of_level_map = attr.attrib(default={})

    @classmethod
    def frompath(cls, path, flavour_name='ecmwf', **kwargs):
        flavour = FLAVOURS[flavour_name].copy()
        config = flavour.pop('dataset', {}).copy()
        config.update(kwargs)
        return cls(ds=cfgrib.Dataset.frompath(path, **config), **flavour)

    def __attrs_post_init__(self):
        self.variable_map = self.variable_map.copy()
        for name, var in self.ds.variables.items():
            if self.ds.encode_vertical and 'GRIB_typeOfLevel' in var.attributes:
                type_of_level = var.attributes['GRIB_typeOfLevel']
                coord_name = self.type_of_level_map.get(type_of_level, type_of_level)
                if isinstance(coord_name, T.Callable):
                    coord_name = coord_name(var.attributes)
                self.variable_map['topLevel'] = coord_name.format(**var.attributes)

    def open_store_variable(self, name, var):
        if isinstance(var.data, cfgrib.dataset.OnDiskArray):
            data = indexing.LazilyOuterIndexedArray(WrapGrib(var.data))
        else:
            data = var.data

        dimensions = tuple(self.variable_map.get(dim, dim) for dim in var.dimensions)
        attrs = var.attributes

        # the coordinates attributes need a special treatment
        if 'coordinates' in attrs:
            coordinates = [self.variable_map.get(d, d) for d in attrs['coordinates'].split()]
            attrs['coordinates'] = ' '.join(coordinates)

        encoding = {}
        # save source so __repr__ can detect if it's local or not
        encoding['source'] = self.ds.stream.path
        encoding['original_shape'] = var.data.shape

        return Variable(dimensions, data, attrs, encoding)

    def get_variables(self):
        return FrozenOrderedDict((self.variable_map.get(k, k), self.open_store_variable(k, v))
                                 for k, v in self.ds.variables.items())

    def get_attrs(self):
        return FrozenOrderedDict(self.ds.attributes)

    def get_dimensions(self):
        return collections.OrderedDict((self.variable_map.get(d, d), s)
                                       for d, s in self.ds.dimensions.items())

    def get_encoding(self):
        encoding = {}
        encoding['unlimited_dims'] = {k for k, v in self.ds.dimensions.items() if v is None}
        return encoding

def open_dataset(path, flavour_name='ecmwf', **kwargs):
    overrides = {}
    for k in list(kwargs):  # copy to allow the .pop()
        if k.startswith('encode_'):
            overrides[k] = kwargs.pop(k)
    store = GribDataStore.frompath(path, flavour_name=flavour_name, **overrides)
    return _open_dataset(store, **kwargs)

#
# write support
#
def ecmwf_dataarray_to_grib(file, data_var, global_attributes={}):
    # type: (T.BinaryIO, str, xr.DataArray) -> None
    from cfgrib import cfmessage
    from cfgrib import dataset
    from cfgrib import eccodes

    grib_attributes = {k[5:]: v for k, v in global_attributes.items() if k[:5] == 'GRIB_'}
    grib_attributes.update({k[5:]: v for k, v in data_var.attrs.items() if k[:5] == 'GRIB_'})

    header_coords_names = []

    if grib_attributes['gridType'] == 'regular_ll':
        geography = 'regular_ll'
        header_coords_names += [n for n, _ in dataset.DATA_TIME_COORDINATE_MAP]
    else:
        raise NotImplementedError("Unsupported 'gridType': %r" % grib_attributes['gridType'])

    if grib_attributes['typeOfLevel'] == 'isobaricInhPa':
        vertical = 'pl'
        header_coords_names += [n for n, _ in dataset.VERTICAL_COORDINATE_MAP]
    elif grib_attributes['typeOfLevel'] in ('surface', 'meanSea'):
        vertical = 'sfc'
    else:
        raise NotImplementedError("Unsupported 'typeOfLevel': %r" % grib_attributes['typeOfLevel'])

    sample_name = '%s_%s_grib2' % (geography, vertical)

    for dim in header_coords_names:
        if dim not in data_var.dims:
            data_var = data_var.expand_dims(dim)
    header_coords_values = [data_var.coords[name].values.tolist() for name in header_coords_names]
    for items in itertools.product(*header_coords_values):
        message = cfmessage.CfMessage.fromsample(sample_name)
        for key, value in grib_attributes.items():
            try:
                message[key] = value
            except eccodes.EcCodesError as ex:
                if ex.code != eccodes.lib.GRIB_READ_ONLY:
                    LOGGER.exception("Can't encode key: %r" % key)

        for coord_name, coord_value in zip(header_coords_names, items):
            message[coord_name] = coord_value

        select = {n: v for n, v in zip(header_coords_names, items)}
        message['values'] = data_var.sel(**select).values.flat[:].tolist()

        message.write(file)

def to_grib(ecmwf_dataset, path, mode='wb', **kwargs):
    # validate Dataset keys, DataArray names, and attr keys/values
    _validate_dataset_names(ecmwf_dataset)
    _validate_attrs(ecmwf_dataset)

    with open(path, mode=mode) as file:
        for data_var in ecmwf_dataset.data_vars.values():
            ecmwf_dataarray_to_grib(file, data_var, global_attributes=ecmwf_dataset.attrs)

def cfgrib2netcdf():
    import argparse

    parser = argparse.ArgumentParser(description='Convert a GRIB file into a NetCDF file.')
    parser.add_argument('input', help='Path to the input GRIB file.')
    parser.add_argument(
        '--flavour_name', default='cds', help='Translation flavour: "cds", "eccodes" or "ecmwf".'
    )
    parser.add_argument(
        '--output', '-o', default='{input}.nc', help='Path to the output file.'
    )

    args = parser.parse_args()
    print('Loading: %r with flavour %r' % (args.input, args.flavour_name))
    ds = open_dataset(args.input, flavour_name=args.flavour_name)
    outpath = args.output.format(input=args.input)
    print('Creating:', outpath)
    ds.to_netcdf(outpath)
<commit_msg>added optional key sample_name in ecmwf_dataarray_to_grib<commit_after>#
# Copyright 2017-2018 European Centre for Medium-Range Weather Forecasts (ECMWF).
#
# 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.
#
# Authors:
#   Alessandro Amici - B-Open - https://bopen.eu
#

from __future__ import absolute_import, division, print_function, unicode_literals

import collections
import itertools
import logging
import typing as T

import attr
import xarray as xr
from xarray import Variable
from xarray.core import indexing
from xarray.core.utils import FrozenOrderedDict
from xarray.backends.api import open_dataset as _open_dataset, _validate_dataset_names, _validate_attrs
from xarray.backends.common import AbstractDataStore, BackendArray

import cfgrib

LOGGER = logging.getLogger(__name__)

class WrapGrib(BackendArray):
    def __init__(self, backend_array):
        self.backend_array = backend_array

    def __getattr__(self, item):
        return getattr(self.backend_array, item)

    def __getitem__(self, item):
        key, np_inds = indexing.decompose_indexer(
            item, self.shape, indexing.IndexingSupport.OUTER_1VECTOR)

        array = self.backend_array[key.tuple]

        if len(np_inds.tuple) > 0:
            array = indexing.NumpyIndexingAdapter(array)[np_inds]

        return array

FLAVOURS = {
    'eccodes': {
        'dataset': {
            'encode_time': False,
            'encode_vertical': False,
            'encode_geography': False,
        },
    },
    'ecmwf': {
        'variable_map': {
            'forecast_reference_time': 'time',
            'forecast_period': 'step',
            'time': 'valid_time',
            'air_pressure': 'level',
            'topLevel': 'level',
        },
        'type_of_level_map': {
            'hybrid': lambda attrs: 'L%d' % ((attrs['GRIB_NV'] - 2) // 2),
        },
    },
    'cds': {
        'variable_map': {
            'number': 'realization',
            'forecast_period': 'leadtime',
            'air_pressure': 'plev',
            'latitude': 'lat',
            'longitude': 'lon',
            'topLevel': 'level',
        },
        'type_of_level_map': {
            'hybrid': lambda attrs: 'L%d' % ((attrs['GRIB_NV'] - 2) // 2),
        },
    },
}

@attr.attrs()
class GribDataStore(AbstractDataStore):
    ds = attr.attrib()
    variable_map = attr.attrib(default={})
    type_of_level_map = attr.attrib(default={})

    @classmethod
    def frompath(cls, path, flavour_name='ecmwf', **kwargs):
        flavour = FLAVOURS[flavour_name].copy()
        config = flavour.pop('dataset', {}).copy()
        config.update(kwargs)
        return cls(ds=cfgrib.Dataset.frompath(path, **config), **flavour)

    def __attrs_post_init__(self):
        self.variable_map = self.variable_map.copy()
        for name, var in self.ds.variables.items():
            if self.ds.encode_vertical and 'GRIB_typeOfLevel' in var.attributes:
                type_of_level = var.attributes['GRIB_typeOfLevel']
                coord_name = self.type_of_level_map.get(type_of_level, type_of_level)
                if isinstance(coord_name, T.Callable):
                    coord_name = coord_name(var.attributes)
                self.variable_map['topLevel'] = coord_name.format(**var.attributes)

    def open_store_variable(self, name, var):
        if isinstance(var.data, cfgrib.dataset.OnDiskArray):
            data = indexing.LazilyOuterIndexedArray(WrapGrib(var.data))
        else:
            data = var.data

        dimensions = tuple(self.variable_map.get(dim, dim) for dim in var.dimensions)
        attrs = var.attributes

        # the coordinates attributes need a special treatment
        if 'coordinates' in attrs:
            coordinates = [self.variable_map.get(d, d) for d in attrs['coordinates'].split()]
            attrs['coordinates'] = ' '.join(coordinates)

        encoding = {}
        # save source so __repr__ can detect if it's local or not
        encoding['source'] = self.ds.stream.path
        encoding['original_shape'] = var.data.shape

        return Variable(dimensions, data, attrs, encoding)

    def get_variables(self):
        return FrozenOrderedDict((self.variable_map.get(k, k), self.open_store_variable(k, v))
                                 for k, v in self.ds.variables.items())

    def get_attrs(self):
        return FrozenOrderedDict(self.ds.attributes)

    def get_dimensions(self):
        return collections.OrderedDict((self.variable_map.get(d, d), s)
                                       for d, s in self.ds.dimensions.items())

    def get_encoding(self):
        encoding = {}
        encoding['unlimited_dims'] = {k for k, v in self.ds.dimensions.items() if v is None}
        return encoding

def open_dataset(path, flavour_name='ecmwf', **kwargs):
    overrides = {}
    for k in list(kwargs):  # copy to allow the .pop()
        if k.startswith('encode_'):
            overrides[k] = kwargs.pop(k)
    store = GribDataStore.frompath(path, flavour_name=flavour_name, **overrides)
    return _open_dataset(store, **kwargs)

#
# write support
#

def sample_name_detection(grib_attributes):
    # type: (T.Mapping) ->  T.Tuple[str, T.List]
    from cfgrib import dataset
    header_coords_names = []

    if grib_attributes['gridType'] == 'regular_ll':
        geography = 'regular_ll'
        header_coords_names += [n for n, _ in dataset.DATA_TIME_COORDINATE_MAP]
    else:
        raise NotImplementedError("Unsupported 'gridType': %r" % grib_attributes['gridType'])

    if grib_attributes['typeOfLevel'] == 'isobaricInhPa':
        vertical = 'pl'
        header_coords_names += [n for n, _ in dataset.VERTICAL_COORDINATE_MAP]
    elif grib_attributes['typeOfLevel'] in ('surface', 'meanSea'):
        vertical = 'sfc'
    else:
        raise NotImplementedError("Unsupported 'typeOfLevel': %r" % grib_attributes['typeOfLevel'])

    sample_name = '%s_%s_grib2' % (geography, vertical)
    return sample_name, header_coords_names

def ecmwf_dataarray_to_grib(file, data_var, global_attributes={}, sample_name=None):
    # type: (T.BinaryIO, str, xr.DataArray) -> None
    from cfgrib import cfmessage
    from cfgrib import eccodes

    grib_attributes = {k[5:]: v for k, v in global_attributes.items() if k[:5] == 'GRIB_'}
    grib_attributes.update({k[5:]: v for k, v in data_var.attrs.items() if k[:5] == 'GRIB_'})

    sample_name_detected, header_coords_names = sample_name_detection(grib_attributes)
    if sample_name is None:
        sample_name = sample_name_detected

    for dim in header_coords_names:
        if dim not in data_var.dims:
            data_var = data_var.expand_dims(dim)
    header_coords_values = [data_var.coords[name].values.tolist() for name in header_coords_names]
    for items in itertools.product(*header_coords_values):
        message = cfmessage.CfMessage.fromsample(sample_name)
        for key, value in grib_attributes.items():
            try:
                message[key] = value
            except eccodes.EcCodesError as ex:
                if ex.code != eccodes.lib.GRIB_READ_ONLY:
                    LOGGER.exception("Can't encode key: %r" % key)

        for coord_name, coord_value in zip(header_coords_names, items):
            message[coord_name] = coord_value

        select = {n: v for n, v in zip(header_coords_names, items)}
        message['values'] = data_var.sel(**select).values.flat[:].tolist()

        message.write(file)

def to_grib(ecmwf_dataset, path, mode='wb', sample_name=None):
    # validate Dataset keys, DataArray names, and attr keys/values
    _validate_dataset_names(ecmwf_dataset)
    _validate_attrs(ecmwf_dataset)

    with open(path, mode=mode) as file:
        for data_var in ecmwf_dataset.data_vars.values():
            ecmwf_dataarray_to_grib(file, data_var, global_attributes=ecmwf_dataset.attrs, sample_name=None)

def cfgrib2netcdf():
    import argparse

    parser = argparse.ArgumentParser(description='Convert a GRIB file into a NetCDF file.')
    parser.add_argument('input', help='Path to the input GRIB file.')
    parser.add_argument(
        '--flavour_name', default='cds', help='Translation flavour: "cds", "eccodes" or "ecmwf".'
    )
    parser.add_argument(
        '--output', '-o', default='{input}.nc', help='Path to the output file.'
    )

    args = parser.parse_args()
    print('Loading: %r with flavour %r' % (args.input, args.flavour_name))
    ds = open_dataset(args.input, flavour_name=args.flavour_name)
    outpath = args.output.format(input=args.input)
    print('Creating:', outpath)
    ds.to_netcdf(outpath)
<|endoftext|>


<commit_before>"""A module for consuming the Penn Dining API"""
from os import path
from base import WrapperBase

BASE_URL = "https://esb.isc-seo.upenn.edu/8091/open_data/dining/"
ENDPOINTS = {
    'MENUS': BASE_URL + 'menus',
    'VENUES': BASE_URL + 'venues',
}

# Normalization for dining menu data
def normalize_weekly(data):
    if not "tblMenu" in data["result_data"]["Document"]:
        data["result_data"]["Document"]["tblMenu"] = []
    if isinstance(data["result_data"]["Document"]["tblMenu"], dict):
        data["result_data"]["Document"]["tblMenu"] = [data["result_data"]["Document"]["tblMenu"]]
    for day in data["result_data"]["Document"]["tblMenu"]:
        if isinstance(day["tblDayPart"], dict):
            day["tblDayPart"] = [day["tblDayPart"]]
        for meal in day["tblDayPart"]:
            if isinstance(meal["tblStation"], dict):
                meal["tblStation"] = [meal["tblStation"]]
            for station in meal["tblStation"]:
                if isinstance(station["tblItem"], dict):
                    station["tblItem"] = [station["tblItem"]]
    return data

class Dining(WrapperBase):
    """The client for the Registrar. Used to make requests to the API.

    :param bearer: The user code for the API
    :param token: The password code for the API

    Usage::

      >>> from penn import Dining
      >>> din = Dining('MY_USERNAME_TOKEN', 'MY_PASSWORD_TOKEN')
    """
    def venues(self):
        """Get a list of all venue objects.

          >>> venues = din.venues()
        """
        response = self._request(ENDPOINTS['VENUES'])
        # Normalize `dateHours` to array
        for venue in response["result_data"]["document"]["venue"]:
            if isinstance(venue.get("dateHours"), dict):
                venue["dateHours"] = [venue["dateHours"]]
        return response

    def menu_daily(self, building_id):
        """Get a menu object corresponding to the daily menu for the
        venue with building_id.

        :param building_id:
          A string representing the id of a building, e.g. "abc".

        >>> commons_today = din.menu_daily("593")
        """
        response = self._request(
            path.join(ENDPOINTS['MENUS'], 'daily', str(building_id))
        )
        # Normalize `tblDayPart` and `tblItem` to array
        meals = response["result_data"]["Document"]["tblMenu"]["tblDayPart"]
        if isinstance(meals, dict):
            response["result_data"]["Document"]["tblMenu"]["tblDayPart"] = [meals]
        for meal in response["result_data"]["Document"]["tblMenu"]["tblDayPart"]:
            for station in meal["tblStation"]:
                if isinstance(station["tblItem"], dict):
                    station["tblItem"] = [station["tblItem"]]
        return response

    def menu_weekly(self, building_id):
        """Get an array of menu objects corresponding to the weekly menu for the
        venue with building_id.

        :param building_id:
            A string representing the id of a building, e.g. "abc".

        >>> commons_week = din.menu_weekly("593")
        """
        response = self._request(path.join(ENDPOINTS['MENUS'], 'weekly', str(building_id)))
        return normalize_weekly(response)
<commit_msg>fixed json inconsistency<commit_after>"""A module for consuming the Penn Dining API"""
from os import path
from base import WrapperBase

BASE_URL = "https://esb.isc-seo.upenn.edu/8091/open_data/dining/"
ENDPOINTS = {
    'MENUS': BASE_URL + 'menus',
    'VENUES': BASE_URL + 'venues',
}

# Normalization for dining menu data
def normalize_weekly(data):
    if not "tblMenu" in data["result_data"]["Document"]:
        data["result_data"]["Document"]["tblMenu"] = []
    if isinstance(data["result_data"]["Document"]["tblMenu"], dict):
        data["result_data"]["Document"]["tblMenu"] = [data["result_data"]["Document"]["tblMenu"]]
    for day in data["result_data"]["Document"]["tblMenu"]:
        if isinstance(day["tblDayPart"], dict):
            day["tblDayPart"] = [day["tblDayPart"]]
        for meal in day["tblDayPart"]:
            if isinstance(meal["tblStation"], dict):
                meal["tblStation"] = [meal["tblStation"]]
            for station in meal["tblStation"]:
                if isinstance(station["tblItem"], dict):
                    station["tblItem"] = [station["tblItem"]]
    return data

class Dining(WrapperBase):
    """The client for the Registrar. Used to make requests to the API.

    :param bearer: The user code for the API
    :param token: The password code for the API

    Usage::

      >>> from penn import Dining
      >>> din = Dining('MY_USERNAME_TOKEN', 'MY_PASSWORD_TOKEN')
    """
    def venues(self):
        """Get a list of all venue objects.

          >>> venues = din.venues()
        """
        response = self._request(ENDPOINTS['VENUES'])
        # Normalize `dateHours` to array
        for venue in response["result_data"]["document"]["venue"]:
            if isinstance(venue.get("dateHours"), dict):
                venue["dateHours"] = [venue["dateHours"]]
            for dh in venue["dateHours"]:
                if isinstance(dh.get("meal"), dict):
                    dh["meal"] = [dh["meal"]]
        return response

    def menu_daily(self, building_id):
        """Get a menu object corresponding to the daily menu for the
        venue with building_id.

        :param building_id:
          A string representing the id of a building, e.g. "abc".

        >>> commons_today = din.menu_daily("593")
        """
        response = self._request(
            path.join(ENDPOINTS['MENUS'], 'daily', str(building_id))
        )
        # Normalize `tblDayPart` and `tblItem` to array
        meals = response["result_data"]["Document"]["tblMenu"]["tblDayPart"]
        if isinstance(meals, dict):
            response["result_data"]["Document"]["tblMenu"]["tblDayPart"] = [meals]
        for meal in response["result_data"]["Document"]["tblMenu"]["tblDayPart"]:
            for station in meal["tblStation"]:
                if isinstance(station["tblItem"], dict):
                    station["tblItem"] = [station["tblItem"]]
        return response

    def menu_weekly(self, building_id):
        """Get an array of menu objects corresponding to the weekly menu for the
        venue with building_id.

        :param building_id:
            A string representing the id of a building, e.g. "abc".

        >>> commons_week = din.menu_weekly("593")
        """
        response = self._request(path.join(ENDPOINTS['MENUS'], 'weekly', str(building_id)))
        return normalize_weekly(response)
<|endoftext|>


<commit_before>"""
calculatepi.py
Author: Avery Wallis

This was an experiment for Math Modeling and Fractions

"""
import math
lists = []
a = 1/2
lists.append(a)
print(lists)

"""
n = int(input("I will estimate pi. How many terms should I use? "))
decimals = int(input("How many decimal places should I use in the result? "))
pi = float(4*(sum([((-1.0)**k)/(2*k+1) for k in range(0,n)])))
print("The approximate value of pi is {0}".format(round(pi, decimals)))
"""<commit_msg>Updated from Brython Server: 9/18/2016 12:08:16 PM<commit_after>"""
calculatepi.py
Author: Avery Wallis

This was an experiment for Math Modeling and Fractions

"""
import math
lists = []
a = 0
b = 0
for x in range(0,10):
    a += 1
    b = b
    lists.append(a)
print(lists)

"""
n = int(input("I will estimate pi. How many terms should I use? "))
decimals = int(input("How many decimal places should I use in the result? "))
pi = float(4*(sum([((-1.0)**k)/(2*k+1) for k in range(0,n)])))
print("The approximate value of pi is {0}".format(round(pi, decimals)))
"""<|endoftext|>


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

import asyncio
import logging
import os
import sys

import peony

from . import exceptions

try:
    import magic
    mime = magic.Magic(mime=True)
except:  # pragma: no cover
    import mimetypes
    mime = mimetypes.MimeTypes()
    magic = None

_logger = logging.getLogger(__name__)

class Handle:

    __slots__ = 'exceptions', 'handler'

    def __init__(self, handler, *exceptions):
        self.exceptions = exceptions
        self.handler = handler

class MetaErrorHandler(type):

    def __new__(cls, name, bases, attrs, **kwargs):
        """ put the :class:`~peony.utils.Handle`s in the right place """
        handlers = {}

        for base in bases:
            if hasattr(base, '_handlers'):
                for key, value in base._handlers.items():
                    handlers[key] = value

        for attr in attrs.values():
            if isinstance(attr, Handle):
                for exception in attr.exceptions:
                    handlers[exception] = attr.handler

        attrs['_handlers'] = handlers

        return super().__new__(cls, name, bases, attrs)

class ErrorHandler(metaclass=MetaErrorHandler):
    """
        Basic error handler

    This error handler just raises all the exceptions that it receives.
    """

    RETRY = CONTINUE = OK = True
    RAISE = STOP = False

    def __init__(self, request):
        self.__request = request

    @staticmethod
    def handle(*exceptions):
        """  """
        def handler_decorator(handler):
            return Handle(handler, *exceptions)

        return handler_decorator

    async def __handle(self, exception_class, **kwargs):
        if exception_class in self._handlers:
            handler = self._handlers[exception_class]
            args = get_args(handler, skip=1)
            handler_kwargs = {key: kwargs[key] for key in args
                              if key in kwargs}
            try:
                return await execute(handler(self, **handler_kwargs))
            except Exception as exc:
                return exc

        for base in exception_class.__bases__:
            return await self.__handle(base, **kwargs)

        return ErrorHandler.RAISE

    async def __call__(self, future=None, **kwargs):
        status = ErrorHandler.OK
        while status:
            try:
                if future is None:
                    return await self.__request(**kwargs)
                else:
                    return await self.__request(future=future, **kwargs)
            except Exception as exc:
                status = await self.__handle(exc.__class__, exception=exc,
                                             **kwargs)

                if isinstance(status, Exception):
                    exc = status
                    status = ErrorHandler.RAISE

                if status is ErrorHandler.RAISE:
                    _logger.debug("raising exception")
                    if future is not None:
                        future.set_exception(exc)
                    else:
                        raise exc

class DefaultErrorHandler(ErrorHandler):
    """
        The default error_handler

    The decorated request will retry infinitely on any handled error
    The exceptions handled are :class:`TimeoutError`,
    :class:`asyncio.TimeoutError`,
    :class:`exceptions.RateLimitExceeded` and
    :class:`exceptions.ServiceUnavailable`
    """

    def __init__(self, request):
        super().__init__(request)
        self.tries = 3

    @ErrorHandler.handle(exceptions.RateLimitExceeded)
    async def handle_rate_limits(self, exception, url):
        delay = int(exception.reset_in) + 1
        fmt = "Sleeping for {}s (rate limit exceeded on endpoint {})"
        _logger.warning(fmt.format(delay, url))
        await asyncio.sleep(delay)
        return ErrorHandler.RETRY

    @ErrorHandler.handle(asyncio.TimeoutError, TimeoutError)
    def handle_timeout_error(self, url):
        fmt = "Request to {url} timed out, retrying"
        _logger.info(fmt.format(url=url))
        return ErrorHandler.RETRY

    @ErrorHandler.handle(exceptions.HTTPServiceUnavailable)
    async def handle_service_unavailable(self):
        self.tries -= 1
        if self.tries > 0:
            _logger.info("Service temporarily unavailable, "
                         "request will be made again very soon")
            await asyncio.sleep(0.5)
            return ErrorHandler.RETRY

def get_args(func, skip=0):
    """
        Hackish way to get the arguments of a function

    Parameters
    ----------
    func : callable
        Function to get the arguments from
    skip : int, optional
        Arguments to skip, defaults to 0 set it to 1 to skip the
        ``self`` argument of a method.

    Returns
    -------
    tuple
        Function's arguments
    """

    code = getattr(func, '__code__', None)
    if code is None:
        code = func.__call__.__code__

    return code.co_varnames[skip:code.co_argcount]

def log_error(msg=None, exc_info=None, logger=None, **kwargs):
    """
        log an exception and its traceback on the logger defined

    Parameters
    ----------
    msg : str, optional
        A message to add to the error
    exc_info : tuple
        Information about the current exception
    logger : logging.Logger
        logger to use
    """
    if logger is None:
        logger = _logger

    if not exc_info:
        exc_info = sys.exc_info()

    if msg is None:
        msg = ""

    exc_class, exc_msg, _ = exc_info

    if all(info is not None for info in exc_info):
        logger.error(msg, exc_info=exc_info)

async def get_media_metadata(data, path=None):
    """
        Get all the file's metadata and read any kind of file object

    Parameters
    ----------
    data : bytes
        first bytes of the file (the mimetype shoudl be guessed from the
        file headers
    path : str, optional
        path to the file

    Returns
    -------
    str
        The mimetype of the media
    str
        The category of the media on Twitter
    """
    if isinstance(data, bytes):
        media_type = await get_type(data, path)

    else:
        raise TypeError("get_metadata input must be a bytes")

    media_category = get_category(media_type)

    _logger.info("media_type: %s, media_category: %s" % (media_type,
                                                         media_category))

    return media_type, media_category

async def get_size(media):
    """
        Get the size of a file

    Parameters
    ----------
    media : file object
        The file object of the media

    Returns
    -------
    int
        The size of the file
    """
    if hasattr(media, 'seek'):
        await execute(media.seek(0, os.SEEK_END))
        size = await execute(media.tell())
        await execute(media.seek(0))
    elif hasattr(media, 'headers'):
        size = int(media.headers['Content-Length'])
    elif isinstance(media, bytes):
        size = len(media)
    else:
        raise TypeError("Can't get size of media of type:",
                        type(media).__name__)

    _logger.info("media size: %dB" % size)
    return size

async def get_type(media, path=None):
    """
    Parameters
    ----------
    media : file object
        A file object of the image
    path : str, optional
        The path to the file

    Returns
    -------
    str
        The mimetype of the media
    str
        The category of the media on Twitter
    """
    if magic:
        if not media:
            raise TypeError("Media data is empty")

        _logger.debug("guessing mimetype using magic")
        media_type = mime.from_buffer(media[:1024])
    else:
        media_type = None
        if path:
            _logger.debug("guessing mimetype using built-in module")
            media_type = mime.guess_type(path)[0]

        if media_type is None:
            msg = ("Could not guess the mimetype of the media.\n"
                   "Please consider installing python-magic\n"
                   "(pip3 install peony-twitter[magic])")
            raise RuntimeError(msg)

    return media_type

def get_category(media_type):
    if media_type.startswith('video'):
        return "tweet_video"
    elif media_type.endswith('gif'):
        return "tweet_gif"
    elif media_type.startswith('image'):
        return "tweet_image"
    else:
        raise RuntimeError("The provided media cannot be handled.\n"
                           "mimetype: %s" % media_type)

async def execute(coro):
    """
        run a function or coroutine

    Parameters
    ----------
    coro : asyncio.coroutine or function
    """
    if asyncio.iscoroutine(coro):
        return await coro
    else:
        return coro

def set_debug():
    """ activates error messages, useful during development """
    logging.basicConfig(level=logging.WARNING)
    peony.logger.setLevel(logging.DEBUG)
<commit_msg>utils: add helper for entities in text<commit_after># -*- coding: utf-8 -*-
import asyncio
import logging
import os
import sys
from functools import partial
from itertools import chain
from typing import Any, Iterable, Mapping

import peony

from . import exceptions

try:
    import magic
    mime = magic.Magic(mime=True)
except:  # pragma: no cover
    import mimetypes
    mime = mimetypes.MimeTypes()
    magic = None

_logger = logging.getLogger(__name__)

class Handle:

    __slots__ = 'exceptions', 'handler'

    def __init__(self, handler, *exceptions):
        self.exceptions = exceptions
        self.handler = handler

class MetaErrorHandler(type):

    def __new__(cls, name, bases, attrs, **kwargs):
        """ put the :class:`~peony.utils.Handle`s in the right place """
        handlers = {}

        for base in bases:
            if hasattr(base, '_handlers'):
                for key, value in base._handlers.items():
                    handlers[key] = value

        for attr in attrs.values():
            if isinstance(attr, Handle):
                for exception in attr.exceptions:
                    handlers[exception] = attr.handler

        attrs['_handlers'] = handlers

        return super().__new__(cls, name, bases, attrs)

class ErrorHandler(metaclass=MetaErrorHandler):
    """
        Basic error handler

    This error handler just raises all the exceptions that it receives.
    """

    RETRY = CONTINUE = OK = True
    RAISE = STOP = False

    def __init__(self, request):
        self.__request = request

    @staticmethod
    def handle(*exceptions):
        """  """
        def handler_decorator(handler):
            return Handle(handler, *exceptions)

        return handler_decorator

    async def __handle(self, exception_class, **kwargs):
        if exception_class in self._handlers:
            handler = self._handlers[exception_class]
            args = get_args(handler, skip=1)
            handler_kwargs = {key: kwargs[key] for key in args
                              if key in kwargs}
            try:
                return await execute(handler(self, **handler_kwargs))
            except Exception as exc:
                return exc

        for base in exception_class.__bases__:
            return await self.__handle(base, **kwargs)

        return ErrorHandler.RAISE

    async def __call__(self, future=None, **kwargs):
        status = ErrorHandler.OK
        while status:
            try:
                if future is None:
                    return await self.__request(**kwargs)
                else:
                    return await self.__request(future=future, **kwargs)
            except Exception as exc:
                status = await self.__handle(exc.__class__, exception=exc,
                                             **kwargs)

                if isinstance(status, Exception):
                    exc = status
                    status = ErrorHandler.RAISE

                if status is ErrorHandler.RAISE:
                    _logger.debug("raising exception")
                    if future is not None:
                        future.set_exception(exc)
                    else:
                        raise exc

class DefaultErrorHandler(ErrorHandler):
    """
        The default error_handler

    The decorated request will retry infinitely on any handled error
    The exceptions handled are :class:`TimeoutError`,
    :class:`asyncio.TimeoutError`,
    :class:`exceptions.RateLimitExceeded` and
    :class:`exceptions.ServiceUnavailable`
    """

    def __init__(self, request):
        super().__init__(request)
        self.tries = 3

    @ErrorHandler.handle(exceptions.RateLimitExceeded)
    async def handle_rate_limits(self, exception, url):
        delay = int(exception.reset_in) + 1
        fmt = "Sleeping for {}s (rate limit exceeded on endpoint {})"
        _logger.warning(fmt.format(delay, url))
        await asyncio.sleep(delay)
        return ErrorHandler.RETRY

    @ErrorHandler.handle(asyncio.TimeoutError, TimeoutError)
    def handle_timeout_error(self, url):
        fmt = "Request to {url} timed out, retrying"
        _logger.info(fmt.format(url=url))
        return ErrorHandler.RETRY

    @ErrorHandler.handle(exceptions.HTTPServiceUnavailable)
    async def handle_service_unavailable(self):
        self.tries -= 1
        if self.tries > 0:
            _logger.info("Service temporarily unavailable, "
                         "request will be made again very soon")
            await asyncio.sleep(0.5)
            return ErrorHandler.RETRY

def get_args(func, skip=0):
    """
        Hackish way to get the arguments of a function

    Parameters
    ----------
    func : callable
        Function to get the arguments from
    skip : int, optional
        Arguments to skip, defaults to 0 set it to 1 to skip the
        ``self`` argument of a method.

    Returns
    -------
    tuple
        Function's arguments
    """

    code = getattr(func, '__code__', None)
    if code is None:
        code = func.__call__.__code__

    return code.co_varnames[skip:code.co_argcount]

def log_error(msg=None, exc_info=None, logger=None, **kwargs):
    """
        log an exception and its traceback on the logger defined

    Parameters
    ----------
    msg : str, optional
        A message to add to the error
    exc_info : tuple
        Information about the current exception
    logger : logging.Logger
        logger to use
    """
    if logger is None:
        logger = _logger

    if not exc_info:
        exc_info = sys.exc_info()

    if msg is None:
        msg = ""

    exc_class, exc_msg, _ = exc_info

    if all(info is not None for info in exc_info):
        logger.error(msg, exc_info=exc_info)

async def get_media_metadata(data, path=None):
    """
        Get all the file's metadata and read any kind of file object

    Parameters
    ----------
    data : bytes
        first bytes of the file (the mimetype shoudl be guessed from the
        file headers
    path : str, optional
        path to the file

    Returns
    -------
    str
        The mimetype of the media
    str
        The category of the media on Twitter
    """
    if isinstance(data, bytes):
        media_type = await get_type(data, path)

    else:
        raise TypeError("get_metadata input must be a bytes")

    media_category = get_category(media_type)

    _logger.info("media_type: %s, media_category: %s" % (media_type,
                                                         media_category))

    return media_type, media_category

async def get_size(media):
    """
        Get the size of a file

    Parameters
    ----------
    media : file object
        The file object of the media

    Returns
    -------
    int
        The size of the file
    """
    if hasattr(media, 'seek'):
        await execute(media.seek(0, os.SEEK_END))
        size = await execute(media.tell())
        await execute(media.seek(0))
    elif hasattr(media, 'headers'):
        size = int(media.headers['Content-Length'])
    elif isinstance(media, bytes):
        size = len(media)
    else:
        raise TypeError("Can't get size of media of type:",
                        type(media).__name__)

    _logger.info("media size: %dB" % size)
    return size

async def get_type(media, path=None):
    """
    Parameters
    ----------
    media : file object
        A file object of the image
    path : str, optional
        The path to the file

    Returns
    -------
    str
        The mimetype of the media
    str
        The category of the media on Twitter
    """
    if magic:
        if not media:
            raise TypeError("Media data is empty")

        _logger.debug("guessing mimetype using magic")
        media_type = mime.from_buffer(media[:1024])
    else:
        media_type = None
        if path:
            _logger.debug("guessing mimetype using built-in module")
            media_type = mime.guess_type(path)[0]

        if media_type is None:
            msg = ("Could not guess the mimetype of the media.\n"
                   "Please consider installing python-magic\n"
                   "(pip3 install peony-twitter[magic])")
            raise RuntimeError(msg)

    return media_type

def get_category(media_type):
    if media_type.startswith('video'):
        return "tweet_video"
    elif media_type.endswith('gif'):
        return "tweet_gif"
    elif media_type.startswith('image'):
        return "tweet_image"
    else:
        raise RuntimeError("The provided media cannot be handled.\n"
                           "mimetype: %s" % media_type)

async def execute(coro):
    """
        run a function or coroutine

    Parameters
    ----------
    coro : asyncio.coroutine or function
    """
    if asyncio.iscoroutine(coro):
        return await coro
    else:
        return coro

def set_debug():
    """ activates error messages, useful during development """
    logging.basicConfig(level=logging.WARNING)
    peony.logger.setLevel(logging.DEBUG)

class Entity:
    """ Helper to use Twitter entities """

    def __init__(self, original: str,
                 entity_type: str,
                 data: Mapping[str, Any]):
        self.data = data
        self.entity_type = entity_type
        self.original = original[self.start:self.end]

    def __getitem__(self, key: str) -> Any:
        return self.data[key]

    @property
    def start(self) -> int:
        return self.data['start']

    @property
    def end(self) -> int:
        return self.data['end']

    @property
    def text(self) -> str:
        """ returns text representing the entity """
        ret = {
            'urls': lambda: self.data['display_url']
        }

        return ret.get(self.entity_type, lambda: self.original)()

    @property
    def url(self) -> str:
        """ returns an url representing the entity """
        ret = {
            'urls': lambda: self.data['expanded_url'],
            'mentions': lambda: f"https://twitter.com/{self.data['username']}",
            'hashtags': lambda: f"https://twitter.com/hashtag/{self.data['tag']}"  # noqa
        }

        return ret.get(self.entity_type, lambda: "")()

def get_twitter_entities(
    text: str,
    entities: Mapping[str, Mapping[str, Any]]
) -> Iterable[Entity]:
    """ Returns twitter entities from an entities dictionnary

    Entities are returned is reversed order for ease of use (start and end
    indexes stay the same if the string is changed in place)
    """
    return sorted(
        chain.from_iterable(
            map(partial(Entity, text, entity_type), entities)
            for entity_type, entities in entities.items()
        ),
        key=lambda e: e.start,
        reverse=True
    )
<|endoftext|>


<commit_before>"""
calculatepi.py
Author: <your name here>
Credit: <list sources used, if any>
Assignment:

Write and submit a Python program that computes an approximate value of π by calculating the following sum:

(see: https://github.com/HHS-IntroProgramming/Calculate-Pi/blob/master/README.md)

This sum approaches the true value of π as n approaches ∞.

Your program must ask the user how many terms to use in the estimate of π, how many decimal places,
then print the estimate using that many decimal places. Exactly like this:

I will estimate pi. How many terms should I use? 100
How many decimal places should I use in the result? 7
The approximate value of pi is 3.1315929

Note: remember that the printed value of pi will be an estimate!

"""
<commit_msg>Updated from Brython Server: 9/14/2015 2:47:43 PM<commit_after>"""
calculatepi.py
Author: <your name here>
Credit: <list sources used, if any>
Assignment:

Write and submit a Python program that computes an approximate value of π by calculating the following sum:

(see: https://github.com/HHS-IntroProgramming/Calculate-Pi/blob/master/README.md)

This sum approaches the true value of π as n approaches ∞.

Your program must ask the user how many terms to use in the estimate of π, how many decimal places,
then print the estimate using that many decimal places. Exactly like this:

I will estimate pi. How many terms should I use? 100
How many decimal places should I use in the result? 7
The approximate value of pi is 3.1315929

Note: remember that the printed value of pi will be an estimate!

"""
import math
dir(math)
n = int(input("I will estimate pi. How many terms should I use? "))
decimals = int(input("How many decimal places should I use in the result? "))
pi = 4.0*sum([((-1.0)**k)/2*(k)+1) for k in range(0,n)])
print("The approximate value of pi is {0:.{1}f}".format(pi, decimals))
print("(The true value of pi is {0:.{1}f})".format(math.pi, decimals))
<|endoftext|>


<commit_before>myPi = 4.0
a = 3.0

while a < 88000 :
    myPi = myPi - (4/a) + (4/(a+2))
    a += 4
    print(myPi)

<commit_msg>Updated from Brython Server: February 2, 2016 2:56:07 PM EST<commit_after>myPi = 4.0
a = 3.0

user = input("I will estimate pi. How many terms should I use?")

while a < 10 :
    myPi = myPi - (4/a) + (4/(a+2))
    a += 4
    print(myPi)

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Copyright 2013 Simone Campagna
#
# 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.
#

"""
The callerframe decorator adds a __caller_frame__ global attribute to the
decorated function's globals; this attribute refers to a FrameInfo object
containing information about the caller function:

 * frame: the caller's frame;
 * filename: the name of the file where the function has been called;
 * line_number: the line number of the call in filename;
 * function_name: the name of the caller function;
 * context: a list of source line containing the call;
 * index: the index of the line in context where the function has been called.

>>> @callerframe
... def log(kind, message):
...     print("{}: function {}: {}".format(kind, __caller_frame__.function_name, message))
...
>>> def foo():
...     log("error", "lost connection")
...
>>> def main():
...     return foo()
...
>>> main()
error: function foo: lost connection

The log function receives information about it's direct caller; but what if we want
to have an error() function based on log()?

>>> def error(message):
...     log("error", message)
...
>>> def foo():
...     error("lost connection")
...
>>> def main():
...     return foo()
...
>>> main()
error: function error: lost connection

This is correct, since error() is the direct caller of the log() function;
nevertheless we would like to show the information about the error()'s caller
instead. In this case it is possible to decorate error() too (no modification is
needed in log):

>>> @callerframe
... def error(message):
...     log("error", message)
...
>>> def foo():
...     error("lost connection")
...
>>> def main():
...     return foo()
...
>>> main()
error: function foo: lost connection

In other words, the first decorated function found in the call stack sets the
caller information. This information is not overwritten by nested calls to
decorated functions.

The attribute name, by default __caller_frame__, can be choosen:

>>> @callerframe("CALLERFRAME")
... def show_caller():
...     print(CALLERFRAME.function_name)
...
>>> def foo():
...     show_caller()
...
>>> foo()
foo

"""

__all__ = [
    'FrameInfo',
    'callerframe',
]
__version__ = "1.0.2"

import collections
import contextlib
import functools
import inspect

FrameInfo = collections.namedtuple("FrameInfo", ("frame", "filename", "line_number",
                                                 "function_name", "context", "index"))

class DecoratorMaker(object):  # pylint: disable=too-few-public-methods
    """DecoratorMaker - creates a decorator/decorator factory object"""
    def __init__(self, decorator_factory):
        self.decorator_factory = decorator_factory
        self._default_decorator = None

    @property
    def default_decorator(self):
        """Returns the default decorator

        Returns
        -------
        function
            the default decorator
        """
        if self._default_decorator is None:
            self._default_decorator = self.decorator_factory()
        return self._default_decorator

    def __call__(self, function, *args, **kwargs):
        if len(args) + len(kwargs) == 0 and callable(function):
            # direct call
            return self.default_decorator(function)  # pylint: disable=not-callable
        else:
            # indirect call
            return self.decorator_factory(function, *args, **kwargs)

@contextlib.contextmanager
def _update_globals(glb, frame, depth, attr_name):
    """Updates the globals dictionary with 'attr_name = <caller frame>', if not available
    """
    remove = False
    if glb.get(attr_name) is None:
        glb[attr_name] = FrameInfo(*inspect.getouterframes(frame)[depth])
        remove = True
    yield glb
    if remove:
        del glb[attr_name]

def callerframe_decorator_factory(attr_name="__caller_frame__"):
    """Returns a callerframe decorator

    Attributes
    ----------
    attr_name: str, optional
        the name of the global attribute to be added to the decorated function
        [defaults to "__caller_frame__"]

    Returns
    -------
    function
        the decorator adding <attr_name> to the decorated function's globals
    """
    def callerframe_decorator(function):
        """Function decorators that insert an 'attr_name = <caller frame>' attribute to the function globals
        """
        @functools.wraps(function)
        def wrap(*args, **kwargs):
            """Decorated function"""
            with _update_globals(function.__globals__, inspect.currentframe(), 1, attr_name):
                return function(*args, **kwargs)
        return wrap
    return callerframe_decorator

callerframe = DecoratorMaker(callerframe_decorator_factory)  # pylint: disable=invalid-name
<commit_msg>new release 1.0.3<commit_after># -*- coding: utf-8 -*-
#
# Copyright 2013 Simone Campagna
#
# 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.
#

"""
The callerframe decorator adds a __caller_frame__ global attribute to the
decorated function's globals; this attribute refers to a FrameInfo object
containing information about the caller function:

 * frame: the caller's frame;
 * filename: the name of the file where the function has been called;
 * line_number: the line number of the call in filename;
 * function_name: the name of the caller function;
 * context: a list of source line containing the call;
 * index: the index of the line in context where the function has been called.

>>> @callerframe
... def log(kind, message):
...     print("{}: function {}: {}".format(kind, __caller_frame__.function_name, message))
...
>>> def foo():
...     log("error", "lost connection")
...
>>> def main():
...     return foo()
...
>>> main()
error: function foo: lost connection

The log function receives information about it's direct caller; but what if we want
to have an error() function based on log()?

>>> def error(message):
...     log("error", message)
...
>>> def foo():
...     error("lost connection")
...
>>> def main():
...     return foo()
...
>>> main()
error: function error: lost connection

This is correct, since error() is the direct caller of the log() function;
nevertheless we would like to show the information about the error()'s caller
instead. In this case it is possible to decorate error() too (no modification is
needed in log):

>>> @callerframe
... def error(message):
...     log("error", message)
...
>>> def foo():
...     error("lost connection")
...
>>> def main():
...     return foo()
...
>>> main()
error: function foo: lost connection

In other words, the first decorated function found in the call stack sets the
caller information. This information is not overwritten by nested calls to
decorated functions.

The attribute name, by default __caller_frame__, can be choosen:

>>> @callerframe("CALLERFRAME")
... def show_caller():
...     print(CALLERFRAME.function_name)
...
>>> def foo():
...     show_caller()
...
>>> foo()
foo

"""

__all__ = [
    'FrameInfo',
    'callerframe',
]
__version__ = "1.0.3"

import collections
import contextlib
import functools
import inspect

FrameInfo = collections.namedtuple("FrameInfo", ("frame", "filename", "line_number",
                                                 "function_name", "context", "index"))

class DecoratorMaker(object):  # pylint: disable=too-few-public-methods
    """DecoratorMaker - creates a decorator/decorator factory object"""
    def __init__(self, decorator_factory):
        self.decorator_factory = decorator_factory
        self._default_decorator = None

    @property
    def default_decorator(self):
        """Returns the default decorator

        Returns
        -------
        function
            the default decorator
        """
        if self._default_decorator is None:
            self._default_decorator = self.decorator_factory()
        return self._default_decorator

    def __call__(self, function, *args, **kwargs):
        if len(args) + len(kwargs) == 0 and callable(function):
            # direct call
            return self.default_decorator(function)  # pylint: disable=not-callable
        else:
            # indirect call
            return self.decorator_factory(function, *args, **kwargs)

@contextlib.contextmanager
def _update_globals(glb, frame, depth, attr_name):
    """Updates the globals dictionary with 'attr_name = <caller frame>', if not available
    """
    remove = False
    if glb.get(attr_name) is None:
        glb[attr_name] = FrameInfo(*inspect.getouterframes(frame)[depth])
        remove = True
    yield glb
    if remove:
        del glb[attr_name]

def callerframe_decorator_factory(attr_name="__caller_frame__"):
    """Returns a callerframe decorator

    Attributes
    ----------
    attr_name: str, optional
        the name of the global attribute to be added to the decorated function
        [defaults to "__caller_frame__"]

    Returns
    -------
    function
        the decorator adding <attr_name> to the decorated function's globals
    """
    def callerframe_decorator(function):
        """Function decorators that insert an 'attr_name = <caller frame>' attribute to the function globals
        """
        @functools.wraps(function)
        def wrap(*args, **kwargs):
            """Decorated function"""
            with _update_globals(function.__globals__, inspect.currentframe(), 1, attr_name):
                return function(*args, **kwargs)
        return wrap
    return callerframe_decorator

callerframe = DecoratorMaker(callerframe_decorator_factory)  # pylint: disable=invalid-name
<|endoftext|>