Example #1
0
def _base64_to_file(b64str, outfpath, writetostrio=False):
    '''This converts the base64 encoded string to a file.

    Parameters
    ----------

    b64str : str
        A base64 encoded strin that is the output of `base64.b64encode`.

    outfpath : str
        The path to where the file will be written. This should include an
        appropriate extension for the file (e.g. a base64 encoded string that
        represents a PNG should have its `outfpath` end in a '.png') so the OS
        can open these files correctly.

    writetostrio : bool
        If this is True, will return a StringIO object with the binary stream
        decoded from the base64-encoded input string `b64str`. This can be
        useful to embed these into other files without having to write them to
        disk.

    Returns
    -------

    str or StringIO object
        If `writetostrio` is False, will return the output file's path as a
        str. If it is True, will return a StringIO object directly. If writing
        the file fails in either case, will return None.

    '''

    try:

        filebytes = base64.b64decode(b64str)

        # if we're writing back to a stringio object
        if writetostrio:

            outobj = StrIO(filebytes)
            return outobj

        # otherwise, we're writing to an actual file
        else:

            with open(outfpath,'wb') as outfd:
                outfd.write(filebytes)

            if os.path.exists(outfpath):
                return outfpath
            else:
                LOGERROR('could not write output file: %s' % outfpath)
                return None

    except Exception:

        LOGEXCEPTION('failed while trying to convert '
                     'b64 string to file %s' % outfpath)
        return None
Example #2
0
    def test_main_err(self):
        parser = get_argparser()
        out = StrIO()
        err = StrIO()

        def flush():
            out.truncate(0)
            err.truncate(0)

        with replace_attr(sys, 'stdout', out, 'stderr', err):
            for raising_args, raising_reg in [
                ([],
                 'too few arguments|the following arguments are required: ext'
                 ),
                (['-r'], 'expected one argument'),
                (['shell', '-r'], 'expected one argument'),
                (['shell', '-w'], 'expected one argument'),
                (['shell', '-c'], 'expected one argument'),
                (['shell', '-t'], 'expected one argument'),
                (['shell', '-p'], 'expected one argument'),
                (['shell', '-R'], 'expected one argument'),
                (['shell', '--renderer_module'], 'expected one argument'),
                (['shell', '--width'], 'expected one argument'),
                (['shell', '--last_exit_code'], 'expected one argument'),
                (['shell', '--last_pipe_status'], 'expected one argument'),
                (['shell', '--config'], 'expected one argument'),
                (['shell', '--theme_option'], 'expected one argument'),
                (['shell', '--config_path'], 'expected one argument'),
                (['shell', '--renderer_arg'], 'expected one argument'),
                (['shell', '--jobnum'], 'expected one argument'),
                (['-r', 'zsh_prompt'],
                 'too few arguments|the following arguments are required: ext'
                 ),
                (['shell', '--last_exit_code', 'i'], 'invalid int value'),
                (['shell', '--last_pipe_status',
                  '1 i'], 'invalid <lambda> value'),
                (['shell', '-R', 'abc'], 'invalid <lambda> value'),
            ]:
                self.assertRaises(SystemExit, parser.parse_args, raising_args)
                self.assertFalse(out.getvalue())
                self.assertRegexpMatches(err.getvalue(), raising_reg)
                flush()
Example #3
0
 def test_main_normal(self):
     parser = get_argparser()
     out = StrIO()
     err = StrIO()
     with replace_attr(sys, 'stdout', out, 'stderr', err):
         for argv, expargs in [
             (['shell'], {
                 'ext': ['shell']
             }),
             (['shell', '-r', '.zsh'], {
                 'ext': ['shell'],
                 'renderer_module': '.zsh'
             }),
             ([
                 'shell',
                 'left',
                 '-r',
                 '.zsh',
                 '--last_exit_code',
                 '10',
                 '--last_pipe_status',
                 '10 20 30',
                 '--jobnum=10',
                 '-w',
                 '100',
                 '-c',
                 'common.term_truecolor=true',
                 '-c',
                 'common.spaces=4',
                 '-t',
                 'default.segment_data.hostname.before=H:',
                 '-p',
                 '.',
                 '-p',
                 '..',
                 '-R',
                 'smth={"abc":"def"}',
             ], {
                 'ext': ['shell'],
                 'side': 'left',
                 'renderer_module': '.zsh',
                 'last_exit_code': 10,
                 'last_pipe_status': [10, 20, 30],
                 'jobnum': 10,
                 'width': 100,
                 'config': {
                     'common': {
                         'term_truecolor': True,
                         'spaces': 4
                     }
                 },
                 'theme_option': {
                     'default': {
                         'segment_data': {
                             'hostname': {
                                 'before': 'H:'
                             }
                         }
                     }
                 },
                 'config_path': ['.', '..'],
                 'renderer_arg': {
                     'smth': {
                         'abc': 'def'
                     }
                 },
             }),
             (['shell', '-R', 'arg=true'], {
                 'ext': ['shell'],
                 'renderer_arg': {
                     'arg': True
                 }
             }),
             (['shell', '-R', 'arg=true', '-R', 'arg='], {
                 'ext': ['shell'],
                 'renderer_arg': {}
             }),
             (['shell', '-R', 'arg='], {
                 'ext': ['shell'],
                 'renderer_arg': {}
             }),
             (['shell', '-t', 'default.segment_info={"hostname": {}}'], {
                 'ext': ['shell'],
                 'theme_option': {
                     'default': {
                         'segment_info': {
                             'hostname': {}
                         }
                     }
                 },
             }),
             (['shell', '-c', 'common={ }'], {
                 'ext': ['shell'],
                 'config': {
                     'common': {}
                 }
             }),
         ]:
             args = parser.parse_args(argv)
             finish_args(args)
             for key, val in expargs.items():
                 self.assertEqual(getattr(args, key), val)
             for key, val in args.__dict__.items():
                 if key not in expargs:
                     self.assertFalse(
                         val,
                         msg=
                         'key {0} is {1} while it should be something false'
                         .format(key, val))
             self.assertFalse(err.getvalue() + out.getvalue(),
                              msg='unexpected output: {0!r} {1!r}'.format(
                                  err.getvalue(),
                                  out.getvalue(),
                              ))
Example #4
0
 def pformat(self, obj: object) -> str:
     sio = StrIO()
     PythonPrinter(stream=sio)._format(obj)
     return sio.getvalue()
Example #5
0
def add_cmd_to_checkplot(cpx,
                         cmdpkl,
                         require_cmd_magcolor=True,
                         save_cmd_pngs=False):
    '''This adds CMD figures to a checkplot dict or pickle.

    Looks up the CMDs in `cmdpkl`, adds the object from `cpx` as a gold(-ish)
    star in the plot, and then saves the figure to a base64 encoded PNG, which
    can then be read and used by the `checkplotserver`.

    Parameters
    ----------

    cpx : str or dict
        This is the input checkplot pickle or dict to add the CMD to.

    cmdpkl : str or dict
        The CMD pickle generated by the `colormagdiagram_cplist` or
        `colormagdiagram_cpdir` functions above, or the dict produced by reading
        this pickle in.

    require_cmd_magcolor : bool
        If this is True, a CMD plot will not be made if the color and mag keys
        required by the CMD are not present or are nan in this checkplot's
        objectinfo dict.

    save_cmd_png : bool
        If this is True, then will save the CMD plots that were generated and
        added back to the checkplotdict as PNGs to the same directory as
        `cpx`. If `cpx` is a dict, will save them to the current working
        directory.

    Returns
    -------

    str or dict
        If `cpx` was a str filename of checkplot pickle, this will return that
        filename to indicate that the CMD was added to the file. If `cpx` was a
        checkplotdict, this will return the checkplotdict with a new key called
        'colormagdiagram' containing the base64 encoded PNG binary streams of
        all CMDs generated.

    '''

    # get the checkplot
    if isinstance(cpx, str) and os.path.exists(cpx):
        cpdict = _read_checkplot_picklefile(cpx)
    elif isinstance(cpx, dict):
        cpdict = cpx
    else:
        LOGERROR('unknown type of checkplot provided as the cpx arg')
        return None

    # get the CMD
    if isinstance(cmdpkl, str) and os.path.exists(cmdpkl):
        with open(cmdpkl, 'rb') as infd:
            cmd = pickle.load(infd)
    elif isinstance(cmdpkl, dict):
        cmd = cmdpkl

    cpdict['colormagdiagram'] = {}

    # get the mags and colors from the CMD dict
    cplist_mags = cmd['mags']
    cplist_colors = cmd['colors']

    # now make the CMD plots for each color-mag combination in the CMD
    for c1, c2, ym, ind in zip(cmd['color_mag1'],
                               cmd['color_mag2'], cmd['yaxis_mag'],
                               range(len(cmd['color_mag1']))):

        # get these from the checkplot for this object
        if (c1 in cpdict['objectinfo']
                and cpdict['objectinfo'][c1] is not None):
            c1mag = cpdict['objectinfo'][c1]
        else:
            c1mag = np.nan

        if (c2 in cpdict['objectinfo']
                and cpdict['objectinfo'][c2] is not None):
            c2mag = cpdict['objectinfo'][c2]
        else:
            c2mag = np.nan

        if (ym in cpdict['objectinfo']
                and cpdict['objectinfo'][ym] is not None):
            ymmag = cpdict['objectinfo'][ym]
        else:
            ymmag = np.nan

        if (require_cmd_magcolor
                and not (np.isfinite(c1mag) and np.isfinite(c2mag)
                         and np.isfinite(ymmag))):

            LOGWARNING("required color: %s-%s or mag: %s are not "
                       "in this checkplot's objectinfo dict "
                       "(objectid: %s), skipping CMD..." %
                       (c1, c2, ym, cpdict['objectid']))
            continue

        # make the CMD for this color-mag combination
        try:

            thiscmd_title = r'%s-%s/%s' % (CMD_LABELS[c1], CMD_LABELS[c2],
                                           CMD_LABELS[ym])

            # make the scatter plot
            fig = plt.figure(figsize=(10, 8))
            plt.plot(cplist_colors[:, ind],
                     cplist_mags[:, ind],
                     rasterized=True,
                     marker='o',
                     linestyle='none',
                     mew=0,
                     ms=3)

            # put this object on the plot
            plt.plot([c1mag - c2mag], [ymmag],
                     ms=20,
                     color='#b0ff05',
                     marker='*',
                     mew=0)

            plt.xlabel(r'$%s - %s$' % (CMD_LABELS[c1], CMD_LABELS[c2]))
            plt.ylabel(r'$%s$' % CMD_LABELS[ym])
            plt.title('%s - $%s$ CMD' % (cpdict['objectid'], thiscmd_title))
            plt.gca().invert_yaxis()

            # now save the figure to StrIO and put it back in the checkplot
            cmdpng = StrIO()
            plt.savefig(cmdpng,
                        bbox_inches='tight',
                        pad_inches=0.0,
                        format='png')
            cmdpng.seek(0)
            cmdb64 = base64.b64encode(cmdpng.read())
            cmdpng.close()

            plt.close('all')
            plt.gcf().clear()

            cpdict['colormagdiagram']['%s-%s/%s' % (c1, c2, ym)] = cmdb64

            # if we're supposed to export to PNG, do so
            if save_cmd_pngs:

                if isinstance(cpx, str):
                    outpng = os.path.join(
                        os.path.dirname(cpx), 'cmd-%s-%s-%s.%s.png' %
                        (cpdict['objectid'], c1, c2, ym))
                else:
                    outpng = 'cmd-%s-%s-%s.%s.png' % (cpdict['objectid'], c1,
                                                      c2, ym)

                _base64_to_file(cmdb64, outpng)

        except Exception:
            LOGEXCEPTION('CMD for %s-%s/%s does not exist in %s, skipping...' %
                         (c1, c2, ym, cmdpkl))
            continue

    #
    # end of making CMDs
    #

    if isinstance(cpx, str):
        cpf = _write_checkplot_picklefile(cpdict, outfile=cpx, protocol=4)
        return cpf
    elif isinstance(cpx, dict):
        return cpdict
Example #6
0
def stream_fixture():
    return StrIO()
Example #7
0
    def post(self, cpfile):
        '''This handles POST requests.

        Also an AJAX endpoint. Updates the persistent checkplot dict using the
        changes from the UI, and then saves it back to disk. This could
        definitely be faster by just loading the checkplot into a server-wide
        shared dict or something.

        '''

        # if self.readonly is set, then don't accept any changes
        # return immediately with a 400
        if self.readonly:

            msg = "checkplotserver is in readonly mode. no updates allowed."
            resultdict = {'status':'error',
                          'message':msg,
                          'readonly':self.readonly,
                          'result':None}

            self.write(resultdict)
            raise tornado.web.Finish()

        # now try to update the contents
        try:

            self.cpfile = base64.b64decode(url_unescape(cpfile)).decode()
            cpcontents = self.get_argument('cpcontents', default=None)
            savetopng = self.get_argument('savetopng', default=None)

            if not self.cpfile or not cpcontents:

                msg = "did not receive a checkplot update payload"
                resultdict = {'status':'error',
                              'message':msg,
                              'readonly':self.readonly,
                              'result':None}

                self.write(resultdict)
                raise tornado.web.Finish()

            cpcontents = json.loads(cpcontents)

            # the only keys in cpdict that can updated from the UI are from
            # varinfo, objectinfo (objecttags), uifilters, and comments
            updated = {'varinfo': cpcontents['varinfo'],
                       'objectinfo':cpcontents['objectinfo'],
                       'comments':cpcontents['comments'],
                       'uifilters':cpcontents['uifilters']}

            # we need to reform the self.cpfile so it points to the full path
            cpfpath = os.path.join(
                os.path.abspath(os.path.dirname(self.cplistfile)),
                self.cpfile
            )

            LOGGER.info('loading %s...' % cpfpath)

            if not os.path.exists(cpfpath):

                msg = "couldn't find checkplot %s" % cpfpath
                LOGGER.error(msg)
                resultdict = {'status':'error',
                              'message':msg,
                              'readonly':self.readonly,
                              'result':None}

                self.write(resultdict)
                raise tornado.web.Finish()

            # dispatch the task
            updated = yield self.executor.submit(checkplot_pickle_update,
                                                 cpfpath, updated)

            # continue processing after this is done
            if updated:

                LOGGER.info('updated checkplot %s successfully' % updated)

                resultdict = {'status':'success',
                              'message':'checkplot update successful',
                              'readonly':self.readonly,
                              'result':{'checkplot':updated,
                                        'unixtime':utime.time(),
                                        'changes':cpcontents,
                                        'cpfpng': None}}

                # handle a savetopng trigger
                if savetopng:

                    cpfpng = os.path.abspath(cpfpath.replace('.pkl','.png'))
                    cpfpng = StrIO()
                    pngdone = yield self.executor.submit(
                        checkplot_pickle_to_png,
                        cpfpath, cpfpng
                    )

                    if pngdone is not None:

                        # we'll send back the PNG, which can then be loaded by
                        # the frontend and reformed into a download
                        pngdone.seek(0)
                        pngbin = pngdone.read()
                        pngb64 = base64.b64encode(pngbin)
                        pngdone.close()
                        del pngbin
                        resultdict['result']['cpfpng'] = pngb64

                    else:
                        resultdict['result']['cpfpng'] = ''

                self.write(resultdict)
                self.finish()

            else:
                LOGGER.error('could not handle checkplot update for %s: %s' %
                             (self.cpfile, cpcontents))
                msg = "checkplot update failed because of a backend error"
                resultdict = {'status':'error',
                              'message':msg,
                              'readonly':self.readonly,
                              'result':None}
                self.write(resultdict)
                self.finish()

        # if something goes wrong, inform the user
        except Exception:

            LOGGER.exception('could not handle checkplot update for %s: %s' %
                             (self.cpfile, cpcontents))
            msg = "checkplot update failed because of an exception"
            resultdict = {'status':'error',
                          'message':msg,
                          'readonly':self.readonly,
                          'result':None}
            self.write(resultdict)
            self.finish()